diff --git a/dist/changes-3.5.0.md b/dist/changes-3.5.0.md index a8d94d9ae40..04d7e8bca1d 100644 --- a/dist/changes-3.5.0.md +++ b/dist/changes-3.5.0.md @@ -127,7 +127,7 @@ FakeVim Todo -* Added option to excluding file patterns from parsing +* Added option to exclude file patterns from parsing Beautifier diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index ac193363dbb..27b5a1b0b23 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -83,7 +83,7 @@ #include #include -#include +#include namespace { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index b548668a16f..3b2d2a0f6c3 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -63,7 +63,7 @@ #include "dummycontextobject.h" -#include +#include namespace QmlDesigner { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index e7547e49489..8f535c89003 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -37,7 +37,7 @@ #include "qt5rendernodeinstanceserver.h" #include "qt5testnodeinstanceserver.h" -#include +#include #if defined(Q_OS_UNIX) #include diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index 20bc738a3d0..85533d6e9f3 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include #include diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index b6836404aa3..0aa0dcc3395 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -34,7 +34,7 @@ #include #include "nodeinstanceserver.h" -#include "designersupport.h" +#include QT_BEGIN_NAMESPACE class QQuickItem; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index e9f587b7efd..b550e61a12d 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -36,7 +36,7 @@ #include "removesharedmemorycommand.h" #include #include -#include +#include namespace QmlDesigner { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 98efd1f0576..6a53f4d9e9c 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -63,7 +63,7 @@ #include "dummycontextobject.h" -#include +#include namespace QmlDesigner { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp index 063fb9c2876..d80abae6fb8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp @@ -65,7 +65,7 @@ #include "dummycontextobject.h" -#include +#include namespace QmlDesigner { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h index 22d2d06e8e4..4c96c18be0e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h @@ -36,7 +36,7 @@ #include "objectnodeinstance.h" #include -#include +#include namespace QmlDesigner { namespace Internal { diff --git a/share/qtcreator/qml/qmlpuppet/qmlprivategate/designersupportdelegate.h b/share/qtcreator/qml/qmlpuppet/qmlprivategate/designersupportdelegate.h new file mode 100644 index 00000000000..05ed2ec5b76 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/qmlprivategate/designersupportdelegate.h @@ -0,0 +1,5 @@ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) +#include +#else +#include +#endif diff --git a/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate.cpp b/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate.cpp index 2ed682ae986..5c972767f95 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate.cpp +++ b/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate.cpp @@ -57,7 +57,7 @@ #include -#include +#include namespace QmlDesigner { diff --git a/share/qtcreator/static.pri b/share/qtcreator/static.pri new file mode 100644 index 00000000000..6cea5a804a3 --- /dev/null +++ b/share/qtcreator/static.pri @@ -0,0 +1,39 @@ +# This pri file is used to deploy files that are not compiled while building +# Qt Creator. It handles copying of files into the build directory if using +# a shadow build and adds the respective install target as well. +# +# Usage: Define variables (details below) and include this pri file afterwards. +# +# STATIC_BASE - base directory for the files listed in STATIC_FILES +# STATIC_FILES - list of files to be deployed + +include(../../qtcreator.pri) + +# used in custom compilers which just copy files +defineReplace(stripStaticBase) { + return($$relative_path($$1, $$STATIC_BASE)) +} + +# handle conditional copying; copydata will be set by qtcreator.pri +!isEmpty(STATIC_FILES) { + isEmpty(STATIC_BASE): \ + error("Using STATIC_FILES without having STATIC_BASE set") + + !isEmpty(copydata) { + copy2build.input += STATIC_FILES + copy2build.output = $$IDE_DATA_PATH/${QMAKE_FUNC_FILE_IN_stripStaticBase} + isEmpty(vcproj):copy2build.variable_out = PRE_TARGETDEPS + win32:copy2build.commands = $$QMAKE_COPY \"${QMAKE_FILE_IN}\" \"${QMAKE_FILE_OUT}\" + unix:copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} + copy2build.name = COPY ${QMAKE_FILE_IN} + copy2build.config += no_link + QMAKE_EXTRA_COMPILERS += copy2build + } + + !osx { + static.files = $$STATIC_FILES + static.base = $$STATIC_BASE + static.path = $$QTC_PREFIX/share/qtcreator + INSTALLS += static + } +} diff --git a/share/qtcreator/static.pro b/share/qtcreator/static.pro index 37a44a1aa66..59bb5d342f4 100644 --- a/share/qtcreator/static.pro +++ b/share/qtcreator/static.pro @@ -1,5 +1,3 @@ -include(../../qtcreator.pri) - TEMPLATE = app TARGET = phony_target CONFIG -= qt sdk separate_debug_info gdb_dwarf_index @@ -23,6 +21,9 @@ isEmpty(vcproj) { QMAKE_EXTRA_COMPILERS += phony_src } +STATIC_BASE = $$PWD + +# files/folders that are conditionally "deployed" to the build directory DATA_DIRS = \ welcomescreen \ examplebrowser \ @@ -46,28 +47,14 @@ macx: DATA_DIRS += scripts for(data_dir, DATA_DIRS) { files = $$files($$PWD/$$data_dir/*, true) # Info.plist.in are handled below - for(file, files):!contains(file, ".*/Info\\.plist\\.in$"):!exists($$file/*):FILES += $$file + for(file, files):!contains(file, ".*/Info\\.plist\\.in$"):!exists($$file/*): \ + STATIC_FILES += $$file } -# conditionally deployed data -!isEmpty(copydata) { - copy2build.input = FILES - copy2build.output = $$IDE_DATA_PATH/${QMAKE_FUNC_FILE_IN_stripSrcDir} - isEmpty(vcproj):copy2build.variable_out = PRE_TARGETDEPS - win32:copy2build.commands = $$QMAKE_COPY \"${QMAKE_FILE_IN}\" \"${QMAKE_FILE_OUT}\" - unix:copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} - copy2build.name = COPY ${QMAKE_FILE_IN} - copy2build.CONFIG += no_link - QMAKE_EXTRA_COMPILERS += copy2build -} +include(static.pri) -!macx { - for(data_dir, DATA_DIRS) { - eval($${data_dir}.files = $$quote($$PWD/$$data_dir)) - eval($${data_dir}.path = $$QTC_PREFIX/share/qtcreator) - INSTALLS += $$data_dir - } -} else { +# stuff that cannot be handled by static.pri +osx { # do version magic for app bundles dumpinfo.input = qml/qmldump/Info.plist.in dumpinfo.output = $$IDE_DATA_PATH/qml/qmldump/Info.plist diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/main.qml b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/main.qml index 5ab93ecb205..8e8a45b4eed 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/main.qml +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/main.qml @@ -4,7 +4,7 @@ import QtQuick.Window %{QtQuickWindowVersion} Window { visible: true -@if %{QmlUISplit} +@if %{IsUiFileInUse} MainForm { anchors.fill: parent mouseArea.onClicked: { diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/qml.qrc b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/qml.qrc index 068979cb030..b6165a029b7 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/qml.qrc +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/qml.qrc @@ -1,7 +1,7 @@ main.qml -@if %{QmlUISplit} +@if %{IsUiFileInUse} MainForm.ui.qml @endif diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json index ff84948f890..de3d358440a 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json @@ -16,7 +16,9 @@ { "key": "MainCppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src')}" }, { "key": "QtQuickVersion", "value": "%{JS: %{QtVersion}.qtQuickVersion}" }, { "key": "QtQuickWindowVersion", "value": "%{JS: %{QtVersion}.qtQuickWindowVersion}" }, - { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" } + { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, + { "key": "UiSupport", "value": "%{JS: '%{QtQuickVersion}' !== '2.3' }" }, + { "key": "IsUiFileInUse", "value": "%{JS: %{UiSupport} && %{QmlUiSplit} }" } ], "pages": @@ -69,12 +71,13 @@ } }, { - "name": "QmlUISplit", + "name": "QmlUiSplit", "trDisplayName": "With ui.qml file", "type": "CheckBox", + "visible": "%{UiSupport}", "data": { - "checked": false + "checked": true } } ] @@ -115,7 +118,7 @@ }, { "source": "MainForm.ui.qml", - "condition": "%{QmlUISplit}" + "condition": "%{IsUiFileInUse}" }, { "source": "qml.qrc" diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/main.qml b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/main.qml index f581c7c226c..3378f4f131b 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/main.qml +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/main.qml @@ -1,6 +1,6 @@ import QtQuick %{QtQuickVersion} import QtQuick.Controls %{QtQuickControlsVersion} -@if %{QmlUISplit} +@if %{IsUiFileInUse} import QtQuick.Dialogs %{QtQuickDialogsVersion} @endif @@ -24,7 +24,7 @@ ApplicationWindow { } } -@if %{QmlUISplit} +@if %{IsUiFileInUse} MainForm { anchors.fill: parent button1.onClicked: messageDialog.show(qsTr("Button 1 pressed")) diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/qml.qrc b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/qml.qrc index 068979cb030..b6165a029b7 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/qml.qrc +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/qml.qrc @@ -1,7 +1,7 @@ main.qml -@if %{QmlUISplit} +@if %{IsUiFileInUse} MainForm.ui.qml @endif diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json index 0a57e21bf3b..fffd8650563 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json @@ -18,7 +18,9 @@ { "key": "QtQuickControlsVersion", "value": "%{JS: %{QtVersion}.qtQuickControlsVersion}" }, { "key": "QtQuickDialogsVersion", "value": "%{JS: %{QtVersion}.qtQuickDialogsVersion}" }, { "key": "QtQuickLayoutsVersion", "value": "%{JS: %{QtVersion}.qtQuickLayoutsVersion}" }, - { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" } + { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, + { "key": "UiSupport", "value": "%{JS: '%{QtQuickVersion}' !== '2.3' }" }, + { "key": "IsUiFileInUse", "value": "%{JS: %{UiSupport} && %{QmlUiSplit} }" } ], "pages": @@ -77,9 +79,10 @@ } }, { - "name": "QmlUISplit", + "name": "QmlUiSplit", "trDisplayName": "With ui.qml file", "type": "CheckBox", + "visible": "%{UiSupport}", "data": { "checked": true @@ -132,7 +135,7 @@ }, { "source": "MainForm.ui.qml", - "condition": "%{QmlUISplit}" + "condition": "%{IsUiFileInUse}" }, { "source": "qml.qrc" diff --git a/share/qtcreator/templates/wizards/projects/qmlproject/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmlproject/qtquickapplication/wizard.json index efc71de1d51..3a1eddfd435 100644 --- a/share/qtcreator/templates/wizards/projects/qmlproject/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmlproject/qtquickapplication/wizard.json @@ -15,7 +15,9 @@ { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectDirectory}/%{ProjectName}', 'qmlproject')}" }, { "key": "MainQmlFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qml')}" }, { "key": "QtQuickVersion", "value": "%{JS: %{QtVersion}.qtQuickVersion}" }, - { "key": "QtQuickWindowVersion", "value": "%{JS: %{QtVersion}.qtQuickWindowVersion}" } + { "key": "QtQuickWindowVersion", "value": "%{JS: %{QtVersion}.qtQuickWindowVersion}" }, + { "key": "UiSupport", "value": "%{JS: '%{QtQuickVersion}' !== '2.3' }" }, + { "key": "IsUiFileInUse", "value": "%{JS: %{UiSupport} && %{QmlUiSplit} }" } ], "pages": @@ -67,9 +69,10 @@ } }, { - "name": "QmlUISplit", + "name": "QmlUiSplit", "trDisplayName": "With ui.qml file", "type": "CheckBox", + "visible": "%{UiSupport}", "data": { "checked": false @@ -102,7 +105,7 @@ { "source": "../../qmake/qtquickapplication/MainForm.ui.qml", "target": "%{ProjectDirectory}/MainForm.ui.qml", - "condition": "%{QmlUISplit}" + "condition": "%{IsUiFileInUse}" }, { "source": "../../git.ignore", diff --git a/share/qtcreator/templates/wizards/projects/qmlproject/qtquickcontrolsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmlproject/qtquickcontrolsapplication/wizard.json index 9e19f93b925..657d5501d0d 100644 --- a/share/qtcreator/templates/wizards/projects/qmlproject/qtquickcontrolsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmlproject/qtquickcontrolsapplication/wizard.json @@ -17,7 +17,9 @@ { "key": "QtQuickVersion", "value": "%{JS: %{QtVersion}.qtQuickVersion}" }, { "key": "QtQuickControlsVersion", "value": "%{JS: %{QtVersion}.qtQuickControlsVersion}" }, { "key": "QtQuickDialogsVersion", "value": "%{JS: %{QtVersion}.qtQuickDialogsVersion}" }, - { "key": "QtQuickLayoutsVersion", "value": "%{JS: %{QtVersion}.qtQuickLayoutsVersion}" } + { "key": "QtQuickLayoutsVersion", "value": "%{JS: %{QtVersion}.qtQuickLayoutsVersion}" }, + { "key": "UiSupport", "value": "%{JS: '%{QtQuickVersion}' !== '2.3' }" }, + { "key": "IsUiFileInUse", "value": "%{JS: %{UiSupport} && %{QmlUiSplit} }" } ], "pages": @@ -75,9 +77,10 @@ } }, { - "name": "QmlUISplit", + "name": "QmlUiSplit", "trDisplayName": "With ui.qml file", "type": "CheckBox", + "visible": "%{UiSupport}", "data": { "checked": true @@ -110,7 +113,7 @@ { "source": "../../qmake/qtquickcontrolsapplication/MainForm.ui.qml", "target": "%{ProjectDirectory}/MainForm.ui.qml", - "condition": "%{QmlUISplit}" + "condition": "%{IsUiFileInUse}" }, { "source": "../../git.ignore", diff --git a/src/libs/clangbackendipc/cmbregisterprojectsforcodecompletioncommand.cpp b/src/libs/clangbackendipc/cmbregisterprojectsforcodecompletioncommand.cpp index 3b8ea87b4f3..3fd1573d9bf 100644 --- a/src/libs/clangbackendipc/cmbregisterprojectsforcodecompletioncommand.cpp +++ b/src/libs/clangbackendipc/cmbregisterprojectsforcodecompletioncommand.cpp @@ -76,7 +76,7 @@ bool operator<(const RegisterProjectPartsForCodeCompletionCommand &first, const QDebug operator<<(QDebug debug, const RegisterProjectPartsForCodeCompletionCommand &command) { - debug.nospace() << "RegisterProjectPartsForCodeCompletion("; + debug.nospace() << "RegisterProjectPartsForCodeCompletionCommand("; for (const ProjectPartContainer &projectContainer : command.projectContainers()) debug.nospace() << projectContainer<< ", "; @@ -88,7 +88,7 @@ QDebug operator<<(QDebug debug, const RegisterProjectPartsForCodeCompletionComma void PrintTo(const RegisterProjectPartsForCodeCompletionCommand &command, ::std::ostream* os) { - *os << "RegisterProjectPartsForCodeCompletion("; + *os << "RegisterProjectPartsForCodeCompletionCommand("; for (const ProjectPartContainer &projectContainer : command.projectContainers()) PrintTo(projectContainer, os); diff --git a/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.cpp b/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.cpp index 7c6743616c7..18e3eb3e347 100644 --- a/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.cpp +++ b/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.cpp @@ -42,44 +42,44 @@ namespace ClangBackEnd { UnregisterProjectPartsForCodeCompletionCommand::UnregisterProjectPartsForCodeCompletionCommand(const Utf8StringVector &filePaths) - : filePaths_(filePaths) + : projectPartIds_(filePaths) { } -const Utf8StringVector &UnregisterProjectPartsForCodeCompletionCommand::filePaths() const +const Utf8StringVector &UnregisterProjectPartsForCodeCompletionCommand::projectPartIds() const { - return filePaths_; + return projectPartIds_; } QDataStream &operator<<(QDataStream &out, const UnregisterProjectPartsForCodeCompletionCommand &command) { - out << command.filePaths_; + out << command.projectPartIds_; return out; } QDataStream &operator>>(QDataStream &in, UnregisterProjectPartsForCodeCompletionCommand &command) { - in >> command.filePaths_; + in >> command.projectPartIds_; return in; } bool operator==(const UnregisterProjectPartsForCodeCompletionCommand &first, const UnregisterProjectPartsForCodeCompletionCommand &second) { - return first.filePaths_ == second.filePaths_; + return first.projectPartIds_ == second.projectPartIds_; } bool operator<(const UnregisterProjectPartsForCodeCompletionCommand &first, const UnregisterProjectPartsForCodeCompletionCommand &second) { - return compareContainer(first.filePaths_, second.filePaths_); + return compareContainer(first.projectPartIds_, second.projectPartIds_); } QDebug operator<<(QDebug debug, const UnregisterProjectPartsForCodeCompletionCommand &command) { debug.nospace() << "UnregisterProjectPartsForCodeCompletionCommand("; - for (const Utf8String &fileNames_ : command.filePaths()) + for (const Utf8String &fileNames_ : command.projectPartIds()) debug.nospace() << fileNames_ << ", "; debug.nospace() << ")"; @@ -91,7 +91,7 @@ void PrintTo(const UnregisterProjectPartsForCodeCompletionCommand &command, ::st { *os << "UnregisterProjectPartsForCodeCompletionCommand("; - for (const Utf8String &fileNames_ : command.filePaths()) + for (const Utf8String &fileNames_ : command.projectPartIds()) *os << fileNames_.constData() << ", "; *os << ")"; diff --git a/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.h b/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.h index 59bf8ce5c58..592a5ae4ef2 100644 --- a/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.h +++ b/src/libs/clangbackendipc/cmbunregisterprojectsforcodecompletioncommand.h @@ -49,12 +49,12 @@ class CMBIPC_EXPORT UnregisterProjectPartsForCodeCompletionCommand public: UnregisterProjectPartsForCodeCompletionCommand() = default; - UnregisterProjectPartsForCodeCompletionCommand(const Utf8StringVector &filePaths); + UnregisterProjectPartsForCodeCompletionCommand(const Utf8StringVector &projectPartIds); - const Utf8StringVector &filePaths() const; + const Utf8StringVector &projectPartIds() const; private: - Utf8StringVector filePaths_; + Utf8StringVector projectPartIds_; }; CMBIPC_EXPORT QDataStream &operator<<(QDataStream &out, const UnregisterProjectPartsForCodeCompletionCommand &command); diff --git a/src/libs/clangbackendipc/codecompletion.cpp b/src/libs/clangbackendipc/codecompletion.cpp index 5e4d010de48..e9c5ad569f2 100644 --- a/src/libs/clangbackendipc/codecompletion.cpp +++ b/src/libs/clangbackendipc/codecompletion.cpp @@ -38,15 +38,11 @@ namespace ClangBackEnd { CodeCompletion::CodeCompletion(const Utf8String &text, - const Utf8String &hint, - const Utf8String &snippet, quint32 priority, Kind completionKind, Availability availability, bool hasParameters) : text_(text), - hint_(hint), - snippet_(snippet), priority_(priority), completionKind_(completionKind), availability_(availability), @@ -63,15 +59,6 @@ const Utf8String &CodeCompletion::text() const { return text_; } -const Utf8String &CodeCompletion::hint() const -{ - return hint_; -} - -const Utf8String &CodeCompletion::snippet() const -{ - return snippet_; -} void CodeCompletion::setCompletionKind(CodeCompletion::Kind completionKind) { @@ -136,8 +123,6 @@ quint32 &CodeCompletion::availabilityAsInt() QDataStream &operator<<(QDataStream &out, const CodeCompletion &command) { out << command.text_; - out << command.hint_; - out << command.snippet_; out << command.chunks_; out << command.priority_; out << command.completionKind_; @@ -150,8 +135,6 @@ QDataStream &operator<<(QDataStream &out, const CodeCompletion &command) QDataStream &operator>>(QDataStream &in, CodeCompletion &command) { in >> command.text_; - in >> command.hint_; - in >> command.snippet_; in >> command.chunks_; in >> command.priority_; in >> command.completionKindAsInt(); @@ -213,8 +196,6 @@ QDebug operator<<(QDebug debug, const CodeCompletion &command) debug.nospace() << "CodeCompletion("; debug.nospace() << command.text_ << ", "; - debug.nospace() << command.hint_ << ", "; - debug.nospace() << command.snippet_ << ", "; debug.nospace() << command.priority_ << ", "; debug.nospace() << completionKindToString(command.completionKind_) << ", "; debug.nospace() << availabilityToString(command.availability_) << ", "; @@ -230,8 +211,6 @@ void PrintTo(const CodeCompletion &command, ::std::ostream* os) *os << "CodeCompletion("; *os << command.text_.constData() << ", "; - *os << command.hint_.constData() << ", "; - *os << command.snippet_.constData() << ", "; *os << command.priority_ << ", "; *os << completionKindToString(command.completionKind_) << ", "; *os << availabilityToString(command.availability_) << ", "; diff --git a/src/libs/clangbackendipc/codecompletion.h b/src/libs/clangbackendipc/codecompletion.h index 44f34a8b090..5c7064b5e5b 100644 --- a/src/libs/clangbackendipc/codecompletion.h +++ b/src/libs/clangbackendipc/codecompletion.h @@ -80,8 +80,6 @@ public: public: CodeCompletion() = default; CodeCompletion(const Utf8String &text, - const Utf8String &hint = Utf8String(), - const Utf8String &snippet = Utf8String(), quint32 priority = 0, Kind completionKind = Other, Availability availability = Available, @@ -90,9 +88,6 @@ public: void setText(const Utf8String &text); const Utf8String &text() const; - const Utf8String &hint() const; - const Utf8String &snippet() const; - void setCompletionKind(Kind completionKind); Kind completionKind() const; @@ -114,8 +109,6 @@ private: private: Utf8String text_; - Utf8String hint_; - Utf8String snippet_; QVector chunks_; quint32 priority_ = 0; Kind completionKind_ = Other; diff --git a/src/libs/utils/ansiescapecodehandler.cpp b/src/libs/utils/ansiescapecodehandler.cpp index bc7e6dcb1a4..d956c90f416 100644 --- a/src/libs/utils/ansiescapecodehandler.cpp +++ b/src/libs/utils/ansiescapecodehandler.cpp @@ -86,53 +86,83 @@ QList AnsiEscapeCodeHandler::parseText(const FormattedText &input DefaultBackgroundColor = 49 }; - QList outputData; - - QTextCharFormat charFormat = m_previousFormatClosed ? input.format : m_previousFormat; - const QString escape = QLatin1String("\x1b["); - const int escapePos = input.text.indexOf(escape); - if (escapePos < 0) { - outputData << FormattedText(input.text, charFormat); - return outputData; - } else if (escapePos != 0) { - outputData << FormattedText(input.text.left(escapePos), charFormat); - } - const QChar semicolon = QLatin1Char(';'); const QChar colorTerminator = QLatin1Char('m'); const QChar eraseToEol = QLatin1Char('K'); - // strippedText always starts with "\e[" - QString strippedText = input.text.mid(escapePos); + + QList outputData; + QTextCharFormat charFormat = m_previousFormatClosed ? input.format : m_previousFormat; + QString strippedText; + if (m_pendingText.isEmpty()) { + strippedText = input.text; + } else { + strippedText = m_pendingText.append(input.text); + m_pendingText.clear(); + } + while (!strippedText.isEmpty()) { - while (strippedText.startsWith(escape)) { - strippedText.remove(0, 2); + QTC_ASSERT(m_pendingText.isEmpty(), break); + const int escapePos = strippedText.indexOf(escape[0]); + if (escapePos < 0) { + outputData << FormattedText(strippedText, charFormat); + break; + } else if (escapePos != 0) { + outputData << FormattedText(strippedText.left(escapePos), charFormat); + strippedText.remove(0, escapePos); + } + QTC_ASSERT(strippedText[0] == escape[0], break); + + while (!strippedText.isEmpty() && escape[0] == strippedText[0]) { + if (escape.startsWith(strippedText)) { + // control secquence is not complete + m_pendingText += strippedText; + strippedText.clear(); + break; + } + if (!strippedText.startsWith(escape)) { + // not a control sequence + m_pendingText.clear(); + outputData << FormattedText(strippedText.left(1), charFormat); + strippedText.remove(0, 1); + continue; + } + m_pendingText += strippedText.mid(0, escape.length()); + strippedText.remove(0, escape.length()); // \e[K is not supported. Just strip it. if (strippedText.startsWith(eraseToEol)) { + m_pendingText.clear(); strippedText.remove(0, 1); continue; } // get the number QString strNumber; QStringList numbers; - while (strippedText.at(0).isDigit() || strippedText.at(0) == semicolon) { + while (!strippedText.isEmpty()) { if (strippedText.at(0).isDigit()) { strNumber += strippedText.at(0); } else { - numbers << strNumber; + if (!strNumber.isEmpty()) + numbers << strNumber; + if (strNumber.isEmpty() || strippedText.at(0) != semicolon) + break; strNumber.clear(); } + m_pendingText += strippedText.mid(0, 1); strippedText.remove(0, 1); } - if (!strNumber.isEmpty()) - numbers << strNumber; + if (strippedText.isEmpty()) + break; // remove terminating char if (!strippedText.startsWith(colorTerminator)) { + m_pendingText.clear(); strippedText.remove(0, 1); - continue; + break; } + // got consistent control sequence, ok to clear pending text + m_pendingText.clear(); strippedText.remove(0, 1); if (numbers.isEmpty()) { @@ -224,17 +254,6 @@ QList AnsiEscapeCodeHandler::parseText(const FormattedText &input } } } - - if (strippedText.isEmpty()) - break; - int index = strippedText.indexOf(escape); - if (index > 0) { - outputData << FormattedText(strippedText.left(index), charFormat); - strippedText.remove(0, index); - } else if (index == -1) { - outputData << FormattedText(strippedText, charFormat); - break; - } } return outputData; } diff --git a/src/libs/utils/ansiescapecodehandler.h b/src/libs/utils/ansiescapecodehandler.h index f1f75817859..34d7593f060 100644 --- a/src/libs/utils/ansiescapecodehandler.h +++ b/src/libs/utils/ansiescapecodehandler.h @@ -62,6 +62,7 @@ private: bool m_previousFormatClosed; QTextCharFormat m_previousFormat; + QString m_pendingText; }; } // namespace Utils diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index baaf0e32174..d26d16ce54c 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -380,7 +380,7 @@ QList Environment::diff(const Environment &other) const if (thisIt == constEnd()) { result.append(EnvironmentItem(otherIt.key(), otherIt.value())); ++otherIt; - } else if (otherIt == constEnd()) { + } else if (otherIt == other.constEnd()) { EnvironmentItem item(thisIt.key(), QString()); item.unset = true; result.append(item); @@ -394,7 +394,8 @@ QList Environment::diff(const Environment &other) const result.append(EnvironmentItem(otherIt.key(), otherIt.value())); ++otherIt; } else { - result.append(EnvironmentItem(otherIt.key(), otherIt.value())); + if (thisIt.value() != otherIt.value()) + result.append(EnvironmentItem(otherIt.key(), otherIt.value())); ++otherIt; ++thisIt; } diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 0dc797406b2..159974d0d23 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -274,7 +274,7 @@ QString MacroExpander::expand(const QString &stringWithVariables) const if (d->m_lockDepth == 0) d->m_aborted = false; - if (d->m_lockDepth > 3) { // Limit recursion. + if (d->m_lockDepth > 10) { // Limit recursion. d->m_aborted = true; return QString(); } diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp index 424bc49a246..4a34181d2b5 100644 --- a/src/libs/utils/treemodel.cpp +++ b/src/libs/utils/treemodel.cpp @@ -770,15 +770,6 @@ QModelIndex TreeItem::index() const return m_model->indexForItem(this); } -void TreeItem::setModel(TreeModel *model) -{ - if (m_model == model) - return; - m_model = model; - foreach (TreeItem *item, m_children) - item->setModel(model); -} - void TreeItem::walkTree(TreeItemVisitor *visitor) { if (visitor->preVisit(this)) { @@ -972,6 +963,9 @@ int TreeModel::topLevelItemCount() const void TreeModel::setRootItem(TreeItem *item) { + QTC_ASSERT(item, return); + QTC_ASSERT(item->m_model == 0, return); + QTC_ASSERT(item->m_parent == 0, return); QTC_CHECK(m_root); if (m_root) { QTC_CHECK(m_root->m_parent == 0); @@ -980,7 +974,7 @@ void TreeModel::setRootItem(TreeItem *item) delete m_root; } m_root = item; - item->setModel(this); + item->propagateModel(this); emit layoutChanged(); } diff --git a/src/libs/utils/treemodel.h b/src/libs/utils/treemodel.h index e33be6ec785..cd3da441389 100644 --- a/src/libs/utils/treemodel.h +++ b/src/libs/utils/treemodel.h @@ -101,7 +101,6 @@ public: QModelIndex index() const; TreeModel *model() const { return m_model; } - void setModel(TreeModel *model); void walkTree(TreeItemVisitor *visitor); void walkTree(std::function f); @@ -117,7 +116,6 @@ private: TreeModel *m_model; // Not owned. QVector m_children; // Owned. QStringList *m_displays; - mutable bool m_populated; Qt::ItemFlags m_flags; friend class TreeModel; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index a472331e12e..d6377ef5a1e 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -1302,7 +1302,7 @@ void AndroidConfigurations::updateAutomaticKitList() } QHash > qtVersionsForArch; - foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) { + foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::unsortedVersions()) { if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT)) continue; QList qtAbis = qtVersion->qtAbis(); diff --git a/src/plugins/clangcodemodel/clangassistproposalitem.cpp b/src/plugins/clangcodemodel/clangassistproposalitem.cpp index 697e5453bb6..41cb7702075 100644 --- a/src/plugins/clangcodemodel/clangassistproposalitem.cpp +++ b/src/plugins/clangcodemodel/clangassistproposalitem.cpp @@ -30,10 +30,13 @@ #include "clangassistproposalitem.h" +#include "completionchunkstotextconverter.h" + #include #include #include +#include #include #include @@ -64,6 +67,21 @@ bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedChar) const return applies; } +static bool hasOnlyBlanksBeforeCursorInLine(QTextCursor textCursor) +{ + textCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + + const auto textBeforeCursor = textCursor.selectedText(); + + const auto nonSpace = std::find_if(textBeforeCursor.cbegin(), + textBeforeCursor.cend(), + [] (const QChar &signBeforeCursor) { + return !signBeforeCursor.isSpace(); + }); + + return nonSpace == textBeforeCursor.cend(); +} + void ClangAssistProposalItem::applyContextualContent(TextEditor::TextEditorWidget *editorWidget, int basePosition) const { @@ -86,6 +104,17 @@ void ClangAssistProposalItem::applyContextualContent(TextEditor::TextEditorWidge if (m_typedChar == QLatin1Char('/')) // Eat the slash m_typedChar = QChar(); } + } else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) { + CompletionChunksToTextConverter converter; + converter.setAddPlaceHolderPositions(true); + converter.setAddSpaces(true); + converter.setAddExtraVerticalSpaceBetweenBraces(true); + + converter.parseChunks(ccr.chunks()); + + toInsert = converter.text(); + if (converter.hasPlaceholderPositions()) + cursorOffset = converter.placeholderPositions().at(0) - converter.text().size(); } else if (!ccr.text().isEmpty()) { const TextEditor::CompletionSettings &completionSettings = TextEditor::TextEditorSettings::instance()->completionSettings(); @@ -165,7 +194,7 @@ void ClangAssistProposalItem::applyContextualContent(TextEditor::TextEditorWidge const int endsPosition = editorWidget->position(TextEditor::EndOfLinePosition); const QString existingText = editorWidget->textAt(editorWidget->position(), endsPosition - editorWidget->position()); int existLength = 0; - if (!existingText.isEmpty()) { + if (!existingText.isEmpty() && ccr.completionKind() != CodeCompletion::KeywordCompletionKind) { // Calculate the exist length in front of the extra chars existLength = toInsert.length() - (editorWidget->position() - basePosition); while (!existingText.startsWith(toInsert.right(existLength))) { @@ -189,6 +218,18 @@ void ClangAssistProposalItem::applyContextualContent(TextEditor::TextEditorWidge editorWidget->replace(length, toInsert); if (cursorOffset) editorWidget->setCursorPosition(editorWidget->position() + cursorOffset); + + // indent the statement + if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) { + auto selectionCursor = editorWidget->textCursor(); + selectionCursor.setPosition(basePosition); + selectionCursor.setPosition(basePosition + toInsert.size(), QTextCursor::KeepAnchor); + + auto basePositionCursor = editorWidget->textCursor(); + basePositionCursor.setPosition(basePosition); + if (hasOnlyBlanksBeforeCursorInLine(basePositionCursor)) + editorWidget->textDocument()->autoIndent(selectionCursor); + } } void ClangAssistProposalItem::keepCompletionOperator(unsigned compOp) diff --git a/src/plugins/clangcodemodel/clangassistproposalmodel.cpp b/src/plugins/clangcodemodel/clangassistproposalmodel.cpp index 3f11eee6311..b07b2eb42e4 100644 --- a/src/plugins/clangcodemodel/clangassistproposalmodel.cpp +++ b/src/plugins/clangcodemodel/clangassistproposalmodel.cpp @@ -39,15 +39,6 @@ namespace ClangCodeModel { namespace Internal { -namespace { - -const ClangAssistProposalItem &toClangAssistProposalItem(TextEditor::AssistProposalItem *assistProposalItem) -{ - return *static_cast(assistProposalItem); -} - -} - bool ClangAssistProposalModel::replaceDotForArrow(TextEditor::IAssistProposalModel *model) { auto clangAssistProposalModel = static_cast(model); diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index 4c41104f467..d03b56b89a6 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -157,7 +157,7 @@ void IpcReceiver::translationUnitDoesNotExist(const TranslationUnitDoesNotExistC void IpcReceiver::projectPartsDoNotExist(const ProjectPartsDoNotExistCommand &command) { - QTC_CHECK(!"Got ProjectDoesNotExistCommand"); + QTC_CHECK(!"Got ProjectPartsDoNotExistCommand"); qCDebug(log) << "<<< ERROR:" << command; } @@ -223,10 +223,8 @@ IpcCommunicator::IpcCommunicator() connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose, this, &IpcCommunicator::onEditorAboutToClose); - - connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, [this]() { - m_sendMode = IgnoreSendRequests; - }); + connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, + this, &IpcCommunicator::onCoreAboutToClose); initializeBackend(); } @@ -376,6 +374,11 @@ void IpcCommunicator::onEditorAboutToClose(Core::IEditor *editor) m_ipcReceiver.deleteProcessorsOfEditorWidget(textEditor->editorWidget()); } +void IpcCommunicator::onCoreAboutToClose() +{ + m_sendMode = IgnoreSendRequests; +} + void IpcCommunicator::initializeBackendWithCurrentData() { registerEmptyProjectForProjectLessFiles(); diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h index 365654fd921..4a4ca40ab0f 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.h +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h @@ -143,6 +143,7 @@ private: void onBackendRestarted(); void onEditorAboutToClose(Core::IEditor *editor); + void onCoreAboutToClose(); IpcReceiver m_ipcReceiver; ClangBackEnd::ConnectionClient m_connection; diff --git a/src/plugins/clangcodemodel/clangcompletionassistinterface.cpp b/src/plugins/clangcodemodel/clangcompletionassistinterface.cpp index 5c7a2a2d0e8..ec46846ccba 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistinterface.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistinterface.cpp @@ -33,6 +33,7 @@ #include "clangutils.h" #include +#include #include #include @@ -58,7 +59,9 @@ ClangCompletionAssistInterface::ClangCompletionAssistInterface( , m_languageFeatures(features) , m_textEditorWidget(textEditorWidget) { - m_unsavedFiles = Utils::createUnsavedFiles(CppTools::CppModelManager::instance()->workingCopy()); + m_unsavedFiles = Utils::createUnsavedFiles( + CppTools::CppModelManager::instance()->workingCopy(), + CppTools::modifiedFiles()); } bool ClangCompletionAssistInterface::objcEnabled() const diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index 1062a3726b8..505b5fcc223 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -37,6 +37,7 @@ #include "clangcompletioncontextanalyzer.h" #include "clangfunctionhintmodel.h" #include "clangutils.h" +#include "completionchunkstotextconverter.h" #include @@ -86,22 +87,25 @@ QList toAssistProposalItems(const CodeCompletions &complet if (slotCompletion && ccr.completionKind() != CodeCompletion::SlotCompletionKind) continue; - const QString txt(ccr.text().toString()); - ClangAssistProposalItem *item = items.value(txt, 0); + QString name; + if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) + name = CompletionChunksToTextConverter::convertToName(ccr.chunks()); + else + name = ccr.text().toString(); + + ClangAssistProposalItem *item = items.value(name, 0); if (item) { item->addOverload(ccr); } else { item = new ClangAssistProposalItem; - items.insert(txt, item); - item->setText(txt); - item->setDetail(ccr.hint().toString()); + items.insert(name, item); + item->setText(name); item->setOrder(ccr.priority()); - const QString snippet = ccr.snippet().toString(); - if (!snippet.isEmpty()) - item->setData(snippet); - else - item->setData(qVariantFromValue(ccr)); + if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) + item->setDetail(CompletionChunksToTextConverter::convertToToolTip(ccr.chunks())); + + item->setData(QVariant::fromValue(ccr)); } // FIXME: show the effective accessebility instead of availability diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp index e12833ca9a1..c4897befbcb 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp @@ -88,24 +88,28 @@ ClangEditorDocumentParser::ClangEditorDocumentParser(const QString &filePath) { } -void ClangEditorDocumentParser::update(CppTools::WorkingCopy workingCopy) +void ClangEditorDocumentParser::updateHelper(const BaseEditorDocumentParser::InMemoryInfo &info) { QTC_ASSERT(m_marker, return); - QMutexLocker lock(m_marker->mutex()); - QMutexLocker lock2(&m_mutex); - updateProjectPart(); - const QStringList options = createOptions(filePath(), projectPart(), true); + // Determine project part + State state_ = state(); + state_.projectPart = determineProjectPart(filePath(), configuration(), state_); + setState(state_); + // Determine command line arguments + const QStringList options = createOptions(filePath(), state_.projectPart, true); qCDebug(log, "Reparse options (cmd line equivalent): %s", commandLine(options, filePath()).toUtf8().constData()); - QTime t; t.start(); + // Run + QTime t; t.start(); + QMutexLocker lock(m_marker->mutex()); m_marker->setFileName(filePath()); m_marker->setCompilationOptions(options); - const Internal::UnsavedFiles unsavedFiles = Utils::createUnsavedFiles(workingCopy); + const Internal::UnsavedFiles unsavedFiles = Utils::createUnsavedFiles(info.workingCopy, + info.modifiedFiles); m_marker->reparse(unsavedFiles); - qCDebug(log) << "Reparse took" << t.elapsed() << "ms."; } diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.h b/src/plugins/clangcodemodel/clangeditordocumentparser.h index e86cf50f949..4784d51672c 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.h +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.h @@ -46,13 +46,13 @@ class ClangEditorDocumentParser : public CppTools::BaseEditorDocumentParser public: ClangEditorDocumentParser(const QString &filePath); - void update(CppTools::WorkingCopy workingCopy) override; - QList diagnostics() const; QList ifdefedOutBlocks() const; SemanticMarker::Ptr semanticMarker() const; private: + void updateHelper(const BaseEditorDocumentParser::InMemoryInfo &info) override; + SemanticMarker::Ptr m_marker; }; diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 090a0cf06de..5ca055b0a14 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -144,9 +144,6 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor() void ClangEditorDocumentProcessor::run() { // Run clang parser - const CppTools::WorkingCopy workingCopy - = CppTools::CppModelManager::instance()->workingCopy(); - disconnect(&m_parserWatcher, &QFutureWatcher::finished, this, &ClangEditorDocumentProcessor::onParserFinished); m_parserWatcher.cancel(); @@ -155,7 +152,9 @@ void ClangEditorDocumentProcessor::run() m_parserRevision = revision(); connect(&m_parserWatcher, &QFutureWatcher::finished, this, &ClangEditorDocumentProcessor::onParserFinished); - const QFuture future = QtConcurrent::run(&runParser, parser(), workingCopy); + const QFuture future = QtConcurrent::run(&runParser, + parser(), + ClangEditorDocumentParser::InMemoryInfo(true)); m_parserWatcher.setFuture(future); // Run builtin processor diff --git a/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp b/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp index 67e8048ee46..f950947448e 100644 --- a/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp +++ b/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp @@ -76,7 +76,7 @@ QString ClangFunctionHintModel::text(int index) const hintText += prettyMethod.mid(end).toHtmlEscaped()); return hintText; #endif - return CompletionChunksToTextConverter::convert(m_functionSymbols.at(index).chunks()); + return CompletionChunksToTextConverter::convertToFunctionSignature(m_functionSymbols.at(index).chunks()); } int ClangFunctionHintModel::activeArgument(const QString &prefix) const diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index 582ed43d782..257d3c966e5 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -32,7 +32,6 @@ #include -#include #include #include @@ -58,15 +57,9 @@ namespace Utils { Q_LOGGING_CATEGORY(verboseRunLog, "qtc.clangcodemodel.verboserun") -UnsavedFiles createUnsavedFiles(WorkingCopy workingCopy) +UnsavedFiles createUnsavedFiles(const WorkingCopy &workingCopy, + const ::Utils::FileNameList &modifiedFiles) { - // TODO: change the modelmanager to hold one working copy, and amend it every time we ask for one. - // TODO: Reason: the UnsavedFile needs a QByteArray. - - QSet< ::Utils::FileName> modifiedFiles; - foreach (IDocument *doc, DocumentManager::modifiedDocuments()) - modifiedFiles.insert(doc->filePath()); - UnsavedFiles result; QHashIterator< ::Utils::FileName, QPair > wcIter = workingCopy.iterator(); while (wcIter.hasNext()) { diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h index 65322d86ad1..d3a78cc45b7 100644 --- a/src/plugins/clangcodemodel/clangutils.h +++ b/src/plugins/clangcodemodel/clangutils.h @@ -43,7 +43,9 @@ namespace Utils { Q_DECLARE_LOGGING_CATEGORY(verboseRunLog) -ClangCodeModel::Internal::UnsavedFiles createUnsavedFiles(CppTools::WorkingCopy workingCopy); +ClangCodeModel::Internal::UnsavedFiles createUnsavedFiles( + const CppTools::WorkingCopy &workingCopy, + const ::Utils::FileNameList &modifiedFiles); QStringList createClangOptions(const CppTools::ProjectPart::Ptr &pPart, CppTools::ProjectFile::Kind fileKind); diff --git a/src/plugins/clangcodemodel/completionchunkstotextconverter.cpp b/src/plugins/clangcodemodel/completionchunkstotextconverter.cpp index 3027039603c..92d9c300744 100644 --- a/src/plugins/clangcodemodel/completionchunkstotextconverter.cpp +++ b/src/plugins/clangcodemodel/completionchunkstotextconverter.cpp @@ -30,15 +30,53 @@ #include "completionchunkstotextconverter.h" +#include +#include + namespace ClangCodeModel { namespace Internal { void CompletionChunksToTextConverter::parseChunks(const QVector &codeCompletionChunks) { m_text.clear(); + m_placeholderPositions.clear(); - for (const auto &codeCompletionChunk : codeCompletionChunks) - parse(codeCompletionChunk); + m_codeCompletionChunks = codeCompletionChunks; + + addExtraVerticalSpaceBetweenBraces(); + + std::for_each(m_codeCompletionChunks.cbegin(), + m_codeCompletionChunks.cend(), + [this] (const ClangBackEnd::CodeCompletionChunk &chunk) + { + parse(chunk); + m_previousCodeCompletionChunk = chunk; + }); +} + +void CompletionChunksToTextConverter::setAddPlaceHolderText(bool addPlaceHolderText) +{ + m_addPlaceHolderText = addPlaceHolderText; +} + +void CompletionChunksToTextConverter::setAddPlaceHolderPositions(bool addPlaceHolderPositions) +{ + m_addPlaceHolderPositions = addPlaceHolderPositions; +} + +void CompletionChunksToTextConverter::setAddResultType(bool addResultType) +{ + m_addResultType = addResultType; +} + +void CompletionChunksToTextConverter::setAddSpaces(bool addSpaces) +{ + m_addSpaces = addSpaces; +} + +void CompletionChunksToTextConverter::setAddExtraVerticalSpaceBetweenBraces(bool addExtraVerticalSpaceBetweenBraces) +{ + m_addExtraVerticalSpaceBetweenBraces = addExtraVerticalSpaceBetweenBraces; } const QString &CompletionChunksToTextConverter::text() const @@ -46,9 +84,42 @@ const QString &CompletionChunksToTextConverter::text() const return m_text; } -QString CompletionChunksToTextConverter::convert(const QVector &codeCompletionChunks) +const std::vector &CompletionChunksToTextConverter::placeholderPositions() const +{ + return m_placeholderPositions; +} + +bool CompletionChunksToTextConverter::hasPlaceholderPositions() const +{ + return m_placeholderPositions.size() > 0; +} + +QString CompletionChunksToTextConverter::convertToFunctionSignature(const QVector &codeCompletionChunks) { CompletionChunksToTextConverter converter; + converter.setAddPlaceHolderText(true); + converter.setAddResultType(true); + + converter.parseChunks(codeCompletionChunks); + + return converter.text(); +} + +QString CompletionChunksToTextConverter::convertToName(const QVector &codeCompletionChunks) +{ + CompletionChunksToTextConverter converter; + + converter.parseChunks(codeCompletionChunks); + + return converter.text(); +} + +QString CompletionChunksToTextConverter::convertToToolTip(const QVector &codeCompletionChunks) +{ + CompletionChunksToTextConverter converter; + converter.setAddPlaceHolderText(true); + converter.setAddSpaces(true); + converter.setAddExtraVerticalSpaceBetweenBraces(true); converter.parseChunks(codeCompletionChunks); @@ -62,17 +133,26 @@ void CompletionChunksToTextConverter::parse(const ClangBackEnd::CodeCompletionCh switch (codeCompletionChunk.kind()) { case CodeCompletionChunk::ResultType: parseResultType(codeCompletionChunk.text()); break; case CodeCompletionChunk::Optional: parseOptional(codeCompletionChunk); break; + case CodeCompletionChunk::Placeholder: parsePlaceHolder(codeCompletionChunk); break; + case CodeCompletionChunk::LeftParen: parseLeftParen(codeCompletionChunk); break; + case CodeCompletionChunk::LeftBrace: parseLeftBrace(codeCompletionChunk); break; default: parseText(codeCompletionChunk.text()); break; } } void CompletionChunksToTextConverter::parseResultType(const Utf8String &resultTypeText) { - m_text += resultTypeText.toString() + QChar(QChar::Space); + if (m_addResultType) + m_text += resultTypeText.toString() + QChar(QChar::Space); } void CompletionChunksToTextConverter::parseText(const Utf8String &text) { + if (canAddSpace() + && m_previousCodeCompletionChunk.kind() == ClangBackEnd::CodeCompletionChunk::RightBrace) { + m_text += QChar(QChar::Space); + } + m_text += text.toString(); } @@ -80,11 +160,91 @@ void CompletionChunksToTextConverter::parseOptional(const ClangBackEnd::CodeComp { m_text += QStringLiteral(""); - m_text += convert(optionalCodeCompletionChunk.optionalChunks()); + m_text += convertToFunctionSignature(optionalCodeCompletionChunk.optionalChunks()); m_text += QStringLiteral(""); } +void CompletionChunksToTextConverter::parsePlaceHolder(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) +{ + if (m_addPlaceHolderText) + m_text += codeCompletionChunk.text().toString(); + + if (m_addPlaceHolderPositions) + m_placeholderPositions.push_back(m_text.size()); +} + +void CompletionChunksToTextConverter::parseLeftParen(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) +{ + if (canAddSpace()) + m_text += QChar(QChar::Space); + + m_text += codeCompletionChunk.text().toString(); +} + +void CompletionChunksToTextConverter::parseLeftBrace(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) +{ + if (canAddSpace()) + m_text += QChar(QChar::Space); + + m_text += codeCompletionChunk.text().toString(); +} + +void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces() +{ + if (m_addExtraVerticalSpaceBetweenBraces) + addExtraVerticalSpaceBetweenBraces(m_codeCompletionChunks.begin()); +} + +void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces(const QVector::iterator &begin) +{ + using ClangBackEnd::CodeCompletionChunk; + + const auto leftBraceCompare = [] (const CodeCompletionChunk &chunk) { + return chunk.kind() == CodeCompletionChunk::LeftBrace; + }; + + const auto rightBraceCompare = [] (const CodeCompletionChunk &chunk) { + return chunk.kind() == CodeCompletionChunk::RightBrace; + }; + + const auto verticalSpaceCompare = [] (const CodeCompletionChunk &chunk) { + return chunk.kind() == CodeCompletionChunk::VerticalSpace; + }; + + auto leftBrace = std::find_if(begin, m_codeCompletionChunks.end(), leftBraceCompare); + + if (leftBrace != m_codeCompletionChunks.end()) { + auto rightBrace = std::find_if(leftBrace, m_codeCompletionChunks.end(), rightBraceCompare); + + if (rightBrace != m_codeCompletionChunks.end()) { + auto verticalSpaceCount = std::count_if(leftBrace, rightBrace, verticalSpaceCompare); + + if (verticalSpaceCount <= 1) { + auto distance = std::distance(leftBrace, rightBrace); + CodeCompletionChunk verticalSpaceChunck(CodeCompletionChunk::VerticalSpace, + Utf8StringLiteral("\n")); + auto verticalSpace = m_codeCompletionChunks.insert(std::next(leftBrace), + verticalSpaceChunck); + std::advance(verticalSpace, distance); + rightBrace = verticalSpace; + } + + auto begin = std::next(rightBrace); + + if (begin != m_codeCompletionChunks.end()) + addExtraVerticalSpaceBetweenBraces(begin); + } + } +} + +bool CompletionChunksToTextConverter::canAddSpace() const +{ + return m_addSpaces + && m_previousCodeCompletionChunk.kind() != ClangBackEnd::CodeCompletionChunk::HorizontalSpace + && m_previousCodeCompletionChunk.kind() != ClangBackEnd::CodeCompletionChunk::RightAngle; +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/completionchunkstotextconverter.h b/src/plugins/clangcodemodel/completionchunkstotextconverter.h index 20d4472b5ba..6e1e6b290fb 100644 --- a/src/plugins/clangcodemodel/completionchunkstotextconverter.h +++ b/src/plugins/clangcodemodel/completionchunkstotextconverter.h @@ -37,6 +37,8 @@ #include +#include + namespace ClangCodeModel { namespace Internal { @@ -45,18 +47,42 @@ class CompletionChunksToTextConverter public: void parseChunks(const QVector &codeCompletionChunks); + void setAddPlaceHolderText(bool addPlaceHolderText); + void setAddPlaceHolderPositions(bool addPlaceHolderPositions); + void setAddResultType(bool addResultType); + void setAddSpaces(bool addSpaces); + void setAddExtraVerticalSpaceBetweenBraces(bool addExtraVerticalSpaceBetweenBraces); + const QString &text() const; + const std::vector &placeholderPositions() const; + bool hasPlaceholderPositions() const; - static QString convert(const QVector &codeCompletionChunks); - + static QString convertToFunctionSignature(const QVector &codeCompletionChunks); + static QString convertToName(const QVector &codeCompletionChunks); + static QString convertToToolTip(const QVector &codeCompletionChunks); private: void parse(const ClangBackEnd::CodeCompletionChunk & codeCompletionChunk); void parseResultType(const Utf8String &text); void parseText(const Utf8String &text); - void parseOptional(const ClangBackEnd::CodeCompletionChunk & optionalCodeCompletionChunk); + void parseOptional(const ClangBackEnd::CodeCompletionChunk &optionalCodeCompletionChunk); + void parsePlaceHolder(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); + void parseLeftParen(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); + void parseLeftBrace(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); + void addExtraVerticalSpaceBetweenBraces(); + void addExtraVerticalSpaceBetweenBraces(const QVector::iterator &); + + bool canAddSpace() const; private: + std::vector m_placeholderPositions; + QVector m_codeCompletionChunks; + ClangBackEnd::CodeCompletionChunk m_previousCodeCompletionChunk; QString m_text; + bool m_addPlaceHolderText = false; + bool m_addPlaceHolderPositions = false; + bool m_addResultType = false; + bool m_addSpaces = false; + bool m_addExtraVerticalSpaceBetweenBraces = false; }; } // namespace Internal diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index 95c43eeb200..584b47b3c21 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -71,10 +71,15 @@ using namespace ClangCodeModel::Internal; namespace { -QString _(const char text[]) { return QString::fromUtf8(text); } +QString _(const char text[]) +{ return QString::fromUtf8(text); } + QString qrcPath(const QByteArray relativeFilePath) { return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath); } +QString fileName(const QString &filePath) +{ return QFileInfo(filePath).fileName(); } + struct LogOutput { LogOutput(const QString &text) : text(text.toUtf8()) {} @@ -260,8 +265,8 @@ QString toString(const FileContainer &fileContainer) { QString out; QTextStream ts(&out); - ts << " Path: " << QFileInfo(fileContainer.filePath().toString()).fileName() - << " ProjectPart: " << fileContainer.projectPartId().toString() << "\n"; + ts << " Path: " << fileName(fileContainer.filePath().toString()) + << " ProjectPart: " << fileName(fileContainer.projectPartId().toString()) << "\n"; return out; } @@ -279,7 +284,7 @@ QString toString(const ProjectPartContainer &projectPartContainer) QString out; QTextStream ts(&out); ts << " ProjectPartContainer" - << " id: " << QFileInfo(projectPartContainer.projectPartId().toString()).fileName(); + << " id: " << fileName(projectPartContainer.projectPartId().toString()); return out; } @@ -330,7 +335,7 @@ QString toString(const UnregisterProjectPartsForCodeCompletionCommand &command) QTextStream ts(&out); ts << "UnregisterProjectPartsForCodeCompletionCommand\n" - << command.filePaths().join(Utf8String::fromUtf8(",")).toByteArray() << "\n"; + << command.projectPartIds().join(Utf8String::fromUtf8(",")).toByteArray() << "\n"; return out; } diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 60c51bbc5ac..ed58364530f 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -180,7 +180,7 @@ void EditorManagerPlaceHolder::currentModeChanged(IMode *mode) static EditorManager *m_instance = 0; static EditorManagerPrivate *d; -static int extractNumericSuffix(QString *fileName) +static int extractNumericSuffix(QString *fileName, QString *postfix = 0) { int i = fileName->length() - 1; for (; i >= 0; --i) { @@ -195,6 +195,8 @@ static int extractNumericSuffix(QString *fileName) const QString suffix = fileName->mid(i + 1); const int result = suffix.toInt(&ok); if (suffix.isEmpty() || ok) { + if (postfix) + *postfix = fileName->mid(i); fileName->truncate(i); return result; } @@ -2552,25 +2554,15 @@ IEditor *EditorManager::openEditorAt(const QString &fileName, int line, int colu fileName, line, column, editorId, flags, newEditor); } -// Extract line number suffix. Return the suffix (e.g. ":132") and truncates the filename accordingly. -QString EditorManager::splitLineNumber(QString *fileName) +// Extract line and column number suffix. Return the suffix (e.g. ":132") and truncates the filename accordingly. +QString EditorManager::splitLineAndColumnNumber(QString *fileName) { - int i = fileName->length() - 1; - for (; i >= 0; --i) { - if (!fileName->at(i).isNumber()) - break; - } - if (i == -1) - return QString(); - const QChar c = fileName->at(i); - if (c == QLatin1Char(':') || c == QLatin1Char('+')) { - const QString result = fileName->mid(i + 1); - bool ok; - result.toInt(&ok); - if (result.isEmpty() || ok) { - fileName->truncate(i); - return QString(c) + result; - } + QString postfix; + if (extractNumericSuffix(fileName, &postfix)) { + QString previousPostfix; + if (extractNumericSuffix(fileName, &previousPostfix)) + postfix.prepend(previousPostfix); + return postfix; } return QString(); } diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index f475e1e5ddb..b6b97fd35a9 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -109,7 +109,7 @@ public: }; Q_DECLARE_FLAGS(OpenEditorFlags, OpenEditorFlag) - static QString splitLineNumber(QString *fileName); + static QString splitLineAndColumnNumber(QString *fileName); static IEditor *openEditor(const QString &fileName, Id editorId = Id(), OpenEditorFlags flags = NoFlags, bool *newEditor = 0); static IEditor *openEditorAt(const QString &fileName, int line, int column = 0, diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp index b505ccfaad6..d7519a43f26 100644 --- a/src/plugins/coreplugin/locator/basefilefilter.cpp +++ b/src/plugins/coreplugin/locator/basefilefilter.cpp @@ -103,7 +103,7 @@ QList BaseFileFilter::matchesFor(QFutureInterface betterEntries; QList goodEntries; QString needle = trimWildcards(QDir::fromNativeSeparators(origEntry)); - const QString lineNoSuffix = EditorManager::splitLineNumber(&needle); + const QString lineNoSuffix = EditorManager::splitLineAndColumnNumber(&needle); QStringMatcher matcher(needle, Qt::CaseInsensitive); const QChar asterisk = QLatin1Char('*'); QRegExp regexp(asterisk + needle+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp index 22144f51a18..df9df7ffb10 100644 --- a/src/plugins/coreplugin/locator/filesystemfilter.cpp +++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp @@ -119,7 +119,7 @@ QList FileSystemFilter::matchesFor(QFutureInterface OpenDocumentsFilter::matchesFor(QFutureInterface goodEntries; QList betterEntries; QString entry = entry_; - const QString lineNoSuffix = EditorManager::splitLineNumber(&entry); + const QString lineNoSuffix = EditorManager::splitLineAndColumnNumber(&entry); const QChar asterisk = QLatin1Char('*'); QString pattern = QString(asterisk); pattern += entry; diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 77bffc7338c..2746303cf0e 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -268,9 +268,11 @@ void CppEditorDocument::setPreprocessorSettings(const CppTools::ProjectPart::Ptr { CppTools::BaseEditorDocumentParser *parser = processor()->parser(); QTC_ASSERT(parser, return); - if (parser->projectPart() != projectPart || parser->editorDefines() != defines) { - parser->setProjectPart(projectPart); - parser->setEditorDefines(defines); + if (parser->projectPart() != projectPart || parser->configuration().editorDefines != defines) { + CppTools::BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.manuallySetProjectPart = projectPart; + config.editorDefines = defines; + parser->setConfiguration(config); emit preprocessorSettingsChanged(!defines.trimmed().isEmpty()); } diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 082c45feb97..736638ef921 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -1832,7 +1832,7 @@ NameAST *nameUnderCursor(const QList &path) bool canLookupDefinition(const CppQuickFixInterface &interface, const NameAST *nameAst) { - QTC_ASSERT(nameAst, return false); + QTC_ASSERT(nameAst && nameAst->name, return false); // Find the enclosing scope unsigned line, column; @@ -1909,7 +1909,7 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa QuickFixOperations &result) { const NameAST *nameAst = nameUnderCursor(interface.path()); - if (!nameAst) + if (!nameAst || !nameAst->name) return; if (canLookupDefinition(interface, nameAst)) @@ -5852,7 +5852,7 @@ bool onConnectOrDisconnectCall(AST *ast, const ExpressionListAST **arguments) return false; const IdExpressionAST *idExpr = call->base_expression->asIdExpression(); - if (!idExpr) + if (!idExpr || !idExpr->name || !idExpr->name->name) return false; const ExpressionListAST *args = call->expression_list; diff --git a/src/plugins/cpptools/baseeditordocumentparser.cpp b/src/plugins/cpptools/baseeditordocumentparser.cpp index 7f1e74785b1..52fd64e1135 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.cpp +++ b/src/plugins/cpptools/baseeditordocumentparser.cpp @@ -31,6 +31,7 @@ #include "baseeditordocumentparser.h" #include "baseeditordocumentprocessor.h" +#include "cpptoolsreuse.h" #include "editordocumenthandle.h" namespace CppTools { @@ -44,15 +45,18 @@ namespace CppTools { It's meant to be used in the C++ editor to get precise results by using the "best" project part for a file. - Derived classes are expected to implement update() by using the protected - mutex, updateProjectPart() and by respecting the options set by the client. + Derived classes are expected to implement updateHelper() this way: + + \list + \li Get a copy of the configuration and the last state. + \li Work on the data and do whatever is necessary. At least, update + the project part with the help of determineProjectPart(). + \li Ensure the new state is set before updateHelper() returns. + \endlist */ BaseEditorDocumentParser::BaseEditorDocumentParser(const QString &filePath) - : m_mutex(QMutex::Recursive) - , m_filePath(filePath) - , m_usePrecompiledHeaders(false) - , m_editorDefinesChangedSinceLastUpdate(false) + : m_filePath(filePath) { } @@ -65,44 +69,39 @@ QString BaseEditorDocumentParser::filePath() const return m_filePath; } +BaseEditorDocumentParser::Configuration BaseEditorDocumentParser::configuration() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_configuration; +} + +void BaseEditorDocumentParser::setConfiguration(const Configuration &configuration) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_configuration = configuration; +} + +void BaseEditorDocumentParser::update(const InMemoryInfo &info) +{ + QMutexLocker locker(&m_updateIsRunning); + updateHelper(info); +} + +BaseEditorDocumentParser::State BaseEditorDocumentParser::state() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_state; +} + +void BaseEditorDocumentParser::setState(const State &state) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_state = state; +} + ProjectPart::Ptr BaseEditorDocumentParser::projectPart() const { - QMutexLocker locker(&m_mutex); - return m_projectPart; -} - -void BaseEditorDocumentParser::setProjectPart(ProjectPart::Ptr projectPart) -{ - QMutexLocker locker(&m_mutex); - m_manuallySetProjectPart = projectPart; -} - -bool BaseEditorDocumentParser::usePrecompiledHeaders() const -{ - QMutexLocker locker(&m_mutex); - return m_usePrecompiledHeaders; -} - -void BaseEditorDocumentParser::setUsePrecompiledHeaders(bool usePrecompiledHeaders) -{ - QMutexLocker locker(&m_mutex); - m_usePrecompiledHeaders = usePrecompiledHeaders; -} - -QByteArray BaseEditorDocumentParser::editorDefines() const -{ - QMutexLocker locker(&m_mutex); - return m_editorDefines; -} - -void BaseEditorDocumentParser::setEditorDefines(const QByteArray &editorDefines) -{ - QMutexLocker locker(&m_mutex); - - if (editorDefines != m_editorDefines) { - m_editorDefines = editorDefines; - m_editorDefinesChangedSinceLastUpdate = true; - } + return state().projectPart; } BaseEditorDocumentParser *BaseEditorDocumentParser::get(const QString &filePath) @@ -115,43 +114,44 @@ BaseEditorDocumentParser *BaseEditorDocumentParser::get(const QString &filePath) return 0; } -void BaseEditorDocumentParser::updateProjectPart() +ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart(const QString &filePath, + const Configuration &config, + const State &state) { - if (m_manuallySetProjectPart) { - m_projectPart = m_manuallySetProjectPart; - return; - } + if (config.manuallySetProjectPart) + return config.manuallySetProjectPart; + + ProjectPart::Ptr projectPart = state.projectPart; CppModelManager *cmm = CppModelManager::instance(); - QList projectParts = cmm->projectPart(m_filePath); + QList projectParts = cmm->projectPart(filePath); if (projectParts.isEmpty()) { - if (m_projectPart) + if (projectPart) // File is not directly part of any project, but we got one before. We will re-use it, // because re-calculating this can be expensive when the dependency table is big. - return; + return projectPart; // Fall-back step 1: Get some parts through the dependency table: - projectParts = cmm->projectPartFromDependencies(Utils::FileName::fromString(m_filePath)); + projectParts = cmm->projectPartFromDependencies(Utils::FileName::fromString(filePath)); if (projectParts.isEmpty()) // Fall-back step 2: Use fall-back part from the model manager: - m_projectPart = cmm->fallbackProjectPart(); + projectPart = cmm->fallbackProjectPart(); else - m_projectPart = projectParts.first(); + projectPart = projectParts.first(); } else { - if (!projectParts.contains(m_projectPart)) + if (!projectParts.contains(projectPart)) // Apparently the project file changed, so update our project part. - m_projectPart = projectParts.first(); + projectPart = projectParts.first(); } + + return projectPart; } -bool BaseEditorDocumentParser::editorDefinesChanged() const +BaseEditorDocumentParser::InMemoryInfo::InMemoryInfo(bool withModifiedFiles) + : workingCopy(CppTools::CppModelManager::instance()->workingCopy()) { - return m_editorDefinesChangedSinceLastUpdate; -} - -void BaseEditorDocumentParser::resetEditorDefinesChanged() -{ - m_editorDefinesChangedSinceLastUpdate = false; + if (withModifiedFiles) + modifiedFiles = CppTools::modifiedFiles(); } } // namespace CppTools diff --git a/src/plugins/cpptools/baseeditordocumentparser.h b/src/plugins/cpptools/baseeditordocumentparser.h index ddd12fbb0aa..b9c8ecaadcd 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.h +++ b/src/plugins/cpptools/baseeditordocumentparser.h @@ -33,6 +33,7 @@ #include "cppmodelmanager.h" #include "cpptools_global.h" +#include "cppworkingcopy.h" #include @@ -42,45 +43,54 @@ class CPPTOOLS_EXPORT BaseEditorDocumentParser : public QObject { Q_OBJECT +public: + static BaseEditorDocumentParser *get(const QString &filePath); + + struct Configuration { + bool usePrecompiledHeaders = false; + QByteArray editorDefines; + ProjectPart::Ptr manuallySetProjectPart; + }; + public: BaseEditorDocumentParser(const QString &filePath); virtual ~BaseEditorDocumentParser(); QString filePath() const; + Configuration configuration() const; + void setConfiguration(const Configuration &configuration); - virtual void update(WorkingCopy workingCopy) = 0; + struct CPPTOOLS_EXPORT InMemoryInfo { + InMemoryInfo(bool withModifiedFiles); + + WorkingCopy workingCopy; + Utils::FileNameList modifiedFiles; + }; + void update(const InMemoryInfo &info); ProjectPart::Ptr projectPart() const; - void setProjectPart(ProjectPart::Ptr projectPart); - - bool usePrecompiledHeaders() const; - void setUsePrecompiledHeaders(bool usePrecompiledHeaders); - - QByteArray editorDefines() const; - void setEditorDefines(const QByteArray &editorDefines); - -public: - static BaseEditorDocumentParser *get(const QString &filePath); protected: - void updateProjectPart(); + struct State { + QByteArray editorDefines; + ProjectPart::Ptr projectPart; + }; + State state() const; + void setState(const State &state); - bool editorDefinesChanged() const; - void resetEditorDefinesChanged(); + static ProjectPart::Ptr determineProjectPart(const QString &filePath, + const Configuration &config, + const State &state); -protected: - mutable QMutex m_mutex; + mutable QMutex m_stateAndConfigurationMutex; private: + virtual void updateHelper(const InMemoryInfo &inMemoryInfo) = 0; + const QString m_filePath; - - ProjectPart::Ptr m_projectPart; - ProjectPart::Ptr m_manuallySetProjectPart; - - bool m_usePrecompiledHeaders; - - QByteArray m_editorDefines; - bool m_editorDefinesChangedSinceLastUpdate; + Configuration m_configuration; + State m_state; + mutable QMutex m_updateIsRunning; }; } // namespace CppTools diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.cpp b/src/plugins/cpptools/baseeditordocumentprocessor.cpp index 373624a9ea5..d627ab3cb76 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.cpp +++ b/src/plugins/cpptools/baseeditordocumentprocessor.cpp @@ -31,6 +31,7 @@ #include "baseeditordocumentprocessor.h" #include "cppworkingcopy.h" +#include "cpptoolsreuse.h" #include "editordocumenthandle.h" #include @@ -118,7 +119,7 @@ QList BaseEditorDocumentProcessor::toTextEditorSelect void BaseEditorDocumentProcessor::runParser(QFutureInterface &future, BaseEditorDocumentParser *parser, - WorkingCopy workingCopy) + BaseEditorDocumentParser::InMemoryInfo info) { future.setProgressRange(0, 1); if (future.isCanceled()) { @@ -126,7 +127,7 @@ void BaseEditorDocumentProcessor::runParser(QFutureInterface &future, return; } - parser->update(workingCopy); + parser->update(info); CppModelManager::instance() ->finishedRefreshingSourceFiles(QSet() << parser->filePath()); diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 4f30a519e33..76c6c057eaa 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -86,7 +86,7 @@ protected: static void runParser(QFutureInterface &future, CppTools::BaseEditorDocumentParser *parser, - CppTools::WorkingCopy workingCopy); + BaseEditorDocumentParser::InMemoryInfo info); // Convenience QString filePath() const { return m_baseTextDocument->filePath().toString(); } diff --git a/src/plugins/cpptools/builtineditordocumentparser.cpp b/src/plugins/cpptools/builtineditordocumentparser.cpp index 24ae027bfc4..41035333f60 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.cpp +++ b/src/plugins/cpptools/builtineditordocumentparser.cpp @@ -40,19 +40,22 @@ using namespace CppTools::Internal; BuiltinEditorDocumentParser::BuiltinEditorDocumentParser(const QString &filePath) : BaseEditorDocumentParser(filePath) - , m_forceSnapshotInvalidation(false) - , m_releaseSourceAndAST(true) { qRegisterMetaType("CPlusPlus::Snapshot"); } -void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) +void BuiltinEditorDocumentParser::updateHelper(const InMemoryInfo &info) { - QMutexLocker locker(&m_mutex); - if (filePath().isEmpty()) return; + const Configuration baseConfig = configuration(); + const bool releaseSourceAndAST_ = releaseSourceAndAST(); + + State baseState = state(); + ExtraState state = extraState(); + WorkingCopy workingCopy = info.workingCopy; + bool invalidateSnapshot = false, invalidateConfig = false, editorDefinesChanged_ = false; CppModelManager *modelManager = CppModelManager::instance(); @@ -62,52 +65,52 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) QString projectConfigFile; LanguageFeatures features = LanguageFeatures::defaultFeatures(); - updateProjectPart(); + baseState.projectPart = determineProjectPart(filePath(), baseConfig, baseState); - if (m_forceSnapshotInvalidation) { + if (state.forceSnapshotInvalidation) { invalidateSnapshot = true; - m_forceSnapshotInvalidation = false; + state.forceSnapshotInvalidation = false; } - if (const ProjectPart::Ptr part = projectPart()) { + if (const ProjectPart::Ptr part = baseState.projectPart) { configFile += part->toolchainDefines; configFile += part->projectDefines; headerPaths = part->headerPaths; projectConfigFile = part->projectConfigFile; - if (usePrecompiledHeaders()) + if (baseConfig.usePrecompiledHeaders) precompiledHeaders = part->precompiledHeaders; features = part->languageFeatures; } - if (configFile != m_configFile) { - m_configFile = configFile; + if (configFile != state.configFile) { + state.configFile = configFile; invalidateSnapshot = true; invalidateConfig = true; } - if (editorDefinesChanged()) { + if (baseConfig.editorDefines != baseState.editorDefines) { + baseState.editorDefines = baseConfig.editorDefines; invalidateSnapshot = true; editorDefinesChanged_ = true; - resetEditorDefinesChanged(); } - if (headerPaths != m_headerPaths) { - m_headerPaths = headerPaths; + if (headerPaths != state.headerPaths) { + state.headerPaths = headerPaths; invalidateSnapshot = true; } - if (projectConfigFile != m_projectConfigFile) { - m_projectConfigFile = projectConfigFile; + if (projectConfigFile != state.projectConfigFile) { + state.projectConfigFile = projectConfigFile; invalidateSnapshot = true; } - if (precompiledHeaders != m_precompiledHeaders) { - m_precompiledHeaders = precompiledHeaders; + if (precompiledHeaders != state.precompiledHeaders) { + state.precompiledHeaders = precompiledHeaders; invalidateSnapshot = true; } unsigned rev = 0; - if (Document::Ptr doc = document()) + if (Document::Ptr doc = state.snapshot.document(filePath())) rev = doc->revision(); else invalidateSnapshot = true; @@ -115,26 +118,26 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) Snapshot globalSnapshot = modelManager->snapshot(); if (invalidateSnapshot) { - m_snapshot = Snapshot(); + state.snapshot = Snapshot(); } else { // Remove changed files from the snapshot QSet toRemove; - foreach (const Document::Ptr &doc, m_snapshot) { + foreach (const Document::Ptr &doc, state.snapshot) { const Utils::FileName fileName = Utils::FileName::fromString(doc->fileName()); if (workingCopy.contains(fileName)) { if (workingCopy.get(fileName).second != doc->editorRevision()) - addFileAndDependencies(&toRemove, fileName); + addFileAndDependencies(&state.snapshot, &toRemove, fileName); continue; } Document::Ptr otherDoc = globalSnapshot.document(fileName); if (!otherDoc.isNull() && otherDoc->revision() != doc->revision()) - addFileAndDependencies(&toRemove, fileName); + addFileAndDependencies(&state.snapshot, &toRemove, fileName); } if (!toRemove.isEmpty()) { invalidateSnapshot = true; foreach (const Utils::FileName &fileName, toRemove) - m_snapshot.remove(fileName); + state.snapshot.remove(fileName); } } @@ -142,19 +145,19 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) if (invalidateSnapshot) { const QString configurationFileName = modelManager->configurationFileName(); if (invalidateConfig) - m_snapshot.remove(configurationFileName); - if (!m_snapshot.contains(configurationFileName)) - workingCopy.insert(configurationFileName, m_configFile); - m_snapshot.remove(filePath()); + state.snapshot.remove(configurationFileName); + if (!state.snapshot.contains(configurationFileName)) + workingCopy.insert(configurationFileName, state.configFile); + state.snapshot.remove(filePath()); static const QString editorDefinesFileName = CppModelManager::editorConfigurationFileName(); if (editorDefinesChanged_) { - m_snapshot.remove(editorDefinesFileName); - workingCopy.insert(editorDefinesFileName, editorDefines()); + state.snapshot.remove(editorDefinesFileName); + workingCopy.insert(editorDefinesFileName, baseState.editorDefines); } - CppSourceProcessor sourceProcessor(m_snapshot, [&](const Document::Ptr &doc) { + CppSourceProcessor sourceProcessor(state.snapshot, [&](const Document::Ptr &doc) { const QString fileName = doc->fileName(); const bool isInEditor = fileName == filePath(); Document::Ptr otherDoc = modelManager->document(fileName); @@ -163,68 +166,64 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) newRev = qMax(rev + 1, newRev); doc->setRevision(newRev); modelManager->emitDocumentUpdated(doc); - if (m_releaseSourceAndAST) + if (releaseSourceAndAST_) doc->releaseSourceAndAST(); }); Snapshot globalSnapshot = modelManager->snapshot(); globalSnapshot.remove(filePath()); sourceProcessor.setGlobalSnapshot(globalSnapshot); sourceProcessor.setWorkingCopy(workingCopy); - sourceProcessor.setHeaderPaths(m_headerPaths); + sourceProcessor.setHeaderPaths(state.headerPaths); sourceProcessor.setLanguageFeatures(features); sourceProcessor.run(configurationFileName); - if (!m_projectConfigFile.isEmpty()) - sourceProcessor.run(m_projectConfigFile); - if (usePrecompiledHeaders()) { - foreach (const QString &precompiledHeader, m_precompiledHeaders) + if (!state.projectConfigFile.isEmpty()) + sourceProcessor.run(state.projectConfigFile); + if (baseConfig.usePrecompiledHeaders) { + foreach (const QString &precompiledHeader, state.precompiledHeaders) sourceProcessor.run(precompiledHeader); } - if (!editorDefines().isEmpty()) + if (!baseState.editorDefines.isEmpty()) sourceProcessor.run(editorDefinesFileName); - sourceProcessor.run(filePath(), usePrecompiledHeaders() ? m_precompiledHeaders - : QStringList()); - m_snapshot = sourceProcessor.snapshot(); - Snapshot newSnapshot = m_snapshot.simplified(document()); - for (Snapshot::const_iterator i = m_snapshot.begin(), ei = m_snapshot.end(); i != ei; ++i) { + sourceProcessor.run(filePath(), baseConfig.usePrecompiledHeaders ? state.precompiledHeaders + : QStringList()); + state.snapshot = sourceProcessor.snapshot(); + Snapshot newSnapshot = state.snapshot.simplified(state.snapshot.document(filePath())); + for (Snapshot::const_iterator i = state.snapshot.begin(), ei = state.snapshot.end(); i != ei; ++i) { if (Client::isInjectedFile(i.key().toString())) newSnapshot.insert(i.value()); } - m_snapshot = newSnapshot; - m_snapshot.updateDependencyTable(); - - emit finished(document(), m_snapshot); + state.snapshot = newSnapshot; + state.snapshot.updateDependencyTable(); } + + setState(baseState); + setExtraState(state); + + if (invalidateSnapshot) + emit finished(state.snapshot.document(filePath()), state.snapshot); } void BuiltinEditorDocumentParser::releaseResources() { - QMutexLocker locker(&m_mutex); - m_snapshot = Snapshot(); - m_forceSnapshotInvalidation = true; + ExtraState s = extraState(); + s.snapshot = Snapshot(); + s.forceSnapshotInvalidation = true; + setExtraState(s); } Document::Ptr BuiltinEditorDocumentParser::document() const { - QMutexLocker locker(&m_mutex); - return m_snapshot.document(filePath()); + return extraState().snapshot.document(filePath()); } Snapshot BuiltinEditorDocumentParser::snapshot() const { - QMutexLocker locker(&m_mutex); - return m_snapshot; + return extraState().snapshot; } ProjectPart::HeaderPaths BuiltinEditorDocumentParser::headerPaths() const { - QMutexLocker locker(&m_mutex); - return m_headerPaths; -} - -void BuiltinEditorDocumentParser::setReleaseSourceAndAST(bool onoff) -{ - QMutexLocker locker(&m_mutex); - m_releaseSourceAndAST = onoff; + return extraState().headerPaths; } BuiltinEditorDocumentParser *BuiltinEditorDocumentParser::get(const QString &filePath) @@ -234,12 +233,39 @@ BuiltinEditorDocumentParser *BuiltinEditorDocumentParser::get(const QString &fil return 0; } -void BuiltinEditorDocumentParser::addFileAndDependencies(QSet *toRemove, +void BuiltinEditorDocumentParser::addFileAndDependencies(Snapshot *snapshot, + QSet *toRemove, const Utils::FileName &fileName) const { + QTC_ASSERT(snapshot, return); + toRemove->insert(fileName); if (fileName != Utils::FileName::fromString(filePath())) { - Utils::FileNameList deps = m_snapshot.filesDependingOn(fileName); + Utils::FileNameList deps = snapshot->filesDependingOn(fileName); toRemove->unite(QSet::fromList(deps)); } } + +BuiltinEditorDocumentParser::ExtraState BuiltinEditorDocumentParser::extraState() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_extraState; +} + +void BuiltinEditorDocumentParser::setExtraState(const ExtraState &extraState) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_extraState = extraState; +} + +bool BuiltinEditorDocumentParser::releaseSourceAndAST() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_releaseSourceAndAST; +} + +void BuiltinEditorDocumentParser::setReleaseSourceAndAST(bool release) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_releaseSourceAndAST = release; +} diff --git a/src/plugins/cpptools/builtineditordocumentparser.h b/src/plugins/cpptools/builtineditordocumentparser.h index ebd8cc28fd5..c27b3366227 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.h +++ b/src/plugins/cpptools/builtineditordocumentparser.h @@ -37,7 +37,6 @@ #include -#include #include namespace CppTools { @@ -49,14 +48,14 @@ class CPPTOOLS_EXPORT BuiltinEditorDocumentParser : public BaseEditorDocumentPar public: BuiltinEditorDocumentParser(const QString &filePath); - void update(WorkingCopy workingCopy) override; - void releaseResources(); + bool releaseSourceAndAST() const; + void setReleaseSourceAndAST(bool release); CPlusPlus::Document::Ptr document() const; CPlusPlus::Snapshot snapshot() const; ProjectPart::HeaderPaths headerPaths() const; - void setReleaseSourceAndAST(bool onoff); + void releaseResources(); signals: void finished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot); @@ -65,18 +64,26 @@ public: static BuiltinEditorDocumentParser *get(const QString &filePath); private: - void addFileAndDependencies(QSet *toRemove, const Utils::FileName &fileName) const; + void updateHelper(const InMemoryInfo &info) override; + void addFileAndDependencies(CPlusPlus::Snapshot *snapshot, + QSet *toRemove, + const Utils::FileName &fileName) const; -private: - QByteArray m_configFile; + struct ExtraState { + QByteArray configFile; - ProjectPart::HeaderPaths m_headerPaths; - QString m_projectConfigFile; - QStringList m_precompiledHeaders; + ProjectPart::HeaderPaths headerPaths; + QString projectConfigFile; + QStringList precompiledHeaders; - CPlusPlus::Snapshot m_snapshot; - bool m_forceSnapshotInvalidation; - bool m_releaseSourceAndAST; + CPlusPlus::Snapshot snapshot; + bool forceSnapshotInvalidation = false; + }; + ExtraState extraState() const; + void setExtraState(const ExtraState &extraState); + + bool m_releaseSourceAndAST = true; + ExtraState m_extraState; }; } // namespace CppTools diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.cpp b/src/plugins/cpptools/builtineditordocumentprocessor.cpp index 12bfe1531c3..b61f698ab46 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.cpp +++ b/src/plugins/cpptools/builtineditordocumentprocessor.cpp @@ -43,8 +43,8 @@ #include #include -#include #include +#include #include @@ -134,7 +134,10 @@ BuiltinEditorDocumentProcessor::BuiltinEditorDocumentProcessor( using namespace Internal; QSharedPointer cms = CppToolsPlugin::instance()->codeModelSettings(); - m_parser.setUsePrecompiledHeaders(cms->pchUsage() != CppCodeModelSettings::PchUse_None); + + BaseEditorDocumentParser::Configuration config = m_parser.configuration(); + config.usePrecompiledHeaders = cms->pchUsage() != CppCodeModelSettings::PchUse_None; + m_parser.setConfiguration(config); if (m_semanticHighlighter) { m_semanticHighlighter->setHighlightingRunner( @@ -163,7 +166,9 @@ BuiltinEditorDocumentProcessor::~BuiltinEditorDocumentProcessor() void BuiltinEditorDocumentProcessor::run() { - m_parserFuture = QtConcurrent::run(&runParser, parser(), CppTools::CppModelManager::instance()->workingCopy()); + m_parserFuture = QtConcurrent::run(&runParser, + parser(), + BuiltinEditorDocumentParser::InMemoryInfo(false)); } BaseEditorDocumentParser *BuiltinEditorDocumentProcessor::parser() diff --git a/src/plugins/cpptools/builtinindexingsupport.cpp b/src/plugins/cpptools/builtinindexingsupport.cpp index fc06b95024b..ef03f75c58d 100644 --- a/src/plugins/cpptools/builtinindexingsupport.cpp +++ b/src/plugins/cpptools/builtinindexingsupport.cpp @@ -162,7 +162,7 @@ void indexFindErrors(QFutureInterface &future, const ParseParams params) // Parse the file as precisely as possible BuiltinEditorDocumentParser parser(file); parser.setReleaseSourceAndAST(false); - parser.update(params.workingCopy); + parser.update(BuiltinEditorDocumentParser::InMemoryInfo(false)); CPlusPlus::Document::Ptr document = parser.document(); QTC_ASSERT(document, return); diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp index 8f9ab8a456c..96f6a6efb2b 100644 --- a/src/plugins/cpptools/cppcompletionassist.cpp +++ b/src/plugins/cpptools/cppcompletionassist.cpp @@ -2188,7 +2188,7 @@ void CppCompletionAssistInterface::getCppSpecifics() const m_gotCppSpecifics = true; if (BuiltinEditorDocumentParser *parser = BuiltinEditorDocumentParser::get(fileName())) { - parser->update(m_workingCopy); + parser->update(BuiltinEditorDocumentParser::InMemoryInfo(false)); m_snapshot = parser->snapshot(); m_headerPaths = parser->headerPaths(); if (Document::Ptr document = parser->document()) diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 7140ffecc25..7aa40b357b7 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -167,13 +167,11 @@ private: const QString &m_filePath; }; -void waitForProcessedEditorDocument(const QString &filePath) +ProjectPart::Ptr projectPartOfEditorDocument(const QString &filePath) { - CppEditorDocumentHandle *editorDocument - = CppModelManager::instance()->cppEditorDocument(filePath); - QVERIFY(editorDocument); - while (editorDocument->processor()->isParserRunning()) - QCoreApplication::processEvents(); + auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + QTC_ASSERT(editorDocument, return ProjectPart::Ptr()); + return editorDocument->processor()->parser()->projectPart(); } } // anonymous namespace @@ -914,8 +912,10 @@ void CppToolsPlugin::test_modelmanager_precompiled_headers() auto *parser = BuiltinEditorDocumentParser::get(fileName); QVERIFY(parser); - parser->setUsePrecompiledHeaders(true); - parser->update(mm->workingCopy()); + BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.usePrecompiledHeaders = true; + parser->setConfiguration(config); + parser->update(BuiltinEditorDocumentParser::InMemoryInfo(false)); // Check if defines from pch are considered Document::Ptr document = mm->document(fileName); @@ -995,8 +995,10 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() const QString filePath = editor->document()->filePath().toString(); BaseEditorDocumentParser *parser = BaseEditorDocumentParser::get(filePath); - parser->setEditorDefines(editorDefines.toUtf8()); - parser->update(mm->workingCopy()); + BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.editorDefines = editorDefines.toUtf8(); + parser->setConfiguration(config); + parser->update(BuiltinEditorDocumentParser::InMemoryInfo(false)); Document::Ptr doc = mm->document(main1File); QCOMPARE(nameOfFirstDeclaration(doc), firstDeclarationName); @@ -1006,7 +1008,6 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); MyTestDataDir testDataDirectory(_("testdata_defines")); const QString fileA = testDataDirectory.file(_("main1.cpp")); // content not relevant @@ -1017,10 +1018,8 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QVERIFY(editorA); EditorCloser closerA(editorA); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - - CppEditorDocumentHandle *editorDocumentA = mm->cppEditorDocument(fileA); - QVERIFY(editorDocumentA); - ProjectPart::Ptr documentAProjectPart = editorDocumentA->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); + ProjectPart::Ptr documentAProjectPart = projectPartOfEditorDocument(fileA); QVERIFY(!documentAProjectPart->project); // Open file B in editor @@ -1028,10 +1027,8 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QVERIFY(editorB); EditorCloser closerB(editorB); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 2); - - CppEditorDocumentHandle *editorDocumentB = mm->cppEditorDocument(fileB); - QVERIFY(editorDocumentB); - ProjectPart::Ptr documentBProjectPart = editorDocumentB->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); + ProjectPart::Ptr documentBProjectPart = projectPartOfEditorDocument(fileB); QVERIFY(!documentBProjectPart->project); // Switch back to document A @@ -1053,16 +1050,14 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() helper.updateProjectInfo(pi); // ... and check for updated editor document A - while (editorDocumentA->processor()->isParserRunning()) - QCoreApplication::processEvents(); - documentAProjectPart = editorDocumentA->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); + documentAProjectPart = projectPartOfEditorDocument(fileA); QCOMPARE(documentAProjectPart->project, project); // Switch back to document B and check if that's updated, too Core::EditorManager::activateEditor(editorB); - while (editorDocumentB->processor()->isParserRunning()) - QCoreApplication::processEvents(); - documentBProjectPart = editorDocumentB->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); + documentBProjectPart = projectPartOfEditorDocument(fileB); QCOMPARE(documentBProjectPart->project, project); } @@ -1131,7 +1126,7 @@ void CppToolsPlugin::test_modelmanager_documentsAndRevisions() TextEditor::BaseTextEditor *editor1; QVERIFY(helper.openBaseTextEditor(filePath1, &editor1)); helper.closeEditorAtEndOfTestCase(editor1); - waitForProcessedEditorDocument(filePath1); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath1)); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 2U); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 1U); @@ -1144,7 +1139,7 @@ void CppToolsPlugin::test_modelmanager_documentsAndRevisions() TextEditor::BaseTextEditor *editor2; QVERIFY(helper.openBaseTextEditor(filePath2, &editor2)); helper.closeEditorAtEndOfTestCase(editor2); - waitForProcessedEditorDocument(filePath2); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath2)); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 3U); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 3U); diff --git a/src/plugins/cpptools/cppsourceprocessor_test.cpp b/src/plugins/cpptools/cppsourceprocessor_test.cpp index 378d1ae4134..518eece2262 100644 --- a/src/plugins/cpptools/cppsourceprocessor_test.cpp +++ b/src/plugins/cpptools/cppsourceprocessor_test.cpp @@ -137,6 +137,7 @@ void CppToolsPlugin::test_cppsourceprocessor_includes_cyclic() const QString filePath = editor->document()->filePath().toString(); auto *processor = BaseEditorDocumentProcessor::get(filePath); QVERIFY(processor); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath)); Snapshot snapshot = processor->snapshot(); QCOMPARE(snapshot.size(), 3); // Configuration file included diff --git a/src/plugins/cpptools/cpptoolsreuse.cpp b/src/plugins/cpptools/cpptoolsreuse.cpp index 1478d34a6fa..8c4962ef344 100644 --- a/src/plugins/cpptools/cpptoolsreuse.cpp +++ b/src/plugins/cpptools/cpptoolsreuse.cpp @@ -32,6 +32,7 @@ #include "cpptoolsplugin.h" +#include #include #include #include @@ -287,4 +288,13 @@ bool skipFileDueToSizeLimit(const QFileInfo &fileInfo, int limitInMB) return false; } +Utils::FileNameList modifiedFiles() +{ + Utils::FileNameList files; + foreach (Core::IDocument *doc, Core::DocumentManager::modifiedDocuments()) + files.append(doc->filePath()); + files.removeDuplicates(); + return files; +} + } // CppTools diff --git a/src/plugins/cpptools/cpptoolsreuse.h b/src/plugins/cpptools/cpptoolsreuse.h index 62d0df71ccf..cb6e0246e68 100644 --- a/src/plugins/cpptools/cpptoolsreuse.h +++ b/src/plugins/cpptools/cpptoolsreuse.h @@ -44,6 +44,10 @@ class QStringRef; class QTextCursor; QT_END_NAMESPACE +namespace Utils { +class FileNameList; +} // namespace Utils + namespace CPlusPlus { class Macro; class Symbol; @@ -52,6 +56,8 @@ class LookupContext; namespace CppTools { +Utils::FileNameList CPPTOOLS_EXPORT modifiedFiles(); + void CPPTOOLS_EXPORT moveCursorToEndOfIdentifier(QTextCursor *tc); void CPPTOOLS_EXPORT moveCursorToStartOfIdentifier(QTextCursor *tc); diff --git a/src/plugins/cpptools/cpptoolstestcase.cpp b/src/plugins/cpptools/cpptoolstestcase.cpp index 8d2a3684a0c..8cd7953320e 100644 --- a/src/plugins/cpptools/cpptoolstestcase.cpp +++ b/src/plugins/cpptools/cpptoolstestcase.cpp @@ -29,6 +29,10 @@ ****************************************************************************/ #include "cpptoolstestcase.h" + +#include "baseeditordocumentparser.h" +#include "baseeditordocumentprocessor.h" +#include "editordocumenthandle.h" #include "cppmodelmanager.h" #include "cppworkingcopy.h" @@ -140,6 +144,31 @@ bool TestCase::garbageCollectGlobalSnapshot() return globalSnapshot().isEmpty(); } +static bool waitForProcessedEditorDocument_internal(CppEditorDocumentHandle *editorDocument, + int timeOutInMs) +{ + QTC_ASSERT(editorDocument, return false); + + QTime timer; + timer.start(); + + forever { + if (!editorDocument->processor()->isParserRunning()) + return true; + if (timer.elapsed() > timeOutInMs) + return false; + + QCoreApplication::processEvents(); + QThread::msleep(20); + } +} + +bool TestCase::waitForProcessedEditorDocument(const QString &filePath, int timeOutInMs) +{ + auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + return waitForProcessedEditorDocument_internal(editorDocument, timeOutInMs); +} + bool TestCase::parseFiles(const QSet &filePaths) { CppModelManager::instance()->updateSourceFiles(filePaths).waitForFinished(); diff --git a/src/plugins/cpptools/cpptoolstestcase.h b/src/plugins/cpptools/cpptoolstestcase.h index 5a95952365d..d35e93c3b4c 100644 --- a/src/plugins/cpptools/cpptoolstestcase.h +++ b/src/plugins/cpptools/cpptoolstestcase.h @@ -95,6 +95,8 @@ public: static CPlusPlus::Snapshot globalSnapshot(); static bool garbageCollectGlobalSnapshot(); + static bool waitForProcessedEditorDocument(const QString &filePath, int timeOutInMs = 5000); + enum { defaultTimeOutInMs = 30 * 1000 /*= 30 secs*/ }; static bool waitUntilCppModelManagerIsAwareOf( ProjectExplorer::Project *project, diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 969b011a9c5..baaef60d340 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -802,6 +802,7 @@ bool Breakpoint::isOneShot() const { return parameters().oneShot; } void Breakpoint::removeAlienBreakpoint() { + b->m_state = BreakpointRemoveProceeding; b->deleteThis(); } diff --git a/src/plugins/debugger/breakpoint.h b/src/plugins/debugger/breakpoint.h index 1d442f39801..64afe5ab4d0 100644 --- a/src/plugins/debugger/breakpoint.h +++ b/src/plugins/debugger/breakpoint.h @@ -137,23 +137,23 @@ enum BreakpointPathUsage enum BreakpointParts { - NoParts = 0, - FileAndLinePart = 0x1, - FunctionPart = 0x2, - AddressPart = 0x4, - ExpressionPart = 0x8, - ConditionPart = 0x10, - IgnoreCountPart = 0x20, - ThreadSpecPart = 0x40, - ModulePart = 0x80, - TracePointPart = 0x100, + NoParts = 0, + FileAndLinePart = (1 << 0), + FunctionPart = (1 << 1), + AddressPart = (1 << 2), + ExpressionPart = (1 << 3), + ConditionPart = (1 << 4), + IgnoreCountPart = (1 << 5), + ThreadSpecPart = (1 << 6), + ModulePart = (1 << 7), + TracePointPart = (1 << 8), - EnabledPart = 0x200, - TypePart = 0x400, - PathUsagePart = 0x800, - CommandPart = 0x1000, - MessagePart = 0x2000, - OneShotPart = 0x4000, + EnabledPart = (1 << 9), + TypePart = (1 << 10), + PathUsagePart = (1 << 11), + CommandPart = (1 << 12), + MessagePart = (1 << 13), + OneShotPart = (1 << 14), AllConditionParts = ConditionPart|IgnoreCountPart|ThreadSpecPart |OneShotPart, diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index 86fdf481e0c..53dc281ff90 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -383,13 +383,17 @@ void BreakpointDialog::setPartsEnabled(unsigned partsMask) m_lineEditModule->setEnabled(partsMask & ModulePart); m_labelTracepoint->setEnabled(partsMask & TracePointPart); + m_labelTracepoint->hide(); m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart); + m_checkBoxTracepoint->hide(); - m_labelCommands->setEnabled(partsMask & TracePointPart); - m_textEditCommands->setEnabled(partsMask & TracePointPart); + m_labelCommands->setEnabled(partsMask & CommandPart); + m_textEditCommands->setEnabled(partsMask & CommandPart); m_labelMessage->setEnabled(partsMask & TracePointPart); + m_labelMessage->hide(); m_lineEditMessage->setEnabled(partsMask & TracePointPart); + m_lineEditMessage->hide(); } void BreakpointDialog::clearOtherParts(unsigned partsMask) @@ -420,9 +424,10 @@ void BreakpointDialog::clearOtherParts(unsigned partsMask) if (partsMask & OneShotPart) m_checkBoxOneShot->setChecked(false); + if (invertedPartsMask & CommandPart) + m_textEditCommands->clear(); if (invertedPartsMask & TracePointPart) { m_checkBoxTracepoint->setChecked(false); - m_textEditCommands->clear(); m_lineEditMessage->clear(); } } @@ -456,9 +461,10 @@ void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) if (partsMask & OneShotPart) data->oneShot = m_checkBoxOneShot->isChecked(); + if (partsMask & CommandPart) + data->command = m_textEditCommands->toPlainText().trimmed(); if (partsMask & TracePointPart) { data->tracepoint = m_checkBoxTracepoint->isChecked(); - data->command = m_textEditCommands->toPlainText().trimmed(); data->message = m_lineEditMessage->text(); } } @@ -467,7 +473,6 @@ void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data) { m_checkBoxEnabled->setChecked(data.enabled); m_comboBoxPathUsage->setCurrentIndex(data.pathUsage); - m_textEditCommands->setPlainText(data.command); m_lineEditMessage->setText(data.message); if (mask & FileAndLinePart) { @@ -508,6 +513,8 @@ void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data) m_checkBoxOneShot->setChecked(data.oneShot); if (mask & TracePointPart) m_checkBoxTracepoint->setChecked(data.tracepoint); + if (mask & CommandPart) + m_textEditCommands->setPlainText(data.command); } void BreakpointDialog::typeChanged(int) @@ -521,10 +528,10 @@ void BreakpointDialog::typeChanged(int) case LastBreakpointType: break; case BreakpointByFileAndLine: - getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart, &m_savedParameters); + getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters); break; case BreakpointByFunction: - getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart, &m_savedParameters); + getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters); break; case BreakpointAtThrow: case BreakpointAtCatch: @@ -537,10 +544,10 @@ void BreakpointDialog::typeChanged(int) break; case BreakpointByAddress: case WatchpointAtAddress: - getParts(AddressPart|AllConditionParts|TracePointPart, &m_savedParameters); + getParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters); break; case WatchpointAtExpression: - getParts(ExpressionPart|AllConditionParts|TracePointPart, &m_savedParameters); + getParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters); break; case BreakpointOnQmlSignalEmit: getParts(FunctionPart, &m_savedParameters); @@ -552,14 +559,14 @@ void BreakpointDialog::typeChanged(int) case LastBreakpointType: break; case BreakpointByFileAndLine: - setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart, m_savedParameters); - setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart); - clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart); + setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters); + setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart); + clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart); break; case BreakpointByFunction: - setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart, m_savedParameters); - setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart); - clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart); + setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters); + setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart); + clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart); break; case BreakpointAtThrow: case BreakpointAtCatch: @@ -567,8 +574,8 @@ void BreakpointDialog::typeChanged(int) case BreakpointAtExec: //case BreakpointAtVFork: case BreakpointAtSysCall: - clearOtherParts(AllConditionParts|ModulePart|TracePointPart); - setPartsEnabled(AllConditionParts|TracePointPart); + clearOtherParts(AllConditionParts|ModulePart|TracePointPart|CommandPart); + setPartsEnabled(AllConditionParts|TracePointPart|CommandPart); break; case BreakpointAtJavaScriptThrow: clearOtherParts(AllParts); @@ -581,14 +588,14 @@ void BreakpointDialog::typeChanged(int) break; case BreakpointByAddress: case WatchpointAtAddress: - setParts(AddressPart|AllConditionParts|TracePointPart, m_savedParameters); - setPartsEnabled(AddressPart|AllConditionParts|TracePointPart); - clearOtherParts(AddressPart|AllConditionParts|TracePointPart); + setParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters); + setPartsEnabled(AddressPart|AllConditionParts|TracePointPart|CommandPart); + clearOtherParts(AddressPart|AllConditionParts|TracePointPart|CommandPart); break; case WatchpointAtExpression: - setParts(ExpressionPart|AllConditionParts|TracePointPart, m_savedParameters); - setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart); - clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart); + setParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters); + setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart|CommandPart); + clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart); break; case BreakpointOnQmlSignalEmit: setParts(FunctionPart, m_savedParameters); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index f05e90d9851..96638cef077 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -134,6 +134,29 @@ Location::Location(const StackFrame &frame, bool marker) } +class LocationMark : public TextMark +{ +public: + LocationMark(DebuggerEngine *engine, const QString &file, int line) + : TextMark(file, line, Constants::TEXT_MARK_CATEGORY_LOCATION), m_engine(engine) + {} + +private: + bool isDraggable() const { return true; } + + void dragToLine(int line) + { + if (m_engine) { + ContextData data; + data.fileName = fileName(); + data.lineNumber = line; + m_engine->executeJumpToLine(data); + } + } + + QPointer m_engine; +}; + ////////////////////////////////////////////////////////////////////// // // DebuggerEnginePrivate @@ -319,7 +342,7 @@ public: DisassemblerAgent m_disassemblerAgent; MemoryAgent m_memoryAgent; - QScopedPointer m_locationMark; + QScopedPointer m_locationMark; QTimer m_locationTimer; bool m_isStateDebugging; @@ -610,7 +633,7 @@ void DebuggerEngine::gotoLocation(const Location &loc) editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true); if (loc.needsMarker()) { - d->m_locationMark.reset(new TextMark(file, line, Constants::TEXT_MARK_CATEGORY_LOCATION)); + d->m_locationMark.reset(new LocationMark(this, file, line)); d->m_locationMark->setIcon(Internal::locationMarkIcon()); d->m_locationMark->setPriority(TextMark::HighPriority); } @@ -1976,13 +1999,18 @@ bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) con return state() == InferiorStopOk && context.isCppEditor; } -void DebuggerEngine::updateWatchData(const QByteArray &iname) +void DebuggerEngine::updateItem(const QByteArray &iname) { UpdateParameters params; params.partialVariable = iname; doUpdateLocals(params); } +void DebuggerEngine::expandItem(const QByteArray &iname) +{ + updateItem(iname); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index eea8e4e7671..c830a0ded35 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -65,6 +65,7 @@ class MemoryAgent; class WatchData; class WatchItem; class BreakHandler; +class LocationMark; class ModulesHandler; class RegisterHandler; class StackHandler; @@ -191,7 +192,8 @@ public: DebuggerRunParameters &runParameters(); virtual bool canHandleToolTip(const DebuggerToolTipContext &) const; - virtual void updateWatchData(const QByteArray &iname); + virtual void expandItem(const QByteArray &iname); // Called when item in tree gets expanded. + virtual void updateItem(const QByteArray &iname); // Called for fresh watch items. virtual void selectWatchData(const QByteArray &iname); virtual void startDebugger(DebuggerRunControl *runControl); @@ -440,6 +442,7 @@ private: virtual void setState(DebuggerState state, bool forced = false); friend class DebuggerEnginePrivate; + friend class LocationMark; DebuggerEnginePrivate *d; }; diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index b4736f23280..0c3abc30320 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -252,8 +252,10 @@ void DebuggerItemManager::autoDetectGdbOrLldbDebuggers() foreach (const QString &base, path) { dir.setPath(base); foreach (const QString &entry, dir.entryList()) { - if (entry.startsWith(QLatin1String("lldb-platform-"))) + if (entry.startsWith(QLatin1String("lldb-platform-")) + || entry.startsWith(QLatin1String("lldb-gdbserver-"))) { continue; + } suspects.append(FileName::fromString(dir.absoluteFilePath(entry))); } } diff --git a/src/plugins/debugger/debuggerprotocol.cpp b/src/plugins/debugger/debuggerprotocol.cpp index f3515afe3a5..5be634c83f8 100644 --- a/src/plugins/debugger/debuggerprotocol.cpp +++ b/src/plugins/debugger/debuggerprotocol.cpp @@ -782,6 +782,16 @@ void DebuggerCommand::arg(const char *name, const char *value) args.append("\","); } +void DebuggerCommand::arg(const char *name, const QList &list) +{ + beginList(name); + foreach (int item, list) { + args.append(QByteArray::number(item)); + args.append(','); + } + endList(); +} + void DebuggerCommand::arg(const char *value) { args.append("\""); @@ -823,5 +833,13 @@ void DebuggerCommand::endGroup() args += "},"; } +QByteArray DebuggerCommand::arguments() const +{ + QByteArray result = args; + if (result.endsWith(',')) + result.chop(1); + return result; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerprotocol.h b/src/plugins/debugger/debuggerprotocol.h index 7bccb4356ba..eac5c03da28 100644 --- a/src/plugins/debugger/debuggerprotocol.h +++ b/src/plugins/debugger/debuggerprotocol.h @@ -32,6 +32,7 @@ #define DEBUGGER_PROTOCOL_H #include +#include #include #include @@ -64,10 +65,13 @@ public: void arg(const char *name, const QString &value); void arg(const char *name, const QByteArray &value); void arg(const char *name, const char *value); + void arg(const char *name, const QList &list); + void beginList(const char *name = 0); void endList(); void beginGroup(const char *name = 0); void endGroup(); + QByteArray arguments() const; QByteArray function; QByteArray args; diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp index 07656201092..e498dcebde3 100644 --- a/src/plugins/debugger/debuggertooltipmanager.cpp +++ b/src/plugins/debugger/debuggertooltipmanager.cpp @@ -1222,7 +1222,7 @@ static void slotTooltipOverrideRequested m_tooltips.push_back(tooltip); tooltip->setState(PendingUnshown); if (engine->canHandleToolTip(context)) { - engine->updateWatchData(context.iname); + engine->updateItem(context.iname); } else { ToolTip::show(point, DebuggerToolTipManager::tr("Expression too complex"), Internal::mainWindow()); diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp index 87f16d648af..c089e8f91c2 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.cpp +++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp @@ -59,8 +59,6 @@ void GdbAttachEngine::setupEngine() if (!runParameters().workingDirectory.isEmpty()) m_gdbProc.setWorkingDirectory(runParameters().workingDirectory); - if (runParameters().environment.size()) - m_gdbProc.setEnvironment(runParameters().environment); startGdb(); } diff --git a/src/plugins/debugger/gdb/coregdbadapter.cpp b/src/plugins/debugger/gdb/coregdbadapter.cpp index c81bef5a1b7..8b71bcb7077 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.cpp +++ b/src/plugins/debugger/gdb/coregdbadapter.cpp @@ -130,7 +130,6 @@ GdbCoreEngine::readExecutableNameFromCore(const QString &debuggerCommand, const CoreInfo cinfo; #if 0 ElfReader reader(coreFile); - cinfo.isCore = false; cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore)); cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); #else @@ -161,7 +160,6 @@ GdbCoreEngine::readExecutableNameFromCore(const QString &debuggerCommand, const } } } - cinfo.isCore = false; #endif return cinfo; } diff --git a/src/plugins/debugger/gdb/coregdbadapter.h b/src/plugins/debugger/gdb/coregdbadapter.h index bd0ee88ee6d..03780b569b8 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.h +++ b/src/plugins/debugger/gdb/coregdbadapter.h @@ -50,7 +50,7 @@ public: { QString rawStringFromCore; QString foundExecutableName; // empty if no corresponding exec could be found - bool isCore; + bool isCore = false; }; static CoreInfo readExecutableNameFromCore(const QString &debuggerCmd, const QString &coreFile); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index e6924371fd7..5eac84b1169 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -598,8 +598,9 @@ void GdbEngine::handleResponse(const QByteArray &buff) // This also triggers when a temporary breakpoint is hit. // We do not really want that, as this loses all information. // FIXME: Use a special marker for this case? - if (!bp.isOneShot()) - bp.removeAlienBreakpoint(); + // if (!bp.isOneShot()) ... is not sufficient. + // It keeps temporary "Jump" breakpoints alive. + bp.removeAlienBreakpoint(); } } else if (asyncClass == "cmd-param-changed") { // New since 2012-08-09 @@ -4260,6 +4261,18 @@ void GdbEngine::loadInitScript() } } +void GdbEngine::setEnvironmentVariables() +{ + Environment sysEnv = Environment::systemEnvironment(); + Environment runEnv = runParameters().environment; + foreach (const EnvironmentItem &item, sysEnv.diff(runEnv)) { + if (item.unset) + postCommand("unset environment " + item.name.toUtf8()); + else + postCommand("-gdb-set environment " + item.name.toUtf8() + '=' + item.value.toUtf8()); + } +} + void GdbEngine::reloadDebuggingHelpers() { runCommand("reloadDumpers"); @@ -4570,7 +4583,7 @@ bool GdbEngine::prepareCommand() QtcProcess::SplitError perr; rp.processArgs = QtcProcess::prepareArgs(rp.processArgs, &perr, HostOsInfo::hostOs(), - &rp.environment, &rp.workingDirectory).toWindowsArgs(); + nullptr, &rp.workingDirectory).toWindowsArgs(); if (perr != QtcProcess::SplitOk) { // perr == BadQuoting is never returned on Windows // FIXME? QTCREATORBUG-2809 diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index b06cd67bcdc..d994a1cbbdd 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -103,6 +103,7 @@ protected: ////////// Gdb Process Management ////////// void handleGdbExit(const DebuggerResponse &response); void loadInitScript(); + void setEnvironmentVariables(); // Something went wrong with the adapter *before* adapterStarted() was emitted. // Make sure to clean up everything before emitting this signal. diff --git a/src/plugins/debugger/gdb/gdbplainengine.cpp b/src/plugins/debugger/gdb/gdbplainengine.cpp index 1980f9c32b5..b49bb315475 100644 --- a/src/plugins/debugger/gdb/gdbplainengine.cpp +++ b/src/plugins/debugger/gdb/gdbplainengine.cpp @@ -57,6 +57,7 @@ GdbPlainEngine::GdbPlainEngine(const DebuggerRunParameters &startParameters) void GdbPlainEngine::setupInferior() { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); + setEnvironmentVariables(); if (!runParameters().processArgs.isEmpty()) { QString args = runParameters().processArgs; postCommand("-exec-arguments " + toLocalEncoding(args)); @@ -127,9 +128,6 @@ void GdbPlainEngine::setupEngine() if (!runParameters().workingDirectory.isEmpty()) m_gdbProc.setWorkingDirectory(runParameters().workingDirectory); - Utils::Environment env = runParameters().environment; - if (env.size()) - m_gdbProc.setEnvironment(env); startGdb(gdbArgs); } diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp index 617f087791c..95f99d136a1 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -91,8 +91,6 @@ void GdbRemoteServerEngine::setupEngine() } if (!runParameters().workingDirectory.isEmpty()) m_gdbProc.setWorkingDirectory(runParameters().workingDirectory); - if (runParameters().environment.size()) - m_gdbProc.setEnvironment(runParameters().environment); if (runParameters().remoteSetupNeeded) notifyEngineRequestRemoteSetup(); @@ -186,6 +184,8 @@ void GdbRemoteServerEngine::setupInferior() if (!args.isEmpty()) postCommand("-exec-arguments " + args.toLocal8Bit()); + setEnvironmentVariables(); + // This has to be issued before 'target remote'. On pre-7.0 the // command is not present and will result in ' No symbol table is // loaded. Use the "file" command.' as gdb tries to set the diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 47037761924..6d135df89ca 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -182,7 +182,7 @@ bool LldbEngine::prepareCommand() QtcProcess::SplitError perr; rp.processArgs = QtcProcess::prepareArgs(rp.processArgs, &perr, HostOsInfo::hostOs(), - &rp.environment, &rp.workingDirectory).toWindowsArgs(); + nullptr, &rp.workingDirectory).toWindowsArgs(); if (perr != QtcProcess::SplitOk) { // perr == BadQuoting is never returned on Windows // FIXME? QTCREATORBUG-2809 @@ -290,6 +290,17 @@ void LldbEngine::startLldbStage2() void LldbEngine::setupInferior() { + Environment sysEnv = Environment::systemEnvironment(); + Environment runEnv = runParameters().environment; + foreach (const EnvironmentItem &item, sysEnv.diff(runEnv)) { + DebuggerCommand cmd("executeDebuggerCommand"); + if (item.unset) + cmd.arg("command", "settings remove target.env-vars " + item.name.toUtf8()); + else + cmd.arg("command", "settings set target.env-vars " + item.name.toUtf8() + '=' + item.value.toUtf8()); + runCommand(cmd); + } + const QString path = stringSetting(ExtraDumperFile); if (!path.isEmpty() && QFileInfo(path).isReadable()) { DebuggerCommand cmd("addDumperModule"); @@ -300,7 +311,7 @@ void LldbEngine::setupInferior() const QString commands = stringSetting(ExtraDumperCommands); if (!commands.isEmpty()) { DebuggerCommand cmd("executeDebuggerCommand"); - cmd.arg("commands", commands.toUtf8()); + cmd.arg("command", commands.toUtf8()); runCommand(cmd); } diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 9c5666eed17..78435e0faa6 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -394,7 +394,7 @@ void PdbEngine::assignValueInDebugger(WatchItem *, const QString &expression, co updateLocals(); } -void PdbEngine::updateWatchData(const QByteArray &iname) +void PdbEngine::updateItem(const QByteArray &iname) { Q_UNUSED(iname); updateAll(); diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index bd16495fbcf..0d17912c879 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -96,7 +96,7 @@ private: bool supportsThreads() const { return true; } bool isSynchronous() const { return true; } - void updateWatchData(const QByteArray &iname); + void updateItem(const QByteArray &iname); QString mainPythonFile() const; QString pythonInterpreter() const; diff --git a/src/plugins/debugger/qml/qmlcppengine.cpp b/src/plugins/debugger/qml/qmlcppengine.cpp index 160720465ae..a4277005120 100644 --- a/src/plugins/debugger/qml/qmlcppengine.cpp +++ b/src/plugins/debugger/qml/qmlcppengine.cpp @@ -114,12 +114,20 @@ bool QmlCppEngine::canHandleToolTip(const DebuggerToolTipContext &ctx) const return success; } -void QmlCppEngine::updateWatchData(const QByteArray &iname) +void QmlCppEngine::updateItem(const QByteArray &iname) { if (iname.startsWith("inspect.")) - m_qmlEngine->updateWatchData(iname); + m_qmlEngine->updateItem(iname); else - m_activeEngine->updateWatchData(iname); + m_activeEngine->updateItem(iname); +} + +void QmlCppEngine::expandItem(const QByteArray &iname) +{ + if (iname.startsWith("inspect.")) + m_qmlEngine->expandItem(iname); + else + m_activeEngine->expandItem(iname); } void QmlCppEngine::selectWatchData(const QByteArray &iname) diff --git a/src/plugins/debugger/qml/qmlcppengine.h b/src/plugins/debugger/qml/qmlcppengine.h index 9985b747783..3b2ecc9137f 100644 --- a/src/plugins/debugger/qml/qmlcppengine.h +++ b/src/plugins/debugger/qml/qmlcppengine.h @@ -48,7 +48,8 @@ public: bool canDisplayTooltip() const; bool canHandleToolTip(const DebuggerToolTipContext &) const; - void updateWatchData(const QByteArray &iname); + void updateItem(const QByteArray &iname); + void expandItem(const QByteArray &iname); void selectWatchData(const QByteArray &iname); void watchPoint(const QPoint &); diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 46e2796446b..735840fee12 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -83,11 +84,14 @@ #endif # define XSDEBUG(s) qDebug() << s +#define CB(callback) [this](const QVariantMap &r) { callback(r); } + using namespace Core; using namespace ProjectExplorer; using namespace QmlDebug; using namespace QmlJS; using namespace TextEditor; +using namespace Utils; namespace Debugger { namespace Internal { @@ -116,6 +120,16 @@ struct QmlV8ObjectData QVariantList properties; }; +typedef std::function QmlCallback; + +struct LookupData +{ + QByteArray iname; + QString name; +}; + +typedef QMultiHash LookupItems; // id -> (iname, exp) + class QmlEnginePrivate : QmlDebugClient { public: @@ -130,17 +144,13 @@ public: void messageReceived(const QByteArray &data); void stateChanged(State state); - void connect(); - void disconnect(); - void continueDebugging(StepAction stepAction); - void evaluate(const QString expr, bool global = false, bool disableBreak = false, - int frame = -1, bool addContext = false); - void lookup(const QList handles, bool includeSource = false); - void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); - void frame(int number = -1); - void scope(int number = -1, int frameNumber = -1); + void evaluate(const QString expr, const QmlCallback &cb); + void lookup(const LookupItems &items); + void backtrace(); + void updateLocals(); + void scope(int number, int frameNumber = -1); void scripts(int types = 4, const QList ids = QList(), bool includeSource = false, const QVariant filter = QVariant()); @@ -150,38 +160,39 @@ public: void clearBreakpoint(int breakpoint); void setExceptionBreak(Exceptions type, bool enabled = false); - void version(); - void clearCache(); - - void sendAndLogV8Request(const QJsonObject &request); - - QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); - QJsonObject initObject(); - - void expandObject(const QByteArray &iname, quint64 objectId); void flushSendBuffer(); - void handleBacktrace(const QVariant &bodyVal, const QVariant &refsVal); - void handleLookup(const QVariant &bodyVal, const QVariant &refsVal); - void handleEvaluate(int sequence, bool success, const QVariant &bodyVal, const QVariant &refsVal); - void handleFrame(const QVariant &bodyVal, const QVariant &refsVal); - void handleScope(const QVariant &bodyVal, const QVariant &refsVal); - StackFrame extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal); + void handleBacktrace(const QVariantMap &response); + void handleLookup(const QVariantMap &response); + void handleExecuteDebuggerCommand(const QVariantMap &response); + void handleEvaluateExpression(const QVariantMap &response, const QByteArray &iname, const QString &expr); + void handleFrame(const QVariantMap &response); + void handleScope(const QVariantMap &response); + void handleVersion(const QVariantMap &response); + StackFrame extractStackFrame(const QVariant &bodyVal); bool canEvaluateScript(const QString &script); void updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, const QString &source); + void runCommand(const DebuggerCommand &command, const QmlCallback &cb = QmlCallback()); + void runDirectCommand(const QByteArray &type, const QByteArray &msg = QByteArray()); + + void clearRefs() { refVals.clear(); } + void memorizeRefs(const QVariant &refs); + QmlV8ObjectData extractData(const QVariant &data) const; + void insertSubItems(WatchItem *parent, const QVariantList &properties); + void checkForFinishedUpdate(); + ConsoleItem *constructLogItemTree(ConsoleItem *parent, const QmlV8ObjectData &objectData); + public: + QHash refVals; // The mapping of target object handles to retrieved values. int sequence = -1; QmlEngine *engine; QHash breakpoints; QHash breakpointsSync; QList breakpointsTemp; - QHash evaluatingExpression; - QHash localsAndWatchers; - QList updateLocalsAndWatchers; - QList debuggerCommands; + LookupItems currentlyLookingUp; // Id -> inames //Cache QList currentFrameScopes; @@ -207,6 +218,8 @@ public: QTimer connectionTimer; QmlDebug::QmlDebugConnection *connection; QmlDebug::QDebugMessageClient *msgClient = 0; + + QHash callbackForToken; }; static void updateDocument(IDocument *document, const QTextDocument *textDocument) @@ -335,11 +348,6 @@ void QmlEngine::connectionEstablished() { attemptBreakpointSynchronization(); - if (!watchHandler()->watcherNames().isEmpty()) - synchronizeWatchers(); - connect(watchModel(), &QAbstractItemModel::layoutChanged, - this, &QmlEngine::synchronizeWatchers); - if (state() == EngineRunRequested) notifyEngineRunAndInferiorRunOk(); } @@ -512,9 +520,6 @@ void QmlEngine::gotoLocation(const Location &location) void QmlEngine::closeConnection() { - disconnect(watchModel(), &QAbstractItemModel::layoutChanged, - this, &QmlEngine::synchronizeWatchers); - if (d->connectionTimer.isActive()) { d->connectionTimer.stop(); } else { @@ -607,7 +612,11 @@ void QmlEngine::notifyEngineRemoteServerRunning(const QByteArray &serverChannel, void QmlEngine::shutdownInferior() { // End session. - d->disconnect(); + // { "seq" : , + // "type" : "request", + // "command" : "disconnect", + // } + d->runCommand(DISCONNECT); if (isSlaveEngine()) resetLocation(); @@ -664,7 +673,7 @@ void QmlEngine::continueInferior() void QmlEngine::interruptInferior() { showMessage(_(INTERRUPT), LogInput); - d->sendMessage(d->packMessage(INTERRUPT)); + d->runDirectCommand(INTERRUPT); notifyInferiorStopOk(); } @@ -742,11 +751,10 @@ void QmlEngine::activateFrame(int index) if (state() != InferiorStopOk && state() != InferiorUnrunnable) return; - if (index != stackHandler()->currentIndex()) - d->frame(d->stackIndexLookup.value(index)); - stackHandler()->setCurrentIndex(index); gotoLocation(stackHandler()->frames().value(index)); + + d->updateLocals(); } void QmlEngine::selectThread(ThreadId threadId) @@ -931,6 +939,11 @@ void QmlEngine::reloadSourceFiles() d->scripts(4, QList(), true, QVariant()); } +void QmlEngine::updateAll() +{ + d->updateLocals(); +} + void QmlEngine::requestModuleSymbols(const QString &moduleName) { Q_UNUSED(moduleName) @@ -945,17 +958,16 @@ bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const } void QmlEngine::assignValueInDebugger(WatchItem *item, - const QString &expression, const QVariant &valueV) + const QString &expression, const QVariant &value) { if (!expression.isEmpty()) { if (item->isInspect() && d->inspectorAdapter.agent()) { - d->inspectorAdapter.agent()->assignValue(item, expression, valueV); + d->inspectorAdapter.agent()->assignValue(item, expression, value); } else { StackHandler *handler = stackHandler(); - QString expression = QString(_("%1 = %2;")).arg(expression).arg(valueV.toString()); + QString exp = QString(_("%1 = %2;")).arg(expression).arg(value.toString()); if (handler->isContentsValid() && handler->currentFrame().isUsable()) { - d->evaluate(expression, false, false, handler->currentIndex()); - d->updateLocalsAndWatchers.append(d->sequence); + d->evaluate(exp, [this](const QVariantMap &) { d->updateLocals(); }); } else { showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( expression), ConsoleOutput); @@ -964,23 +976,32 @@ void QmlEngine::assignValueInDebugger(WatchItem *item, } } -void QmlEngine::updateWatchData(const QByteArray &iname) +void QmlEngine::expandItem(const QByteArray &iname) { -// qDebug() << "UPDATE WATCH DATA" << data.toString(); - //showStatusMessage(tr("Stopped."), 5000); const WatchItem *item = watchHandler()->findItem(iname); - // invalid expressions or out of scope variables - if (!item) - return; + QTC_ASSERT(item, return); if (item->isInspect()) { d->inspectorAdapter.agent()->updateWatchData(*item); } else { - if (!item->name.isEmpty()) { - if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) - d->expandObject(item->iname, item->id); - } - synchronizeWatchers(); + LookupItems items; + items.insert(int(item->id), {item->iname, item->name}); + d->lookup(items); + } +} + +void QmlEngine::updateItem(const QByteArray &iname) +{ + const WatchItem *item = watchHandler()->findItem(iname); + QTC_ASSERT(item, return); + + if (state() == InferiorStopOk) { + // The Qt side Q_ASSERTs otherwise. So postpone the evaluation, + // it will be triggered from from upateLocals() later. + QString exp = QString::fromUtf8(item->exp); + d->evaluate(exp, [this, iname, exp](const QVariantMap &response) { + d->handleEvaluateExpression(response, iname, exp); + }); } } @@ -991,23 +1012,6 @@ void QmlEngine::selectWatchData(const QByteArray &iname) d->inspectorAdapter.agent()->watchDataSelected(item->id); } -void QmlEngine::synchronizeWatchers() -{ - if (state() != InferiorStopOk) - return; - - QStringList watchers = watchHandler()->watchedExpressions(); - - // send watchers list - foreach (const QString &exp, watchers) { - StackHandler *handler = stackHandler(); - if (handler->isContentsValid() && handler->currentFrame().isUsable()) { - d->evaluate(exp, false, false, handler->currentIndex()); - d->evaluatingExpression.insert(d->sequence, exp); - } - } -} - static ConsoleItem *constructLogItemTree(ConsoleItem *parent, const QVariant &result, const QString &key = QString()) @@ -1123,8 +1127,6 @@ void QmlEngine::updateCurrentContext() context = grandParentData->name; } - synchronizeWatchers(); - if (auto consoleManager = ConsoleManagerInterface::instance()) consoleManager->setContext(tr("Context:") + QLatin1Char(' ') + context); } @@ -1136,8 +1138,7 @@ void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages StackHandler *handler = stackHandler(); if (handler->isContentsValid() && handler->currentFrame().isUsable()) { - d->evaluate(command, false, false, handler->currentIndex()); - d->debuggerCommands.append(d->sequence); + d->evaluate(command, CB(d->handleExecuteDebuggerCommand)); } else { //Currently cannot evaluate if not in a javascript break d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( @@ -1310,26 +1311,6 @@ void QmlEngine::logServiceActivity(const QString &service, const QString &logMes showMessage(service + QLatin1Char(' ') + logMessage, LogDebug); } -void QmlEnginePrivate::connect() -{ - engine->showMessage(_(CONNECT), LogInput); - sendMessage(packMessage(CONNECT)); -} - -void QmlEnginePrivate::disconnect() -{ - // { "seq" : , - // "type" : "request", - // "command" : "disconnect", - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(DISCONNECT)); - - const QByteArray msg = QJsonDocument(jsonVal).toJson(QJsonDocument::Compact); - engine->showMessage(QString::fromUtf8(msg), LogInput); - sendMessage(packMessage(DISCONNECT, msg)); -} - void QmlEnginePrivate::continueDebugging(StepAction action) { // { "seq" : , @@ -1339,32 +1320,22 @@ void QmlEnginePrivate::continueDebugging(StepAction action) // "stepcount" : // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(CONTINEDEBUGGING)); - if (action != Continue) { - QJsonObject args; - switch (action) { - case StepIn: - args.insert(_(STEPACTION), _(IN)); - break; - case StepOut: - args.insert(_(STEPACTION), _(OUT)); - break; - case Next: - args.insert(_(STEPACTION), _(NEXT)); - break; - default:break; - } + DebuggerCommand cmd(CONTINEDEBUGGING); + + if (action == StepIn) + cmd.arg(STEPACTION, IN); + else if (action == StepOut) + cmd.arg(STEPACTION, OUT); + else if (action == Next) + cmd.arg(STEPACTION, NEXT); + + runCommand(cmd); - jsonVal.insert(_(ARGUMENTS), args); - } - sendAndLogV8Request(jsonVal); previousStepAction = action; } -void QmlEnginePrivate::evaluate(const QString expr, bool global, - bool disableBreak, int frame, bool addContext) +void QmlEnginePrivate::evaluate(const QString expr, const QmlCallback &cb) { // { "seq" : , // "type" : "request", @@ -1380,48 +1351,53 @@ void QmlEnginePrivate::evaluate(const QString expr, bool global, // ] // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(EVALUATE)); - QJsonObject args { - { _(EXPRESSION), expr } - }; + // The Qt side Q_ASSERTs otherwise. So ignore the request and hope + // it will be repeated soon enough (which it will, e.g. in updateLocals) + QTC_ASSERT(engine->state() == InferiorStopOk, return); - if (frame != -1) - args.insert(_(FRAME), frame); + DebuggerCommand cmd(EVALUATE); - if (global) - args.insert(_(GLOBAL), global); + cmd.arg(EXPRESSION, expr); + cmd.arg(FRAME, engine->stackHandler()->currentIndex()); - if (disableBreak) - args.insert(_(DISABLE_BREAK), disableBreak); - - if (addContext) { - WatchHandler *watchHandler = engine->watchHandler(); - QAbstractItemModel *watchModel = watchHandler->model(); - int rowCount = watchModel->rowCount(); - - QJsonArray ctxtList; - while (rowCount) { - QModelIndex index = watchModel->index(--rowCount, 0); - const WatchData *data = watchHandler->watchItem(index); - const QJsonObject ctxt { - { _(NAME), data->name }, - { _(HANDLE), int(data->id) } - }; - - ctxtList.push_front(ctxt); - } - - args.insert(_(ADDITIONAL_CONTEXT), ctxtList); - } - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + runCommand(cmd, cb); } -void QmlEnginePrivate::lookup(QList handles, bool includeSource) +void QmlEnginePrivate::handleEvaluateExpression(const QVariantMap &response, + const QByteArray &iname, + const QString &exp) +{ + // { "seq" : , + // "type" : "response", + // "request_seq" : , + // "command" : "evaluate", + // "body" : ... + // "running" : + // "success" : true + // } + + QVariant bodyVal = response.value(_(BODY)).toMap(); + QmlV8ObjectData body = extractData(bodyVal); + WatchHandler *watchHandler = engine->watchHandler(); + + auto item = new WatchItem(iname, exp); + item->exp = exp.toLatin1(); + item->id = body.handle; + bool success = response.value(_("success")).toBool(); + if (success) { + item->type = body.type; + item->value = body.value.toString(); + item->wantsChildren = body.properties.count(); + } else { + //Do not set type since it is unknown + item->setError(body.value.toString()); + } + insertSubItems(item, body.properties); + watchHandler->insertItem(item); +} + +void QmlEnginePrivate::lookup(const LookupItems &items) { // { "seq" : , // "type" : "request", @@ -1432,25 +1408,19 @@ void QmlEnginePrivate::lookup(QList handles, bool includeSource) // script objects are returned>, // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(LOOKUP)); - QJsonObject args; + if (items.isEmpty()) + return; - QJsonArray array; - foreach (int handle, handles) - array.push_back(handle); - args.insert(_(HANDLES), array); + QList handles = items.keys(); + currentlyLookingUp += items; - if (includeSource) - args.insert(_(INCLUDESOURCE), includeSource); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + DebuggerCommand cmd(LOOKUP); + cmd.arg(HANDLES, handles); + runCommand(cmd, CB(handleLookup)); } -void QmlEnginePrivate::backtrace(int fromFrame, int toFrame, bool bottom) +void QmlEnginePrivate::backtrace() { // { "seq" : , // "type" : "request", @@ -1461,45 +1431,22 @@ void QmlEnginePrivate::backtrace(int fromFrame, int toFrame, bool bottom) // stack is requested> // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(BACKTRACE)); - QJsonObject args; - - if (fromFrame != -1) - args.insert(_(FROMFRAME), fromFrame); - - if (toFrame != -1) - args.insert(_(TOFRAME), toFrame); - - if (bottom) - args.insert(_(BOTTOM), bottom); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + DebuggerCommand cmd(BACKTRACE); + runCommand(cmd, CB(handleBacktrace)); } -void QmlEnginePrivate::frame(int number) +void QmlEnginePrivate::updateLocals() { // { "seq" : , // "type" : "request", // "command" : "frame", - // "arguments" : { "number" : - // } + // "arguments" : { "number" : } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(FRAME)); - if (number != -1) { - const QJsonObject args { - { _(NUMBER), number } - }; - - jsonVal.insert(_(ARGUMENTS), args); - } - - sendAndLogV8Request(jsonVal); + DebuggerCommand cmd(FRAME); + cmd.arg(NUMBER, stackIndexLookup.value(engine->stackHandler()->currentIndex())); + runCommand(cmd, CB(handleFrame)); } void QmlEnginePrivate::scope(int number, int frameNumber) @@ -1512,21 +1459,13 @@ void QmlEnginePrivate::scope(int number, int frameNumber) // frame if missing> // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SCOPE)); - if (number != -1) { - QJsonObject args { - { _(NUMBER), number } - }; + DebuggerCommand cmd(SCOPE); + cmd.arg(NUMBER, number); + if (frameNumber != -1) + cmd.arg(FRAMENUMBER, frameNumber); - if (frameNumber != -1) - args.insert(_(FRAMENUMBER), frameNumber); - - jsonVal.insert(_(ARGUMENTS), args); - } - - sendAndLogV8Request(jsonVal); + runCommand(cmd, CB(handleScope)); } void QmlEnginePrivate::scripts(int types, const QList ids, bool includeSource, @@ -1547,37 +1486,24 @@ void QmlEnginePrivate::scripts(int types, const QList ids, bool includeSour // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SCRIPTS)); - QJsonObject args { - { _(TYPES), types } - }; + DebuggerCommand cmd(SCRIPTS); + cmd.arg(TYPES, types); - if (ids.count()) { - QJsonArray array; - foreach (int id, ids) { - array.push_back(id); - } - args.insert(_(IDS), array); - } + if (ids.count()) + cmd.arg(IDS, ids); if (includeSource) - args.insert(_(INCLUDESOURCE), includeSource); + cmd.arg(INCLUDESOURCE, includeSource); - QJsonValue filterValue; if (filter.type() == QVariant::String) - filterValue = filter.toString(); + cmd.arg(FILTER, filter.toString()); else if (filter.type() == QVariant::Int) - filterValue = filter.toInt(); + cmd.arg(FILTER, filter.toInt()); else QTC_CHECK(!filter.isValid()); - args.insert(_(FILTER), filterValue); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + runCommand(cmd); } void QmlEnginePrivate::setBreakpoint(const QString type, const QString target, @@ -1601,36 +1527,28 @@ void QmlEnginePrivate::setBreakpoint(const QString type, const QString target, QmlDebugStream rs(¶ms, QIODevice::WriteOnly); rs << target.toUtf8() << enabled; engine->showMessage(QString(_("%1 %2 %3")).arg(_(BREAKONSIGNAL), target, enabled ? _("enabled") : _("disabled")), LogInput); - sendMessage(packMessage(BREAKONSIGNAL, params)); + runDirectCommand(BREAKONSIGNAL, params); } else { - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SETBREAKPOINT)); + DebuggerCommand cmd(SETBREAKPOINT); + cmd.arg(TYPE, type); + cmd.arg(ENABLED, enabled); - QJsonObject args { - { _(TYPE), type }, - { _(ENABLED), enabled } - }; if (type == _(SCRIPTREGEXP)) - args.insert(_(TARGET), Utils::FileName::fromString(target).fileName()); + cmd.arg(TARGET, Utils::FileName::fromString(target).fileName()); else - args.insert(_(TARGET), target); + cmd.arg(TARGET, target); if (line) - args.insert(_(LINE), line - 1); - + cmd.arg(LINE, line - 1); if (column) - args.insert(_(COLUMN), column - 1); - + cmd.arg(COLUMN, column - 1); if (!condition.isEmpty()) - args.insert(_(CONDITION), condition); - + cmd.arg(CONDITION, condition); if (ignoreCount != -1) - args.insert(_(IGNORECOUNT), ignoreCount); + cmd.arg(IGNORECOUNT, ignoreCount); - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + runCommand(cmd); } } @@ -1642,16 +1560,10 @@ void QmlEnginePrivate::clearBreakpoint(int breakpoint) // "arguments" : { "breakpoint" : // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT)); - QJsonObject args { - { _(BREAKPOINT), breakpoint } - }; - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + DebuggerCommand cmd(CLEARBREAKPOINT); + cmd.arg(BREAKPOINT, breakpoint); + runCommand(cmd); } void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled) @@ -1663,54 +1575,22 @@ void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled) // "enabled" : // } // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK)); - - QJsonObject args; + DebuggerCommand cmd(SETEXCEPTIONBREAK); if (type == AllExceptions) - args.insert(_(TYPE), _(ALL)); - //Not Supported - // else if (type == UncaughtExceptions) - // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT))); + cmd.arg(TYPE, ALL); + + //Not Supported: + // else if (type == UncaughtExceptions) + // cmd.args(TYPE, UNCAUGHT); if (enabled) - args.insert(_(ENABLED), enabled); + cmd.arg(ENABLED, enabled); - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); + runCommand(cmd); } -void QmlEnginePrivate::version() -{ - // { "seq" : , - // "type" : "request", - // "command" : "version", - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(VERSION)); - - sendAndLogV8Request(jsonVal); -} - -QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success) -{ - *success = false; - QVariant variant; - const QVariantList refs = refsVal.toList(); - foreach (const QVariant &ref, refs) { - const QVariantMap refData = ref.toMap(); - if (refData.value(_(HANDLE)).toInt() == handle) { - variant = refData; - *success = true; - break; - } - } - return variant; -} - -QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) +QmlV8ObjectData QmlEnginePrivate::extractData(const QVariant &data) const { // { "handle" : , // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> @@ -1770,10 +1650,8 @@ QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) if (dataMap.contains(_(REF))) { objectData.handle = dataMap.value(_(REF)).toInt(); - bool success; - QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success); - if (success) { - QmlV8ObjectData data = extractData(dataFromRef, refsVal); + if (refVals.contains(objectData.handle)) { + QmlV8ObjectData data = refVals.value(objectData.handle); objectData.type = data.type; objectData.value = data.value; objectData.properties = data.properties; @@ -1821,53 +1699,40 @@ QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) return objectData; } -void QmlEnginePrivate::clearCache() +void QmlEnginePrivate::runCommand(const DebuggerCommand &command, const QmlCallback &cb) { - currentFrameScopes.clear(); - updateLocalsAndWatchers.clear(); + QByteArray msg = "{\"seq\":" + QByteArray::number(++sequence) + "," + + "\"type\":\"request\"," + + "\"command\":\"" + command.function + "\"," + + "\"arguments\":{" + command.arguments() + "}}"; + if (cb) + callbackForToken[sequence] = cb; + + runDirectCommand(V8REQUEST, msg); } -QByteArray QmlEnginePrivate::packMessage(const QByteArray &type, const QByteArray &message) +void QmlEnginePrivate::runDirectCommand(const QByteArray &type, const QByteArray &msg) { - SDEBUG(message); + // Leave item as variable, serialization depends on it. + QByteArray cmd = V8DEBUG; + + engine->showMessage(QString::fromLatin1("%1 %2").arg(_(type), _(msg)), LogInput); + QByteArray request; QmlDebugStream rs(&request, QIODevice::WriteOnly); - QByteArray cmd = V8DEBUG; - rs << cmd << type << message; - return request; + rs << cmd << type << msg; + sendMessage(request); } -QJsonObject QmlEnginePrivate::initObject() +void QmlEnginePrivate::memorizeRefs(const QVariant &refs) { - return QJsonObject { - {_(SEQ), ++sequence}, - {_(TYPE), _(REQUEST)} - }; -} - -void QmlEnginePrivate::sendAndLogV8Request(const QJsonObject &request) -{ - const QByteArray msg = QJsonDocument(request).toJson(QJsonDocument::Compact); - engine->showMessage(QString::fromLatin1("%1 %2").arg(_(V8REQUEST), QString::fromUtf8(msg)), LogInput); - sendMessage(packMessage(V8REQUEST, msg)); -} - -void QmlEnginePrivate::expandObject(const QByteArray &iname, quint64 objectId) -{ - if (objectId == 0) { - //We may have got the global object - const WatchItem *watch = engine->watchHandler()->findItem(iname); - if (watch->value == QLatin1String("global")) { - StackHandler *stackHandler = engine->stackHandler(); - if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { - evaluate(watch->name, false, false, stackHandler->currentIndex()); - evaluatingExpression.insert(sequence, QLatin1String(iname)); - } - return; + if (refs.isValid()) { + foreach (const QVariant &ref, refs.toList()) { + const QVariantMap refData = ref.toMap(); + int handle = refData.value(_(HANDLE)).toInt(); + refVals[handle] = extractData(refData); } } - localsAndWatchers.insertMulti(objectId, iname); - lookup(QList() << objectId); } void QmlEnginePrivate::messageReceived(const QByteArray &data) @@ -1903,38 +1768,25 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) if (type == _("response")) { + const QString debugCommand(resp.value(_(COMMAND)).toString()); + + memorizeRefs(resp.value(_(REFS))); + bool success = resp.value(_("success")).toBool(); if (!success) { SDEBUG("Request was unsuccessful"); } - const QString debugCommand(resp.value(_(COMMAND)).toString()); + int requestSeq = resp.value(_("request_seq")).toInt(); + if (callbackForToken.contains(requestSeq)) { + callbackForToken[requestSeq](resp); - if (debugCommand == _(DISCONNECT)) { + } else if (debugCommand == _(DISCONNECT)) { //debugging session ended } else if (debugCommand == _(CONTINEDEBUGGING)) { //do nothing, wait for next break - } else if (debugCommand == _(BACKTRACE)) { - if (success) - handleBacktrace(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(LOOKUP)) { - if (success) - handleLookup(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(EVALUATE)) { - int seq = resp.value(_("request_seq")).toInt(); - if (success) { - handleEvaluate(seq, success, resp.value(_(BODY)), resp.value(_(REFS))); - } else { - QVariantMap map; - map.insert(_(TYPE), QVariant(_("string"))); - map.insert(_(VALUE), resp.value(_("message"))); - handleEvaluate(seq, success, QVariant(map), QVariant()); - } - } else if (debugCommand == _(SETBREAKPOINT)) { // { "seq" : , // "type" : "response", @@ -1994,14 +1846,6 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) // } - } else if (debugCommand == _(FRAME)) { - if (success) - handleFrame(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(SCOPE)) { - if (success) - handleScope(resp.value(_(BODY)), resp.value(_(REFS))); - } else if (debugCommand == _(SCRIPTS)) { // { "seq" : , // "type" : "response", @@ -2059,11 +1903,6 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) engine->sourceFilesHandler()->setSourceFiles(files); //update open editors } - } else if (debugCommand == _(VERSION)) { - engine->showMessage(QString(_("Using V8 Version: %1")).arg( - resp.value(_(BODY)).toMap(). - value(_("V8Version")).toString()), LogOutput); - } else { // DO NOTHING } @@ -2072,6 +1911,8 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) const QString eventType(resp.value(_(EVENT)).toString()); if (eventType == _("break")) { + + clearRefs(); const QVariantMap breakData = resp.value(_(BODY)).toMap(); const QString invocationText = breakData.value(_("invocationText")).toString(); const QString scriptUrl = breakData.value(_("script")).toMap().value(_("name")).toString(); @@ -2189,13 +2030,13 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) //This is most probably due to a wrong eval expression. //Redirect output to console. if (eventType.isEmpty()) { - bool success = resp.value(_("success")).toBool(); - QVariantMap map; - map.insert(_(TYPE), QVariant(_("string"))); - map.insert(_(VALUE), resp.value(_("message"))); - //Since there is no sequence value, best estimate is - //last sequence value - handleEvaluate(sequence, success, QVariant(map), QVariant()); + QmlV8ObjectData entry; + entry.type = "string"; + entry.value = resp.value(_("message")); + if (auto consoleManager = ConsoleManagerInterface::instance()) { + if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), entry)) + consoleManager->printToConsolePane(item); + } } } //EVENT @@ -2206,7 +2047,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) } } -void QmlEnginePrivate::handleBacktrace(const QVariant &bodyVal, const QVariant &refsVal) +void QmlEnginePrivate::handleBacktrace(const QVariantMap &response) { // { "seq" : , // "type" : "response", @@ -2221,7 +2062,7 @@ void QmlEnginePrivate::handleBacktrace(const QVariant &bodyVal, const QVariant & // "success" : true // } - const QVariantMap body = bodyVal.toMap(); + const QVariantMap body = response.value(_(BODY)).toMap(); const QVariantList frames = body.value(_("frames")).toList(); int fromFrameIndex = body.value(_("fromFrame")).toInt(); @@ -2233,7 +2074,7 @@ void QmlEnginePrivate::handleBacktrace(const QVariant &bodyVal, const QVariant & int i = 0; stackIndexLookup.clear(); foreach (const QVariant &frame, frames) { - StackFrame stackFrame = extractStackFrame(frame, refsVal); + StackFrame stackFrame = extractStackFrame(frame); if (stackFrame.level < 0) continue; stackIndexLookup.insert(i, stackFrame.level); @@ -2242,15 +2083,12 @@ void QmlEnginePrivate::handleBacktrace(const QVariant &bodyVal, const QVariant & i++; } stackHandler->setFrames(stackFrames); + stackHandler->setCurrentIndex(0); - //Populate locals and watchers wrt top frame - //Update all Locals visible in current scope - //Traverse the scope chain and store the local properties - //in a list and show them in the Locals Window. - handleFrame(frames.value(0), refsVal); + updateLocals(); } -StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal) +StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal) { // { "seq" : , // "type" : "response", @@ -2293,17 +2131,17 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal, const QV return stackFrame; } - QmlV8ObjectData objectData = extractData(body.value(_("func")), refsVal); + QmlV8ObjectData objectData = extractData(body.value(_("func"))); QString functionName = objectData.value.toString(); if (functionName.isEmpty()) functionName = tr("Anonymous Function"); stackFrame.function = functionName; - objectData = extractData(body.value(_("script")), refsVal); + objectData = extractData(body.value(_("script"))); stackFrame.file = engine->toFileInProject(objectData.value.toString()); stackFrame.usable = QFileInfo(stackFrame.file).isReadable(); - objectData = extractData(body.value(_("receiver")), refsVal); + objectData = extractData(body.value(_("receiver"))); stackFrame.to = objectData.value.toString(); stackFrame.line = body.value(_("line")).toInt() + 1; @@ -2311,7 +2149,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal, const QV return stackFrame; } -void QmlEnginePrivate::handleFrame(const QVariant &bodyVal, const QVariant &refsVal) +void QmlEnginePrivate::handleFrame(const QVariantMap &response) { // { "seq" : , // "type" : "response", @@ -2343,32 +2181,26 @@ void QmlEnginePrivate::handleFrame(const QVariant &bodyVal, const QVariant &refs // "running" : // "success" : true // } - QVariantMap currentFrame = bodyVal.toMap(); + QVariantMap body = response.value(_(BODY)).toMap(); StackHandler *stackHandler = engine->stackHandler(); WatchHandler * watchHandler = engine->watchHandler(); watchHandler->notifyUpdateStarted(); - clearCache(); const int frameIndex = stackHandler->currentIndex(); - QSet expandedInames = watchHandler->expandedINames(); - QHash handlesToLookup; - // Store handles of all expanded watch data - foreach (const QByteArray &iname, expandedInames) { - const WatchItem *item = watchHandler->findItem(iname); - if (item && item->isLocal()) - handlesToLookup.insert(item->id, iname); - } if (frameIndex < 0) return; const StackFrame frame = stackHandler->currentFrame(); if (!frame.isUsable()) return; - //Set "this" variable + // Always add a "this" variable { - auto item = new WatchItem("local.this", QLatin1String("this")); - QmlV8ObjectData objectData = extractData(currentFrame.value(_("receiver")), refsVal); + QByteArray iname = "local.this"; + QString exp = QLatin1String("this"); + + auto item = new WatchItem(iname, exp); + QmlV8ObjectData objectData = extractData(body.value(_("receiver"))); item->id = objectData.handle; item->type = objectData.type; item->value = objectData.value.toString(); @@ -2380,9 +2212,13 @@ void QmlEnginePrivate::handleFrame(const QVariant &bodyVal, const QVariant &refs item->id = 0; } watchHandler->insertItem(item); + evaluate(exp, [this, iname, exp](const QVariantMap &response) { + handleEvaluateExpression(response, iname, exp); + }); } - const QVariantList scopes = currentFrame.value(_("scopes")).toList(); + currentFrameScopes.clear(); + const QVariantList scopes = body.value(_("scopes")).toList(); foreach (const QVariant &scope, scopes) { //Do not query for global types (0) //Showing global properties increases clutter. @@ -2394,14 +2230,28 @@ void QmlEnginePrivate::handleFrame(const QVariant &bodyVal, const QVariant &refs } engine->gotoLocation(stackHandler->currentFrame()); - // Expand watch data that were previously expanded - QHash::const_iterator itEnd = handlesToLookup.constEnd(); - for (QHash::const_iterator it = handlesToLookup.constBegin(); it != itEnd; ++it) - expandObject(it.value(), it.key()); - engine->stackFrameCompleted(); + // Send watchers list + if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { + QStringList watchers = watchHandler->watchedExpressions(); + foreach (const QString &exp, watchers) { + const QByteArray iname = watchHandler->watcherName(exp.toLatin1()); + evaluate(exp, [this, iname, exp](const QVariantMap &response) { + handleEvaluateExpression(response, iname, exp); + }); + } + } + + // Expand locals and watchers that were previously expanded + LookupItems itemsToLookup; + foreach (const QByteArray &iname, watchHandler->expandedINames()) { + const WatchItem *item = watchHandler->findItem(iname); + if (item && item->isLocal()) + itemsToLookup.insert(int(item->id), {item->iname, item->name}); + } + lookup(itemsToLookup); } -void QmlEnginePrivate::handleScope(const QVariant &bodyVal, const QVariant &refsVal) +void QmlEnginePrivate::handleScope(const QVariantMap &response) { // { "seq" : , // "type" : "response", @@ -2424,18 +2274,18 @@ void QmlEnginePrivate::handleScope(const QVariant &bodyVal, const QVariant &refs // "running" : // "success" : true // } - QVariantMap bodyMap = bodyVal.toMap(); + QVariantMap bodyMap = response.value(_(BODY)).toMap(); //Check if the frameIndex is same as current Stack Index StackHandler *stackHandler = engine->stackHandler(); if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex()) return; - QmlV8ObjectData objectData = extractData(bodyMap.value(_("object")), refsVal); + QmlV8ObjectData objectData = extractData(bodyMap.value(_("object"))); - QList handlesToLookup; + LookupItems itemsToLookup; foreach (const QVariant &property, objectData.properties) { - QmlV8ObjectData localData = extractData(property, refsVal); + QmlV8ObjectData localData = extractData(property); auto item = new WatchItem; item->exp = localData.name; //Check for v8 specific local data @@ -2444,29 +2294,29 @@ void QmlEnginePrivate::handleScope(const QVariant &bodyVal, const QVariant &refs item->name = QLatin1String(item->exp); item->iname = QByteArray("local.") + item->exp; + item->id = localData.handle; - int handle = localData.handle; if (localData.value.isValid()) { - item->id = handle; item->type = localData.type; item->value = localData.value.toString(); item->setHasChildren(localData.properties.count()); engine->watchHandler()->insertItem(item); } else { - handlesToLookup << handle; - localsAndWatchers.insertMulti(handle, item->exp); + itemsToLookup.insert(int(item->id), {item->iname, item->name}); } } + lookup(itemsToLookup); + checkForFinishedUpdate(); +} - if (!handlesToLookup.isEmpty()) - lookup(handlesToLookup); - else +void QmlEnginePrivate::checkForFinishedUpdate() +{ + if (currentlyLookingUp.isEmpty()) engine->watchHandler()->notifyUpdateFinished(); } -ConsoleItem *constructLogItemTree(ConsoleItem *parent, - const QmlV8ObjectData &objectData, - const QVariant &refsVal) +ConsoleItem *QmlEnginePrivate::constructLogItemTree(ConsoleItem *parent, + const QmlV8ObjectData &objectData) { bool sorted = boolSetting(SortStructMembers); if (!objectData.value.isValid()) @@ -2483,10 +2333,10 @@ ConsoleItem *constructLogItemTree(ConsoleItem *parent, QSet childrenFetched; foreach (const QVariant &property, objectData.properties) { - const QmlV8ObjectData childObjectData = extractData(property, refsVal); + const QmlV8ObjectData childObjectData = extractData(property); if (childObjectData.handle == objectData.handle) continue; - ConsoleItem *child = constructLogItemTree(item, childObjectData, refsVal); + ConsoleItem *child = constructLogItemTree(item, childObjectData); if (child) { const QString text = child->text(); if (childrenFetched.contains(text)) @@ -2499,11 +2349,11 @@ ConsoleItem *constructLogItemTree(ConsoleItem *parent, return item; } -static void insertSubItems(WatchItem *parent, const QVariantList &properties, const QVariant &refsVal) +void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &properties) { QTC_ASSERT(parent, return); foreach (const QVariant &property, properties) { - QmlV8ObjectData propertyData = extractData(property, refsVal); + QmlV8ObjectData propertyData = extractData(property); auto item = new WatchItem; item->name = QString::fromUtf8(propertyData.name); @@ -2526,74 +2376,27 @@ static void insertSubItems(WatchItem *parent, const QVariantList &properties, co item->setHasChildren(propertyData.properties.count()); parent->appendChild(item); } + + if (boolSetting(SortStructMembers)) + parent->sortChildren([](const TreeItem *item1, const TreeItem *item2) -> bool { + return static_cast(item1)->name + < static_cast(item2)->name; + }); } -void QmlEnginePrivate::handleEvaluate(int sequence, bool success, - const QVariant &bodyVal, const QVariant &refsVal) +void QmlEnginePrivate::handleExecuteDebuggerCommand(const QVariantMap &response) { - // { "seq" : , - // "type" : "response", - // "request_seq" : , - // "command" : "evaluate", - // "body" : ... - // "running" : - // "success" : true - // } - WatchHandler *watchHandler = engine->watchHandler(); - if (updateLocalsAndWatchers.contains(sequence)) { - updateLocalsAndWatchers.removeOne(sequence); - //Update the locals - foreach (int index, currentFrameScopes) - scope(index); - //Also update "this" - QByteArray iname("local.this"); - const WatchItem *parent = watchHandler->findItem(iname); - localsAndWatchers.insertMulti(parent->id, iname); - lookup(QList() << parent->id); - - } else if (debuggerCommands.contains(sequence)) { - updateLocalsAndWatchers.removeOne(sequence); - QmlV8ObjectData body = extractData(bodyVal, refsVal); - if (auto consoleManager = ConsoleManagerInterface::instance()) { - if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body, refsVal)) - consoleManager->printToConsolePane(item); - } - //Update the locals - foreach (int index, currentFrameScopes) - scope(index); - - } else { - QmlV8ObjectData body = extractData(bodyVal, refsVal); - if (evaluatingExpression.contains(sequence)) { - QString exp = evaluatingExpression.take(sequence); - //Do we have request to evaluate a local? - if (exp.startsWith(QLatin1String("local."))) { - WatchItem *item = watchHandler->findItem(exp.toLatin1()); - insertSubItems(item, body.properties, refsVal); - } else { - QByteArray iname = watchHandler->watcherName(exp.toLatin1()); - SDEBUG(QString(iname)); - - auto item = new WatchItem(iname, exp); - item->exp = exp.toLatin1(); - item->id = body.handle; - if (success) { - item->type = body.type; - item->value = body.value.toString(); - item->wantsChildren = body.properties.count(); - } else { - //Do not set type since it is unknown - item->setError(body.value.toString()); - } - watchHandler->insertItem(item); - insertSubItems(item, body.properties, refsVal); - } - //Insert the newly evaluated expression to the Watchers Window - } + QmlV8ObjectData body = extractData(response.value(_(BODY))); + if (auto consoleManager = ConsoleManagerInterface::instance()) { + if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body)) + consoleManager->printToConsolePane(item); } + // Update the locals + foreach (int index, currentFrameScopes) + scope(index); } -void QmlEnginePrivate::handleLookup(const QVariant &bodyVal, const QVariant &refsVal) +void QmlEnginePrivate::handleLookup(const QVariantMap &response) { // { "seq" : , // "type" : "response", @@ -2603,36 +2406,30 @@ void QmlEnginePrivate::handleLookup(const QVariant &bodyVal, const QVariant &ref // "running" : // "success" : true // } - const QVariantMap body = bodyVal.toMap(); + const QVariantMap body = response.value(_(BODY)).toMap(); QStringList handlesList = body.keys(); - WatchHandler *watchHandler = engine->watchHandler(); - foreach (const QString &handle, handlesList) { - QmlV8ObjectData bodyObjectData = extractData(body.value(handle), refsVal); - QByteArray prepend = localsAndWatchers.take(handle.toInt()); - - if (prepend.startsWith("local.") || prepend.startsWith("watch.")) { - // Data for expanded local/watch. - // Could be an object or function. - WatchItem *parent = watchHandler->findItem(prepend); - insertSubItems(parent, bodyObjectData.properties, refsVal); - } else { - //rest + foreach (const QString &handleString, handlesList) { + int handle = handleString.toInt(); + QmlV8ObjectData bodyObjectData = extractData(body.value(handleString)); + QList vals = currentlyLookingUp.values(handle); + currentlyLookingUp.remove(handle); + foreach (const LookupData &res, vals) { auto item = new WatchItem; - item->exp = prepend; - item->name = QLatin1String(item->exp); - item->iname = QByteArray("local.") + item->exp; - item->id = handle.toInt(); + item->iname = res.iname; + item->name = res.name; + item->id = handle; item->type = bodyObjectData.type; item->value = bodyObjectData.value.toString(); item->setHasChildren(bodyObjectData.properties.count()); + insertSubItems(item, bodyObjectData.properties); engine->watchHandler()->insertItem(item); } } - engine->watchHandler()->notifyUpdateFinished(); + checkForFinishedUpdate(); } void QmlEnginePrivate::stateChanged(State state) @@ -2642,13 +2439,18 @@ void QmlEnginePrivate::stateChanged(State state) if (state == QmlDebugClient::Enabled) { /// Start session. flushSendBuffer(); - connect(); - //Query for the V8 version. This is - //only for logging to the debuggerlog - version(); + runDirectCommand(CONNECT); + runCommand(VERSION); // Only used for logging. } } +void QmlEnginePrivate::handleVersion(const QVariantMap &response) +{ + engine->showMessage(QString(_("Using V8 Version: %1")).arg( + response.value(_(BODY)).toMap(). + value(_("V8Version")).toString()), LogOutput); +} + void QmlEnginePrivate::sendMessage(const QByteArray &msg) { if (state() == Enabled) diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 2fe073aa498..18527199950 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -77,8 +77,6 @@ private slots: void appStartupFailed(const QString &errorMessage); void appendMessage(const QString &msg, Utils::OutputFormat); - void synchronizeWatchers(); - private: void notifyEngineRemoteServerRunning(const QByteArray &, int pid); void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result); @@ -131,7 +129,9 @@ private: void reloadSourceFiles(); void reloadFullStack() {} - void updateWatchData(const QByteArray &iname); + void updateAll(); + void updateItem(const QByteArray &iname); + void expandItem(const QByteArray &iname); void selectWatchData(const QByteArray &iname); void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); bool evaluateScript(const QString &expression); diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp index 9f33111cd8a..265d0bbc1be 100644 --- a/src/plugins/debugger/watchdata.cpp +++ b/src/plugins/debugger/watchdata.cpp @@ -645,7 +645,12 @@ template void readNumericVectorHelper(std::vector *v, const QByteArray &ba) { const T *p = (const T *) ba.data(); - std::copy(p, p + ba.size() / sizeof(T), std::back_insert_iterator >(*v)); + const int n = ba.size() / sizeof(T); + v->resize(n); + // Losing precision in case of 64 bit ints is ok here, as the result + // is only used to plot data. + for (int i = 0; i != n; ++i) + (*v)[i] = static_cast(p[i]); } void readNumericVector(std::vector *v, const QByteArray &rawData, DebuggerEncoding encoding) diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 6828971500a..b8fbacb202f 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -301,7 +301,6 @@ public: SeparatedView *m_separatedView; // Not owned. QSet m_expandedINames; - QSet m_fetchTriggered; QTimer m_requestUpdateTimer; QHash m_reportedTypeFormats; // Type name -> Dumper Formats @@ -331,7 +330,7 @@ WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine) this, &WatchModel::updateStarted); connect(action(SortStructMembers), &SavedAction::valueChanged, - m_engine, &DebuggerEngine::updateAll); + m_engine, &DebuggerEngine::updateLocals); connect(action(ShowStdNamespace), &SavedAction::valueChanged, m_engine, &DebuggerEngine::updateAll); connect(action(ShowQtNamespace), &SavedAction::valueChanged, @@ -670,20 +669,16 @@ bool WatchItem::canFetchMore() const return false; if (!model->m_contentsValid && !isInspect()) return false; - return !model->m_fetchTriggered.contains(iname); + return true; } void WatchItem::fetchMore() { WatchModel *model = watchModel(); - if (model->m_fetchTriggered.contains(iname)) - return; - model->m_expandedINames.insert(iname); - model->m_fetchTriggered.insert(iname); if (children().isEmpty()) { setChildrenNeeded(); - model->m_engine->updateWatchData(iname); + model->m_engine->expandItem(iname); } } @@ -940,12 +935,12 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role case LocalsTypeFormatRole: setTypeFormat(item->type, value.toInt()); - m_engine->updateWatchData(item->iname); + m_engine->updateLocals(); break; case LocalsIndividualFormatRole: { setIndividualFormat(item->iname, value.toInt()); - m_engine->updateWatchData(item->iname); + m_engine->updateLocals(); break; } } @@ -1177,6 +1172,8 @@ void WatchModel::insertItem(WatchItem *item) if (!found) parent->appendChild(item); + item->update(); + item->walkTree([this](TreeItem *sub) { showEditValue(static_cast(sub)); }); } @@ -1304,14 +1301,14 @@ void WatchHandler::watchExpression(const QString &exp0, const QString &name) item->exp = exp; item->name = name.isEmpty() ? exp0 : name; item->iname = watcherName(exp); + m_model->insertItem(item); saveWatchers(); if (m_model->m_engine->state() == DebuggerNotReady) { item->setAllUnneeded(); item->setValue(QString(QLatin1Char(' '))); - m_model->insertItem(item); } else { - m_model->m_engine->updateWatchData(item->iname); + m_model->m_engine->updateItem(item->iname); } updateWatchersWindow(); } @@ -1665,7 +1662,6 @@ QString WatchHandler::editorContents() void WatchHandler::scheduleResetLocation() { - m_model->m_fetchTriggered.clear(); m_model->m_contentsValid = false; m_model->m_resetLocationScheduled = true; } diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 5ed7b29e674..8db2e37ed40 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -240,7 +240,7 @@ private: bool m_submitActionTriggered; }; -} // namespace Git } // namespace Internal +} // namespace Git #endif // GITPLUGIN_H diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index c9a04cad215..c893b8ccf27 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -60,7 +60,7 @@ QVariant QtKitInformation::defaultValue(ProjectExplorer::Kit *k) const Q_UNUSED(k); // find "Qt in PATH": - QList versionList = QtVersionManager::versions(); + QList versionList = QtVersionManager::unsortedVersions(); BaseQtVersion *result = findOrDefault(versionList, [](const BaseQtVersion *v) { return v->autodetectionSource() == QLatin1String("PATH"); }); @@ -174,7 +174,7 @@ int QtKitInformation::qtVersionId(const ProjectExplorer::Kit *k) id = -1; } else { QString source = data.toString(); - foreach (BaseQtVersion *v, QtVersionManager::versions()) { + foreach (BaseQtVersion *v, QtVersionManager::unsortedVersions()) { if (v->autodetectionSource() != source) continue; id = v->uniqueId(); diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index 7298eb319e8..3a44f6861db 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -354,7 +354,7 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) qDebug() << ""; } } - foreach (BaseQtVersion *qtVersion, QtVersionManager::versions()) { + foreach (BaseQtVersion *qtVersion, m_versions) { if (qtVersion->autodetectionSource().startsWith(QLatin1String("SDK."))) { if (!sdkVersions.contains(qtVersion->autodetectionSource())) { if (debug) @@ -515,6 +515,13 @@ int QtVersionManager::getUniqueId() return m_idcount++; } +QList QtVersionManager::unsortedVersions() +{ + QList versions; + QTC_ASSERT(isLoaded(), return versions); + return m_versions.values(); +} + QList QtVersionManager::versions() { QList versions; diff --git a/src/plugins/qtsupport/qtversionmanager.h b/src/plugins/qtsupport/qtversionmanager.h index 0754a566757..b99bb3e42d0 100644 --- a/src/plugins/qtsupport/qtversionmanager.h +++ b/src/plugins/qtsupport/qtversionmanager.h @@ -56,6 +56,9 @@ public: static QList versions(); static QList validVersions(); + // Sorting is slow due to needing to potentially run qmake --query for each version + static QList unsortedVersions(); + // Note: DO NOT STORE THIS POINTER! // The QtVersionManager will delete it at random times and you will // need to get a new pointer by calling this function again! diff --git a/src/shared/qbs b/src/shared/qbs index fc44e70c9db..d69b32061f2 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit fc44e70c9db97048f67a36cd672ffe5e10972620 +Subproject commit d69b32061f2cc97892ba1a2f7d851e21f0787a8d diff --git a/src/tools/clangbackend/ipcsource/clangipcserver.cpp b/src/tools/clangbackend/ipcsource/clangipcserver.cpp index d192da2193f..e8b860520be 100644 --- a/src/tools/clangbackend/ipcsource/clangipcserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangipcserver.cpp @@ -99,7 +99,7 @@ void ClangIpcServer::registerProjectPartsForCodeCompletion(const RegisterProject void ClangIpcServer::unregisterProjectPartsForCodeCompletion(const UnregisterProjectPartsForCodeCompletionCommand &command) { try { - projects.remove(command.filePaths()); + projects.remove(command.projectPartIds()); } catch (const ProjectPartDoNotExistException &exception) { client()->projectPartsDoNotExist(ProjectPartsDoNotExistCommand(exception.projectPartIds())); } catch (const std::exception &exception) { diff --git a/src/tools/clangbackend/ipcsource/codecompletionchunkconverter.cpp b/src/tools/clangbackend/ipcsource/codecompletionchunkconverter.cpp index 7fb2429c257..f58f6bda81e 100644 --- a/src/tools/clangbackend/ipcsource/codecompletionchunkconverter.cpp +++ b/src/tools/clangbackend/ipcsource/codecompletionchunkconverter.cpp @@ -37,6 +37,7 @@ namespace ClangBackEnd { void CodeCompletionChunkConverter::extractCompletionChunks(CXCompletionString completionString) { const uint completionChunkCount = clang_getNumCompletionChunks(completionString); + chunks.reserve(completionChunkCount); for (uint chunkIndex = 0; chunkIndex < completionChunkCount; ++chunkIndex) { const CodeCompletionChunk::Kind kind = chunkKind(completionString, chunkIndex); diff --git a/src/tools/clangbackend/ipcsource/codecompletionsextractor.cpp b/src/tools/clangbackend/ipcsource/codecompletionsextractor.cpp index e12c35fde98..56e826db6fd 100644 --- a/src/tools/clangbackend/ipcsource/codecompletionsextractor.cpp +++ b/src/tools/clangbackend/ipcsource/codecompletionsextractor.cpp @@ -91,6 +91,7 @@ bool CodeCompletionsExtractor::peek(const Utf8String &name) QVector CodeCompletionsExtractor::extractAll() { QVector codeCompletions; + codeCompletions.reserve(cxCodeCompleteResults->NumResults); while (next()) codeCompletions.append(currentCodeCompletion_); @@ -108,6 +109,7 @@ void CodeCompletionsExtractor::extractCompletionKind() extractMethodCompletionKind(); break; case CXCursor_FunctionDecl: + case CXCursor_ConversionFunction: currentCodeCompletion_.setCompletionKind(CodeCompletion::FunctionCompletionKind); break; case CXCursor_VariableRef: @@ -258,6 +260,7 @@ void CodeCompletionsExtractor::adaptPriority() decreasePriorityForNonAvailableCompletions(); decreasePriorityForQObjectInternals(); decreasePriorityForSignals(); + decreasePriorityForOperators(); } void CodeCompletionsExtractor::decreasePriorityForNonAvailableCompletions() @@ -294,6 +297,23 @@ void CodeCompletionsExtractor::decreasePriorityForQObjectInternals() currentCodeCompletion_.setPriority(priority); } +bool isOperator(CXCursorKind cxCursorKind, const Utf8String &name) +{ + return cxCursorKind == CXCursor_ConversionFunction + || (cxCursorKind == CXCursor_CXXMethod + && name.startsWith(Utf8StringLiteral("operator"))); +} + +void CodeCompletionsExtractor::decreasePriorityForOperators() +{ + quint32 priority = currentCodeCompletion_.priority(); + + if (isOperator(currentCxCodeCompleteResult.CursorKind, currentCodeCompletion().text())) + priority *= 100; + + currentCodeCompletion_.setPriority(priority); +} + bool CodeCompletionsExtractor::hasText(const Utf8String &text, CXCompletionString cxCompletionString) const { const uint completionChunkCount = clang_getNumCompletionChunks(cxCompletionString); diff --git a/src/tools/clangbackend/ipcsource/codecompletionsextractor.h b/src/tools/clangbackend/ipcsource/codecompletionsextractor.h index 2456d42fe9c..f01b12b64cb 100644 --- a/src/tools/clangbackend/ipcsource/codecompletionsextractor.h +++ b/src/tools/clangbackend/ipcsource/codecompletionsextractor.h @@ -72,6 +72,7 @@ private: void decreasePriorityForDestructors(); void decreasePriorityForSignals(); void decreasePriorityForQObjectInternals(); + void decreasePriorityForOperators(); bool hasText(const Utf8String &text, CXCompletionString cxCompletionString) const; diff --git a/src/tools/clangbackend/ipcsource/projectpart.cpp b/src/tools/clangbackend/ipcsource/projectpart.cpp index 9e25a7864e1..ed240c2fa4b 100644 --- a/src/tools/clangbackend/ipcsource/projectpart.cpp +++ b/src/tools/clangbackend/ipcsource/projectpart.cpp @@ -98,9 +98,11 @@ ProjectPart &ProjectPart::operator=(ProjectPart &&other) return *this; } -void ProjectPart::clearProjectPartId() +void ProjectPart::clear() { d->projectPartId.clear(); + d->clearArguments(); + updateLastChangeTimePoint(); } const Utf8String &ProjectPart::projectPartId() const diff --git a/src/tools/clangbackend/ipcsource/projectpart.h b/src/tools/clangbackend/ipcsource/projectpart.h index f5191cb5ad3..5c318be7f5f 100644 --- a/src/tools/clangbackend/ipcsource/projectpart.h +++ b/src/tools/clangbackend/ipcsource/projectpart.h @@ -58,7 +58,7 @@ public: ProjectPart(ProjectPart &&project); ProjectPart &operator=(ProjectPart &&project); - void clearProjectPartId(); + void clear(); const Utf8String &projectPartId() const; diff --git a/src/tools/clangbackend/ipcsource/projects.cpp b/src/tools/clangbackend/ipcsource/projects.cpp index 34dcc5e059b..5bb960fccbd 100644 --- a/src/tools/clangbackend/ipcsource/projects.cpp +++ b/src/tools/clangbackend/ipcsource/projects.cpp @@ -46,11 +46,16 @@ void ProjectParts::remove(const Utf8StringVector &projectPartIds) { Utf8StringVector processedProjectPartFilePaths = projectPartIds; - auto removeBeginIterator = std::remove_if(projects_.begin(), projects_.end(), [&processedProjectPartFilePaths] (const ProjectPart &project) { - return processedProjectPartFilePaths.removeFast(project.projectPartId()); - }); + const auto removeBeginIterator = std::remove_if(projects_.begin(), projects_.end(), + [&processedProjectPartFilePaths] (ProjectPart &project) { + const bool isRemoved = processedProjectPartFilePaths.removeFast(project.projectPartId()); + + if (isRemoved) + project.clear(); + + return isRemoved; + }); - std::for_each(removeBeginIterator, projects_.end(), [](ProjectPart &project) { project.clearProjectPartId(); }); projects_.erase(removeBeginIterator, projects_.end()); if (!processedProjectPartFilePaths.isEmpty()) diff --git a/src/tools/clangbackend/ipcsource/translationunit.cpp b/src/tools/clangbackend/ipcsource/translationunit.cpp index 02b43e77ce2..17871176fae 100644 --- a/src/tools/clangbackend/ipcsource/translationunit.cpp +++ b/src/tools/clangbackend/ipcsource/translationunit.cpp @@ -52,7 +52,8 @@ public: ~TranslationUnitData(); public: - time_point lastChangeTimePoint; + time_point lastProjectPartChangeTimePoint; + time_point lastUnsavedFilesChangeTimePoint; ProjectPart projectPart; Utf8String filePath; CXTranslationUnit translationUnit = nullptr; @@ -63,7 +64,8 @@ public: TranslationUnitData::TranslationUnitData(const Utf8String &filePath, const UnsavedFiles &unsavedFiles, const ProjectPart &projectPart) - : lastChangeTimePoint(std::chrono::steady_clock::now()), + : lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), + lastUnsavedFilesChangeTimePoint(lastProjectPartChangeTimePoint), projectPart(projectPart), filePath(filePath), unsavedFiles(unsavedFiles) @@ -109,10 +111,9 @@ CXIndex TranslationUnit::index() const CXTranslationUnit TranslationUnit::cxTranslationUnit() const { checkIfNull(); - - removeOutdatedTranslationUnit(); - + removeTranslationUnitIfProjectPartWasChanged(); createTranslationUnitIfNeeded(); + reparseTranslationUnitIfUnsavedFilesAreChanged(); return d->translationUnit; } @@ -131,9 +132,19 @@ const Utf8String &TranslationUnit::projectPartId() const return d->projectPart.projectPartId(); } -const time_point &TranslationUnit::lastChangeTimePoint() const +const time_point &TranslationUnit::lastProjectPartChangeTimePoint() const { - return d->lastChangeTimePoint; + return d->lastProjectPartChangeTimePoint; +} + +const time_point &TranslationUnit::lastUnsavedFilesChangeTimePoint() const +{ + return d->lastUnsavedFilesChangeTimePoint; +} + +bool TranslationUnit::isNeedingReparse() const +{ + return d->lastUnsavedFilesChangeTimePoint < d->unsavedFiles.lastChangeTimePoint(); } void TranslationUnit::checkIfNull() const @@ -148,26 +159,31 @@ void TranslationUnit::checkIfFileExists() const throw TranslationUnitFileNotExitsException(d->filePath); } -void TranslationUnit::updateLastChangeTimePoint() const +void TranslationUnit::updateLastProjectPartChangeTimePoint() const { - d->lastChangeTimePoint = std::chrono::steady_clock::now(); + d->lastProjectPartChangeTimePoint = std::chrono::steady_clock::now(); } -void TranslationUnit::removeOutdatedTranslationUnit() const +void TranslationUnit::updateLastUnsavedFilesChangeTimePoint() const { - if (d->projectPart.lastChangeTimePoint() >= d->lastChangeTimePoint) { + d->lastUnsavedFilesChangeTimePoint = std::chrono::steady_clock::now(); +} + +void TranslationUnit::removeTranslationUnitIfProjectPartWasChanged() const +{ + if (projectPartIsOutdated()) { clang_disposeTranslationUnit(d->translationUnit); d->translationUnit = nullptr; } } +bool TranslationUnit::projectPartIsOutdated() const +{ + return d->projectPart.lastChangeTimePoint() >= d->lastProjectPartChangeTimePoint; +} + void TranslationUnit::createTranslationUnitIfNeeded() const { - const auto options = CXTranslationUnit_DetailedPreprocessingRecord - | CXTranslationUnit_CacheCompletionResults - | CXTranslationUnit_PrecompiledPreamble - | CXTranslationUnit_SkipFunctionBodies; - if (!d->translationUnit) { d->translationUnit = CXTranslationUnit(); CXErrorCode errorCode = clang_parseTranslationUnit2(index(), @@ -176,12 +192,16 @@ void TranslationUnit::createTranslationUnitIfNeeded() const d->projectPart.argumentCount(), d->unsavedFiles.cxUnsavedFiles(), d->unsavedFiles.count(), - options, + defaultOptions(), &d->translationUnit); checkTranslationUnitErrorCode(errorCode); - updateLastChangeTimePoint(); + // We need to reparse to create the precompiled preamble, which will speed up further calls, + // e.g. clang_codeCompleteAt() dramatically. + reparseTranslationUnit(); + + updateLastProjectPartChangeTimePoint(); } } @@ -193,6 +213,29 @@ void TranslationUnit::checkTranslationUnitErrorCode(CXErrorCode errorCode) const } } +void TranslationUnit::reparseTranslationUnit() const +{ + clang_reparseTranslationUnit(d->translationUnit, + d->unsavedFiles.count(), + d->unsavedFiles.cxUnsavedFiles(), + clang_defaultReparseOptions(d->translationUnit)); + + updateLastUnsavedFilesChangeTimePoint(); +} + +void TranslationUnit::reparseTranslationUnitIfUnsavedFilesAreChanged() const +{ + if (isNeedingReparse()) + reparseTranslationUnit(); +} + +int TranslationUnit::defaultOptions() +{ + return CXTranslationUnit_CacheCompletionResults + | CXTranslationUnit_PrecompiledPreamble + | CXTranslationUnit_SkipFunctionBodies; +} + uint TranslationUnit::unsavedFilesCount() const { return d->unsavedFiles.count(); diff --git a/src/tools/clangbackend/ipcsource/translationunit.h b/src/tools/clangbackend/ipcsource/translationunit.h index 5383023869e..5491f0acec8 100644 --- a/src/tools/clangbackend/ipcsource/translationunit.h +++ b/src/tools/clangbackend/ipcsource/translationunit.h @@ -82,15 +82,24 @@ public: const Utf8String &filePath() const; const Utf8String &projectPartId() const; - const time_point &lastChangeTimePoint() const; + const time_point &lastProjectPartChangeTimePoint() const; + const time_point &lastUnsavedFilesChangeTimePoint() const; + + bool isNeedingReparse() const; private: void checkIfNull() const; void checkIfFileExists() const; - void updateLastChangeTimePoint() const; - void removeOutdatedTranslationUnit() const; + void updateLastProjectPartChangeTimePoint() const; + void updateLastUnsavedFilesChangeTimePoint() const; + void removeTranslationUnitIfProjectPartWasChanged() const; + bool projectPartIsOutdated() const; void createTranslationUnitIfNeeded() const; void checkTranslationUnitErrorCode(CXErrorCode errorCode) const; + void reparseTranslationUnit() const; + void reparseTranslationUnitIfUnsavedFilesAreChanged() const; + void printIncludes() const; + static int defaultOptions(); private: mutable std::shared_ptr d; diff --git a/src/tools/sdktool/main.cpp b/src/tools/sdktool/main.cpp index b3b69ae4665..51cc49ac864 100644 --- a/src/tools/sdktool/main.cpp +++ b/src/tools/sdktool/main.cpp @@ -66,7 +66,7 @@ void printHelp(const Operation *op) void printHelp(const QList &operations) { std::cout << "Qt Creator SDK setup tool." << std::endl; - std::cout << " Usage:" << qPrintable(qApp->arguments().at(0)) + std::cout << " Usage: " << qPrintable(qApp->arguments().at(0)) << " " << std::endl << std::endl; std::cout << "ARGS:" << std::endl; std::cout << " --help|-h Print this help text" << std::endl; diff --git a/tests/auto/utils/ansiescapecodehandler/tst_ansiescapecodehandler.cpp b/tests/auto/utils/ansiescapecodehandler/tst_ansiescapecodehandler.cpp index 16675bb885b..9825758cfed 100644 --- a/tests/auto/utils/ansiescapecodehandler/tst_ansiescapecodehandler.cpp +++ b/tests/auto/utils/ansiescapecodehandler/tst_ansiescapecodehandler.cpp @@ -57,6 +57,7 @@ private Q_SLOTS: void testSimpleFormat(); void testSimpleFormat_data(); void testLineOverlappingFormat(); + void testSplitControlSequence(); private: const QString red; @@ -225,6 +226,31 @@ void tst_AnsiEscapeCodeHandler::testSimpleFormat_data() << (FormattedTextList() << FormattedText("All text after this is ", defaultFormat) << FormattedText("not deleted", defaultFormat)); + + QTest::newRow("Unfinished control sequence \\x1b") + << QString("A text before \x1b") << QTextCharFormat() + << (FormattedTextList() + << FormattedText("A text before ", defaultFormat)); + + QTest::newRow("Unfinished control sequence \\x1b[") + << QString("A text before \x1b[") << QTextCharFormat() + << (FormattedTextList() + << FormattedText("A text before ", defaultFormat)); + + QTest::newRow("Unfinished control sequence \\x1b[3") + << QString("A text before \x1b[3") << QTextCharFormat() + << (FormattedTextList() + << FormattedText("A text before ", defaultFormat)); + + QTest::newRow("Unfinished control sequence \\x1b[31") + << QString("A text before \x1b[31") << QTextCharFormat() + << (FormattedTextList() + << FormattedText("A text before ", defaultFormat)); + + QTest::newRow("Unfinished control sequence \\x1b[31,") + << QString("A text before \x1b[31,") << QTextCharFormat() + << (FormattedTextList() + << FormattedText("A text before ", defaultFormat)); } void tst_AnsiEscapeCodeHandler::testLineOverlappingFormat() @@ -254,6 +280,29 @@ void tst_AnsiEscapeCodeHandler::testLineOverlappingFormat() QCOMPARE(result[3].format, defaultFormat); } +void tst_AnsiEscapeCodeHandler::testSplitControlSequence() +{ + // Test line-overlapping formats + const QString line1 = "Normal line \x1b"; + const QString line2 = "[1m bold line"; + + QTextCharFormat defaultFormat; + + AnsiEscapeCodeHandler handler; + FormattedTextList result; + result.append(handler.parseText(FormattedText(line1, defaultFormat))); + result.append(handler.parseText(FormattedText(line2, defaultFormat))); + + QTextCharFormat boldFormat; + boldFormat.setFontWeight(QFont::Bold); + + QCOMPARE(result.size(), 2); + QCOMPARE(result[0].text, QLatin1String("Normal line ")); + QCOMPARE(result[0].format, defaultFormat); + QCOMPARE(result[1].text, QLatin1String(" bold line")); + QCOMPARE(result[1].format, boldFormat); +} + QTEST_APPLESS_MAIN(tst_AnsiEscapeCodeHandler) #include "tst_ansiescapecodehandler.moc" diff --git a/tests/system/objects.map b/tests/system/objects.map index e96b9ea1c1b..791fe94ae73 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -104,7 +104,6 @@ :Help Widget_Help::Internal::HelpWidget {type='Help::Internal::HelpWidget' unnamed='1' visible='1' windowTitle?='Help -*'} :Hits_QCLuceneResultWidget {aboveWidget=':Hits_QLabel' type='QCLuceneResultWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Hits_QLabel {text~='\\\\d+ - \\\\d+ of \\\\d+ Hits' type='QLabel' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:JavaScript.QmlProfilerEventsTable_QmlProfiler::Internal::QV8ProfilerEventsMainView {container=':*Qt Creator.JavaScript_QDockWidget' name='QmlProfilerEventsTable' type='QmlProfiler::Internal::QV8ProfilerEventsMainView' visible='1'} :Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' leftWidget=':QtVersionLabel_KitPage' type='QComboBox' unnamed='1' visible='1'} :Locals and Expressions_Debugger::Internal::WatchTreeView {container=':DebugModeWidget.Locals and Expressions_QDockWidget' name='WatchWindow' type='Debugger::Internal::WatchTreeView' visible='1' windowTitle='Locals and Expressions'} :Minimal required Qt version:_QLabel {text='Minimal required Qt version:' type='QLabel' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 7df1631dd78..951ed4645bd 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -634,7 +634,7 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False): result.extend([Targets.DESKTOP_521_DEFAULT, Targets.DESKTOP_531_DEFAULT]) if platform.system() != 'Darwin': result.append(Targets.DESKTOP_541_GCC) - if not ("BlackBerry" in templateName or re.search("custom Qt Creator plugin", text)) and (version == None or version < "5.0"): + if not templateName == "Qt Creator Plugin" and (version == None or version < "5.0"): result.append(Targets.SIMULATOR) elif 'Platform independent' in text: result = list(Targets.ALL_TARGETS) @@ -644,7 +644,7 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False): else: test.warning("Returning None (__getSupportedPlatforms__())", "Parsed text: '%s'" % text) - return None, None + return [], None if getAsStrings: result = Targets.getTargetsAsStrings(result) return result, version diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py index 08e36005031..1cd77662b95 100644 --- a/tests/system/shared/utils.py +++ b/tests/system/shared/utils.py @@ -380,7 +380,7 @@ def getConfiguredKits(): iterateKits(True, True, __setQtVersionForKit__, kitsWithQtVersionName) # merge defined target names with their configured Qt versions and devices for kit, qtVersion in kitsWithQtVersionName.iteritems(): - if kit in ('Fremantle', 'Harmattan'): + if kit in ('Fremantle', 'Harmattan', 'Qt Simulator'): test.verify(qtVersion == 'None', "The outdated kit '%s' should not have a Qt version" % kit) elif qtVersion in qtVersionNames: diff --git a/tests/system/suite_CSUP/tst_CSUP06/test.py b/tests/system/suite_CSUP/tst_CSUP06/test.py index a8881a80507..c71fca6f4a8 100644 --- a/tests/system/suite_CSUP/tst_CSUP06/test.py +++ b/tests/system/suite_CSUP/tst_CSUP06/test.py @@ -94,7 +94,7 @@ def checkIncludeCompletion(editor): if inclSnippet in missing: test.compare(changedLine, currentLine.lstrip("/"), "Include has not been modified.") else: - test.verify(changedLine.endswith('>') or changedLine.endswith('"'), + test.verify(changedLine[-1] in '>"/', "'%s' has been completed to '%s'" % (currentLine.lstrip("/"), changedLine)) performAutoCompletionTest(editor, ".*Complete includes.*", "//#include", @@ -118,9 +118,12 @@ def checkSymbolCompletion(editor, isClangCodeModel): if not isClangCodeModel: expectedSuggestion["using namespace st"] = ["std", "st"] missing.remove("using namespace st") - elif platform.system() in ('Microsoft', 'Windows'): - expectedSuggestion["using namespace st"] = ["std", "stdext"] - missing.remove("using namespace st") + else: + missing.remove("internal.o") + expectedSuggestion["internal.o"] = ["one", "operator="] + if platform.system() in ('Microsoft', 'Windows'): + expectedSuggestion["using namespace st"] = ["std", "stdext"] + missing.remove("using namespace st") # define test function to perform the _real_ auto completion test on the current line def testSymb(currentLine, *args): missing, expectedSug, expectedRes = args diff --git a/tests/system/suite_debugger/tst_cli_output_console/test.py b/tests/system/suite_debugger/tst_cli_output_console/test.py index 24671749844..4a1386d089b 100644 --- a/tests/system/suite_debugger/tst_cli_output_console/test.py +++ b/tests/system/suite_debugger/tst_cli_output_console/test.py @@ -57,7 +57,7 @@ def main(): invokeMenuItem("File", "Save All") openDocument(project + "." + project + "\\.pro") proEditor = waitForObject(":Qt Creator_TextEditor::TextEditorWidget") - test.verify("CONFIG += console" in str(proEditor.plainText), "Verifying that program is configured with console") + test.verify("CONFIG += console" in str(proEditor.plainText), "Verifying that program is configured with console") availableConfigs = iterateBuildConfigs(len(checkedTargets)) if not availableConfigs: diff --git a/tests/system/suite_debugger/tst_simple_analyze/test.py b/tests/system/suite_debugger/tst_simple_analyze/test.py index dddff7d5e87..f5b926794f5 100644 --- a/tests/system/suite_debugger/tst_simple_analyze/test.py +++ b/tests/system/suite_debugger/tst_simple_analyze/test.py @@ -36,10 +36,10 @@ def main(): return # using a temporary directory won't mess up a potentially existing workingDir = tempDir() - # we need a Qt >= 4.8 - analyzerTargets = Targets.desktopTargetClasses() & ~Targets.DESKTOP_474_GCC + # we need a Qt >= 5.3 - we use checkedTargets, so we should get only valid targets + analyzerTargets = Targets.desktopTargetClasses() if platform.system() in ('Windows', 'Microsoft') and JIRA.isBugStillOpen(14307): - analyzerTargets &= ~Targets.DESKTOP_480_DEFAULT & ~Targets.DESKTOP_541_GCC + analyzerTargets &= ~Targets.DESKTOP_541_GCC checkedTargets, projectName = createNewQtQuickApplication(workingDir, targets=analyzerTargets) editor = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") if placeCursorToLine(editor, "MouseArea.*", True): @@ -59,13 +59,12 @@ def main(): invokeMenuItem("File", "Save All") availableConfigs = iterateBuildConfigs(len(checkedTargets), "Debug") if not availableConfigs: - test.fatal("Haven't found a suitable Qt version (need Qt 4.8) - leaving without debugging.") + test.fatal("Haven't found a suitable Qt version (need Qt 5.3+) - leaving without debugging.") else: - performTest(workingDir, projectName, len(checkedTargets), availableConfigs, False) - performTest(workingDir, projectName, len(checkedTargets), availableConfigs, True) + performTest(workingDir, projectName, len(checkedTargets), availableConfigs) invokeMenuItem("File", "Exit") -def performTest(workingDir, projectName, targetCount, availableConfigs, disableOptimizer): +def performTest(workingDir, projectName, targetCount, availableConfigs): def __elapsedTime__(elapsedTimeLabelText): return float(re.search("Elapsed:\s+(-?\d+\.\d+) s", elapsedTimeLabelText).group(1)) @@ -75,9 +74,6 @@ def performTest(workingDir, projectName, targetCount, availableConfigs, disableO invokeMenuItem('Build', 'Clean Project "%s"' % projectName) qtVersion = verifyBuildConfig(targetCount, kit, config, True, enableQmlDebug=True)[0] test.log("Selected kit using Qt %s" % qtVersion) - if disableOptimizer: - batchEditRunEnvironment(targetCount, kit, ["QML_DISABLE_OPTIMIZER=1"]) - switchViewTo(ViewConstants.EDIT) # explicitly build before start debugging for adding the executable as allowed program to WinFW invokeMenuItem("Build", "Rebuild All") waitForCompile() @@ -106,30 +102,18 @@ def performTest(workingDir, projectName, targetCount, availableConfigs, disableO "Elapsed time should be positive in string '%s'" % str(elapsedLabel.text)) except: test.fatal("Could not read elapsed time from '%s'" % str(elapsedLabel.text)) - if safeClickTab("V8"): - model = findObject(":JavaScript.QmlProfilerEventsTable_QmlProfiler::" - "Internal::QV8ProfilerEventsMainView").model() - test.compare(model.rowCount(), 0) if safeClickTab("Events"): colPercent, colTotal, colCalls, colMean, colMedian, colLongest, colShortest = range(2, 9) model = waitForObject(":Events.QmlProfilerEventsTable_QmlProfiler::" "Internal::QmlProfilerEventsMainView").model() - if qtVersion.startswith("5."): - if disableOptimizer: - compareEventsTab(model, "events_qt50_nonOptimized.tsv") - else: - compareEventsTab(model, "events_qt50.tsv") - numberOfMsRows = 3 - else: - if disableOptimizer: - compareEventsTab(model, "events_qt48_nonOptimized.tsv") - else: - compareEventsTab(model, "events_qt48.tsv") - numberOfMsRows = 2 + compareEventsTab(model, "events_qt5.tsv") + numberOfMsRows = 3 test.compare(dumpItems(model, column=colPercent)[0], '100.00 %') - for i in [colTotal, colMean, colMedian, colLongest, colShortest]: + # cannot run following test on colShortest (unstable) + for i in [colTotal, colMean, colMedian, colLongest]: for item in dumpItems(model, column=i)[:numberOfMsRows]: test.verify(item.endswith(' ms'), "Verify that '%s' ends with ' ms'" % item) + for i in [colTotal, colMean, colMedian, colLongest, colShortest]: for item in dumpItems(model, column=i): test.verify(not item.startswith('0.000 '), "Check for implausible durations (QTCREATORBUG-8996): %s" % item) diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48.tsv deleted file mode 100644 index 443e686825b..00000000000 --- a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48.tsv +++ /dev/null @@ -1,8 +0,0 @@ -"0" "1" "4" "9" -"" "" "1" "Main Program" -"main.qml:12" "Signal" "2" "triggered(): { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" -"main.qml:1" "Create" "1" "main.qml" -"main.qml:1" "Compile" "1" "main.qml" -"main.qml:31" "Binding" "1" "text: qsTr(""Hello World"")" -"" "Binding" "2" "Source code not available" -"main.qml:11" "Binding" "3" "running: runCount < 2" diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48_nonOptimized.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48_nonOptimized.tsv deleted file mode 100644 index 661b87864f3..00000000000 --- a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt48_nonOptimized.tsv +++ /dev/null @@ -1,9 +0,0 @@ -"0" "1" "4" "9" -"" "" "1" "Main Program" -"main.qml:12" "Signal" "2" "triggered(): { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" -"main.qml:1" "Create" "1" "main.qml" -"main.qml:1" "Compile" "1" "main.qml" -"main.qml:31" "Binding" "1" "text: qsTr(""Hello World"")" -"main.qml:11" "Binding" "3" "running: runCount < 2" -"main.qml:32" "Binding" "1" "centerIn: parent" -"main.qml:24" "Binding" "1" "fill: parent" diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.tsv new file mode 100644 index 00000000000..8b8833faeb8 --- /dev/null +++ b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.tsv @@ -0,0 +1,15 @@ +"0" "1" "4" "9" +"" "" "1" "Main Program" +"main.qml:4" "Create" "2" "QtQuick.Window/Window" +"main.qml:12" "Signal" "2" "onTriggered: { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" +"main.qml:12" "JavaScript" "2" "onTriggered" +"main.qml:30" "Create" "2" "QtQuick/Text" +"main.qml:1" "Compile" "1" "main.qml" +"main.qml:7" "Create" "2" "QtQuick/Timer" +"main.qml:32" "Binding" "1" "anchors.centerIn: parent" +"main.qml:23" "Create" "2" "QtQuick/MouseArea" +"main.qml:11" "Binding" "3" "running: runCount < 2" +"main.qml:32" "JavaScript" "1" "expression for centerIn" +"main.qml:24" "Binding" "1" "anchors.fill: parent" +"main.qml:11" "JavaScript" "3" "expression for running" +"main.qml:24" "JavaScript" "1" "expression for fill" diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50.tsv deleted file mode 100644 index c9027ef163e..00000000000 --- a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50.tsv +++ /dev/null @@ -1,8 +0,0 @@ -"0" "1" "4" "9" -"" "" "1" "Main Program" -"main.qml:1" "Create" "1" "main.qml" -"main.qml:12" "Signal" "2" "triggered(): { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" -"main.qml:1" "Compile" "1" "main.qml" -"main.qml:31" "Binding" "1" "text: qsTr(""Hello World"")" -"" "Binding" "2" "Source code not available" -"main.qml:11" "Binding" "3" "running: runCount < 2" diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50_nonOptimized.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50_nonOptimized.tsv deleted file mode 100644 index 9b1f613781b..00000000000 --- a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt50_nonOptimized.tsv +++ /dev/null @@ -1,9 +0,0 @@ -"0" "1" "4" "9" -"" "" "1" "Main Program" -"main.qml:1" "Create" "1" "main.qml" -"main.qml:12" "Signal" "2" "triggered(): { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" -"main.qml:1" "Compile" "1" "main.qml" -"main.qml:31" "Binding" "1" "text: qsTr(""Hello World"")" -"main.qml:11" "Binding" "3" "running: runCount < 2" -"main.qml:32" "Binding" "1" "centerIn: parent" -"main.qml:24" "Binding" "1" "fill: parent" diff --git a/tests/system/suite_debugger/tst_simple_debug/test.py b/tests/system/suite_debugger/tst_simple_debug/test.py index 97e25f5e70c..c5723aced48 100644 --- a/tests/system/suite_debugger/tst_simple_debug/test.py +++ b/tests/system/suite_debugger/tst_simple_debug/test.py @@ -50,12 +50,12 @@ def main(): invokeMenuItem("File", "Save All") filesAndLines = [ { "%s.Resources.qml\.qrc./.main\\.qml" % projectName : 'onTriggered.*' }, - { "%s.Sources.main\\.cpp" % projectName : "viewer.setOrientation\\(.+\\);" } + { "%s.Sources.main\\.cpp" % projectName : "QQmlApplicationEngine engine;" } ] test.log("Setting breakpoints") result = setBreakpointsForCurrentProject(filesAndLines) if result: - expectedBreakpointsOrder = [{os.path.join(workingDir, projectName, "main.cpp"):10}, + expectedBreakpointsOrder = [{os.path.join(workingDir, projectName, "main.cpp"):8}, {os.path.join(workingDir, projectName, "main.qml"):10}] # Only use 4.7.4 to work around QTBUG-25187 availableConfigs = iterateBuildConfigs(len(checkedTargets), "Debug") diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index 8d0de10b226..da9fa6d4b29 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -30,12 +30,17 @@ source("../../shared/qtcreator.py") -import re - def main(): global tmpSettingsDir - quickCombinations = ["1.1", "2.1", "2.2", "2.3", "2.4", - "Controls 1.0", "Controls 1.1", "Controls 1.2", "Controls 1.3"] + qtVersionsForQuick = ["5.3"] + availableBuildSystems = ["qmake", "Qbs"] + if platform.system() != 'Darwin': + qtVersionsForQuick.append("5.4") + if which("cmake"): + availableBuildSystems.append("CMake") + else: + test.warning("Could not find cmake in PATH - several tests won't run without.") + startApplication("qtcreator" + SettingsPath) if not startedWithoutPluginError(): return @@ -50,8 +55,11 @@ def main(): comboBox = findObject(":New.comboBox_QComboBox") targets = zip(*kits.values())[0] test.verify(comboBox.enabled, "Verifying whether combobox is enabled.") - test.compare(comboBox.currentText, "Desktop Templates") - selectFromCombo(comboBox, "All Templates") + test.compare(comboBox.currentText, "All Templates") + try: + selectFromCombo(comboBox, "All Templates") + except: + test.warning("Could not explicitly select 'All Templates' from combobox.") for category in [item.replace(".", "\\.") for item in dumpItems(catModel, projects)]: # skip non-configurable if "Import" in category: @@ -62,27 +70,39 @@ def main(): for template in dumpItems(templatesView.model(), templatesView.rootIndex()): template = template.replace(".", "\\.") # skip non-configurable - if (template != "Qt Quick UI" and "(CMake Build)" not in template - and "(Qbs Build)" not in template): + if not template in ["Qt Quick UI", "Qt Quick Controls UI", "Qt Canvas 3D Application"]: availableProjectTypes.append({category:template}) - clickButton(waitForObject("{text='Cancel' type='QPushButton' unnamed='1' visible='1'}")) + safeClickButton("Cancel") for current in availableProjectTypes: category = current.keys()[0] template = current.values()[0] displayedPlatforms = __createProject__(category, template) - if template == "Qt Quick Application": - for counter, qComb in enumerate(quickCombinations): - requiredQtVersion = __createProjectHandleQtQuickSelection__(qComb) + if template == "Qt Quick Application" or template == "Qt Quick Controls Application": + for counter, qtVersion in enumerate(qtVersionsForQuick): + requiredQtVersion = __createProjectHandleQtQuickSelection__(qtVersion) __modifyAvailableTargets__(displayedPlatforms, requiredQtVersion, True) verifyKitCheckboxes(kits, displayedPlatforms) # FIXME: if QTBUG-35203 is fixed replace by triggering the shortcut for Back - clickButton(waitForObject("{type='QPushButton' text='Cancel' visible='1'}")) + safeClickButton("Cancel") # are there more Quick combinations - then recreate this project - if counter < len(quickCombinations) - 1: + if counter < len(qtVersionsForQuick) - 1: displayedPlatforms = __createProject__(category, template) continue + elif template.startswith("Plain C"): + for counter, buildSystem in enumerate(availableBuildSystems): + combo = "{name='BuildSystem' type='Utils::TextFieldComboBox' visible='1'}" + selectFromCombo(combo, buildSystem) + clickButton(waitForObject(":Next_QPushButton")) + verifyKitCheckboxes(kits, displayedPlatforms) + # FIXME: if QTBUG-35203 is fixed replace by triggering the shortcut for Back + safeClickButton("Cancel") + if counter < len(availableBuildSystems) - 1: + displayedPlatforms = __createProject__(category, template) + continue + verifyKitCheckboxes(kits, displayedPlatforms) - clickButton(waitForObject("{type='QPushButton' text='Cancel' visible='1'}")) + safeClickButton("Cancel") + invokeMenuItem("File", "Exit") def verifyKitCheckboxes(kits, displayedPlatforms): @@ -106,18 +126,41 @@ def verifyKitCheckboxes(kits, displayedPlatforms): % str(availableCheckboxes)) def __createProject__(category, template): + def safeGetTextBrowserText(): + try: + return str(waitForObject(":frame.templateDescription_QTextBrowser", 500).plainText) + except: + return "" + invokeMenuItem("File", "New File or Project...") selectFromCombo(waitForObject(":New.comboBox_QComboBox"), "All Templates") categoriesView = waitForObject(":New.templateCategoryView_QTreeView") clickItem(categoriesView, "Projects." + category, 5, 5, 0, Qt.LeftButton) templatesView = waitForObject("{name='templatesView' type='QListView' visible='1'}") + test.log("Verifying '%s' -> '%s'" % (category.replace("\\.", "."), template.replace("\\.", "."))) - textBrowser = findObject(":frame.templateDescription_QTextBrowser") - origTxt = str(textBrowser.plainText) + origTxt = safeGetTextBrowserText() clickItem(templatesView, template, 5, 5, 0, Qt.LeftButton) - waitFor("origTxt != str(textBrowser.plainText)", 2000) - displayedPlatforms = __getSupportedPlatforms__(str(textBrowser.plainText), template, True)[0] - clickButton(waitForObject("{text='Choose...' type='QPushButton' unnamed='1' visible='1'}")) + waitFor("origTxt != safeGetTextBrowserText() != ''", 2000) + displayedPlatforms = __getSupportedPlatforms__(safeGetTextBrowserText(), template, True)[0] + safeClickButton("Choose...") # don't check because project could exist __createProjectSetNameAndPath__(os.path.expanduser("~"), 'untitled', False) return displayedPlatforms + +def safeClickButton(buttonLabel): + buttonString = "{type='QPushButton' text='%s' visible='1' unnamed='1'}" + for _ in range(5): + try: + clickButton(buttonString % buttonLabel) + return + except: + if buttonLabel == "Cancel": + try: + clickButton("{name='qt_wizard_cancel' type='QPushButton' text='Cancel' " + "visible='1'}") + return + except: + pass + snooze(1) + test.fatal("Even safeClickButton failed...") diff --git a/tests/system/suite_general/tst_save_before_build/test.py b/tests/system/suite_general/tst_save_before_build/test.py index 4b4bb3a7e80..4601b4a6518 100644 --- a/tests/system/suite_general/tst_save_before_build/test.py +++ b/tests/system/suite_general/tst_save_before_build/test.py @@ -48,7 +48,7 @@ def main(): return ensureSaveBeforeBuildChecked(False) # create qt quick application - createNewQtQuickApplication(tempDir(), "SampleApp", qtQuickVersion="2.2") + createNewQtQuickApplication(tempDir(), "SampleApp") for expectDialog in [True, False]: files = ["SampleApp.SampleApp\\.pro", "SampleApp.deployment.deployment\\.pri", diff --git a/tests/system/suite_tools/tst_git_local/test.py b/tests/system/suite_tools/tst_git_local/test.py index 121c286b82d..7e6021e8f26 100644 --- a/tests/system/suite_tools/tst_git_local/test.py +++ b/tests/system/suite_tools/tst_git_local/test.py @@ -109,9 +109,11 @@ def __clickCommit__(count): description = waitForObject(":Qt Creator_DiffEditor::Internal::DescriptionEditorWidget") waitFor('len(str(description.plainText)) != 0', 5000) show = str(description.plainText) + id = "Nobody " + time = "\w{3} \w{3} \d{1,2} \d{2}:\d{2}:\d{2} \d{4}.* seconds ago\)" expected = [{"commit %s" % commit:False}, - {"Author: Nobody ": False}, - {"Date:\s+\w{3} \w{3} \d{1,2} \d{2}:\d{2}:\d{2} \d{4}.*":True}] + {"Author: %s, %s" % (id, time): True}, + {"Committer: %s, %s" % (id, time): True}] for line, exp in zip(show.splitlines(), expected): expLine = exp.keys()[0] isRegex = exp.values()[0] diff --git a/tests/unit/unittest/clangipcservertest.cpp b/tests/unit/unittest/clangipcservertest.cpp index 1c7c74da79d..690e03b0b19 100644 --- a/tests/unit/unittest/clangipcservertest.cpp +++ b/tests/unit/unittest/clangipcservertest.cpp @@ -149,8 +149,6 @@ TEST_F(ClangIpcServer, GetCodeCompletion) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Function"), - Utf8String(), - Utf8String(), 34, CodeCompletion::FunctionCompletionKind); @@ -167,8 +165,6 @@ TEST_F(ClangIpcServer, GetCodeCompletionDependingOnArgumets) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"), - Utf8String(), - Utf8String(), 34, CodeCompletion::VariableCompletionKind); @@ -214,8 +210,6 @@ TEST_F(ClangIpcServer, GetCodeCompletionForUnsavedFile) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), - Utf8String(), - Utf8String(), 34, CodeCompletion::FunctionCompletionKind); @@ -233,8 +227,6 @@ TEST_F(ClangIpcServer, GetNoCodeCompletionAfterRemovingUnsavedFile) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), - Utf8String(), - Utf8String(), 34, CodeCompletion::FunctionCompletionKind); @@ -255,8 +247,6 @@ TEST_F(ClangIpcServer, GetNewCodeCompletionAfterUpdatingUnsavedFile) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Method3"), - Utf8String(), - Utf8String(), 34, CodeCompletion::FunctionCompletionKind); @@ -367,8 +357,6 @@ TEST_F(ClangIpcServer, TicketNumberIsForwarded) 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Function"), - Utf8String(), - Utf8String(), 34, CodeCompletion::FunctionCompletionKind); diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp index efb4551a0ab..62bbbb6b9ca 100644 --- a/tests/unit/unittest/codecompletiontest.cpp +++ b/tests/unit/unittest/codecompletiontest.cpp @@ -95,28 +95,18 @@ TEST_F(CodeCompleter, FunctionInUnsavedFile) { ASSERT_THAT(completer.complete(100, 1), AllOf(Contains(CodeCompletion(Utf8StringLiteral("functionWithArguments"), - Utf8String(), - Utf8String(), 0, CodeCompletion::FunctionCompletionKind)), Contains(CodeCompletion(Utf8StringLiteral("function"), - Utf8String(), - Utf8String(), 0, CodeCompletion::FunctionCompletionKind)), Contains(CodeCompletion(Utf8StringLiteral("newFunction"), - Utf8String(), - Utf8String(), 0, CodeCompletion::FunctionCompletionKind)), Contains(CodeCompletion(Utf8StringLiteral("f"), - Utf8String(), - Utf8String(), 0, CodeCompletion::FunctionCompletionKind)), Not(Contains(CodeCompletion(Utf8StringLiteral("otherFunction"), - Utf8String(), - Utf8String(), 0, CodeCompletion::FunctionCompletionKind))))); } @@ -126,8 +116,6 @@ TEST_F(CodeCompleter, Macro) { ASSERT_THAT(completer.complete(100, 1), Contains(CodeCompletion(Utf8StringLiteral("Macro"), - Utf8String(), - Utf8String(), 0, CodeCompletion::PreProcessorCompletionKind))); } @@ -136,8 +124,6 @@ TEST_F(CodeCompleter, Keyword) { ASSERT_THAT(completer.complete(100, 1), Contains(CodeCompletion(Utf8StringLiteral("switch"), - Utf8String(), - Utf8String(), 0, CodeCompletion::KeywordCompletionKind))); } diff --git a/tests/unit/unittest/completionchunkstotextconvertertest.cpp b/tests/unit/unittest/completionchunkstotextconvertertest.cpp index 97f017c0695..bc2bb4219a3 100644 --- a/tests/unit/unittest/completionchunkstotextconvertertest.cpp +++ b/tests/unit/unittest/completionchunkstotextconvertertest.cpp @@ -39,11 +39,15 @@ namespace { using ClangBackEnd::CodeCompletionChunk; +using Converter = ClangCodeModel::Internal::CompletionChunksToTextConverter; class CompletionChunksToTextConverter : public ::testing::Test { protected: - ClangCodeModel::Internal::CompletionChunksToTextConverter converter; + void setupConverterForKeywords(); + +protected: + Converter converter; CodeCompletionChunk integerResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("int")}; CodeCompletionChunk enumerationResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("Enumeration")}; CodeCompletionChunk functionName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Function")}; @@ -52,18 +56,36 @@ protected: CodeCompletionChunk enumerationName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumeration")}; CodeCompletionChunk className{CodeCompletionChunk::TypedText, Utf8StringLiteral("Class")}; CodeCompletionChunk leftParen{CodeCompletionChunk::LeftParen, Utf8StringLiteral("(")}; - CodeCompletionChunk rightParen{CodeCompletionChunk::LeftParen, Utf8StringLiteral(")")}; + CodeCompletionChunk rightParen{CodeCompletionChunk::RightParen, Utf8StringLiteral(")")}; CodeCompletionChunk comma{CodeCompletionChunk::Comma, Utf8StringLiteral(", ")}; + CodeCompletionChunk semicolon{CodeCompletionChunk::SemiColon, Utf8StringLiteral(";")}; CodeCompletionChunk functionArgumentX{CodeCompletionChunk::Placeholder, Utf8StringLiteral("char x")}; CodeCompletionChunk functionArgumentY{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int y")}; CodeCompletionChunk functionArgumentZ{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int z")}; + CodeCompletionChunk switchName{CodeCompletionChunk::TypedText, Utf8StringLiteral("switch")}; + CodeCompletionChunk condition{CodeCompletionChunk::Placeholder, Utf8StringLiteral("condition")}; + CodeCompletionChunk leftBrace{CodeCompletionChunk::LeftBrace, Utf8StringLiteral("{")}; + CodeCompletionChunk rightBrace{CodeCompletionChunk::RightBrace, Utf8StringLiteral("}")}; + CodeCompletionChunk verticalSpace{CodeCompletionChunk::VerticalSpace, Utf8StringLiteral("\n")}; + CodeCompletionChunk throwName{CodeCompletionChunk::TypedText, Utf8StringLiteral("throw")}; + CodeCompletionChunk voidResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("void")}; + CodeCompletionChunk forName{CodeCompletionChunk::TypedText, Utf8StringLiteral("for")}; + CodeCompletionChunk initStatement{CodeCompletionChunk::Placeholder, Utf8StringLiteral("init-statement")}; + CodeCompletionChunk initExpression{CodeCompletionChunk::Placeholder, Utf8StringLiteral("init-expression")}; + CodeCompletionChunk statements{CodeCompletionChunk::Placeholder, Utf8StringLiteral("statements")}; + CodeCompletionChunk constCastName{CodeCompletionChunk::TypedText, Utf8StringLiteral("const_cast")}; + CodeCompletionChunk leftAngle{CodeCompletionChunk::LeftAngle, Utf8StringLiteral("<")}; + CodeCompletionChunk rightAngle{CodeCompletionChunk::RightAngle, Utf8StringLiteral(">")}; + CodeCompletionChunk elseName{CodeCompletionChunk::TypedText, Utf8StringLiteral("else")}; + CodeCompletionChunk ifName{CodeCompletionChunk::TypedText, Utf8StringLiteral("if")}; + CodeCompletionChunk horizontalSpace{CodeCompletionChunk::HorizontalSpace, Utf8StringLiteral(" ")}; CodeCompletionChunk optional{CodeCompletionChunk::Optional, Utf8String(), {comma, functionArgumentY, comma, functionArgumentZ}}; }; TEST_F(CompletionChunksToTextConverter, ParseIsClearingText) { QVector completionChunks({integerResultType, functionName, leftParen, rightParen}); - converter.parseChunks(completionChunks); + converter.setAddResultType(true); converter.parseChunks(completionChunks); @@ -73,6 +95,7 @@ TEST_F(CompletionChunksToTextConverter, ParseIsClearingText) TEST_F(CompletionChunksToTextConverter, ConvertFunction) { QVector completionChunks({integerResultType, functionName, leftParen, rightParen}); + converter.setAddResultType(true); converter.parseChunks(completionChunks); @@ -82,6 +105,8 @@ TEST_F(CompletionChunksToTextConverter, ConvertFunction) TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithParameters) { QVector completionChunks({integerResultType, functionName, leftParen, functionArgumentX,rightParen}); + converter.setAddResultType(true); + converter.setAddPlaceHolderText(true); converter.parseChunks(completionChunks); @@ -91,6 +116,8 @@ TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithParameters) TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithOptionalParameter) { QVector completionChunks({integerResultType, functionName, leftParen, functionArgumentX, optional,rightParen}); + converter.setAddResultType(true); + converter.setAddPlaceHolderText(true); converter.parseChunks(completionChunks); @@ -100,6 +127,7 @@ TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithOptionalParameter) TEST_F(CompletionChunksToTextConverter, ConvertVariable) { QVector completionChunks({integerResultType, variableName}); + converter.setAddResultType(true); converter.parseChunks(completionChunks); @@ -109,6 +137,7 @@ TEST_F(CompletionChunksToTextConverter, ConvertVariable) TEST_F(CompletionChunksToTextConverter, Enumerator) { QVector completionChunks({enumerationResultType, enumeratorName}); + converter.setAddResultType(true); converter.parseChunks(completionChunks); @@ -124,4 +153,91 @@ TEST_F(CompletionChunksToTextConverter, Enumeration) ASSERT_THAT(converter.text(), QStringLiteral("Class")); } +TEST_F(CompletionChunksToTextConverter, Switch) +{ + QVector completionChunks({switchName, + leftParen, + condition, + rightParen, + leftBrace, + verticalSpace, + rightBrace}); + setupConverterForKeywords(); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("switch () {\n\n}")); + ASSERT_THAT(converter.placeholderPositions().at(0), 8); +} + +TEST_F(CompletionChunksToTextConverter, For) +{ + QVector completionChunks({forName, + leftParen, + initStatement, + semicolon, + initExpression, + semicolon, + condition, + rightParen, + leftBrace, + verticalSpace, + statements, + verticalSpace, + rightBrace}); + setupConverterForKeywords(); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("for (;;) {\n\n}")); +} + +TEST_F(CompletionChunksToTextConverter, const_cast) +{ + QVector completionChunks({constCastName, + leftAngle, + rightAngle, + leftParen, + rightParen}); + setupConverterForKeywords(); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("const_cast<>()")); +} + +TEST_F(CompletionChunksToTextConverter, Throw) +{ + QVector completionChunks({voidResultType, throwName}); + + auto completionName = Converter::convertToName(completionChunks); + + ASSERT_THAT(completionName, QStringLiteral("throw")); +} + +TEST_F(CompletionChunksToTextConverter, ElseIf) +{ + QVector completionChunks({elseName, + horizontalSpace, + ifName, + horizontalSpace, + leftBrace, + verticalSpace, + statements, + verticalSpace, + rightBrace}); + + setupConverterForKeywords(); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("else if {\n\n}")); +} + +void CompletionChunksToTextConverter::setupConverterForKeywords() +{ + converter.setAddPlaceHolderPositions(true); + converter.setAddSpaces(true); + converter.setAddExtraVerticalSpaceBetweenBraces(true); +} } diff --git a/tests/unit/unittest/projecttest.cpp b/tests/unit/unittest/projecttest.cpp index facf06dc376..78aab7c7bea 100644 --- a/tests/unit/unittest/projecttest.cpp +++ b/tests/unit/unittest/projecttest.cpp @@ -153,7 +153,7 @@ TEST(ProjectPart, ProjectPartProjectPartIdIsEmptyfterRemoving) ASSERT_TRUE(project.projectPartId().isEmpty()); } -TEST(Project, ThrowsForNotExistingProjectPartButRemovesAllExistingProject) +TEST(ProjectPart, ThrowsForNotExistingProjectPartButRemovesAllExistingProject) { ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); ClangBackEnd::ProjectParts projects; @@ -165,6 +165,22 @@ TEST(Project, ThrowsForNotExistingProjectPartButRemovesAllExistingProject) ASSERT_THAT(projects.projects(), Not(Contains(project))); } +TEST(ProjectPart, ProjectPartIsClearedAfterRemove) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + ClangBackEnd::ProjectPart project = *projects.findProjectPart(projectContainer.projectPartId()); + const auto lastChangeTimePoint = project.lastChangeTimePoint(); + std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + + projects.remove({projectContainer.projectPartId()}); + + ASSERT_THAT(project.projectPartId(), Utf8String()); + ASSERT_THAT(project.argumentCount(), 0); + ASSERT_THAT(project.lastChangeTimePoint(), Gt(lastChangeTimePoint)); +} + TEST(ProjectPart, HasProjectPart) { ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); diff --git a/tests/unit/unittest/translationunittest.cpp b/tests/unit/unittest/translationunittest.cpp index 25235940bf0..29ae9cd4686 100644 --- a/tests/unit/unittest/translationunittest.cpp +++ b/tests/unit/unittest/translationunittest.cpp @@ -132,15 +132,75 @@ TEST(TranslationUnit, ResetedTranslationUnitIsNull) ASSERT_TRUE(translationUnit.isNull()); } -TEST(TranslationUnit, TimeStampIsUpdatedAsNewCxTranslationUnitIsGenerated) +TEST(TranslationUnit, TimeStampForProjectPartChangeIsUpdatedAsNewCxTranslationUnitIsGenerated) { TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); - auto lastChangeTimePoint = translationUnit.lastChangeTimePoint(); + auto lastChangeTimePoint = translationUnit.lastProjectPartChangeTimePoint(); std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); translationUnit.cxTranslationUnit(); - ASSERT_THAT(translationUnit.lastChangeTimePoint(), Gt(lastChangeTimePoint)); + ASSERT_THAT(translationUnit.lastProjectPartChangeTimePoint(), Gt(lastChangeTimePoint)); +} + +TEST(TranslationUnit, TimeStampForProjectPartChangeIsUpdatedAsProjectPartIsCleared) +{ + ProjectPart projectPart(Utf8StringLiteral("/path/to/projectfile")); + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), projectPart); + translationUnit.cxTranslationUnit(); + auto lastChangeTimePoint = translationUnit.lastProjectPartChangeTimePoint(); + std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + + projectPart.clear(); + translationUnit.cxTranslationUnit(); + + ASSERT_THAT(translationUnit.lastProjectPartChangeTimePoint(), Gt(lastChangeTimePoint)); +} + +TEST(TranslationUnit, ReparseIsNeededAfterUnsavedFilesAreChanged) +{ + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), + unsavedFiles, + ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + translationUnit.cxTranslationUnit(); + unsavedFiles.clear(); + translationUnit.cxTranslationUnit(); + + unsavedFiles.clear(); + + ASSERT_TRUE(translationUnit.isNeedingReparse()); +} + +TEST(TranslationUnit, NeedsNoReparseAfterUnsavedFilesAreNotChanged) +{ + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), + unsavedFiles, + ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + translationUnit.cxTranslationUnit(); + unsavedFiles.clear(); + translationUnit.cxTranslationUnit(); + + ASSERT_FALSE(translationUnit.isNeedingReparse()); +} + +TEST(TranslationUnit, TimeStampForUnsavedFilesChange) +{ + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), + unsavedFiles, + ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + translationUnit.cxTranslationUnit(); + unsavedFiles.clear(); + translationUnit.cxTranslationUnit(); + auto lastChangeTimePoint = translationUnit.lastUnsavedFilesChangeTimePoint(); + std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + + unsavedFiles.clear(); + translationUnit.cxTranslationUnit(); + + ASSERT_THAT(translationUnit.lastUnsavedFilesChangeTimePoint(), Gt(lastChangeTimePoint)); } diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index e765da89d7a..826bf52f8c1 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -1,5 +1,4 @@ -QT += core network testlib -QT -= gui +QT += core network testlib widgets TARGET = unittest CONFIG += console c++14 testcase