diff options
Diffstat (limited to 'src/libs/qmljs')
23 files changed, 253 insertions, 729 deletions
diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt new file mode 100644 index 0000000000..6a4ef362bc --- /dev/null +++ b/src/libs/qmljs/CMakeLists.txt @@ -0,0 +1,53 @@ +add_qtc_library(qmljs + DEPENDS ExtensionSystem Utils + PUBLIC_DEPENDS CPlusPlus Qt5::Widgets Qt5::Xml LanguageUtils + SOURCES + jsoncheck.cpp jsoncheck.h + parser/qmldirparser.cpp parser/qmldirparser_p.h + parser/qmlerror.cpp parser/qmlerror.h + parser/qmljsast.cpp parser/qmljsast_p.h + parser/qmljsastfwd_p.h + parser/qmljsastvisitor.cpp parser/qmljsastvisitor_p.h + parser/qmljsengine_p.cpp parser/qmljsengine_p.h + parser/qmljsglobal_p.h + parser/qmljsgrammar.cpp parser/qmljsgrammar_p.h + parser/qmljskeywords_p.h + parser/qmljslexer.cpp parser/qmljslexer_p.h + parser/qmljsmemorypool_p.h + parser/qmljsparser.cpp parser/qmljsparser_p.h + persistenttrie.cpp persistenttrie.h + qmljs_global.h + qmljsbind.cpp qmljsbind.h + qmljsbundle.cpp qmljsbundle.h + qmljscheck.cpp qmljscheck.h + qmljscodeformatter.cpp qmljscodeformatter.h + qmljscompletioncontextfinder.cpp qmljscompletioncontextfinder.h + qmljsconstants.h + qmljscontext.cpp qmljscontext.h + qmljsdialect.cpp qmljsdialect.h + qmljsdocument.cpp qmljsdocument.h + qmljsevaluate.cpp qmljsevaluate.h + qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h + qmljsicons.cpp qmljsicons.h + qmljsicontextpane.h + qmljsimportdependencies.cpp qmljsimportdependencies.h + qmljsindenter.cpp qmljsindenter.h + qmljsinterpreter.cpp qmljsinterpreter.h + qmljslineinfo.cpp qmljslineinfo.h + qmljslink.cpp qmljslink.h + qmljsmodelmanagerinterface.cpp qmljsmodelmanagerinterface.h + qmljsplugindumper.cpp qmljsplugindumper.h + qmljspropertyreader.cpp qmljspropertyreader.h + qmljsreformatter.cpp qmljsreformatter.h + qmljsrewriter.cpp qmljsrewriter.h + qmljsscanner.cpp qmljsscanner.h + qmljsscopeastpath.cpp qmljsscopeastpath.h + qmljsscopebuilder.cpp qmljsscopebuilder.h + qmljsscopechain.cpp qmljsscopechain.h + qmljssimplereader.cpp qmljssimplereader.h + qmljsstaticanalysismessage.cpp qmljsstaticanalysismessage.h + qmljstypedescriptionreader.cpp qmljstypedescriptionreader.h + qmljsutils.cpp qmljsutils.h + qmljsvalueowner.cpp qmljsvalueowner.h + qmljsviewercontext.cpp qmljsviewercontext.h +) diff --git a/src/libs/qmljs/parser/qmljslexer.cpp b/src/libs/qmljs/parser/qmljslexer.cpp index 19d367be1b..ab7a33917e 100644 --- a/src/libs/qmljs/parser/qmljslexer.cpp +++ b/src/libs/qmljs/parser/qmljslexer.cpp @@ -116,6 +116,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _tokenText.reserve(1024); _errorMessage.clear(); _tokenSpell = QStringRef(); + _rawString = QStringRef(); _codePtr = code.unicode(); _endPtr = _codePtr + code.length(); @@ -149,13 +150,20 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) void Lexer::scanChar() { - unsigned sequenceLength = isLineTerminatorSequence(); + if (_skipLinefeed) { + Q_ASSERT(*_codePtr == QLatin1Char('\n')); + ++_codePtr; + _skipLinefeed = false; + } _char = *_codePtr++; - if (sequenceLength == 2) - _char = *_codePtr++; - ++_currentColumnNumber; + if (isLineTerminator()) { + if (_char == QLatin1Char('\r')) { + if (_codePtr < _endPtr && *_codePtr == QLatin1Char('\n')) + _skipLinefeed = true; + _char = QLatin1Char('\n'); + } ++_currentLineNumber; _currentColumnNumber = 0; } @@ -232,6 +240,7 @@ int Lexer::lex() again: _tokenSpell = QStringRef(); + _rawString = QStringRef(); _tokenKind = scanToken(); _tokenLength = _codePtr - _tokenStartPtr - 1; @@ -807,12 +816,15 @@ int Lexer::scanString(ScanStringMode mode) QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode); bool multilineStringLiteral = false; - const QChar *startCode = _codePtr; + const QChar *startCode = _codePtr - 1; + // in case we just parsed a \r, we need to reset this flag to get things working + // correctly in the loop below and afterwards + _skipLinefeed = false; if (_engine) { while (_codePtr <= _endPtr) { - if (isLineTerminator() && quote != QLatin1Char('`')) { - if (qmlMode()) + if (isLineTerminator()) { + if ((quote == QLatin1Char('`') || qmlMode())) break; _errorCode = IllegalCharacter; _errorMessage = QCoreApplication::translate("QmlParser", "Stray newline in string literal"); @@ -822,7 +834,8 @@ int Lexer::scanString(ScanStringMode mode) } else if (_char == '$' && quote == QLatin1Char('`')) { break; } else if (_char == quote) { - _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); + _tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1); + _rawString = _tokenSpell; scanChar(); if (quote == QLatin1Char('`')) @@ -835,28 +848,36 @@ int Lexer::scanString(ScanStringMode mode) else return T_STRING_LITERAL; } - scanChar(); + // don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result + _char = *_codePtr++; + ++_currentColumnNumber; } } + // rewind by one char, so things gets scanned correctly + --_codePtr; + _validTokenText = true; - _tokenText.resize(0); - startCode--; - while (startCode != _codePtr - 1) - _tokenText += *startCode++; + _tokenText = QString(startCode, _codePtr - startCode); + + auto setRawString = [&](const QChar *end) { + QString raw(startCode, end - startCode - 1); + raw.replace(QLatin1String("\r\n"), QLatin1String("\n")); + raw.replace(QLatin1Char('\r'), QLatin1Char('\n')); + _rawString = _engine->newStringRef(raw); + }; + + scanChar(); while (_codePtr <= _endPtr) { - if (unsigned sequenceLength = isLineTerminatorSequence()) { - multilineStringLiteral = true; - _tokenText += _char; - if (sequenceLength == 2) - _tokenText += *_codePtr; - scanChar(); - } else if (_char == mode) { + if (_char == quote) { scanChar(); - if (_engine) + if (_engine) { _tokenSpell = _engine->newStringRef(_tokenText); + if (quote == QLatin1Char('`')) + setRawString(_codePtr - 1); + } if (quote == QLatin1Char('`')) _bracesCount = _outerTemplateBraceCount.pop(); @@ -871,8 +892,10 @@ int Lexer::scanString(ScanStringMode mode) scanChar(); scanChar(); _bracesCount = 1; - if (_engine) + if (_engine) { _tokenSpell = _engine->newStringRef(_tokenText); + setRawString(_codePtr - 2); + } return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE); } else if (_char == QLatin1Char('\\')) { diff --git a/src/libs/qmljs/parser/qmljslexer_p.h b/src/libs/qmljs/parser/qmljslexer_p.h index 39128b4409..5773606c39 100644 --- a/src/libs/qmljs/parser/qmljslexer_p.h +++ b/src/libs/qmljs/parser/qmljslexer_p.h @@ -146,6 +146,7 @@ public: int tokenStartColumn() const { return _tokenColumn; } inline QStringRef tokenSpell() const { return _tokenSpell; } + inline QStringRef rawString() const { return _rawString; } double tokenValue() const { return _tokenValue; } QString tokenText() const; @@ -198,6 +199,7 @@ private: QString _tokenText; QString _errorMessage; QStringRef _tokenSpell; + QStringRef _rawString; const QChar *_codePtr; const QChar *_endPtr; @@ -233,6 +235,7 @@ private: bool _followsClosingBrace; bool _delimited; bool _qmlMode; + bool _skipLinefeed = false; int _generatorLevel = 0; bool _staticIsKeyword = false; }; diff --git a/src/libs/qmljs/persistenttrie.cpp b/src/libs/qmljs/persistenttrie.cpp index f3be5a48ca..08c17dd164 100644 --- a/src/libs/qmljs/persistenttrie.cpp +++ b/src/libs/qmljs/persistenttrie.cpp @@ -545,7 +545,6 @@ QDebug &operator<<(QDebug &dbg, const Trie &trie) } Trie::Trie() {} Trie::Trie(const TrieNode::Ptr &trie) : trie(trie) {} -Trie::Trie(const Trie &o) : trie(o.trie){} QStringList Trie::complete(const QString &root, const QString &base, LookupFlags flags) const diff --git a/src/libs/qmljs/persistenttrie.h b/src/libs/qmljs/persistenttrie.h index 35ab652967..184525b13e 100644 --- a/src/libs/qmljs/persistenttrie.h +++ b/src/libs/qmljs/persistenttrie.h @@ -79,7 +79,6 @@ class QMLJS_EXPORT Trie public: Trie(); Trie(const TrieNode::Ptr &t); - Trie(const Trie &o); QStringList complete(const QString &root, const QString &base = QString(), LookupFlags flags = LookupFlags(CaseInsensitive|Partial)) const; diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index 2aa95a9f7a..47967446bf 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -36,7 +36,6 @@ HEADERS += \ $$PWD/jsoncheck.h \ $$PWD/qmljssimplereader.h \ $$PWD/persistenttrie.h \ - $$PWD/qmljsqrcparser.h \ $$PWD/qmljsconstants.h \ $$PWD/qmljsimportdependencies.h \ $$PWD/qmljsviewercontext.h \ @@ -70,7 +69,6 @@ SOURCES += \ $$PWD/jsoncheck.cpp \ $$PWD/qmljssimplereader.cpp \ $$PWD/persistenttrie.cpp \ - $$PWD/qmljsqrcparser.cpp \ $$PWD/qmljsimportdependencies.cpp \ $$PWD/qmljsviewercontext.cpp \ $$PWD/qmljsdialect.cpp diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs index ddfb4c200e..a2580cab1e 100644 --- a/src/libs/qmljs/qmljs.qbs +++ b/src/libs/qmljs/qmljs.qbs @@ -42,7 +42,6 @@ Project { "qmljsmodelmanagerinterface.cpp", "qmljsmodelmanagerinterface.h", "qmljsplugindumper.cpp", "qmljsplugindumper.h", "qmljspropertyreader.cpp", "qmljspropertyreader.h", - "qmljsqrcparser.cpp", "qmljsqrcparser.h", "qmljsreformatter.cpp", "qmljsreformatter.h", "qmljsrewriter.cpp", "qmljsrewriter.h", "qmljsscanner.cpp", "qmljsscanner.h", diff --git a/src/libs/qmljs/qmljsbundle.cpp b/src/libs/qmljs/qmljsbundle.cpp index ec46379f2c..9789d37492 100644 --- a/src/libs/qmljs/qmljsbundle.cpp +++ b/src/libs/qmljs/qmljsbundle.cpp @@ -35,11 +35,6 @@ namespace QmlJS { typedef PersistentTrie::Trie Trie; -QmlBundle::QmlBundle(const QmlBundle &o) - : m_name(o.m_name), m_searchPaths(o.searchPaths()), m_installPaths(o.installPaths()), - m_supportedImports(o.m_supportedImports), m_implicitImports(o.m_implicitImports) -{ } - QmlBundle::QmlBundle() { } diff --git a/src/libs/qmljs/qmljsbundle.h b/src/libs/qmljs/qmljsbundle.h index 0bf00ab755..8057abc6d7 100644 --- a/src/libs/qmljs/qmljsbundle.h +++ b/src/libs/qmljs/qmljsbundle.h @@ -51,7 +51,6 @@ class QMLJS_EXPORT QmlBundle { typedef PersistentTrie::Trie Trie; public: - QmlBundle(const QmlBundle &o); QmlBundle(); QmlBundle(const QString &name, const Trie &searchPaths, diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index ea43a4dec8..e5243830cd 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -384,6 +384,7 @@ protected: _possiblyUndeclaredUses.clear(); _seenNonDeclarationStatement = false; _formalParameterNames.clear(); + QTC_ASSERT(_block == 0, _block = 0); } void postVisit(Node *ast) @@ -399,8 +400,11 @@ protected: if (ast->name.isEmpty()) return false; const QString &name = ast->name.toString(); - if (!_declaredFunctions.contains(name) && !_declaredVariables.contains(name)) + if (!_declaredFunctions.contains(name) + && !(_declaredVariables.contains(name) + || _declaredBlockVariables.contains({name, _block}))) { _possiblyUndeclaredUses[name].append(ast->identifierToken); + } return false; } @@ -416,13 +420,26 @@ protected: if (ast->bindingIdentifier.isEmpty() || !ast->isVariableDeclaration()) return true; const QString &name = ast->bindingIdentifier.toString(); - - if (_formalParameterNames.contains(name)) + VariableScope scope = ast->scope; + if (_formalParameterNames.contains(name)) { addMessage(WarnAlreadyFormalParameter, ast->identifierToken, name); - else if (_declaredFunctions.contains(name)) + } else if (_declaredFunctions.contains(name)) { addMessage(WarnAlreadyFunction, ast->identifierToken, name); - else if (_declaredVariables.contains(name)) - addMessage(WarnDuplicateDeclaration, ast->identifierToken, name); + } else if (scope == VariableScope::Let || scope == VariableScope::Const) { + if (_declaredBlockVariables.contains({name, _block})) + addMessage(WarnDuplicateDeclaration, ast->identifierToken, name); + } else if (scope == VariableScope::Var) { + if (_declaredVariables.contains(name)) { + addMessage(WarnDuplicateDeclaration, ast->identifierToken, name); + } else { + for (auto k : _declaredBlockVariables.keys()) { + if (k.first == name) { + addMessage(WarnDuplicateDeclaration, ast->identifierToken, name); + break; + } + } + } + } if (_possiblyUndeclaredUses.contains(name)) { foreach (const SourceLocation &loc, _possiblyUndeclaredUses.value(name)) { @@ -430,7 +447,10 @@ protected: } _possiblyUndeclaredUses.remove(name); } - _declaredVariables[name] = ast; + if (scope == VariableScope::Let || scope == VariableScope::Const) + _declaredBlockVariables[{name, _block}] = ast; + else + _declaredVariables[name] = ast; return true; } @@ -451,7 +471,7 @@ protected: if (_formalParameterNames.contains(name)) addMessage(WarnAlreadyFormalParameter, ast->identifierToken, name); - else if (_declaredVariables.contains(name)) + else if (_declaredVariables.contains(name) || _declaredBlockVariables.contains({name, _block})) addMessage(WarnAlreadyVar, ast->identifierToken, name); else if (_declaredFunctions.contains(name)) addMessage(WarnDuplicateDeclaration, ast->identifierToken, name); @@ -469,6 +489,25 @@ protected: return false; } + bool visit(Block *) override + { + ++_block; + return true; + } + + void endVisit(Block *) override + { + auto it = _declaredBlockVariables.begin(); + auto end = _declaredBlockVariables.end(); + while (it != end) { + if (it.key().second == _block) + it = _declaredBlockVariables.erase(it); + else + ++it; + } + --_block; + } + private: void addMessage(Type type, const SourceLocation &loc, const QString &arg1 = QString()) { @@ -478,9 +517,11 @@ private: QList<Message> _messages; QStringList _formalParameterNames; QHash<QString, PatternElement *> _declaredVariables; + QHash<QPair<QString, uint>, PatternElement *> _declaredBlockVariables; QHash<QString, FunctionDeclaration *> _declaredFunctions; QHash<QString, QList<SourceLocation> > _possiblyUndeclaredUses; bool _seenNonDeclarationStatement; + uint _block = 0; }; class IdsThatShouldNotBeUsedInDesigner : public QStringList @@ -1635,7 +1676,7 @@ bool Check::visit(CallExpression *ast) if (!whiteListedFunction && !isMathFunction && !isDateFunction && !isDirectInConnectionsScope) addMessage(ErrFunctionsNotSupportedInQmlUi, location); - static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"}; + static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "Array", "QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"}; if (!name.isEmpty() && name.at(0).isUpper() && !globalFunctions.contains(name)) { addMessage(WarnExpectedNewWithUppercaseFunction, location); diff --git a/src/libs/qmljs/qmljsdialect.cpp b/src/libs/qmljs/qmljsdialect.cpp index 0cb463b2e5..cfe71b2265 100644 --- a/src/libs/qmljs/qmljsdialect.cpp +++ b/src/libs/qmljs/qmljsdialect.cpp @@ -30,7 +30,6 @@ namespace QmlJS { - bool Dialect::isQmlLikeLanguage() const { switch (m_dialect) { @@ -233,7 +232,7 @@ QDebug operator << (QDebug &dbg, const Dialect &dialect) return dbg; } -PathAndLanguage::PathAndLanguage(const Utils::FileName &path, Dialect language) +PathAndLanguage::PathAndLanguage(const Utils::FilePath &path, Dialect language) : m_path(path), m_language(language) { } @@ -294,11 +293,11 @@ void PathsAndLanguages::compact() return; int oldCompactionPlace = 0; - Utils::FileName oldPath = m_list.first().path(); + Utils::FilePath oldPath = m_list.first().path(); QList<PathAndLanguage> compactedList; bool restrictFailed = false; for (int i = 1; i < m_list.length(); ++i) { - Utils::FileName newPath = m_list.at(i).path(); + Utils::FilePath newPath = m_list.at(i).path(); if (newPath == oldPath) { int newCompactionPlace = i - 1; compactedList << m_list.mid(oldCompactionPlace, newCompactionPlace - oldCompactionPlace); diff --git a/src/libs/qmljs/qmljsdialect.h b/src/libs/qmljs/qmljsdialect.h index 8d1ca30deb..230642e145 100644 --- a/src/libs/qmljs/qmljsdialect.h +++ b/src/libs/qmljs/qmljsdialect.h @@ -78,11 +78,8 @@ QMLJS_EXPORT QDebug operator << (QDebug &dbg, const Dialect &dialect); class QMLJS_EXPORT PathAndLanguage { public: - PathAndLanguage(const Utils::FileName &path = Utils::FileName(), Dialect language = Dialect::AnyLanguage); - PathAndLanguage(const PathAndLanguage &o) - : m_path(o.path()), m_language(o.language()) - { } - Utils::FileName path() const { + PathAndLanguage(const Utils::FilePath &path = Utils::FilePath(), Dialect language = Dialect::AnyLanguage); + Utils::FilePath path() const { return m_path; } Dialect language() const { @@ -91,7 +88,7 @@ public: bool operator ==(const PathAndLanguage &other) const; bool operator < (const PathAndLanguage &other) const; private: - Utils::FileName m_path; + Utils::FilePath m_path; Dialect m_language; }; @@ -130,11 +127,8 @@ public: explicit PathsAndLanguages(const QList<PathAndLanguage> &list) : m_list(list) { } - PathsAndLanguages(const PathsAndLanguages &o) - : m_list(o.m_list) - { } - bool maybeInsert(const Utils::FileName &path, Dialect language = Dialect::AnyLanguage) { + bool maybeInsert(const Utils::FilePath &path, Dialect language = Dialect::AnyLanguage) { return maybeInsert(PathAndLanguage(path, language)); } diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index a4551b1c1c..6ebe5b0b2f 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -458,14 +458,6 @@ Snapshot::~Snapshot() { } -Snapshot::Snapshot(const Snapshot &o) - : _documents(o._documents), - _documentsByPath(o._documentsByPath), - _libraries(o._libraries), - _dependencies(o._dependencies) -{ -} - void Snapshot::insert(const Document::Ptr &document, bool allowInvalid) { if (document && (allowInvalid || document->qmlProgram() || document->jsProgram())) { diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 4029373992..82df720776 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -230,7 +230,6 @@ class QMLJS_EXPORT Snapshot public: Snapshot(); - Snapshot(const Snapshot &o); ~Snapshot(); typedef Base::iterator iterator; diff --git a/src/libs/qmljs/qmljsimportdependencies.cpp b/src/libs/qmljs/qmljsimportdependencies.cpp index adc9c9ff57..afa8d58b3a 100644 --- a/src/libs/qmljs/qmljsimportdependencies.cpp +++ b/src/libs/qmljs/qmljsimportdependencies.cpp @@ -25,9 +25,9 @@ #include "qmljsimportdependencies.h" #include "qmljsinterpreter.h" -#include "qmljsqrcparser.h" #include "qmljsviewercontext.h" +#include <utils/qrcparser.h> #include <utils/qtcassert.h> #include <QCryptographicHash> @@ -139,10 +139,10 @@ ImportKey::ImportKey(ImportType::Enum type, const QString &path, int majorVersio break; case ImportType::File: case ImportType::QrcFile: - splitPath = QrcParser::normalizedQrcFilePath(path).split(QLatin1Char('/')); + splitPath = Utils::QrcParser::normalizedQrcFilePath(path).split(QLatin1Char('/')); break; case ImportType::QrcDirectory: - splitPath = QrcParser::normalizedQrcDirectoryPath(path).split(QLatin1Char('/')); + splitPath = Utils::QrcParser::normalizedQrcDirectoryPath(path).split(QLatin1Char('/')); if (splitPath.length() > 1 && splitPath.last().isEmpty()) splitPath.removeLast(); break; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 03083501b6..91f195f8fc 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -2340,6 +2340,16 @@ Import::Import(const Import &other) valid(other.valid), used(false) { } +Import &Import::operator=(const Import &other) +{ + object = other.object; + info = other.info; + libraryPath = other.libraryPath; + valid = other.valid; + used = false; + return *this; +} + TypeScope::TypeScope(const Imports *imports, ValueOwner *valueOwner) : ObjectValue(valueOwner) , m_imports(imports) diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 9da1aa8635..f2e953180c 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -1038,6 +1038,7 @@ class QMLJS_EXPORT Import { public: Import(); Import(const Import &other); + Import &operator=(const Import &other); // const! ObjectValue *object; diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 737ec6cc46..1acdb68ed9 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -30,9 +30,10 @@ #include "qmljsbind.h" #include "qmljsutils.h" #include "qmljsmodelmanagerinterface.h" -#include "qmljsqrcparser.h" #include "qmljsconstants.h" +#include <utils/qrcparser.h> + #include <QDir> using namespace LanguageUtils; @@ -557,7 +558,7 @@ void LinkPrivate::loadImplicitDirectoryImports(Imports *imports, Document::Ptr d foreach (const QString &path, ModelManagerInterface::instance()->qrcPathsForFile(doc->fileName())) { processImport(ImportInfo::qrcDirectoryImport( - QrcParser::qrcDirectoryPathForQrcFilePath(path))); + Utils::QrcParser::qrcDirectoryPathForQrcFilePath(path))); } } diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index 82d4da3f4a..6bb1bc8ff9 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -52,6 +52,8 @@ #include <stdio.h> +using namespace Utils; + namespace QmlJS { QMLJS_EXPORT Q_LOGGING_CATEGORY(qmljsLog, "qtc.qmljs.common", QtWarningMsg) @@ -1098,14 +1100,14 @@ void ModelManagerInterface::updateImportPaths() PathAndLanguage pAndL = iPaths.at(i); const QString canonicalPath = pAndL.path().toFileInfo().canonicalFilePath(); if (!canonicalPath.isEmpty()) - allImportPaths.maybeInsert(Utils::FileName::fromString(canonicalPath), + allImportPaths.maybeInsert(Utils::FilePath::fromString(canonicalPath), pAndL.language()); } } while (vCtxsIter.hasNext()) { vCtxsIter.next(); foreach (const QString &path, vCtxsIter.value().paths) - allImportPaths.maybeInsert(Utils::FileName::fromString(path), vCtxsIter.value().language); + allImportPaths.maybeInsert(Utils::FilePath::fromString(path), vCtxsIter.value().language); } pInfoIter.toFront(); while (pInfoIter.hasNext()) { @@ -1116,7 +1118,7 @@ void ModelManagerInterface::updateImportPaths() .searchPaths().stringList()) { const QString canonicalPath = QFileInfo(path).canonicalFilePath(); if (!canonicalPath.isEmpty()) - allImportPaths.maybeInsert(Utils::FileName::fromString(canonicalPath), l); + allImportPaths.maybeInsert(Utils::FilePath::fromString(canonicalPath), l); } } } @@ -1125,16 +1127,16 @@ void ModelManagerInterface::updateImportPaths() pInfoIter.next(); QString pathAtt = pInfoIter.value().qtQmlPath; if (!pathAtt.isEmpty()) - allImportPaths.maybeInsert(Utils::FileName::fromString(pathAtt), Dialect::QmlQtQuick2); + allImportPaths.maybeInsert(Utils::FilePath::fromString(pathAtt), Dialect::QmlQtQuick2); } { QString pathAtt = defaultProjectInfo().qtQmlPath; if (!pathAtt.isEmpty()) - allImportPaths.maybeInsert(Utils::FileName::fromString(pathAtt), Dialect::QmlQtQuick2); + allImportPaths.maybeInsert(Utils::FilePath::fromString(pathAtt), Dialect::QmlQtQuick2); } foreach (const QString &path, m_defaultImportPaths) - allImportPaths.maybeInsert(Utils::FileName::fromString(path), Dialect::Qml); + allImportPaths.maybeInsert(Utils::FilePath::fromString(path), Dialect::Qml); allImportPaths.compact(); { diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index 5de25eead3..cd36cbca2f 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -28,11 +28,11 @@ #include "qmljs_global.h" #include "qmljsbundle.h" #include "qmljsdocument.h" -#include "qmljsqrcparser.h" #include "qmljsdialect.h" #include <cplusplus/CppDocument.h> #include <utils/environment.h> +#include <utils/qrcparser.h> #include <QFuture> #include <QHash> @@ -255,7 +255,7 @@ private: void cleanupFutures(); void iterateQrcFiles(ProjectExplorer::Project *project, QrcResourceSelector resources, - std::function<void(QrcParser::ConstPtr)> callback); + std::function<void(Utils::QrcParser::ConstPtr)> callback); mutable QMutex m_mutex; QmlJS::Snapshot m_validSnapshot; @@ -272,7 +272,7 @@ private: QTimer *m_asyncResetTimer = nullptr; QHash<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments; QFuture<void> m_cppQmlTypesUpdater; - QrcCache m_qrcCache; + Utils::QrcCache m_qrcCache; QHash<QString, QString> m_qrcContents; CppDataHash m_cppDataHash; diff --git a/src/libs/qmljs/qmljsqrcparser.cpp b/src/libs/qmljs/qmljsqrcparser.cpp deleted file mode 100644 index 0a68ebe2fb..0000000000 --- a/src/libs/qmljs/qmljsqrcparser.cpp +++ /dev/null @@ -1,556 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "qmljsqrcparser.h" -#include "qmljsconstants.h" -#include <QFile> -#include <QDir> -#include <QFileInfo> -#include <QStringList> -#include <QDomDocument> -#include <QLocale> -#include <QMutex> -#include <QSet> -#include <QMutexLocker> -#include <QMultiHash> -#include <QCoreApplication> -#include <utils/qtcassert.h> - -namespace QmlJS { - -namespace Internal { -/*! - * \class QrcParser - * \brief Parses one or more qrc files, and keeps their content cached - * - * A Qrc resource contains files read from the filesystem but organized in a possibly different way. - * - * To easily describe that with a simple structure we use a map from qrc paths to the paths in the - * filesystem. - * By using a map we can easily find all qrc paths that start with a given prefix, and thus loop - * on a qrc directory. - * - * Qrc files also support languages, those are mapped to a prefix of the qrc path. - * For example the french /image/bla.png (lang=fr) will have the path "fr/image/bla.png". - * The empty language represent the default resource. - * Languages are looked up using the locale uiLanguages() property - * - * For a single qrc a given path maps to a single file, but when one has multiple - * (platform specific exclusive) qrc files, then multiple files match, so QStringList are used. - * - * Especially the collect* functions are thought as low level interface. - */ -class QrcParserPrivate -{ - Q_DECLARE_TR_FUNCTIONS(QmlJS::QrcParser) -public: - typedef QMap<QString,QStringList> SMap; - QrcParserPrivate(QrcParser *q); - bool parseFile(const QString &path, const QString &contents); - QString firstFileAtPath(const QString &path, const QLocale &locale) const; - void collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale = 0) const; - bool hasDirAtPath(const QString &path, const QLocale *locale = 0) const; - void collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs = false, - const QLocale *locale = 0) const; - void collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *res, - const QLocale *locale = 0) const; - - QStringList errorMessages() const; - QStringList languages() const; -private: - static QString fixPrefix(const QString &prefix); - QStringList allUiLanguages(const QLocale *locale) const; - - SMap m_resources; - SMap m_files; - QStringList m_languages; - QStringList m_errorMessages; -}; - -class QrcCachePrivate -{ - Q_DECLARE_TR_FUNCTIONS(QmlJS::QrcCachePrivate) -public: - QrcCachePrivate(QrcCache *q); - QrcParser::Ptr addPath(const QString &path, const QString &contents); - void removePath(const QString &path); - QrcParser::Ptr updatePath(const QString &path, const QString &contents); - QrcParser::Ptr parsedPath(const QString &path); - void clear(); -private: - QHash<QString, QPair<QrcParser::Ptr,int> > m_cache; - QMutex m_mutex; -}; -} // namespace Internal - -/*! \brief normalizes the path to a file in a qrc resource by dropping the "qrc:/" or ":" and - * any extra slashes at the beginning - */ -QString QrcParser::normalizedQrcFilePath(const QString &path) { - QString normPath = path; - int endPrefix = 0; - if (path.startsWith(QLatin1String("qrc:/"))) - endPrefix = 4; - else if (path.startsWith(QLatin1String(":/"))) - endPrefix = 1; - if (endPrefix < path.size() && path.at(endPrefix) == QLatin1Char('/')) - while (endPrefix + 1 < path.size() && path.at(endPrefix+1) == QLatin1Char('/')) - ++endPrefix; - normPath = path.right(path.size()-endPrefix); - if (!normPath.startsWith(QLatin1Char('/'))) - normPath.insert(0, QLatin1Char('/')); - return normPath; -} - -/*! \brief normalizes the path to a directory in a qrc resource by dropping the "qrc:/" or ":" and - * any extra slashes at the beginning, and ensuring it ends with a slash - */ -QString QrcParser::normalizedQrcDirectoryPath(const QString &path) { - QString normPath = normalizedQrcFilePath(path); - if (!normPath.endsWith(QLatin1Char('/'))) - normPath.append(QLatin1Char('/')); - return normPath; -} - -QString QrcParser::qrcDirectoryPathForQrcFilePath(const QString &file) -{ - return file.left(file.lastIndexOf(QLatin1Char('/'))); -} - -QrcParser::QrcParser() -{ - d = new Internal::QrcParserPrivate(this); -} - -QrcParser::~QrcParser() -{ - delete d; -} - -bool QrcParser::parseFile(const QString &path, const QString &contents) -{ - return d->parseFile(path, contents); -} - -/*! \brief returns fs path of the first (active) file at the given qrc path - */ -QString QrcParser::firstFileAtPath(const QString &path, const QLocale &locale) const -{ - return d->firstFileAtPath(path, locale); -} - -/*! \brief adds al the fs paths for the given qrc path to *res - * If locale is null all possible files are added, otherwise just the first match - * using that locale. - */ -void QrcParser::collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale) const -{ - d->collectFilesAtPath(path, res, locale); -} - -/*! \brief returns true if the given path is a non empty directory - */ -bool QrcParser::hasDirAtPath(const QString &path, const QLocale *locale) const -{ - return d->hasDirAtPath(path, locale); -} - -/*! \brief adds the directory contents of the given qrc path to res - * - * adds the qrcFileName => fs paths associations contained in the given qrc path - * to res. If addDirs is true directories are also added. - * If locale is null all possible files are added, otherwise just the first match - * using that locale. - */ -void QrcParser::collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs, - const QLocale *locale) const -{ - d->collectFilesInPath(path, res, addDirs, locale); -} - -void QrcParser::collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *res, - const QLocale *locale) const -{ - d->collectResourceFilesForSourceFile(sourceFile, res, locale); -} - -/*! \brief returns the errors found while parsing - */ -QStringList QrcParser::errorMessages() const -{ - return d->errorMessages(); -} - -/*! \brief returns all languages used in this qrc resource - */ -QStringList QrcParser::languages() const -{ - return d->languages(); -} - -/*! \brief if the contents are valid - */ -bool QrcParser::isValid() const -{ - return errorMessages().isEmpty(); -} - -QrcParser::Ptr QrcParser::parseQrcFile(const QString &path, const QString &contents) -{ - Ptr res(new QrcParser); - if (!path.isEmpty()) - res->parseFile(path, contents); - return res; -} - -// ---------------- - -QrcCache::QrcCache() -{ - d = new Internal::QrcCachePrivate(this); -} - -QrcCache::~QrcCache() -{ - delete d; -} - -QrcParser::ConstPtr QrcCache::addPath(const QString &path, const QString &contents) -{ - return d->addPath(path, contents); -} - -void QrcCache::removePath(const QString &path) -{ - d->removePath(path); -} - -QrcParser::ConstPtr QrcCache::updatePath(const QString &path, const QString &contents) -{ - return d->updatePath(path, contents); -} - -QrcParser::ConstPtr QrcCache::parsedPath(const QString &path) -{ - return d->parsedPath(path); -} - -void QrcCache::clear() -{ - d->clear(); -} - -// -------------------- - -namespace Internal { - -QrcParserPrivate::QrcParserPrivate(QrcParser *) -{ } - -bool QrcParserPrivate::parseFile(const QString &path, const QString &contents) -{ - QDomDocument doc; - QDir baseDir(QFileInfo(path).path()); - - if (contents.isEmpty()) { - // Regular file - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - m_errorMessages.append(file.errorString()); - return false; - } - - QString error_msg; - int error_line, error_col; - if (!doc.setContent(&file, &error_msg, &error_line, &error_col)) { - m_errorMessages.append(tr("XML error on line %1, col %2: %3") - .arg(error_line).arg(error_col).arg(error_msg)); - return false; - } - } else { - // Virtual file from qmake evaluator - QString error_msg; - int error_line, error_col; - if (!doc.setContent(contents, &error_msg, &error_line, &error_col)) { - m_errorMessages.append(tr("XML error on line %1, col %2: %3") - .arg(error_line).arg(error_col).arg(error_msg)); - return false; - } - } - - QDomElement root = doc.firstChildElement(QLatin1String("RCC")); - if (root.isNull()) { - m_errorMessages.append(tr("The <RCC> root element is missing.")); - return false; - } - - QDomElement relt = root.firstChildElement(QLatin1String("qresource")); - for (; !relt.isNull(); relt = relt.nextSiblingElement(QLatin1String("qresource"))) { - - QString prefix = fixPrefix(relt.attribute(QLatin1String("prefix"))); - const QString language = relt.attribute(QLatin1String("lang")); - if (!m_languages.contains(language)) - m_languages.append(language); - - QDomElement felt = relt.firstChildElement(QLatin1String("file")); - for (; !felt.isNull(); felt = felt.nextSiblingElement(QLatin1String("file"))) { - const QString fileName = felt.text(); - const QString alias = felt.attribute(QLatin1String("alias")); - QString filePath = baseDir.absoluteFilePath(fileName); - QString accessPath; - if (!alias.isEmpty()) - accessPath = language + prefix + alias; - else - accessPath = language + prefix + fileName; - QStringList &resources = m_resources[accessPath]; - if (!resources.contains(filePath)) - resources.append(filePath); - QStringList &files = m_files[filePath]; - if (!files.contains(accessPath)) - files.append(accessPath); - } - } - return true; -} - -// path is assumed to be a normalized absolute path -QString QrcParserPrivate::firstFileAtPath(const QString &path, const QLocale &locale) const -{ - QTC_CHECK(path.startsWith(QLatin1Char('/'))); - QStringList langs = allUiLanguages(&locale); - foreach (const QString &language, langs) { - if (m_languages.contains(language)) { - SMap::const_iterator res = m_resources.find(language + path); - if (res != m_resources.end()) - return res.value().at(0); - } - } - return QString(); -} - -void QrcParserPrivate::collectFilesAtPath(const QString &path, QStringList *files, - const QLocale *locale) const -{ - QTC_CHECK(path.startsWith(QLatin1Char('/'))); - QStringList langs = allUiLanguages(locale); - foreach (const QString &language, langs) { - if (m_languages.contains(language)) { - SMap::const_iterator res = m_resources.find(language + path); - if (res != m_resources.end()) - (*files) << res.value(); - } - } -} - -// path is expected to be normalized and start and end with a slash -bool QrcParserPrivate::hasDirAtPath(const QString &path, const QLocale *locale) const -{ - QTC_CHECK(path.startsWith(QLatin1Char('/'))); - QTC_CHECK(path.endsWith(QLatin1Char('/'))); - QStringList langs = allUiLanguages(locale); - foreach (const QString &language, langs) { - if (m_languages.contains(language)) { - QString key = language + path; - SMap::const_iterator res = m_resources.lowerBound(key); - if (res != m_resources.end() && res.key().startsWith(key)) - return true; - } - } - return false; -} - -void QrcParserPrivate::collectFilesInPath(const QString &path, QMap<QString,QStringList> *contents, - bool addDirs, const QLocale *locale) const -{ - QTC_CHECK(path.startsWith(QLatin1Char('/'))); - QTC_CHECK(path.endsWith(QLatin1Char('/'))); - SMap::const_iterator end = m_resources.end(); - QStringList langs = allUiLanguages(locale); - foreach (const QString &language, langs) { - QString key = language + path; - SMap::const_iterator res = m_resources.lowerBound(key); - while (res != end && res.key().startsWith(key)) { - const QString &actualKey = res.key(); - int endDir = actualKey.indexOf(QLatin1Char('/'), key.size()); - if (endDir == -1) { - QString fileName = res.key().right(res.key().size()-key.size()); - QStringList &els = (*contents)[fileName]; - foreach (const QString &val, res.value()) - if (!els.contains(val)) - els << val; - ++res; - } else { - QString dirName = res.key().mid(key.size(), endDir - key.size() + 1); - if (addDirs) - contents->insert(dirName, QStringList()); - QString key2 = key + dirName; - do { - ++res; - } while (res != end && res.key().startsWith(key2)); - } - } - } -} - -void QrcParserPrivate::collectResourceFilesForSourceFile(const QString &sourceFile, - QStringList *results, - const QLocale *locale) const -{ - // TODO: use FileName from fileutils for file paths - - QStringList langs = allUiLanguages(locale); - SMap::const_iterator file = m_files.find(sourceFile); - if (file == m_files.end()) - return; - foreach (const QString &resource, file.value()) { - foreach (const QString &language, langs) { - if (resource.startsWith(language) && !results->contains(resource)) - results->append(resource); - } - } -} - -QStringList QrcParserPrivate::errorMessages() const -{ - return m_errorMessages; -} - -QStringList QrcParserPrivate::languages() const -{ - return m_languages; -} - -QString QrcParserPrivate::fixPrefix(const QString &prefix) -{ - const QChar slash = QLatin1Char('/'); - QString result = QString(slash); - for (int i = 0; i < prefix.size(); ++i) { - const QChar c = prefix.at(i); - if (c == slash && result.at(result.size() - 1) == slash) - continue; - result.append(c); - } - - if (!result.endsWith(slash)) - result.append(slash); - - return result; -} - -QStringList QrcParserPrivate::allUiLanguages(const QLocale *locale) const -{ - if (!locale) - return languages(); - QStringList langs = locale->uiLanguages(); - foreach (const QString &language, langs) { // qt4 support - if (language.contains(QLatin1Char('_')) || language.contains(QLatin1Char('-'))) { - QStringList splits = QString(language).replace(QLatin1Char('_'), QLatin1Char('-')) - .split(QLatin1Char('-')); - if (splits.size() > 1 && !langs.contains(splits.at(0))) - langs.append(splits.at(0)); - } - } - if (!langs.contains(QString())) - langs.append(QString()); - return langs; -} - -// ---------------- - -QrcCachePrivate::QrcCachePrivate(QrcCache *) -{ } - -QrcParser::Ptr QrcCachePrivate::addPath(const QString &path, const QString &contents) -{ - QPair<QrcParser::Ptr,int> currentValue; - { - QMutexLocker l(&m_mutex); - currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); - currentValue.second += 1; - if (currentValue.second > 1) { - m_cache.insert(path, currentValue); - return currentValue.first; - } - } - QrcParser::Ptr newParser = QrcParser::parseQrcFile(path, contents); - if (!newParser->isValid()) - qCWarning(qmljsLog) << "adding invalid qrc " << path << " to the cache:" << newParser->errorMessages(); - { - QMutexLocker l(&m_mutex); - QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); - if (currentValue.first.isNull()) - currentValue.first = newParser; - currentValue.second += 1; - m_cache.insert(path, currentValue); - return currentValue.first; - } -} - -void QrcCachePrivate::removePath(const QString &path) -{ - QPair<QrcParser::Ptr,int> currentValue; - { - QMutexLocker l(&m_mutex); - currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); - if (currentValue.second == 1) { - m_cache.remove(path); - } else if (currentValue.second > 1) { - currentValue.second -= 1; - m_cache.insert(path, currentValue); - } else { - QTC_CHECK(!m_cache.contains(path)); - } - } -} - -QrcParser::Ptr QrcCachePrivate::updatePath(const QString &path, const QString &contents) -{ - QrcParser::Ptr newParser = QrcParser::parseQrcFile(path, contents); - { - QMutexLocker l(&m_mutex); - QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); - currentValue.first = newParser; - if (currentValue.second == 0) - currentValue.second = 1; // add qrc files that are not in the resources of a project - m_cache.insert(path, currentValue); - return currentValue.first; - } -} - -QrcParser::Ptr QrcCachePrivate::parsedPath(const QString &path) -{ - QMutexLocker l(&m_mutex); - QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); - return currentValue.first; -} - -void QrcCachePrivate::clear() -{ - QMutexLocker l(&m_mutex); - m_cache.clear(); -} - -} // namespace Internal -} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsqrcparser.h b/src/libs/qmljs/qmljsqrcparser.h deleted file mode 100644 index 6973fa6d39..0000000000 --- a/src/libs/qmljs/qmljsqrcparser.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once -#include "qmljs_global.h" - -#include <QMap> -#include <QSharedPointer> -#include <QString> -#include <QStringList> - -QT_FORWARD_DECLARE_CLASS(QLocale) - -namespace QmlJS { - -namespace Internal { -class QrcParserPrivate; -class QrcCachePrivate; -} - -class QMLJS_EXPORT QrcParser -{ -public: - typedef QSharedPointer<QrcParser> Ptr; - typedef QSharedPointer<const QrcParser> ConstPtr; - ~QrcParser(); - bool parseFile(const QString &path, const QString &contents); - QString firstFileAtPath(const QString &path, const QLocale &locale) const; - void collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale = 0) const; - bool hasDirAtPath(const QString &path, const QLocale *locale = 0) const; - void collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs = false, - const QLocale *locale = 0) const; - void collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *results, - const QLocale *locale = 0) const; - - QStringList errorMessages() const; - QStringList languages() const; - bool isValid() const; - - static Ptr parseQrcFile(const QString &path, const QString &contents); - static QString normalizedQrcFilePath(const QString &path); - static QString normalizedQrcDirectoryPath(const QString &path); - static QString qrcDirectoryPathForQrcFilePath(const QString &file); -private: - QrcParser(); - QrcParser(const QrcParser &); - Internal::QrcParserPrivate *d; -}; - -class QMLJS_EXPORT QrcCache -{ -public: - QrcCache(); - ~QrcCache(); - QrcParser::ConstPtr addPath(const QString &path, const QString &contents); - void removePath(const QString &path); - QrcParser::ConstPtr updatePath(const QString &path, const QString &contents); - QrcParser::ConstPtr parsedPath(const QString &path); - void clear(); -private: - Internal::QrcCachePrivate *d; -}; -} diff --git a/src/libs/qmljs/qmljsreformatter.cpp b/src/libs/qmljs/qmljsreformatter.cpp index e95ff2f339..d60d6179f3 100644 --- a/src/libs/qmljs/qmljsreformatter.cpp +++ b/src/libs/qmljs/qmljsreformatter.cpp @@ -96,6 +96,7 @@ class Rewriter : protected Visitor int _lastNewlineOffset = -1; bool _hadEmptyLine = false; int _binaryExpDepth = 0; + bool _hasOpenComment = false; public: Rewriter(Document::Ptr doc) @@ -201,6 +202,9 @@ protected: void out(const QString &str, const SourceLocation &lastLoc = SourceLocation()) { + if (_hasOpenComment) { + newLine(); + } if (lastLoc.isValid()) { QList<SourceLocation> comments = _doc->engine()->comments(); for (; _nextComment < comments.size(); ++_nextComment) { @@ -371,6 +375,7 @@ protected: { // if preceded by a newline, it's an empty line! _hadEmptyLine = _line.trimmed().isEmpty(); + _hasOpenComment = false; // if the preceding line wasn't empty, reindent etc. if (!_hadEmptyLine) { @@ -524,6 +529,7 @@ protected: out(" "); out(toString(nextCommentLoc)); + _hasOpenComment = true; } } } @@ -531,6 +537,39 @@ protected: bool visit(UiPragma *ast) override { out("pragma ", ast->pragmaToken); + out(ast->name.toString()); + newLine(); + return false; + } + + bool visit(UiEnumDeclaration *ast) override + { + out(ast->enumToken); + out(" "); + out(ast->name.toString()); + out(" "); + out("{"); // TODO: out(ast->lbraceToken); + newLine(); + + accept(ast->members); + + out(ast->rbraceToken); + return false; + } + + bool visit(UiEnumMemberList *list) override + { + for (UiEnumMemberList *it = list; it; it = it->next) { + out(it->memberToken); + if (it->valueToken.isValid()) { + out(" = "); + out(it->valueToken); + } + if (it->next) { + out(","); + } + newLine(); + } return false; } @@ -563,9 +602,10 @@ protected: bool visit(UiObjectInitializer *ast) override { out(ast->lbraceToken); - if (ast->members) + if (ast->members) { lnAcceptIndented(ast->members); - newLine(); + newLine(); + } out(ast->rbraceToken); return false; } @@ -593,10 +633,10 @@ protected: if (!ast->typeModifier.isNull()) { out(ast->typeModifierToken); out("<"); - out(ast->typeToken); + accept(ast->memberType); out(">"); } else { - out(ast->typeToken); + accept(ast->memberType); } out(" "); if (ast->statement) { @@ -677,8 +717,10 @@ protected: bool visit(ObjectPattern *ast) override { out(ast->lbraceToken); - lnAcceptIndented(ast->properties); - newLine(); + if (ast->properties) { + lnAcceptIndented(ast->properties); + newLine(); + } out(ast->rbraceToken); return false; } @@ -914,14 +956,23 @@ protected: bool visit(VariableStatement *ast) override { - out("var ", ast->declarationKindToken); + out(ast->declarationKindToken); + out(" "); accept(ast->declarations); return false; } bool visit(PatternElement *ast) override { - + if (ast->isForDeclaration) { + if (ast->scope == VariableScope::Var) { + out("var "); + } else if (ast->scope == VariableScope::Let) { + out("let "); + } else if (ast->scope == VariableScope::Const) { + out("const "); + } + } out(ast->identifierToken); if (ast->initializer) { if (ast->isVariableDeclaration()) @@ -985,7 +1036,12 @@ protected: out(ast->forToken); out(" "); out(ast->lparenToken); - accept(ast->initialiser); + if (ast->initialiser) { + accept(ast->initialiser); + } else if (ast->declarations) { + out("var "); + accept(ast->declarations); + } out("; ", ast->firstSemicolonToken); accept(ast->condition); out("; ", ast->secondSemicolonToken); @@ -1273,6 +1329,8 @@ protected: { for (FormalParameterList *it = ast; it; it = it->next) { out(it->element->bindingIdentifier.toString()); // TODO + if (it->next) + out(", "); } return false; } |