diff --git a/doc/images/qmldesigner-new-project.png b/doc/images/qmldesigner-new-project.png index 40baa557397..1619fd18b3b 100644 Binary files a/doc/images/qmldesigner-new-project.png and b/doc/images/qmldesigner-new-project.png differ diff --git a/doc/images/qtcreator-keyboard-shortcuts.png b/doc/images/qtcreator-keyboard-shortcuts.png index 514c17eb8a0..064ba04fa8c 100644 Binary files a/doc/images/qtcreator-keyboard-shortcuts.png and b/doc/images/qtcreator-keyboard-shortcuts.png differ diff --git a/doc/images/qtcreator-new-qt-quick-project-wizard.png b/doc/images/qtcreator-new-qt-quick-project-wizard.png index 259e2fd83e2..5a676cd42d6 100644 Binary files a/doc/images/qtcreator-new-qt-quick-project-wizard.png and b/doc/images/qtcreator-new-qt-quick-project-wizard.png differ diff --git a/doc/src/editors/creator-editors.qdoc b/doc/src/editors/creator-editors.qdoc index 124be5bb69d..22e93118fb5 100644 --- a/doc/src/editors/creator-editors.qdoc +++ b/doc/src/editors/creator-editors.qdoc @@ -1466,6 +1466,11 @@ \endlist + The locations of search hits, breakpoints, and bookmarks in your document + are highlighted on the editor scroll bar. To turn highlighting off, select + \uicontrol Tools > \uicontrol Options > \uicontrol {Text Editor} > + \uicontrol {Highlight search results on the scrollbar}. + \section1 Advanced Search To search through projects, files on a file system or currently open files: @@ -2403,9 +2408,10 @@ \endlist - To move directly to a particular line in the document when you open the - document, append a plus sign (+) or a colon (:) to the file name in the - locator. For example, to open main.cpp to line 41, enter: \c {main.cpp:41}. + To move directly to a particular line and column in the document when you + open the document, append them to the file name in the locator, separated by + plus signs (+) or colons (:). For example, to open main.cpp to line 41 and + column 2, enter: \c {main.cpp:41:2}. If the path to a file is very long, it might not fit into the locator window. To view the full path, press \key Alt when the filename is selected @@ -2446,7 +2452,7 @@ \li Locating symbols in the current document (\c {.}) \li Locating a specific line and column in the document displayed in - your editor (\c {l}) + your editor (\c {l :}) \li Opening help topics, including Qt documentation (\c {?}) @@ -2460,6 +2466,8 @@ \li Executing version control system commands (\c {git}). For more information, see \l{Using Version Control Systems} + \li Running external tools (\c x) + \endlist To use a specific locator filter, type the assigned prefix followed by diff --git a/doc/src/howto/creator-cli.qdoc b/doc/src/howto/creator-cli.qdoc index 194d29f0fe1..392621642fd 100644 --- a/doc/src/howto/creator-cli.qdoc +++ b/doc/src/howto/creator-cli.qdoc @@ -31,16 +31,16 @@ \title Using Command Line Options You can start \QC and specify some options from the command line. For - example, you can open a file to any line. + example, you can open a file to any line and column. To specify command line options, enter the following command in the \QC installation or build directory: - \c {qtcreator [option] [filename[:line_number]]} + \c {qtcreator [option] [filename[:line_number[:column_number]]]} \note You can use either a colon (:) or a plus sign (+) as a separator - between the filename and line number. You can also use a space between the - separator and the line number. + between the filename and line number and the line number and the column + number. You can also use a space between the separator and the line number. For example, on Windows: @@ -48,9 +48,9 @@ \li \c {C:\qtcreator\bin>qtcreator -help} - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100} + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100:2} - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100} + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100+2} \endlist diff --git a/doc/src/howto/creator-external-tools.qdoc b/doc/src/howto/creator-external-tools.qdoc index c507749c3f4..67bf9c2276c 100644 --- a/doc/src/howto/creator-external-tools.qdoc +++ b/doc/src/howto/creator-external-tools.qdoc @@ -36,6 +36,9 @@ for use. You can change their default configurations and configure new tools. + To run the tools, select \uicontrol Tools > \uicontrol External, or use the + \c x filter in the locator. + \section1 Using Qt Linguist You can use the Qt Linguist release manager tools, lupdate and lrelease, diff --git a/doc/src/howto/creator-keyboard-shortcuts.qdoc b/doc/src/howto/creator-keyboard-shortcuts.qdoc index aae209af9cd..8c2a0323b96 100644 --- a/doc/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/src/howto/creator-keyboard-shortcuts.qdoc @@ -64,8 +64,19 @@ \li Select a command from the list. - \li In \uicontrol{Key Sequence} enter the shortcut key you want to associate - with the selected command. + \li In the \uicontrol{Key Sequence} field, you have the following + options: + + \list + + \li Enter the shortcut key you want to associate with the + selected command. + + \li Select \uicontrol Record, press the keys to use as the + keyboard shortcut, and select \uicontrol {Stop Recording} + when you are done. + + \endlist \li To revert to the default shortcut, select \uicontrol Reset. diff --git a/doc/src/projects/creator-projects-creating.qdoc b/doc/src/projects/creator-projects-creating.qdoc index 2d9b680330e..bc343c836bc 100644 --- a/doc/src/projects/creator-projects-creating.qdoc +++ b/doc/src/projects/creator-projects-creating.qdoc @@ -114,6 +114,12 @@ Create a Qt Quick application using Qt Quick Controls + \li Qt Canvas 3D Application + + Create a Qt Quick application that imports the Qt Canvas 3D + module and, optionally, includes \l{http://threejs.org} + {three.js}. + \li Qt Console Application Use a single main.cpp file diff --git a/doc/src/qtquick/qtquick-creating.qdoc b/doc/src/qtquick/qtquick-creating.qdoc index 4e20650988f..ea861f5b18d 100644 --- a/doc/src/qtquick/qtquick-creating.qdoc +++ b/doc/src/qtquick/qtquick-creating.qdoc @@ -46,6 +46,10 @@ \li \uicontrol {Qt Quick Controls Application} is like \uicontrol {Qt Quick Application}, but using Qt Quick Controls. + \li \uicontrol {Qt Canvas 3D Application} creates a Qt Quick application + that imports the Qt Canvas 3D module and, optionally, includes + \l{http://threejs.org}{three.js}. + \li \uicontrol {Qt Quick UI} (in the \uicontrol {Other Project} category) creates a Qt Quick UI project with a single QML file that contains the main view. You can review Qt Quick UI projects in a diff --git a/qtcreator.pri b/qtcreator.pri index f68fa435d59..71e7d4477e0 100644 --- a/qtcreator.pri +++ b/qtcreator.pri @@ -1,9 +1,9 @@ !isEmpty(QTCREATOR_PRI_INCLUDED):error("qtcreator.pri already included") QTCREATOR_PRI_INCLUDED = 1 -QTCREATOR_VERSION = 3.4.81 -QTCREATOR_COMPAT_VERSION = 3.4.81 -BINARY_ARTIFACTS_BRANCH = master +QTCREATOR_VERSION = 3.4.82 +QTCREATOR_COMPAT_VERSION = 3.4.82 +BINARY_ARTIFACTS_BRANCH = 3.5 # enable c++11 CONFIG += c++11 diff --git a/qtcreator.qbs b/qtcreator.qbs index b2dd6494765..e9295856bbb 100644 --- a/qtcreator.qbs +++ b/qtcreator.qbs @@ -6,11 +6,11 @@ Project { property bool withAutotests: qbs.buildVariant === "debug" property string ide_version_major: '3' property string ide_version_minor: '4' - property string ide_version_release: '81' + property string ide_version_release: '82' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '3' property string ide_compat_version_minor: '4' - property string ide_compat_version_release: '81' + property string ide_compat_version_release: '82' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release property path ide_source_tree: path property string ide_app_path: qbs.targetOS.contains("osx") ? "" : "bin" diff --git a/scripts/deployqt.py b/scripts/deployqt.py index 23d20747851..00b70d1af46 100755 --- a/scripts/deployqt.py +++ b/scripts/deployqt.py @@ -191,7 +191,9 @@ def copy_qt_libs(install_dir, qt_libs_dir, qt_plugin_dir, qt_import_dir, qt_qml_ target = os.path.join(install_dir, 'bin', 'imports', qtimport) if (os.path.exists(target)): shutil.rmtree(target) - shutil.copytree(os.path.join(qt_import_dir, qtimport), target, ignore=copy_ignore_func, symlinks=True) + import_path = os.path.join(qt_import_dir, qtimport) + if os.path.exists(import_path): + shutil.copytree(import_path, target, ignore=copy_ignore_func, symlinks=True) if (os.path.exists(qt_qml_dir)): print "Copying qt quick 2 imports" @@ -230,20 +232,33 @@ def copyPreservingLinks(source, destination): shutil.copy(source, destination) def copy_libclang(install_dir, llvm_install_dir): - libsources = [] - libtarget = "" + # contains pairs of (source, target directory) + deployinfo = [] if sys.platform.startswith("win"): - libsources = [os.path.join(llvm_install_dir, 'bin', 'libclang.dll')] - libtarget = os.path.join(install_dir, 'bin') + deployinfo.append((os.path.join(llvm_install_dir, 'bin', 'libclang.dll'), + os.path.join(install_dir, 'bin'))) + deployinfo.append((os.path.join(llvm_install_dir, 'bin', 'clang-cl.exe'), + os.path.join(install_dir, 'bin'))) else: libsources = glob(os.path.join(llvm_install_dir, 'lib', 'libclang.so*')) - libtarget = os.path.join(install_dir, 'lib', 'qtcreator') + for libsource in libsources: + deployinfo.append((libsource, os.path.join(install_dir, 'lib', 'qtcreator'))) + clangbinary = os.path.join(llvm_install_dir, 'bin', 'clang') + clangbinary_targetdir = os.path.join(install_dir, 'bin') + deployinfo.append((clangbinary, clangbinary_targetdir)) + # copy link target if clang is actually a symlink + if os.path.islink(clangbinary): + linktarget = os.readlink(clangbinary) + deployinfo.append((os.path.join(os.path.dirname(clangbinary), linktarget), + os.path.join(clangbinary_targetdir, linktarget))) + resourcesource = os.path.join(llvm_install_dir, 'lib', 'clang') resourcetarget = os.path.join(install_dir, 'share', 'qtcreator', 'cplusplus', 'clang') + print "copying libclang..." - for libsource in libsources: - print libsource, '->', libtarget - copyPreservingLinks(libsource, libtarget) + for source, target in deployinfo: + print source, '->', target + copyPreservingLinks(source, target) print resourcesource, '->', resourcetarget if (os.path.exists(resourcetarget)): shutil.rmtree(resourcetarget) diff --git a/scripts/deployqtHelper_mac.sh b/scripts/deployqtHelper_mac.sh index 120a4d030a9..be7929a5ff8 100755 --- a/scripts/deployqtHelper_mac.sh +++ b/scripts/deployqtHelper_mac.sh @@ -61,6 +61,12 @@ if [ $LLVM_INSTALL_DIR ]; then # use recursive copy to make it copy symlinks as symlinks cp -Rf "$LLVM_INSTALL_DIR"/lib/libclang.*dylib "$1/Contents/Frameworks/" || exit 1 cp -Rf "$LLVM_INSTALL_DIR"/lib/clang "$1/Contents/Resources/cplusplus/" || exit 1 + clangsource="$LLVM_INSTALL_DIR"/bin/clang + clanglinktarget="$(readlink "$clangsource")" + cp -Rf "$clangsource" "$1/Contents/Resources/" || exit 1 + if [ $clanglinktarget ]; then + cp -Rf "$(dirname "$clangsource")/$clanglinktarget" "$1/Contents/Resources/$clanglinktarget" || exit 1 + fi fi _CLANG_CODEMODEL_LIB="$1/Contents/PlugIns/libClangCodeModel_debug.dylib" if [ ! -f "$_CLANG_CODEMODEL_LIB" ]; then diff --git a/scripts/unixdeployqt.sh b/scripts/unixdeployqt.sh deleted file mode 100755 index be843f3e303..00000000000 --- a/scripts/unixdeployqt.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -################################################################################ -# Copyright (C) 2015 The Qt Company Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of The Qt Company Ltd, nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -################################################################################ - -if [ $# -lt 1 ]; then - echo "Usage: $(basename $1) [qmake_path]" - exit 1 -fi - -INSTALL_DIR="$1" -QMAKE_BIN="${2:-$(which qmake)}" - -if [ ! -e "$QMAKE_BIN" ]; then - echo "Could not detetermine location of 'qmake'!" - exit 1; -fi - -CHRPATH=$(which chrpath) -if [ ! -e "$CHRPATH" ]; then - echo "Cannot find required binary 'chrpath'." - exit 1 -fi - -QT_INSTALL_LIBS="$($QMAKE_BIN -query QT_INSTALL_LIBS)" -QT_INSTALL_PLUGINS="$($QMAKE_BIN -query QT_INSTALL_PLUGINS)" -QT_INSTALL_IMPORTS="$($QMAKE_BIN -query QT_INSTALL_IMPORTS)" -QT_INSTALL_TRANSLATIONS="$($QMAKE_BIN -query QT_INSTALL_TRANSLATIONS)" - -plugins="accessible designer iconengines imageformats sqldrivers" -imports="Qt QtWebKit" -tr_catalogs="assistant designer qt qt_help" -tr_languages="$(cd $INSTALL_DIR/share/qtcreator/translations; echo qtcreator_* | sed -e 's,[^_]*_\([^.]*\)\.,\1 ,g')" - -function fix_rpaths() -{ - pushd $INSTALL_DIR/lib - find qtcreator/ -maxdepth 1 -name "*.so*" -type f -exec $CHRPATH -r \$ORIGIN {} \; - cd $INSTALL_DIR/lib/qtcreator - find plugins/ -maxdepth 2 -name "*.so" -type f -exec $CHRPATH -r \$ORIGIN/../.. {} \; - - cd $INSTALL_DIR/bin - # all executable files in bin - find -maxdepth 1 -type f -executable -exec $CHRPATH -r \$ORIGIN/../lib/qtcreator {} \; - # all lib of imports and plugins one level underneath bin - find -mindepth 2 -maxdepth 2 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../lib/qtcreator {} \; - find -mindepth 3 -maxdepth 3 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../../lib/qtcreator {} \; - find -mindepth 4 -maxdepth 4 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../../../lib/qtcreator {} \; -} - -function copy_binaries() -{ - cp -a $QT_INSTALL_LIBS/*.so* $INSTALL_DIR/lib/qtcreator - - for plugin in $plugins; do - cp -a $QT_INSTALL_PLUGINS/$plugin $INSTALL_DIR/bin - done - - for import in $imports; do - cp -a $QT_INSTALL_IMPORTS/$import $INSTALL_DIR/bin - done -} - -function copy_translations() -{ - for language in $tr_languages; do - for catalog in $tr_catalogs; do - cp -a $QT_INSTALL_TRANSLATIONS/${catalog}_${language}.qm $INSTALL_DIR/share/qtcreator/translations - done - done -} - -copy_binaries -copy_translations -fix_rpaths - diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 28c2c4c2e0d..16682a47c78 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -387,12 +387,27 @@ class DumperBase: self.isCli = False # Later set, or not set: - # cachedQtVersion self.stringCutOff = 10000 self.displayStringLimit = 100 + self.resetCaches() + + self.childrenPrefix = 'children=[' + self.childrenSuffix = '],' + + self.dumpermodules = [ + "qttypes", + "stdtypes", + "misctypes", + "boosttypes", + "creatortypes", + "personaltypes", + ] + + + def resetCaches(self): # This is a cache mapping from 'type name' to 'display alternatives'. - self.qqFormats = {} + self.qqFormats = { "QVariant (QVariantMap)" : mapForms() } # This is a cache of all known dumpers. self.qqDumpers = {} @@ -407,18 +422,6 @@ class DumperBase: # to not be QObject derived, it contains a 0 value. self.knownStaticMetaObjects = {} - self.childrenPrefix = 'children=[' - self.childrenSuffix = '],' - - self.dumpermodules = [ - "qttypes", - "stdtypes", - "misctypes", - "boosttypes", - "creatortypes", - "personaltypes", - ] - def putNewline(self): pass @@ -1674,10 +1677,7 @@ class DumperBase: pass def setupDumpers(self, _ = {}): - self.qqDumpers = {} - self.qqFormats = {} - self.qqEditable = {} - self.typeCache = {} + self.resetCaches() for mod in self.dumpermodules: m = importlib.import_module(mod) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 95d3c1f26d1..088a618b0ca 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -144,6 +144,7 @@ ScanStackCommand() class PlainDumper: def __init__(self, printer): self.printer = printer + self.typeCache = {} def __call__(self, d, value): printer = self.printer.invoke(value) @@ -223,6 +224,7 @@ class Dumper(DumperBase): # These values will be kept between calls to 'showData'. self.isGdb = True self.childEventAddress = None + self.typeCache = {} self.typesReported = {} self.typesToReport = {} self.qtNamespaceToReport = None diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 268da78f71c..90a256f4842 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -1031,6 +1031,13 @@ def qdump__QMultiMap(d, value): qdump__QMap(d, value) +def qform__QVariantMap(): + return mapForms() + +def qdump__QVariantMap(d, value): + qdump__QMap(d, value) + + def qdump__QMetaObjectPrivate(d, value): d.putEmptyValue() d.putNumChild(1) @@ -1899,6 +1906,16 @@ def qdump__QUrl(d, value): d.putGenericItem("fragment", stringType, fragment, Hex4EncodedLittleEndian) d.putFields(value) + +def qdump__QUuid(d, value): + v = value["data4"] + d.putValue("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" + % (value["data1"], value["data2"], value["data3"], + v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7])) + d.putNumChild(1) + d.putPlainChildren(value) + + def qdumpHelper_QVariant_0(d, blob): # QVariant::Invalid d.putBetterType("%sQVariant (invalid)" % d.qtNamespace()) diff --git a/share/qtcreator/static.pro b/share/qtcreator/static.pro index 4e0c6b84005..37a44a1aa66 100644 --- a/share/qtcreator/static.pro +++ b/share/qtcreator/static.pro @@ -72,12 +72,6 @@ for(data_dir, DATA_DIRS) { dumpinfo.input = qml/qmldump/Info.plist.in dumpinfo.output = $$IDE_DATA_PATH/qml/qmldump/Info.plist QMAKE_SUBSTITUTES += dumpinfo - puppetinfo.input = qml/qmlpuppet/qmlpuppet/Info.plist.in - puppetinfo.output = $$IDE_DATA_PATH/qml/qmlpuppet/qmlpuppet/Info.plist - QMAKE_SUBSTITUES += puppetinfo - puppet2info.input = qml/qmlpuppet/qml2puppet/Info.plist.in - puppet2info.output = $$IDE_DATA_PATH/qml/qmlpuppet/qml2puppet/Info.plist - QMAKE_SUBSTITUES += puppet2info } SRCRESOURCEDIR = $$IDE_SOURCE_TREE/src/share/qtcreator/ diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json index d633eddf6bf..ff84948f890 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json @@ -7,8 +7,8 @@ "trDisplayName": "Qt Quick Application", "trDisplayCategory": "Application", "icon": "qml_wizard.png", - "featuresRequired": [ "QtSupport.Wizards.FeatureQt", "QtSupport.Wizards.FeatureQtQuick.2" ], - "enabled": "${JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 && [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.3" ], + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 }", "options": [ @@ -38,6 +38,7 @@ "type": "ComboBox", "data": { + "index": 2, "items": [ { @@ -46,8 +47,7 @@ "{ 'qtQuickVersion': '2.5', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.5') >= 0}" + }" }, { "trKey": "Qt 5.4", @@ -55,8 +55,7 @@ "{ 'qtQuickVersion': '2.4', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.4') >= 0}" + }" }, { "trKey": "Qt 5.3", @@ -64,8 +63,7 @@ "{ 'qtQuickVersion': '2.3', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}" + }" } ] } diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json index 7738f28e5ac..0a57e21bf3b 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json @@ -7,8 +7,8 @@ "trDisplayName": "Qt Quick Controls Application", "trDisplayCategory": "Application", "icon": "../qtquickapplication/qml_wizard.png", - "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], - "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 && [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0 }", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.3" ], + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0}", "options": [ @@ -40,6 +40,7 @@ "type": "ComboBox", "data": { + "index": 2, "items": [ { @@ -50,8 +51,7 @@ 'qtQuickControlsVersion': '1.4', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.5') >= 0}" + }" }, { "trKey": "Qt 5.4", @@ -61,8 +61,7 @@ 'qtQuickControlsVersion': '1.3', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.1' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.4') >= 0}" + }" }, { "trKey": "Qt 5.3", @@ -72,8 +71,7 @@ 'qtQuickControlsVersion': '1.2', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.1' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}" + }" } ] } diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index 64053b4a5bc..5af16f3e4f7 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -164,8 +164,8 @@ void AutotoolsProject::loadProjectTree() // The thread is still busy parsing a previus configuration. // Wait until the thread has been finished and delete it. // TODO: Discuss whether blocking is acceptable. - disconnect(m_makefileParserThread, SIGNAL(finished()), - this, SLOT(makefileParsingFinished())); + disconnect(m_makefileParserThread, &QThread::finished, + this, &AutotoolsProject::makefileParsingFinished); m_makefileParserThread->wait(); delete m_makefileParserThread; m_makefileParserThread = 0; @@ -402,6 +402,24 @@ QList AutotoolsProject::nodes(FolderNode *parent) const return list; } +static QStringList filterIncludes(const QString &absSrc, const QString &absBuild, + const QStringList &in) +{ + QStringList result; + foreach (const QString i, in) { + QString out = i; + out.replace(QLatin1String("$(top_srcdir)"), absSrc); + out.replace(QLatin1String("$(abs_top_srcdir)"), absSrc); + + out.replace(QLatin1String("$(top_builddir)"), absBuild); + out.replace(QLatin1String("$(abs_top_builddir)"), absBuild); + + result << out; + } + + return result; +} + void AutotoolsProject::updateCppCodeModel() { CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); @@ -427,7 +445,12 @@ void AutotoolsProject::updateCppCodeModel() ppBuilder.setCFlags(cflags); ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setIncludePaths(m_makefileParserThread->includePaths()); + const QString absSrc = projectDirectory().toString(); + const Target *target = activeTarget(); + const QString absBuild = (target && target->activeBuildConfiguration()) + ? target->activeBuildConfiguration()->buildDirectory().toString() : QString(); + + ppBuilder.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths())); ppBuilder.setDefines(m_makefileParserThread->defines()); const QList languages = ppBuilder.createProjectPartsForFiles(m_files); diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp index 3c1e94d1335..48436899ebe 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp @@ -33,6 +33,7 @@ #include "makefileparser.h" #include +#include #include #include @@ -131,12 +132,12 @@ QByteArray MakefileParser::defines() const QStringList MakefileParser::cflags() const { - return m_cflags; + return m_cppflags + m_cflags; } QStringList MakefileParser::cxxflags() const { - return m_cxxflags; + return m_cppflags + m_cxxflags; } void MakefileParser::cancel() @@ -443,9 +444,22 @@ QString MakefileParser::parseIdentifierBeforeAssign(const QString &line) QStringList MakefileParser::parseTermsAfterAssign(const QString &line) { int assignPos = line.indexOf(QLatin1Char('=')) + 1; - if (assignPos >= line.size()) + if (assignPos <= 0 || assignPos >= line.size()) return QStringList(); - return line.mid(assignPos).split(QLatin1Char(' '), QString::SkipEmptyParts); + + const QStringList parts = Utils::QtcProcess::splitArgs(line.mid(assignPos)); + QStringList result; + for (int i = 0; i < parts.count(); ++i) { + const QString cur = parts.at(i); + const QString next = (i == parts.count() - 1) ? QString() : parts.at(i + 1); + if (cur == QLatin1String("-D") || cur == QLatin1String("-U") || cur == QLatin1String("-I")) { + result << cur + next; + ++i; + } else { + result << cur; + } + } + return result; } bool MakefileParser::maybeParseDefine(const QString &term) @@ -493,6 +507,15 @@ bool MakefileParser::maybeParseCXXFlag(const QString &term) return false; } +bool MakefileParser::maybeParseCPPFlag(const QString &term) +{ + if (term.startsWith(QLatin1Char('-'))) { + m_cppflags += term; + return true; + } + return false; +} + void MakefileParser::addAllSources() { QStringList extensions; @@ -523,6 +546,12 @@ void MakefileParser::parseIncludePaths() QString line; do { line = textStream.readLine(); + while (line.endsWith(QLatin1Char('\\'))) { + line.chop(1); + QString next = textStream.readLine(); + line.append(next); + } + const QString varName = parseIdentifierBeforeAssign(line); if (varName.isEmpty()) continue; @@ -537,11 +566,14 @@ void MakefileParser::parseIncludePaths() foreach (const QString &term, parseTermsAfterAssign(line)) maybeParseDefine(term) || maybeParseInclude(term, dirName) || maybeParseCFlag(term); - } else if (varName.endsWith(QLatin1String("CPPFLAGS")) - || varName.endsWith(QLatin1String("CXXFLAGS"))) { + } else if (varName.endsWith(QLatin1String("CXXFLAGS"))) { foreach (const QString &term, parseTermsAfterAssign(line)) maybeParseDefine(term) || maybeParseInclude(term, dirName) || maybeParseCXXFlag(term); + } else if (varName.endsWith(QLatin1String("CPPFLAGS"))) { + foreach (const QString &term, parseTermsAfterAssign(line)) + maybeParseDefine(term) || maybeParseInclude(term, dirName) + || maybeParseCPPFlag(term); } } while (!line.isNull()); diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.h b/src/plugins/autotoolsprojectmanager/makefileparser.h index 463c56e65a8..32569d6a116 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.h +++ b/src/plugins/autotoolsprojectmanager/makefileparser.h @@ -258,6 +258,11 @@ private: */ bool maybeParseCXXFlag(const QString &term); + /** + * If term is compiler flag -, adds it to cppflags and returns true. + */ + bool maybeParseCPPFlag(const QString &term); + private: bool m_success; ///< Return value for MakefileParser::parse(). @@ -271,7 +276,8 @@ private: QStringList m_includePaths; ///< Return value for MakefileParser::includePaths() QByteArray m_defines; ///< Return value for MakefileParser::defines() QStringList m_cflags; ///< Return value for MakefileParser::cflags() - QStringList m_cxxflags; ///< Return value for MakefileParser::cxxflags() + QStringList m_cxxflags; ///< Return value for MakefileParser::cxxflags() + QStringList m_cppflags; ///< The cpp flags, which will be part of both cflags and cxxflags QString m_line; ///< Current line of the makefile QTextStream m_textStream; ///< Textstream that represents the makefile diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.h b/src/plugins/clangcodemodel/clangeditordocumentparser.h index cbdce90f2d8..e86cf50f949 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.h +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.h @@ -28,7 +28,6 @@ ** ****************************************************************************/ - #ifndef CLANGEDITORDOCUMENTPARSER_H #define CLANGEDITORDOCUMENTPARSER_H @@ -36,7 +35,6 @@ #include - namespace CppTools { class WorkingCopy; } namespace ClangCodeModel { @@ -45,9 +43,6 @@ class ClangEditorDocumentParser : public CppTools::BaseEditorDocumentParser { Q_OBJECT -public: - typedef QSharedPointer Ptr; - public: ClangEditorDocumentParser(const QString &filePath); @@ -59,8 +54,6 @@ public: private: SemanticMarker::Ptr m_marker; - QStringList m_options; - Internal::UnsavedFiles m_unsavedFiles; }; } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 36bd7aebfdb..8115b52577d 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -69,6 +69,10 @@ ModelManagerSupportClang::ModelManagerSupportClang() this, &ModelManagerSupportClang::onEditorOpened); CppTools::CppModelManager *modelManager = cppModelManager(); + connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportContentsUpdated, + this, &ModelManagerSupportClang::onAbstractEditorSupportContentsUpdated); + connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportRemoved, + this, &ModelManagerSupportClang::onAbstractEditorSupportRemoved); connect(modelManager, &CppTools::CppModelManager::projectPartsUpdated, this, &ModelManagerSupportClang::onProjectPartsUpdated); connect(modelManager, &CppTools::CppModelManager::projectPartsRemoved, @@ -113,9 +117,8 @@ void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor) Core::IDocument *document = editor->document(); QTC_ASSERT(document, return); TextEditor::TextDocument *textDocument = qobject_cast(document); - QTC_ASSERT(textDocument, return); - if (cppModelManager()->isCppEditor(editor)) { + if (textDocument && cppModelManager()->isCppEditor(editor)) { // Handle externally changed documents connect(textDocument, &Core::IDocument::reloadFinished, this, &ModelManagerSupportClang::onCppDocumentReloadFinished, diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index 58de8258508..95c43eeb200 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -615,6 +615,102 @@ bool hasSnippet(ProposalModel model, const QByteArray &text) return false; } +class MonitorGeneratedUiFile : public QObject +{ + Q_OBJECT + +public: + MonitorGeneratedUiFile(); + bool waitUntilGenerated(int timeout = 10000) const; + +private: + void onUiFileGenerated() { m_isGenerated = true; } + + bool m_isGenerated = false; +}; + +MonitorGeneratedUiFile::MonitorGeneratedUiFile() +{ + connect(CppTools::CppModelManager::instance(), + &CppTools::CppModelManager::abstractEditorSupportContentsUpdated, + this, &MonitorGeneratedUiFile::onUiFileGenerated); +} + +bool MonitorGeneratedUiFile::waitUntilGenerated(int timeout) const +{ + if (m_isGenerated) + return true; + + QTime time; + time.start(); + + forever { + if (m_isGenerated) + return true; + + if (time.elapsed() > timeout) + return false; + + QCoreApplication::processEvents(); + QThread::msleep(20); + } + + return false; +} + +class WriteFileAndWaitForReloadedDocument : public QObject +{ +public: + WriteFileAndWaitForReloadedDocument(const QString &filePath, + const QByteArray &fileContents, + Core::IDocument *document) + : m_filePath(filePath) + , m_fileContents(fileContents) + { + QTC_CHECK(document); + connect(document, &Core::IDocument::reloadFinished, + this, &WriteFileAndWaitForReloadedDocument::onReloadFinished); + } + + void onReloadFinished() + { + m_onReloadFinished = true; + } + + bool wait() const + { + QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false); + + QTime totalTime; + totalTime.start(); + + QTime writeFileAgainTime; + writeFileAgainTime.start(); + + forever { + if (m_onReloadFinished) + return true; + + if (totalTime.elapsed() > 10000) + return false; + + if (writeFileAgainTime.elapsed() > 1000) { + // The timestamp did not change, try again now. + QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false); + writeFileAgainTime.restart(); + } + + QCoreApplication::processEvents(); + QThread::msleep(20); + } + } + +private: + bool m_onReloadFinished = false; + QString m_filePath; + QByteArray m_fileContents; +}; + } // anonymous namespace namespace ClangCodeModel { @@ -866,11 +962,11 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByModifyingIncludedFileExt ProposalModel proposal = completionResults(openSource.editor()); QVERIFY(hasItem(proposal, "globalFromHeader")); - // Simulate external modification - QThread::sleep(1); // Ensures different time stamp and thus that the difference will be noticed - QVERIFY(writeFile(headerDocument.filePath, "int globalFromHeaderReloaded;\n")); - QSignalSpy waitForReloadedDocument(openHeader.editor()->document(), - SIGNAL(reloadFinished(bool))); + // Simulate external modification and wait for reload + WriteFileAndWaitForReloadedDocument waitForReloadedDocument( + headerDocument.filePath, + "int globalFromHeaderReloaded;\n", + openHeader.editor()->document()); QVERIFY(waitForReloadedDocument.wait()); // Retrigger completion and check if its updated @@ -883,6 +979,8 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByCompletingUiObject() CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("qt-widgets-app")); QVERIFY(testDir.isValid()); + MonitorGeneratedUiFile monitorGeneratedUiFile; + // Open project const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro"); CppTools::Tests::ProjectOpenerAndCloser projectManager; @@ -897,11 +995,11 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByCompletingUiObject() QVERIFY(openSource.succeeded()); // ...and check comletions + QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); ProposalModel proposal = completionResults(openSource.editor()); QVERIFY(hasItem(proposal, "menuBar")); QVERIFY(hasItem(proposal, "statusBar")); QVERIFY(hasItem(proposal, "centralWidget")); - QEXPECT_FAIL("", "Signals are not yet done", Abort); QVERIFY(hasItem(proposal, "setupUi")); } @@ -921,6 +1019,7 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() // ... and modify it, so we have an unsaved file. insertTextAtTopOfEditor(openHeader.editor(), "int someGlobal;\n"); // Open project ... + MonitorGeneratedUiFile monitorGeneratedUiFile; const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro"); CppTools::Tests::ProjectOpenerAndCloser projectManager; const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true); @@ -931,6 +1030,7 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() QVERIFY(testDocument.isCreatedAndHasValidCursorPosition()); OpenEditorAtCursorPosition openSource(testDocument); QVERIFY(openSource.succeeded()); + QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); // Check commands that would have been sent QVERIFY(compare(LogOutput(spy.senderLog), @@ -939,6 +1039,8 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() " ProjectPartContainer id: qt-widgets-app.pro\n" "RegisterTranslationUnitForCodeCompletionCommand\n" " Path: myheader.h ProjectPart: \n" + "RegisterTranslationUnitForCodeCompletionCommand\n" + " Path: ui_mainwindow.h ProjectPart: \n" ))); spy.senderLog.clear(); @@ -966,3 +1068,5 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() } // namespace Tests } // namespace Internal } // namespace ClangCodeModel + +#include "clangcodecompletion_test.moc" diff --git a/src/plugins/cpptools/abstracteditorsupport.cpp b/src/plugins/cpptools/abstracteditorsupport.cpp index e9f77f9e318..843ce0f06f0 100644 --- a/src/plugins/cpptools/abstracteditorsupport.cpp +++ b/src/plugins/cpptools/abstracteditorsupport.cpp @@ -53,6 +53,11 @@ void AbstractEditorSupport::updateDocument() m_modelmanager->updateSourceFiles(QSet() << fileName()); } +void AbstractEditorSupport::notifyAboutUpdatedContents() const +{ + m_modelmanager->emitAbstractEditorSupportContentsUpdated(fileName(), contents()); +} + QString AbstractEditorSupport::licenseTemplate(const QString &file, const QString &className) { return Internal::CppFileSettings::licenseTemplate(file, className); diff --git a/src/plugins/cpptools/abstracteditorsupport.h b/src/plugins/cpptools/abstracteditorsupport.h index b1919fcff24..606fcca9a23 100644 --- a/src/plugins/cpptools/abstracteditorsupport.h +++ b/src/plugins/cpptools/abstracteditorsupport.h @@ -51,6 +51,7 @@ public: virtual QString fileName() const = 0; void updateDocument(); + void notifyAboutUpdatedContents() const; unsigned revision() const { return m_revision; } static QString licenseTemplate(const QString &file = QString(), const QString &className = QString()); diff --git a/src/plugins/cpptools/baseeditordocumentparser.h b/src/plugins/cpptools/baseeditordocumentparser.h index 544bc92e473..ddd12fbb0aa 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.h +++ b/src/plugins/cpptools/baseeditordocumentparser.h @@ -41,8 +41,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BaseEditorDocumentParser : public QObject { Q_OBJECT - Q_DISABLE_COPY(BaseEditorDocumentParser) - BaseEditorDocumentParser(); public: BaseEditorDocumentParser(const QString &filePath); diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 8054d866e58..4f30a519e33 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -49,8 +49,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BaseEditorDocumentProcessor : public QObject { Q_OBJECT - Q_DISABLE_COPY(BaseEditorDocumentProcessor) - BaseEditorDocumentProcessor(); public: BaseEditorDocumentProcessor(TextEditor::TextDocument *document); diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.h b/src/plugins/cpptools/builtineditordocumentprocessor.h index 396ba1bf1a7..b0db31e0097 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.h +++ b/src/plugins/cpptools/builtineditordocumentprocessor.h @@ -42,7 +42,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BuiltinEditorDocumentProcessor : public BaseEditorDocumentProcessor { Q_OBJECT - BuiltinEditorDocumentProcessor(); public: BuiltinEditorDocumentProcessor(TextEditor::TextDocument *document, diff --git a/src/plugins/cpptools/cppeditoroutline.h b/src/plugins/cpptools/cppeditoroutline.h index c40ef4327d1..7017b44d9be 100644 --- a/src/plugins/cpptools/cppeditoroutline.h +++ b/src/plugins/cpptools/cppeditoroutline.h @@ -51,7 +51,6 @@ namespace CppTools { class CPPTOOLS_EXPORT CppEditorOutline : public QObject { Q_OBJECT - Q_DISABLE_COPY(CppEditorOutline) public: explicit CppEditorOutline(TextEditor::TextEditorWidget *editorWidget); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 01132b9ebbc..0d1b13621d6 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -958,6 +958,17 @@ void CppModelManager::emitDocumentUpdated(Document::Ptr doc) emit documentUpdated(doc); } +void CppModelManager::emitAbstractEditorSupportContentsUpdated(const QString &filePath, + const QByteArray &contents) +{ + emit abstractEditorSupportContentsUpdated(filePath, contents); +} + +void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath) +{ + emit abstractEditorSupportRemoved(filePath); +} + void CppModelManager::onProjectAdded(ProjectExplorer::Project *) { QMutexLocker locker(&d->m_projectMutex); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 9a141ac8e3f..24b2d2d6ee7 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -111,6 +111,9 @@ public: bool replaceDocument(Document::Ptr newDoc); void emitDocumentUpdated(CPlusPlus::Document::Ptr doc); + void emitAbstractEditorSupportContentsUpdated(const QString &filePath, + const QByteArray &contents); + void emitAbstractEditorSupportRemoved(const QString &filePath); bool isCppEditor(Core::IEditor *editor) const; @@ -173,6 +176,9 @@ signals: void gcFinished(); // Needed for tests. + void abstractEditorSupportContentsUpdated(const QString &filePath, const QByteArray &contents); + void abstractEditorSupportRemoved(const QString &filePath); + public slots: void updateModifiedSourceFiles(); void GC(); diff --git a/src/plugins/cpptools/cppsemanticinfoupdater.h b/src/plugins/cpptools/cppsemanticinfoupdater.h index 40eb7a2749b..9b362543261 100644 --- a/src/plugins/cpptools/cppsemanticinfoupdater.h +++ b/src/plugins/cpptools/cppsemanticinfoupdater.h @@ -43,7 +43,6 @@ class SemanticInfoUpdaterPrivate; class SemanticInfoUpdater : public QObject { Q_OBJECT - Q_DISABLE_COPY(SemanticInfoUpdater) public: explicit SemanticInfoUpdater(); diff --git a/src/plugins/cpptools/semantichighlighter.h b/src/plugins/cpptools/semantichighlighter.h index d00db6e9dad..8485539e2e8 100644 --- a/src/plugins/cpptools/semantichighlighter.h +++ b/src/plugins/cpptools/semantichighlighter.h @@ -49,7 +49,6 @@ namespace CppTools { class CPPTOOLS_EXPORT SemanticHighlighter : public QObject { Q_OBJECT - Q_DISABLE_COPY(SemanticHighlighter) public: enum Kind { diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 3322d95b83f..f682cdc4b57 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -145,16 +145,13 @@ QtcPlugin { name: "QML Debugger" prefix: "qml/" files: [ - "baseqmldebuggerclient.cpp", "baseqmldebuggerclient.h", "interactiveinterpreter.cpp", "interactiveinterpreter.h", - "qmladapter.cpp", "qmladapter.h", "qmlcppengine.cpp", "qmlcppengine.h", "qmlengine.cpp", "qmlengine.h", + "qmlengineutils.cpp", "qmlengineutils.h", "qmlinspectoradapter.cpp", "qmlinspectoradapter.h", "qmlinspectoragent.cpp", "qmlinspectoragent.h", - "qmlv8debuggerclient.cpp", "qmlv8debuggerclient.h", - "qmlv8debuggerclientconstants.h", - "qscriptdebuggerclient.cpp", "qscriptdebuggerclient.h" + "qmlv8debuggerclientconstants.h" ] } diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp b/src/plugins/debugger/qml/baseqmldebuggerclient.cpp deleted file mode 100644 index 5a7f9287a1e..00000000000 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "baseqmldebuggerclient.h" -#include - -#include - -namespace Debugger { -namespace Internal { - -class BaseQmlDebuggerClientPrivate -{ -public: - QList sendBuffer; -}; - -BaseQmlDebuggerClient::BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName) - : QmlDebugClient(clientName, client), - d(new BaseQmlDebuggerClientPrivate()) -{ -} - -BaseQmlDebuggerClient::~BaseQmlDebuggerClient() -{ - delete d; -} - -bool BaseQmlDebuggerClient::acceptsBreakpoint(Breakpoint /*bp*/) -{ - return false; -} - -void BaseQmlDebuggerClient::stateChanged(State state) -{ - emit newState(state); -} - -void BaseQmlDebuggerClient::sendMessage(const QByteArray &msg) -{ - if (state() == Enabled) - QmlDebugClient::sendMessage(msg); - else - d->sendBuffer.append(msg); -} - -void BaseQmlDebuggerClient::flushSendBuffer() -{ - QTC_ASSERT(state() == Enabled, return); - foreach (const QByteArray &msg, d->sendBuffer) - QmlDebugClient::sendMessage(msg); - d->sendBuffer.clear(); -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.h b/src/plugins/debugger/qml/baseqmldebuggerclient.h deleted file mode 100644 index 47435fbf996..00000000000 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef BASEQMLDEBUGGERCLIENT_H -#define BASEQMLDEBUGGERCLIENT_H - -#include -#include - -namespace Debugger { -namespace Internal { - -class WatchData; -class WatchItem; -class BreakHandler; -class BreakpointModelId; -class QmlEngine; -class BaseQmlDebuggerClientPrivate; - -class BaseQmlDebuggerClient : public QmlDebug::QmlDebugClient -{ - Q_OBJECT - -public: - BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName); - virtual ~BaseQmlDebuggerClient(); - - virtual void startSession() = 0; - virtual void endSession() = 0; - virtual void resetSession() = 0; - - virtual void executeStep() = 0; - virtual void executeStepOut() = 0; - virtual void executeNext() = 0; - virtual void executeStepI() = 0; - - virtual void executeRunToLine(const ContextData &data) = 0; - - virtual void continueInferior() = 0; - virtual void interruptInferior() = 0; - - virtual void activateFrame(int index) = 0; - - virtual bool acceptsBreakpoint(Breakpoint bp); - virtual void insertBreakpoint(Breakpoint bp, int adjustedLine, - int adjustedColumn = -1) = 0; - virtual void removeBreakpoint(Breakpoint bp) = 0; - virtual void changeBreakpoint(Breakpoint bp) = 0; - virtual void synchronizeBreakpoints() = 0; - - virtual void assignValueInDebugger(const WatchData *data, - const QString &expression, - const QVariant &valueV) = 0; - - virtual void updateWatchData(const WatchData &data) = 0; - virtual void executeDebuggerCommand(const QString &command) = 0; - - virtual void synchronizeWatchers(const QStringList &watchers) = 0; - - virtual void expandObject(const QByteArray &iname, quint64 objectId) = 0; - - virtual void setEngine(QmlEngine *engine) = 0; - - virtual void getSourceFiles() {} - - void flushSendBuffer(); - -signals: - void newState(QmlDebug::QmlDebugClient::State state); - void stackFrameCompleted(); - -protected: - virtual void stateChanged(State state); - void sendMessage(const QByteArray &msg); - -private: - BaseQmlDebuggerClientPrivate *d; - friend class BaseQmlDebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // BASEQMLDEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index 3dd714070d4..18ed7fe1f55 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -1,10 +1,7 @@ HEADERS += \ $$PWD/qmlengine.h \ - $$PWD/qmladapter.h \ - $$PWD/baseqmldebuggerclient.h \ + $$PWD/qmlengineutils.h \ $$PWD/qmlcppengine.h \ - $$PWD/qscriptdebuggerclient.h \ - $$PWD/qmlv8debuggerclient.h \ $$PWD/interactiveinterpreter.h \ $$PWD/qmlv8debuggerclientconstants.h \ $$PWD/qmlinspectoragent.h \ @@ -12,11 +9,8 @@ HEADERS += \ SOURCES += \ $$PWD/qmlengine.cpp \ - $$PWD/qmladapter.cpp \ - $$PWD/baseqmldebuggerclient.cpp \ + $$PWD/qmlengineutils.cpp \ $$PWD/qmlcppengine.cpp \ - $$PWD/qscriptdebuggerclient.cpp \ - $$PWD/qmlv8debuggerclient.cpp \ $$PWD/interactiveinterpreter.cpp \ $$PWD/qmlinspectoragent.cpp \ $$PWD/qmlinspectoradapter.cpp diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp deleted file mode 100644 index 73f29d0400f..00000000000 --- a/src/plugins/debugger/qml/qmladapter.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "qmladapter.h" - -#include -#include "qmlengine.h" -#include "qmlv8debuggerclient.h" -#include "qscriptdebuggerclient.h" - -#include - -#include - -using namespace QmlDebug; - -namespace Debugger { -namespace Internal { - -/*! - QmlAdapter manages the connection & clients for QML/JS debugging. - */ - -QmlAdapter::QmlAdapter(DebuggerEngine *engine, QObject *parent) - : QObject(parent) - , m_engine(engine) - , m_qmlClient(0) - , m_conn(0) - , m_msgClient(0) -{ - m_connectionTimer.setInterval(4000); - m_connectionTimer.setSingleShot(true); - connect(&m_connectionTimer, &QTimer::timeout, this, &QmlAdapter::checkConnectionState); - - m_conn = new QmlDebugConnection(this); - connect(m_conn, &QmlDebugConnection::stateMessage, - this, &QmlAdapter::showConnectionStateMessage); - connect(m_conn, &QmlDebugConnection::errorMessage, - this, &QmlAdapter::showConnectionErrorMessage); - connect(m_conn, &QmlDebugConnection::error, - this, &QmlAdapter::connectionErrorOccurred); - connect(m_conn, &QmlDebugConnection::opened, - &m_connectionTimer, &QTimer::stop); - connect(m_conn, &QmlDebugConnection::opened, - this, &QmlAdapter::connected); - connect(m_conn, &QmlDebugConnection::closed, - this, &QmlAdapter::disconnected); - - createDebuggerClients(); - m_msgClient = new QDebugMessageClient(m_conn); - connect(m_msgClient, &QDebugMessageClient::newState, this, &QmlAdapter::clientStateChanged); - -} - -QmlAdapter::~QmlAdapter() -{ -} - -void QmlAdapter::beginConnectionTcp(const QString &address, quint16 port) -{ - if (m_engine.isNull() || !m_conn || m_conn->isOpen()) - return; - - m_conn->connectToHost(address, port); - - //A timeout to check the connection state - m_connectionTimer.start(); -} - -void QmlAdapter::closeConnection() -{ - if (m_connectionTimer.isActive()) { - m_connectionTimer.stop(); - } else { - if (m_conn) - m_conn->close(); - } -} - -void QmlAdapter::connectionErrorOccurred(QDebugSupport::Error error) -{ - // this is only an error if we are already connected and something goes wrong. - if (isConnected()) { - emit connectionError(error); - } else { - m_connectionTimer.stop(); - emit connectionStartupFailed(); - } -} - -void QmlAdapter::clientStateChanged(QmlDebugClient::State state) -{ - QString serviceName; - float version = 0; - if (QmlDebugClient *client = qobject_cast(sender())) { - serviceName = client->name(); - version = client->remoteVersion(); - } - - logServiceStateChange(serviceName, version, state); -} - -void QmlAdapter::debugClientStateChanged(QmlDebugClient::State state) -{ - if (state != QmlDebugClient::Enabled) - return; - QmlDebugClient *client = qobject_cast(sender()); - QTC_ASSERT(client, return); - - m_qmlClient = qobject_cast(client); - m_qmlClient->startSession(); -} - -void QmlAdapter::checkConnectionState() -{ - if (!isConnected()) { - closeConnection(); - emit connectionStartupFailed(); - } -} - -bool QmlAdapter::isConnected() const -{ - return m_conn && m_qmlClient && m_conn->isOpen(); -} - -void QmlAdapter::createDebuggerClients() -{ - QScriptDebuggerClient *debugClient1 = new QScriptDebuggerClient(m_conn); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - QmlV8DebuggerClient *debugClient2 = new QmlV8DebuggerClient(m_conn); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - m_debugClients.insert(debugClient1->name(),debugClient1); - m_debugClients.insert(debugClient2->name(),debugClient2); - - debugClient1->setEngine((QmlEngine*)(m_engine.data())); - debugClient2->setEngine((QmlEngine*)(m_engine.data())); -} - -QmlDebugConnection *QmlAdapter::connection() const -{ - return m_conn; -} - -DebuggerEngine *QmlAdapter::debuggerEngine() const -{ - return m_engine.data(); -} - -void QmlAdapter::showConnectionStateMessage(const QString &message) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(_("QML Debugger: ") + message, LogStatus); -} - -void QmlAdapter::showConnectionErrorMessage(const QString &message) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(_("QML Debugger: ") + message, LogError); -} - -BaseQmlDebuggerClient *QmlAdapter::activeDebuggerClient() const -{ - return m_qmlClient; -} - -QHash QmlAdapter::debuggerClients() const -{ - return m_debugClients; -} - -QDebugMessageClient *QmlAdapter::messageClient() const -{ - return m_msgClient; -} - -void QmlAdapter::logServiceStateChange(const QString &service, float version, - QmlDebugClient::State newState) -{ - switch (newState) { - case QmlDebugClient::Unavailable: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'."). - arg(service).arg(QString::number(version))); - break; - } - case QmlDebugClient::Enabled: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'."). - arg(service).arg(QString::number(version))); - break; - } - - case QmlDebugClient::NotConnected: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'."). - arg(service).arg(QString::number(version))); - break; - } - } -} - -void QmlAdapter::logServiceActivity(const QString &service, const QString &logMessage) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(service + QLatin1Char(' ') + logMessage, LogDebug); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h deleted file mode 100644 index e9f39368785..00000000000 --- a/src/plugins/debugger/qml/qmladapter.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QMLADAPTER_H -#define QMLADAPTER_H - -#include - -#include -#include - -namespace QmlDebug { -class BaseEngineDebugClient; -class QmlDebugConnection; -class QDebugMessageClient; -} - -namespace Debugger { -namespace Internal { - -class BaseQmlDebuggerClient; -class DebuggerEngine; -class QmlAdapterPrivate; - -class QmlAdapter : public QObject -{ - Q_OBJECT - -public: - explicit QmlAdapter(DebuggerEngine *engine, QObject *parent = 0); - virtual ~QmlAdapter(); - - void beginConnectionTcp(const QString &address, quint16 port); - void closeConnection(); - - QmlDebug::QmlDebugConnection *connection() const; - DebuggerEngine *debuggerEngine() const; - - BaseQmlDebuggerClient *activeDebuggerClient() const; - QHash debuggerClients() const; - - QmlDebug::QDebugMessageClient *messageClient() const; - -public slots: - void logServiceStateChange(const QString &service, float version, - QmlDebug::QmlDebugClient::State newState); - void logServiceActivity(const QString &service, const QString &logMessage); - -signals: - void connected(); - void disconnected(); - void connectionStartupFailed(); - void connectionError(QDebugSupport::Error error); - void serviceConnectionError(const QString serviceName); - -private slots: - void connectionErrorOccurred(QDebugSupport::Error socketError); - void clientStateChanged(QmlDebug::QmlDebugClient::State state); - void debugClientStateChanged(QmlDebug::QmlDebugClient::State state); - void checkConnectionState(); - void showConnectionStateMessage(const QString &message); - void showConnectionErrorMessage(const QString &message); - -private: - bool isConnected() const; - void createDebuggerClients(); - -private: - QPointer m_engine; - BaseQmlDebuggerClient *m_qmlClient; - QTimer m_connectionTimer; - QmlDebug::QmlDebugConnection *m_conn; - QHash m_debugClients; - QmlDebug::QDebugMessageClient *m_msgClient; -}; - -} // namespace Internal -} // namespace Debugger - -#endif // QMLADAPTER_H diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index c2b2ca32467..46e2796446b 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -29,46 +29,53 @@ ****************************************************************************/ #include "qmlengine.h" -#include "baseqmldebuggerclient.h" -#include "qmlinspectoragent.h" +#include "interactiveinterpreter.h" +#include "qmlinspectoradapter.h" +#include "qmlinspectoragent.h" +#include "qmlv8debuggerclientconstants.h" +#include "qmlengineutils.h" + +#include #include #include #include -#include #include -#include #include #include -#include +#include +#include #include +#include #include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include #include #include #include +#include + +#include +#include +#include + +#include +#include + +#include + #include #include #include +#include +#include +#include +#include #include #include +#include -#define DEBUG_QML 1 +#define DEBUG_QML 0 #if DEBUG_QML # define SDEBUG(s) qDebug() << s #else @@ -76,185 +83,139 @@ #endif # define XSDEBUG(s) qDebug() << s +using namespace Core; +using namespace ProjectExplorer; +using namespace QmlDebug; using namespace QmlJS; -using namespace AST; +using namespace TextEditor; namespace Debugger { namespace Internal { -static QTreeView *inspectorTreeView() +enum Exceptions { - return Internal::inspectorView(); -} - -class ASTWalker : public Visitor -{ -public: - void operator()(Node *ast, quint32 *l, quint32 *c) - { - done = false; - line = l; - column = c; - Node::accept(ast, this); - } - - bool preVisit(Node *ast) - { - return ast->lastSourceLocation().startLine >= *line && !done; - } - - //Case 1: Breakpoint is between sourceStart(exclusive) and - // sourceEnd(inclusive) --> End tree walk. - //Case 2: Breakpoint is on sourceStart --> Check for the start - // of the first executable code. Set the line number and - // column number. End tree walk. - //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable" - // code and check for Case 2. End tree walk. - - //Add more types when suitable. - - bool visit(UiScriptBinding *ast) - { - if (!ast->statement) - return true; - - quint32 sourceStartLine = ast->firstSourceLocation().startLine; - quint32 statementStartLine; - quint32 statementColumn; - - if (ast->statement->kind == Node::Kind_ExpressionStatement) { - statementStartLine = ast->statement->firstSourceLocation(). - startLine; - statementColumn = ast->statement->firstSourceLocation().startColumn; - - } else if (ast->statement->kind == Node::Kind_Block) { - Block *block = static_cast(ast->statement); - if (!block || !block->statements) - return true; - statementStartLine = block->statements->firstSourceLocation(). - startLine; - statementColumn = block->statements->firstSourceLocation(). - startColumn; - - } else { - return true; - } - - - //Case 1 - //Check for possible relocation within the binding statement - - //Rewritten to (function () { { }}) - //The offset 16 is position of inner lbrace without token length. - const int offset = 16; - - //Case 2 - if (statementStartLine == *line) { - if (sourceStartLine == *line) - *column = offset + ast->qualifiedId->identifierToken.length; - done = true; - } - - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - if (sourceStartLine == *line) - *column = offset + ast->qualifiedId->identifierToken.length; - else - *column = statementColumn; - done = true; - } - return true; - } - - bool visit(FunctionDeclaration *ast) { - quint32 sourceStartLine = ast->firstSourceLocation().startLine; - quint32 sourceStartColumn = ast->firstSourceLocation().startColumn; - quint32 statementStartLine = ast->body->firstSourceLocation().startLine; - quint32 statementColumn = ast->body->firstSourceLocation().startColumn; - - //Case 1 - //Check for possible relocation within the function declaration - - //Case 2 - if (statementStartLine == *line) { - if (sourceStartLine == *line) - *column = statementColumn - sourceStartColumn + 1; - done = true; - } - - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - if (sourceStartLine == *line) - *column = statementColumn - sourceStartColumn + 1; - else - *column = statementColumn; - done = true; - } - return true; - } - - bool visit(EmptyStatement *ast) - { - *line = ast->lastSourceLocation().startLine + 1; - return true; - } - - bool visit(VariableStatement *ast) { test(ast); return true; } - bool visit(VariableDeclarationList *ast) { test(ast); return true; } - bool visit(VariableDeclaration *ast) { test(ast); return true; } - bool visit(ExpressionStatement *ast) { test(ast); return true; } - bool visit(IfStatement *ast) { test(ast); return true; } - bool visit(DoWhileStatement *ast) { test(ast); return true; } - bool visit(WhileStatement *ast) { test(ast); return true; } - bool visit(ForStatement *ast) { test(ast); return true; } - bool visit(LocalForStatement *ast) { test(ast); return true; } - bool visit(ForEachStatement *ast) { test(ast); return true; } - bool visit(LocalForEachStatement *ast) { test(ast); return true; } - bool visit(ContinueStatement *ast) { test(ast); return true; } - bool visit(BreakStatement *ast) { test(ast); return true; } - bool visit(ReturnStatement *ast) { test(ast); return true; } - bool visit(WithStatement *ast) { test(ast); return true; } - bool visit(SwitchStatement *ast) { test(ast); return true; } - bool visit(CaseBlock *ast) { test(ast); return true; } - bool visit(CaseClauses *ast) { test(ast); return true; } - bool visit(CaseClause *ast) { test(ast); return true; } - bool visit(DefaultClause *ast) { test(ast); return true; } - bool visit(LabelledStatement *ast) { test(ast); return true; } - bool visit(ThrowStatement *ast) { test(ast); return true; } - bool visit(TryStatement *ast) { test(ast); return true; } - bool visit(Catch *ast) { test(ast); return true; } - bool visit(Finally *ast) { test(ast); return true; } - bool visit(FunctionExpression *ast) { test(ast); return true; } - bool visit(DebuggerStatement *ast) { test(ast); return true; } - - void test(Node *ast) - { - quint32 statementStartLine = ast->firstSourceLocation().startLine; - //Case 1/2 - if (statementStartLine <= *line && - *line <= ast->lastSourceLocation().startLine) - done = true; - - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - *column = ast->firstSourceLocation().startColumn; - done = true; - } - } - - bool done; - quint32 *line; - quint32 *column; + NoExceptions, + UncaughtExceptions, + AllExceptions }; -ConsoleManagerInterface *qmlConsoleManager() +enum StepAction { - return ConsoleManagerInterface::instance(); + Continue, + StepIn, + StepOut, + Next +}; + +struct QmlV8ObjectData +{ + int handle; + QByteArray name; + QByteArray type; + QVariant value; + QVariantList properties; +}; + +class QmlEnginePrivate : QmlDebugClient +{ +public: + QmlEnginePrivate(QmlEngine *engine_, QmlDebugConnection *connection_) + : QmlDebugClient(QLatin1String("V8Debugger"), connection_), + engine(engine_), + inspectorAdapter(engine, connection_), + connection(connection_) + {} + + void sendMessage(const QByteArray &msg); + 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 scripts(int types = 4, const QList ids = QList(), + bool includeSource = false, const QVariant filter = QVariant()); + + void setBreakpoint(const QString type, const QString target, + bool enabled = true,int line = 0, int column = 0, + const QString condition = QString(), int ignoreCount = -1); + 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); + + bool canEvaluateScript(const QString &script); + void updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, const QString &source); + +public: + int sequence = -1; + QmlEngine *engine; + QHash breakpoints; + QHash breakpointsSync; + QList breakpointsTemp; + + QHash evaluatingExpression; + QHash localsAndWatchers; + QList updateLocalsAndWatchers; + QList debuggerCommands; + + //Cache + QList currentFrameScopes; + QHash stackIndexLookup; + + StepAction previousStepAction = Continue; + + QList sendBuffer; + + QHash sourceDocuments; + QHash > sourceEditors; + InteractiveInterpreter interpreter; + ApplicationLauncher applicationLauncher; + QmlInspectorAdapter inspectorAdapter; + QmlOutputParser outputParser; + + QTimer noDebugOutputTimer; + QHash pendingBreakpoints; + QList queryIds; + bool retryOnConnectFail = false; + bool automaticConnect = false; + + QTimer connectionTimer; + QmlDebug::QmlDebugConnection *connection; + QmlDebug::QDebugMessageClient *msgClient = 0; +}; + +static void updateDocument(IDocument *document, const QTextDocument *textDocument) +{ + if (auto baseTextDocument = qobject_cast(document)) + baseTextDocument->document()->setPlainText(textDocument->toPlainText()); } + /////////////////////////////////////////////////////////////////////// // // QmlEngine @@ -262,94 +223,97 @@ ConsoleManagerInterface *qmlConsoleManager() /////////////////////////////////////////////////////////////////////// QmlEngine::QmlEngine(const DebuggerRunParameters &startParameters, DebuggerEngine *masterEngine) - : DebuggerEngine(startParameters) - , m_adapter(this) - , m_inspectorAdapter(&m_adapter, this) - , m_retryOnConnectFail(false) - , m_automaticConnect(false) + : DebuggerEngine(startParameters), + d(new QmlEnginePrivate(this, new QmlDebugConnection(this))) { setObjectName(QLatin1String("QmlEngine")); if (masterEngine) setMasterEngine(masterEngine); - connect(&m_adapter, SIGNAL(connectionError(QDebugSupport::Error)), - SLOT(connectionError(QDebugSupport::Error))); - connect(&m_adapter, SIGNAL(serviceConnectionError(QString)), - SLOT(serviceConnectionError(QString))); - connect(&m_adapter, SIGNAL(connected()), - SLOT(connectionEstablished())); - connect(&m_adapter, SIGNAL(connectionStartupFailed()), - SLOT(connectionStartupFailed())); - - connect(stackHandler(), SIGNAL(stackChanged()), + connect(stackHandler(), &StackHandler::stackChanged, + this, &QmlEngine::updateCurrentContext); + connect(stackHandler(), &StackHandler::currentIndexChanged, + this, &QmlEngine::updateCurrentContext); + connect(inspectorView(), SIGNAL(currentIndexChanged(QModelIndex)), SLOT(updateCurrentContext())); - connect(stackHandler(), SIGNAL(currentIndexChanged()), - SLOT(updateCurrentContext())); - connect(inspectorTreeView(), SIGNAL(currentIndexChanged(QModelIndex)), - SLOT(updateCurrentContext())); - connect(m_inspectorAdapter.agent(), SIGNAL( - expressionResult(quint32,QVariant)), - SLOT(expressionEvaluated(quint32,QVariant))); - connect(m_adapter.messageClient(), - SIGNAL(message(QtMsgType,QString, - QmlDebug::QDebugContextInfo)), - SLOT(appendDebugOutput(QtMsgType,QString, - QmlDebug::QDebugContextInfo))); + connect(d->inspectorAdapter.agent(), &QmlInspectorAgent::expressionResult, + this, &QmlEngine::expressionEvaluated); + connect(&d->applicationLauncher, &ApplicationLauncher::processExited, + this, &QmlEngine::disconnected); + connect(&d->applicationLauncher, &ApplicationLauncher::appendMessage, + this, &QmlEngine::appendMessage); + connect(&d->applicationLauncher, &ApplicationLauncher::processStarted, + &d->noDebugOutputTimer, static_cast(&QTimer::start)); - connect(&m_applicationLauncher, - SIGNAL(processExited(int,QProcess::ExitStatus)), - SLOT(disconnected())); - connect(&m_applicationLauncher, - SIGNAL(appendMessage(QString,Utils::OutputFormat)), - SLOT(appendMessage(QString,Utils::OutputFormat))); - connect(&m_applicationLauncher, - SIGNAL(processStarted()), - &m_noDebugOutputTimer, - SLOT(start())); - - m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher - ::msgWinCannotRetrieveDebuggingOutput()); - connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)), - this, SLOT(beginConnection(quint16))); - connect(&m_outputParser, SIGNAL(noOutputMessage()), - this, SLOT(tryToConnect())); - connect(&m_outputParser, SIGNAL(errorMessage(QString)), - this, SLOT(appStartupFailed(QString))); + d->outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput()); + connect(&d->outputParser, &QmlOutputParser::waitingForConnectionOnPort, + this, &QmlEngine::beginConnection); + connect(&d->outputParser, &QmlOutputParser::noOutputMessage, + this, [this] { tryToConnect(); }); + connect(&d->outputParser, &QmlOutputParser::errorMessage, + this, &QmlEngine::appStartupFailed); // Only wait 8 seconds for the 'Waiting for connection' on application output, // then just try to connect (application output might be redirected / blocked) - m_noDebugOutputTimer.setSingleShot(true); - m_noDebugOutputTimer.setInterval(8000); - connect(&m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect())); + d->noDebugOutputTimer.setSingleShot(true); + d->noDebugOutputTimer.setInterval(8000); + connect(&d->noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect())); - ModelManagerInterface *mmIface = ModelManagerInterface::instance(); - if (mmIface) { - connect(ModelManagerInterface::instance(), SIGNAL(documentUpdated(QmlJS::Document::Ptr)), - this, SLOT(documentUpdated(QmlJS::Document::Ptr))); + if (auto mmIface = ModelManagerInterface::instance()) { + connect(mmIface, &ModelManagerInterface::documentUpdated, + this, &QmlEngine::documentUpdated); } // we won't get any debug output if (startParameters.useTerminal) { - m_noDebugOutputTimer.setInterval(0); - m_retryOnConnectFail = true; - m_automaticConnect = true; + d->noDebugOutputTimer.setInterval(0); + d->retryOnConnectFail = true; + d->automaticConnect = true; } - if (qmlConsoleManager()) - qmlConsoleManager()->setScriptEvaluator(this); + + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->setScriptEvaluator(this); + + + d->connectionTimer.setInterval(4000); + d->connectionTimer.setSingleShot(true); + connect(&d->connectionTimer, &QTimer::timeout, + this, &QmlEngine::checkConnectionState); + + connect(d->connection, &QmlDebugConnection::stateMessage, + this, &QmlEngine::showConnectionStateMessage); + connect(d->connection, &QmlDebugConnection::errorMessage, + this, &QmlEngine::showConnectionErrorMessage); + connect(d->connection, &QmlDebugConnection::error, + this, &QmlEngine::connectionErrorOccurred); + connect(d->connection, &QmlDebugConnection::opened, + &d->connectionTimer, &QTimer::stop); + connect(d->connection, &QmlDebugConnection::opened, + this, &QmlEngine::connectionEstablished); + connect(d->connection, &QmlDebugConnection::closed, + this, &QmlEngine::disconnected); + + d->msgClient = new QDebugMessageClient(d->connection); + connect(d->msgClient, &QDebugMessageClient::newState, + this, &QmlEngine::clientStateChanged); + connect(d->msgClient, &QDebugMessageClient::message, + this, &appendDebugOutput); } QmlEngine::~QmlEngine() { - QSet documentsToClose; + QSet documentsToClose; - QHash >::iterator iter; - for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) { - QWeakPointer textEditPtr = iter.value(); + QHash >::iterator iter; + for (iter = d->sourceEditors.begin(); iter != d->sourceEditors.end(); ++iter) { + QWeakPointer textEditPtr = iter.value(); if (textEditPtr) documentsToClose << textEditPtr.data()->document(); } - Core::EditorManager::closeDocuments(documentsToClose.toList()); + EditorManager::closeDocuments(documentsToClose.toList()); + + delete d; } void QmlEngine::setupInferior() @@ -358,7 +322,7 @@ void QmlEngine::setupInferior() notifyInferiorSetupOk(); - if (m_automaticConnect) + if (d->automaticConnect) beginConnection(); } @@ -373,7 +337,8 @@ void QmlEngine::connectionEstablished() if (!watchHandler()->watcherNames().isEmpty()) synchronizeWatchers(); - connect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers())); + connect(watchModel(), &QAbstractItemModel::layoutChanged, + this, &QmlEngine::synchronizeWatchers); if (state() == EngineRunRequested) notifyEngineRunAndInferiorRunOk(); @@ -382,13 +347,13 @@ void QmlEngine::connectionEstablished() void QmlEngine::tryToConnect(quint16 port) { showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus); - m_retryOnConnectFail = true; + d->retryOnConnectFail = true; if (state() == EngineRunRequested) { if (isSlaveEngine()) { // Probably cpp is being debugged and hence we did not get the output yet. if (!masterEngine()->isDying()) { - m_noDebugOutputTimer.setInterval(4000); - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.setInterval(4000); + d->noDebugOutputTimer.start(); } else appStartupFailed(tr("No application output received in time")); @@ -396,15 +361,15 @@ void QmlEngine::tryToConnect(quint16 port) beginConnection(port); } } else { - m_automaticConnect = true; + d->automaticConnect = true; } } void QmlEngine::beginConnection(quint16 port) { - m_noDebugOutputTimer.stop(); + d->noDebugOutputTimer.stop(); - if (state() != EngineRunRequested && m_retryOnConnectFail) + if (state() != EngineRunRequested && d->retryOnConnectFail) return; QTC_ASSERT(state() == EngineRunRequested, return); @@ -428,19 +393,24 @@ void QmlEngine::beginConnection(quint16 port) if (runParameters().qmlServerPort > 0) port = runParameters().qmlServerPort; - m_adapter.beginConnectionTcp(host, port); -} + if (!d->connection || d->connection->isOpen()) + return; + d->connection->connectToHost(host, port); + + //A timeout to check the connection state + d->connectionTimer.start(); +} void QmlEngine::connectionStartupFailed() { - if (m_retryOnConnectFail) { + if (d->retryOnConnectFail) { // retry after 3 seconds ... QTimer::singleShot(3000, this, SLOT(beginConnection())); return; } - QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow()); + QMessageBox *infoBox = new QMessageBox(ICore::mainWindow()); infoBox->setIcon(QMessageBox::Critical); infoBox->setWindowTitle(tr("Qt Creator")); infoBox->setText(tr("Could not connect to the in-process QML debugger." @@ -450,8 +420,8 @@ void QmlEngine::connectionStartupFailed() infoBox->setDefaultButton(QMessageBox::Retry); infoBox->setModal(true); - connect(infoBox, SIGNAL(finished(int)), - this, SLOT(errorMessageBoxFinished(int))); + connect(infoBox, &QDialog::finished, + this, &QmlEngine::errorMessageBoxFinished); infoBox->show(); } @@ -462,14 +432,14 @@ void QmlEngine::appStartupFailed(const QString &errorMessage) "\n%1").arg(errorMessage); if (isMasterEngine()) { - QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow()); + QMessageBox *infoBox = new QMessageBox(ICore::mainWindow()); infoBox->setIcon(QMessageBox::Critical); infoBox->setWindowTitle(tr("Qt Creator")); infoBox->setText(error); infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help); infoBox->setDefaultButton(QMessageBox::Ok); - connect(infoBox, SIGNAL(finished(int)), - this, SLOT(errorMessageBoxFinished(int))); + connect(infoBox, &QDialog::finished, + this, &QmlEngine::errorMessageBoxFinished); infoBox->show(); } else { showMessage(error, StatusBar); @@ -486,7 +456,7 @@ void QmlEngine::errorMessageBoxFinished(int result) break; } case QMessageBox::Help: { - Core::HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html")); + HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html")); // fall through } default: @@ -500,37 +470,15 @@ void QmlEngine::errorMessageBoxFinished(int result) } } -void QmlEngine::connectionError(QDebugSupport::Error error) +void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) const { - if (error == QDebugSupport::RemoteClosedConnectionError) - showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar); - - if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits - notifyInferiorSpontaneousStop(); - notifyInferiorIll(); - } -} - -void QmlEngine::serviceConnectionError(const QString &serviceName) -{ - showMessage(tr("QML Debugger: Could not connect to service \"%1\".") - .arg(serviceName), StatusBar); -} - -bool QmlEngine::canDisplayTooltip() const -{ - return false; -} - -void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) -{ - m_outputParser.processOutput(output); + d->outputParser.processOutput(output); } void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const { if (channel == AppOutput || channel == AppError) - const_cast(this)->filterApplicationMessage(msg, channel); + filterApplicationMessage(msg, channel); DebuggerEngine::showMessage(msg, channel, timeout); } @@ -539,25 +487,23 @@ void QmlEngine::gotoLocation(const Location &location) const QString fileName = location.fileName(); if (QUrl(fileName).isLocalFile()) { // internal file from source files -> show generated .js - QTC_ASSERT(m_sourceDocuments.contains(fileName), return); + QTC_ASSERT(d->sourceDocuments.contains(fileName), return); QString titlePattern = tr("JS Source for %1").arg(fileName); //Check if there are open documents with the same title - foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments()) { + foreach (IDocument *document, DocumentModel::openedDocuments()) { if (document->displayName() == titlePattern) { - Core::EditorManager::activateEditorForDocument(document); + EditorManager::activateEditorForDocument(document); return; } } - Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + IEditor *editor = EditorManager::openEditorWithContents( QmlJSEditor::Constants::C_QMLJSEDITOR_ID, &titlePattern); if (editor) { editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true); - QPlainTextEdit *plainTextEdit = - qobject_cast(editor->widget()); - if (plainTextEdit) + if (auto plainTextEdit = qobject_cast(editor->widget())) plainTextEdit->setReadOnly(true); - updateDocument(editor->document(), m_sourceDocuments.value(fileName)); + updateDocument(editor->document(), d->sourceDocuments.value(fileName)); } } else { DebuggerEngine::gotoLocation(location); @@ -566,8 +512,15 @@ void QmlEngine::gotoLocation(const Location &location) void QmlEngine::closeConnection() { - disconnect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers())); - m_adapter.closeConnection(); + disconnect(watchModel(), &QAbstractItemModel::layoutChanged, + this, &QmlEngine::synchronizeWatchers); + + if (d->connectionTimer.isActive()) { + d->connectionTimer.stop(); + } else { + if (d->connection) + d->connection->close(); + } } void QmlEngine::runEngine() @@ -576,25 +529,25 @@ void QmlEngine::runEngine() if (!isSlaveEngine()) { if (runParameters().startMode == AttachToRemoteServer) - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.start(); else if (runParameters().startMode == AttachToRemoteProcess) beginConnection(); else startApplicationLauncher(); } else { - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.start(); } } void QmlEngine::startApplicationLauncher() { - if (!m_applicationLauncher.isRunning()) { + if (!d->applicationLauncher.isRunning()) { appendMessage(tr("Starting %1 %2").arg( QDir::toNativeSeparators(runParameters().executable), runParameters().processArgs) + QLatin1Char('\n') , Utils::NormalMessageFormat); - m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui, + d->applicationLauncher.start(ApplicationLauncher::Gui, runParameters().executable, runParameters().processArgs); } @@ -602,10 +555,10 @@ void QmlEngine::startApplicationLauncher() void QmlEngine::stopApplicationLauncher() { - if (m_applicationLauncher.isRunning()) { - disconnect(&m_applicationLauncher, SIGNAL(processExited(int,QProcess::ExitStatus)), - this, SLOT(disconnected())); - m_applicationLauncher.stop(); + if (d->applicationLauncher.isRunning()) { + disconnect(&d->applicationLauncher, &ApplicationLauncher::processExited, + this, &QmlEngine::disconnected); + d->applicationLauncher.stop(); } } @@ -622,12 +575,12 @@ void QmlEngine::notifyEngineRemoteSetupFinished(const RemoteSetupResult &result) // The remote setup can take while especialy with mixed debugging. // Just waiting for 8 seconds is not enough. Increase the timeout // to 60 s - // In case we get an output the m_outputParser will start the connection. - m_noDebugOutputTimer.setInterval(60000); + // In case we get an output the d->outputParser will start the connection. + d->noDebugOutputTimer.setInterval(60000); } else { if (isMasterEngine()) - QMessageBox::critical(Core::ICore::dialogParent(), tr("Failed to start application"), + QMessageBox::critical(ICore::dialogParent(), tr("Failed to start application"), tr("Application startup failed: %1").arg(result.reason)); notifyEngineSetupFailed(); } @@ -646,16 +599,15 @@ void QmlEngine::notifyEngineRemoteServerRunning(const QByteArray &serverChannel, notifyEngineSetupOk(); // The remote setup can take a while especially with mixed debugging. - // Just waiting for 8 seconds is not enough. Increase the timeout - // to 60 s - // In case we get an output the m_outputParser will start the connection. - m_noDebugOutputTimer.setInterval(60000); + // Just waiting for 8 seconds is not enough. Increase the timeout to 60 s. + // In case we get an output the d->outputParser will start the connection. + d->noDebugOutputTimer.setInterval(60000); } void QmlEngine::shutdownInferior() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->endSession(); + // End session. + d->disconnect(); if (isSlaveEngine()) resetLocation(); @@ -667,12 +619,11 @@ void QmlEngine::shutdownInferior() void QmlEngine::shutdownEngine() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->resetSession(); + clearExceptionSelection(); - if (qmlConsoleManager()) - qmlConsoleManager()->setScriptEvaluator(0); - m_noDebugOutputTimer.stop(); + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->setScriptEvaluator(0); + d->noDebugOutputTimer.stop(); // double check (ill engine?): stopApplicationLauncher(); @@ -688,12 +639,12 @@ void QmlEngine::setupEngine() // we need to get the port first notifyEngineRequestRemoteSetup(); } else { - m_applicationLauncher.setEnvironment(runParameters().environment); - m_applicationLauncher.setWorkingDirectory(runParameters().workingDirectory); + d->applicationLauncher.setEnvironment(runParameters().environment); + d->applicationLauncher.setWorkingDirectory(runParameters().workingDirectory); // We can't do this in the constructore because runControl() isn't yet defined - connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)), - runControl(), SLOT(bringApplicationToForeground(qint64)), + connect(&d->applicationLauncher, &ApplicationLauncher::bringToForegroundRequested, + runControl(), &RunControl::bringApplicationToForeground, Qt::UniqueConnection); notifyEngineSetupOk(); @@ -703,8 +654,8 @@ void QmlEngine::setupEngine() void QmlEngine::continueInferior() { QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->continueInferior(); + clearExceptionSelection(); + d->continueDebugging(Continue); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -712,39 +663,39 @@ void QmlEngine::continueInferior() void QmlEngine::interruptInferior() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->interruptInferior(); + showMessage(_(INTERRUPT), LogInput); + d->sendMessage(d->packMessage(INTERRUPT)); notifyInferiorStopOk(); } void QmlEngine::executeStep() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStep(); + clearExceptionSelection(); + d->continueDebugging(StepIn); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeStepI() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStepI(); + clearExceptionSelection(); + d->continueDebugging(StepIn); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeStepOut() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStepOut(); + clearExceptionSelection(); + d->continueDebugging(StepOut); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeNext() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeNext(); + clearExceptionSelection(); + d->continueDebugging(Next); notifyInferiorRunRequested(); notifyInferiorRunOk(); } @@ -765,8 +716,11 @@ void QmlEngine::executeRunToLine(const ContextData &data) bool valid; if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid)) modifiedData.lineNumber = line; - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData); + d->setBreakpoint(QString(_(SCRIPTREGEXP)), modifiedData.fileName, + true, modifiedData.lineNumber); + clearExceptionSelection(); + d->continueDebugging(Continue); + notifyInferiorRunRequested(); notifyInferiorRunOk(); } @@ -788,8 +742,10 @@ void QmlEngine::activateFrame(int index) if (state() != InferiorStopOk && state() != InferiorUnrunnable) return; - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->activateFrame(index); + if (index != stackHandler()->currentIndex()) + d->frame(d->stackIndexLookup.value(index)); + + stackHandler()->setCurrentIndex(index); gotoLocation(stackHandler()->frames().value(index)); } @@ -811,31 +767,39 @@ void QmlEngine::insertBreakpoint(Breakpoint bp) bool valid = false; if (!adjustBreakpointLineAndColumn(params.fileName, &line, &column, &valid)) { - pendingBreakpoints.insertMulti(params.fileName, bp); + d->pendingBreakpoints.insertMulti(params.fileName, bp); return; } if (!valid) return; } - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->insertBreakpoint(bp, line, column); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->insertBreakpoint(bp, line, column); - } + if (params.type == BreakpointAtJavaScriptThrow) { + bp.notifyBreakpointInsertOk(); + d->setExceptionBreak(AllExceptions, params.enabled); + + } else if (params.type == BreakpointByFileAndLine) { + d->setBreakpoint(QString(_(SCRIPTREGEXP)), params.fileName, + params.enabled, line, column, + QLatin1String(params.condition), params.ignoreCount); + + } else if (params.type == BreakpointOnQmlSignalEmit) { + d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); + bp.notifyBreakpointInsertOk(); } + + d->breakpointsSync.insert(d->sequence, bp.id()); } void QmlEngine::removeBreakpoint(Breakpoint bp) { const BreakpointParameters ¶ms = bp.parameters(); if (params.type == BreakpointByFileAndLine && - pendingBreakpoints.contains(params.fileName)) { - auto it = pendingBreakpoints.find(params.fileName); - while (it != pendingBreakpoints.end() && it.key() == params.fileName) { + d->pendingBreakpoints.contains(params.fileName)) { + auto it = d->pendingBreakpoints.find(params.fileName); + while (it != d->pendingBreakpoints.end() && it.key() == params.fileName) { if (it.value() == bp.id()) { - pendingBreakpoints.erase(it); + d->pendingBreakpoints.erase(it); return; } ++it; @@ -846,13 +810,15 @@ void QmlEngine::removeBreakpoint(Breakpoint bp) QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp << this << state); bp.notifyBreakpointRemoveProceeding(); - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->removeBreakpoint(bp); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->removeBreakpoint(bp); - } - } + int breakpoint = d->breakpoints.value(bp.id()); + d->breakpoints.remove(bp.id()); + + if (params.type == BreakpointAtJavaScriptThrow) + d->setExceptionBreak(AllExceptions); + else if (params.type == BreakpointOnQmlSignalEmit) + d->setBreakpoint(QString(_(EVENT)), params.functionName, false); + else + d->clearBreakpoint(breakpoint); if (bp.state() == BreakpointRemoveProceeding) bp.notifyBreakpointRemoveOk(); @@ -864,12 +830,24 @@ void QmlEngine::changeBreakpoint(Breakpoint bp) QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << bp << this << state); bp.notifyBreakpointChangeProceeding(); - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->changeBreakpoint(bp); + const BreakpointParameters ¶ms = bp.parameters(); + + BreakpointResponse br = bp.response(); + if (params.type == BreakpointAtJavaScriptThrow) { + d->setExceptionBreak(AllExceptions, params.enabled); + br.enabled = params.enabled; + bp.setResponse(br); + } else if (params.type == BreakpointOnQmlSignalEmit) { + d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); + br.enabled = params.enabled; + bp.setResponse(br); } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->changeBreakpoint(bp); - } + //V8 supports only minimalistic changes in breakpoint + //Remove the breakpoint and add again + bp.notifyBreakpointChangeOk(); + bp.removeBreakpoint(); + BreakHandler *handler = d->engine->breakHandler(); + handler->appendBreakpoint(params); } if (bp.state() == BreakpointChangeProceeding) @@ -918,14 +896,6 @@ void QmlEngine::attemptBreakpointSynchronization() } DebuggerEngine::attemptBreakpointSynchronization(); - - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->synchronizeBreakpoints(); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->synchronizeBreakpoints(); - } - } } bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const @@ -937,10 +907,10 @@ bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const //TODO: enable setting of breakpoints before start of debug session //For now, the event breakpoint can be set after the activeDebuggerClient is known //This is because the older client does not support BreakpointOnQmlSignalHandler - bool acceptBreakpoint = false; - if (m_adapter.activeDebuggerClient()) - acceptBreakpoint = m_adapter.activeDebuggerClient()->acceptsBreakpoint(bp); - return acceptBreakpoint; + BreakpointType type = bp.type(); + return type == BreakpointOnQmlSignalEmit + || type == BreakpointByFileAndLine + || type == BreakpointAtJavaScriptThrow; } void QmlEngine::loadSymbols(const QString &moduleName) @@ -958,8 +928,7 @@ void QmlEngine::reloadModules() void QmlEngine::reloadSourceFiles() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->getSourceFiles(); + d->scripts(4, QList(), true, QVariant()); } void QmlEngine::requestModuleSymbols(const QString &moduleName) @@ -967,12 +936,6 @@ void QmlEngine::requestModuleSymbols(const QString &moduleName) Q_UNUSED(moduleName) } -////////////////////////////////////////////////////////////////////// -// -// Tooltip specific stuff -// -////////////////////////////////////////////////////////////////////// - bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const { // This is processed by QML inspector, which has dependencies to @@ -981,20 +944,23 @@ bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const return true; } -////////////////////////////////////////////////////////////////////// -// -// Watch specific stuff -// -////////////////////////////////////////////////////////////////////// - void QmlEngine::assignValueInDebugger(WatchItem *item, const QString &expression, const QVariant &valueV) { if (!expression.isEmpty()) { - if (item->isInspect() && m_inspectorAdapter.agent()) - m_inspectorAdapter.agent()->assignValue(item, expression, valueV); - else if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->assignValueInDebugger(item, expression, valueV); + if (item->isInspect() && d->inspectorAdapter.agent()) { + d->inspectorAdapter.agent()->assignValue(item, expression, valueV); + } else { + StackHandler *handler = stackHandler(); + QString expression = QString(_("%1 = %2;")).arg(expression).arg(valueV.toString()); + if (handler->isContentsValid() && handler->currentFrame().isUsable()) { + d->evaluate(expression, false, false, handler->currentIndex()); + d->updateLocalsAndWatchers.append(d->sequence); + } else { + showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( + expression), ConsoleOutput); + } + } } } @@ -1008,14 +974,11 @@ void QmlEngine::updateWatchData(const QByteArray &iname) return; if (item->isInspect()) { - m_inspectorAdapter.agent()->updateWatchData(*item); + d->inspectorAdapter.agent()->updateWatchData(*item); } else { - if (!item->name.isEmpty() && m_adapter.activeDebuggerClient()) { - if (item->isValueNeeded()) - m_adapter.activeDebuggerClient()->updateWatchData(*item); - if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) { - m_adapter.activeDebuggerClient()->expandObject(item->iname, item->id); - } + if (!item->name.isEmpty()) { + if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) + d->expandObject(item->iname, item->id); } synchronizeWatchers(); } @@ -1025,26 +988,30 @@ void QmlEngine::selectWatchData(const QByteArray &iname) { const WatchItem *item = watchHandler()->findItem(iname); if (item && item->isInspect()) - m_inspectorAdapter.agent()->watchDataSelected(item->id); + d->inspectorAdapter.agent()->watchDataSelected(item->id); } void QmlEngine::synchronizeWatchers() { - QStringList watchedExpressions = watchHandler()->watchedExpressions(); + if (state() != InferiorStopOk) + return; + + QStringList watchers = watchHandler()->watchedExpressions(); + // send watchers list - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) - client->synchronizeWatchers(watchedExpressions); + 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); + } } } -ConsoleItem *constructLogItemTree(ConsoleItem *parent, - const QVariant &result, - const QString &key = QString()) +static ConsoleItem *constructLogItemTree(ConsoleItem *parent, + const QVariant &result, + const QString &key = QString()) { - using namespace QmlJS; bool sorted = boolSetting(SortStructMembers); if (!result.isValid()) return 0; @@ -1086,13 +1053,10 @@ ConsoleItem *constructLogItemTree(ConsoleItem *parent, void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result) { - if (queryIds.contains(queryId)) { - queryIds.removeOne(queryId); - using namespace QmlJS; - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { - ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result); - if (item) + if (d->queryIds.contains(queryId)) { + d->queryIds.removeOne(queryId); + if (auto consoleManager = ConsoleManagerInterface::instance()) { + if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result)) consoleManager->printToConsolePane(item); } } @@ -1116,18 +1080,12 @@ bool QmlEngine::hasCapability(unsigned cap) const void QmlEngine::quitDebugger() { - m_noDebugOutputTimer.stop(); - m_automaticConnect = false; - m_retryOnConnectFail = false; + d->noDebugOutputTimer.stop(); + d->automaticConnect = false; + d->retryOnConnectFail = false; DebuggerEngine::quitDebugger(); } -void QmlEngine::inferiorSpontaneousStop() -{ - if (state() == InferiorRunOk) - notifyInferiorSpontaneousStop(); -} - void QmlEngine::disconnected() { showMessage(tr("QML Debugger disconnected."), StatusBar); @@ -1137,9 +1095,9 @@ void QmlEngine::disconnected() void QmlEngine::documentUpdated(Document::Ptr doc) { QString fileName = doc->fileName(); - if (pendingBreakpoints.contains(fileName)) { - QList bps = pendingBreakpoints.values(fileName); - pendingBreakpoints.remove(fileName); + if (d->pendingBreakpoints.contains(fileName)) { + QList bps = d->pendingBreakpoints.values(fileName); + d->pendingBreakpoints.remove(fileName); foreach (const Breakpoint bp, bps) insertBreakpoint(bp); } @@ -1151,13 +1109,12 @@ void QmlEngine::updateCurrentContext() if (state() == InferiorStopOk) { context = stackHandler()->currentFrame().function; } else { - QModelIndex currentIndex = inspectorTreeView()->currentIndex(); + QModelIndex currentIndex = inspectorView()->currentIndex(); const WatchData *currentData = watchHandler()->watchItem(currentIndex); if (!currentData) return; const WatchData *parentData = watchHandler()->watchItem(currentIndex.parent()); - const WatchData *grandParentData = watchHandler()->watchItem( - currentIndex.parent().parent()); + const WatchData *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent()); if (currentData->id != parentData->id) context = currentData->name; else if (parentData->id != grandParentData->id) @@ -1168,44 +1125,24 @@ void QmlEngine::updateCurrentContext() synchronizeWatchers(); - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) + if (auto consoleManager = ConsoleManagerInterface::instance()) consoleManager->setContext(tr("Context:") + QLatin1Char(' ') + context); } -void QmlEngine::appendDebugOutput(QtMsgType type, const QString &message, - const QmlDebug::QDebugContextInfo &info) -{ - using namespace QmlJS; - ConsoleItem::ItemType itemType; - switch (type) { - case QtDebugMsg: - itemType = ConsoleItem::DebugType; - break; - case QtWarningMsg: - itemType = ConsoleItem::WarningType; - break; - case QtCriticalMsg: - case QtFatalMsg: - itemType = ConsoleItem::ErrorType; - break; - default: - //This case is not possible - return; - } - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { - ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message); - item->file = info.file; - item->line = info.line; - consoleManager->printToConsolePane(item); - } -} - void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) { - if ((languages & QmlLanguage) && m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeDebuggerCommand(command); + if (!(languages & QmlLanguage)) + return; + + StackHandler *handler = stackHandler(); + if (handler->isContentsValid() && handler->currentFrame().isUsable()) { + d->evaluate(command, false, false, handler->currentIndex()); + d->debuggerCommands.append(d->sequence); + } else { + //Currently cannot evaluate if not in a javascript break + d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( + command), ConsoleOutput); + } } bool QmlEngine::evaluateScript(const QString &expression) @@ -1214,17 +1151,15 @@ bool QmlEngine::evaluateScript(const QString &expression) // Evaluate expression based on engine state // When engine->state() == InferiorStopOk, the expression is sent to debuggerClient. if (state() != InferiorStopOk) { - QModelIndex currentIndex = inspectorTreeView()->currentIndex(); - QmlInspectorAgent *agent = m_inspectorAdapter.agent(); + QModelIndex currentIndex = inspectorView()->currentIndex(); + QmlInspectorAgent *agent = d->inspectorAdapter.agent(); quint32 queryId = agent->queryExpressionResult(watchHandler()->watchItem(currentIndex)->id, expression); if (queryId) { - queryIds << queryId; + d->queryIds.append(queryId); } else { didEvaluate = false; - using namespace QmlJS; - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { + if (auto consoleManager = ConsoleManagerInterface::instance()) { consoleManager->printToConsolePane(ConsoleItem::ErrorType, _("Error evaluating expression.")); } @@ -1235,42 +1170,15 @@ bool QmlEngine::evaluateScript(const QString &expression) return didEvaluate; } -QString QmlEngine::qmlImportPath() const -{ - return runParameters().environment.value(QLatin1String("QML_IMPORT_PATH")); -} - -void QmlEngine::logMessage(const QString &service, LogDirection direction, const QString &message) -{ - QString msg = service; - msg += direction == LogSend ? QLatin1String(": sending ") : QLatin1String(": receiving "); - msg += message; - showMessage(msg, LogDebug); -} - -void QmlEngine::setSourceFiles(const QStringList &fileNames) -{ - QMap files; - foreach (const QString &file, fileNames) { - QString shortName = file; - QString fullName = toFileInProject(file); - files.insert(shortName, fullName); - } - - sourceFilesHandler()->setSourceFiles(files); - //update open editors - -} - -void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, - const QString &source) +void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, + const QString &source) { QTextDocument *document = 0; - if (m_sourceDocuments.contains(fileName)) { - document = m_sourceDocuments.value(fileName); + if (sourceDocuments.contains(fileName)) { + document = sourceDocuments.value(fileName); } else { document = new QTextDocument(this); - m_sourceDocuments.insert(fileName, document); + sourceDocuments.insert(fileName, document); } // We're getting an unordered set of snippets that can even interleave @@ -1307,7 +1215,7 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int //update open editors QString titlePattern = tr("JS Source for %1").arg(fileName); //Check if there are open editors with the same title - foreach (Core::IDocument *doc, Core::DocumentModel::openedDocuments()) { + foreach (IDocument *doc, DocumentModel::openedDocuments()) { if (doc->displayName() == titlePattern) { updateDocument(doc, document); break; @@ -1315,45 +1223,1446 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int } } -void QmlEngine::updateDocument(Core::IDocument *document, const QTextDocument *textDocument) +bool QmlEnginePrivate::canEvaluateScript(const QString &script) { - TextEditor::TextDocument *baseTextDocument - = qobject_cast(document); - if (!baseTextDocument) - return; - - baseTextDocument->document()->setPlainText(textDocument->toPlainText()); + interpreter.clearText(); + interpreter.appendText(script); + return interpreter.canEvaluate(); } -bool QmlEngine::canEvaluateScript(const QString &script) +void QmlEngine::connectionErrorOccurred(QDebugSupport::Error error) { - m_interpreter.clearText(); - m_interpreter.appendText(script); - return m_interpreter.canEvaluate(); + // this is only an error if we are already connected and something goes wrong. + if (isConnected()) { + if (error == QDebugSupport::RemoteClosedConnectionError) + showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar); + + if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits + notifyInferiorSpontaneousStop(); + notifyInferiorIll(); + } + } else { + d->connectionTimer.stop(); + connectionStartupFailed(); + } } -bool QmlEngine::adjustBreakpointLineAndColumn( - const QString &filePath, quint32 *line, quint32 *column, bool *valid) +void QmlEngine::clientStateChanged(QmlDebugClient::State state) { - bool success = false; - //check if file is in the latest snapshot - //ignoring documentChangedOnDisk - //TODO:: update breakpoints if document is changed. - ModelManagerInterface *mmIface = ModelManagerInterface::instance(); - if (mmIface) { - Document::Ptr doc = mmIface->newestSnapshot(). - document(filePath); - if (doc.isNull()) { - ModelManagerInterface::instance()->updateSourceFiles( - QStringList() << filePath, false); - } else { - ASTWalker walker; - walker(doc->ast(), line, column); - *valid = walker.done; - success = true; + QString serviceName; + float version = 0; + if (QmlDebugClient *client = qobject_cast(sender())) { + serviceName = client->name(); + version = client->remoteVersion(); + } + + logServiceStateChange(serviceName, version, state); +} + +void QmlEngine::checkConnectionState() +{ + if (!isConnected()) { + closeConnection(); + connectionStartupFailed(); + } +} + +bool QmlEngine::isConnected() const +{ + return d->connection->isOpen(); +} + +void QmlEngine::showConnectionStateMessage(const QString &message) +{ + showMessage(_("QML Debugger: ") + message, LogStatus); +} + +void QmlEngine::showConnectionErrorMessage(const QString &message) +{ + showMessage(_("QML Debugger: ") + message, LogError); +} + +void QmlEngine::logServiceStateChange(const QString &service, float version, + QmlDebugClient::State newState) +{ + switch (newState) { + case QmlDebugClient::Unavailable: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'."). + arg(service).arg(QString::number(version))); + break; + } + case QmlDebugClient::Enabled: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'."). + arg(service).arg(QString::number(version))); + break; + } + + case QmlDebugClient::NotConnected: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'."). + arg(service).arg(QString::number(version))); + break; + } + } +} + +void QmlEngine::logServiceActivity(const QString &service, const QString &logMessage) +{ + 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" : , + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "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; + } + + jsonVal.insert(_(ARGUMENTS), args); + } + sendAndLogV8Request(jsonVal); + previousStepAction = action; +} + +void QmlEnginePrivate::evaluate(const QString expr, bool global, + bool disableBreak, int frame, bool addContext) +{ + // { "seq" : , + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : , + // "frame" : , + // "global" : , + // "disable_break" : , + // "additional_context" : [ + // { "name" : , "handle" : }, + // { "name" : , "handle" : }, + // ... + // ] + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(EVALUATE)); + + QJsonObject args { + { _(EXPRESSION), expr } + }; + + if (frame != -1) + args.insert(_(FRAME), frame); + + if (global) + args.insert(_(GLOBAL), global); + + 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); +} + +void QmlEnginePrivate::lookup(QList handles, bool includeSource) +{ + // { "seq" : , + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : , + // "includeSource" : , + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(LOOKUP)); + + QJsonObject args; + + QJsonArray array; + foreach (int handle, handles) + array.push_back(handle); + args.insert(_(HANDLES), array); + + if (includeSource) + args.insert(_(INCLUDESOURCE), includeSource); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : , + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : + // "toFrame" : + // "bottom" : + // } + // } + 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); +} + +void QmlEnginePrivate::frame(int number) +{ + // { "seq" : , + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(FRAME)); + + if (number != -1) { + const QJsonObject args { + { _(NUMBER), number } + }; + + jsonVal.insert(_(ARGUMENTS), args); + } + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::scope(int number, int frameNumber) +{ + // { "seq" : , + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : + // "frameNumber" : + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SCOPE)); + + if (number != -1) { + QJsonObject args { + { _(NUMBER), number } + }; + + if (frameNumber != -1) + args.insert(_(FRAMENUMBER), frameNumber); + + jsonVal.insert(_(ARGUMENTS), args); + } + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::scripts(int types, const QList ids, bool includeSource, + const QVariant filter) +{ + // { "seq" : , + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : + // "ids" : + // "includeSource" : + // "filter" : + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SCRIPTS)); + + QJsonObject args { + { _(TYPES), types } + }; + + if (ids.count()) { + QJsonArray array; + foreach (int id, ids) { + array.push_back(id); + } + args.insert(_(IDS), array); + } + + if (includeSource) + args.insert(_(INCLUDESOURCE), includeSource); + + QJsonValue filterValue; + if (filter.type() == QVariant::String) + filterValue = filter.toString(); + else if (filter.type() == QVariant::Int) + filterValue = filter.toInt(); + else + QTC_CHECK(!filter.isValid()); + + args.insert(_(FILTER), filterValue); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::setBreakpoint(const QString type, const QString target, + bool enabled, int line, int column, + const QString condition, int ignoreCount) +{ + // { "seq" : , + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> + // "target" : + // "line" : + // "column" : + // "enabled" : + // "condition" : + // "ignoreCount" : + // } + // } + if (type == _(EVENT)) { + QByteArray params; + 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)); + + } else { + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SETBREAKPOINT)); + + QJsonObject args { + { _(TYPE), type }, + { _(ENABLED), enabled } + }; + if (type == _(SCRIPTREGEXP)) + args.insert(_(TARGET), Utils::FileName::fromString(target).fileName()); + else + args.insert(_(TARGET), target); + + if (line) + args.insert(_(LINE), line - 1); + + if (column) + args.insert(_(COLUMN), column - 1); + + if (!condition.isEmpty()) + args.insert(_(CONDITION), condition); + + if (ignoreCount != -1) + args.insert(_(IGNORECOUNT), ignoreCount); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); + } +} + +void QmlEnginePrivate::clearBreakpoint(int breakpoint) +{ + // { "seq" : , + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT)); + + QJsonObject args { + { _(BREAKPOINT), breakpoint } + }; + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled) +{ + // { "seq" : , + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : , + // "enabled" : + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK)); + + QJsonObject args; + + if (type == AllExceptions) + args.insert(_(TYPE), _(ALL)); + //Not Supported + // else if (type == UncaughtExceptions) + // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT))); + + if (enabled) + args.insert(_(ENABLED), enabled); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +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 success; + return variant; +} + +QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) +{ + // { "handle" : , + // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> + // } + + // {"handle":,"type":"undefined"} + + // {"handle":,"type":"null"} + + // { "handle":, + // "type" : <"boolean", "number" or "string"> + // "value" : + // } + + // {"handle":7,"type":"boolean","value":true} + + // {"handle":8,"type":"number","value":42} + + // { "handle" : , + // "type" : "object", + // "className" : , + // "constructorFunction" : {"ref":}, + // "protoObject" : {"ref":}, + // "prototypeObject" : {"ref":}, + // "properties" : [ {"name" : , + // "ref" : + // }, + // ... + // ] + // } + + // { "handle" : , + // "type" : "function", + // "className" : "Function", + // "constructorFunction" : {"ref":}, + // "protoObject" : {"ref":}, + // "prototypeObject" : {"ref":}, + // "name" : , + // "inferredName" : + // "source" : , + // "script" : , + // "scriptId" : , + // "position" : , + // "line" : , + // "column" : , + // "properties" : [ {"name" : , + // "ref" : + // }, + // ... + // ] + // } + + QmlV8ObjectData objectData; + const QVariantMap dataMap = data.toMap(); + + objectData.name = dataMap.value(_(NAME)).toByteArray(); + + 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); + objectData.type = data.type; + objectData.value = data.value; + objectData.properties = data.properties; + } + } else { + objectData.handle = dataMap.value(_(HANDLE)).toInt(); + QString type = dataMap.value(_(TYPE)).toString(); + + if (type == _("undefined")) { + objectData.type = QByteArray("undefined"); + objectData.value = QVariant(_("undefined")); + + } else if (type == _("null")) { + objectData.type = QByteArray("null"); + objectData.value= QVariant(_("null")); + + } else if (type == _("boolean")) { + objectData.type = QByteArray("boolean"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("number")) { + objectData.type = QByteArray("number"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("string")) { + objectData.type = QByteArray("string"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("object")) { + objectData.type = QByteArray("object"); + objectData.value = dataMap.value(_("className")); + objectData.properties = dataMap.value(_("properties")).toList(); + + } else if (type == _("function")) { + objectData.type = QByteArray("function"); + objectData.value = dataMap.value(_(NAME)); + objectData.properties = dataMap.value(_("properties")).toList(); + + } else if (type == _("script")) { + objectData.type = QByteArray("script"); + objectData.value = dataMap.value(_(NAME)); + } + } + + return objectData; +} + +void QmlEnginePrivate::clearCache() +{ + currentFrameScopes.clear(); + updateLocalsAndWatchers.clear(); +} + +QByteArray QmlEnginePrivate::packMessage(const QByteArray &type, const QByteArray &message) +{ + SDEBUG(message); + QByteArray request; + QmlDebugStream rs(&request, QIODevice::WriteOnly); + QByteArray cmd = V8DEBUG; + rs << cmd << type << message; + return request; +} + +QJsonObject QmlEnginePrivate::initObject() +{ + 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; + } + } + localsAndWatchers.insertMulti(objectId, iname); + lookup(QList() << objectId); +} + +void QmlEnginePrivate::messageReceived(const QByteArray &data) +{ + QmlDebugStream ds(data); + QByteArray command; + ds >> command; + + if (command == V8DEBUG) { + QByteArray type; + QByteArray response; + ds >> type >> response; + + engine->showMessage(QLatin1String(type), LogOutput); + if (type == CONNECT) { + //debugging session started + + } else if (type == INTERRUPT) { + //debug break requested + + } else if (type == BREAKONSIGNAL) { + //break on signal handler requested + + } else if (type == V8MESSAGE) { + const QString responseString = QLatin1String(response); + SDEBUG(responseString); + engine->showMessage(QLatin1String(V8MESSAGE) + QLatin1Char(' ') + responseString, LogOutput); + + const QVariantMap resp = + QJsonDocument::fromJson(responseString.toUtf8()).toVariant().toMap(); + + const QString type(resp.value(_(TYPE)).toString()); + + if (type == _("response")) { + + bool success = resp.value(_("success")).toBool(); + if (!success) { + SDEBUG("Request was unsuccessful"); + } + + const QString debugCommand(resp.value(_(COMMAND)).toString()); + + 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", + // "request_seq" : , + // "command" : "setbreakpoint", + // "body" : { "type" : <"function" or "script"> + // "breakpoint" : + // } + // "running" : + // "success" : true + // } + + int seq = resp.value(_("request_seq")).toInt(); + const QVariantMap breakpointData = resp.value(_(BODY)).toMap(); + int index = breakpointData.value(_("breakpoint")).toInt(); + + if (breakpointsSync.contains(seq)) { + BreakpointModelId id = breakpointsSync.take(seq); + breakpoints.insert(id, index); + + //Is actual position info present? Then breakpoint was + //accepted + const QVariantList actualLocations = + breakpointData.value(_("actual_locations")).toList(); + if (actualLocations.count()) { + //The breakpoint requested line should be same as + //actual line + BreakHandler *handler = engine->breakHandler(); + Breakpoint bp = handler->breakpointById(id); + if (bp.state() != BreakpointInserted) { + BreakpointResponse br = bp.response(); + br.lineNumber = breakpointData.value(_("line")).toInt() + 1; + bp.setResponse(br); + bp.notifyBreakpointInsertOk(); + } + } + + + } else { + breakpointsTemp.append(index); + } + + + } else if (debugCommand == _(CLEARBREAKPOINT)) { + // DO NOTHING + + } else if (debugCommand == _(SETEXCEPTIONBREAK)) { + // { "seq" : , + // "type" : "response", + // "request_seq" : , + // "command" : "setexceptionbreak", + // "body" : { "type" : , + // "enabled" : + // } + // "running" : true + // "success" : true + // } + + + } 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", + // "request_seq" : , + // "command" : "scripts", + // "body" : [ { "name" : , + // "id" : + // "lineOffset" : + // "columnOffset" : + // "lineCount" : + // "data" : + // "source" : + // "sourceStart" : + // "sourceLength" : + // "scriptType" :