diff options
author | Eike Ziller <eike.ziller@digia.com> | 2013-11-22 09:11:51 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@digia.com> | 2013-11-22 09:11:51 +0100 |
commit | 559e66cb353c510c2d624d60a6d28a37fa66dcb0 (patch) | |
tree | e8cf129eaafdd6167e62427f8aa5ec3a2321b7b5 | |
parent | 759fd36a1eebb6af878b95484e9dc2ef91b90580 (diff) | |
parent | cf3a9b019204e734324f9eedd128f2547e4a2e79 (diff) | |
download | qt-creator-559e66cb353c510c2d624d60a6d28a37fa66dcb0.tar.gz |
Merge remote-tracking branch 'origin/3.0'
105 files changed, 2534 insertions, 463 deletions
diff --git a/doc/images/analyzer-issues.png b/doc/images/analyzer-issues.png Binary files differindex c60f86607c..7bcaeeecf6 100644..100755 --- a/doc/images/analyzer-issues.png +++ b/doc/images/analyzer-issues.png diff --git a/doc/images/creator-qbs-project.png b/doc/images/creator-qbs-project.png Binary files differindex ec4dcadae1..76466d4758 100644 --- a/doc/images/creator-qbs-project.png +++ b/doc/images/creator-qbs-project.png diff --git a/doc/images/qtcreator-analyze-menu.png b/doc/images/qtcreator-analyze-menu.png Binary files differindex 9a95504164..986ab48d91 100644..100755 --- a/doc/images/qtcreator-analyze-menu.png +++ b/doc/images/qtcreator-analyze-menu.png diff --git a/doc/images/qtcreator-qml-performance-monitor.png b/doc/images/qtcreator-qml-performance-monitor.png Binary files differindex 6cd007d8e5..28c2a143d4 100644..100755 --- a/doc/images/qtcreator-qml-performance-monitor.png +++ b/doc/images/qtcreator-qml-performance-monitor.png diff --git a/doc/images/qtcreator-valgrind-callgrind.png b/doc/images/qtcreator-valgrind-callgrind.png Binary files differindex 756c86e70d..e29d252027 100644..100755 --- a/doc/images/qtcreator-valgrind-callgrind.png +++ b/doc/images/qtcreator-valgrind-callgrind.png diff --git a/doc/images/qtcreator-valgrind-memcheck-options.png b/doc/images/qtcreator-valgrind-memcheck-options.png Binary files differindex baca54ecf6..a102b61367 100644..100755 --- a/doc/images/qtcreator-valgrind-memcheck-options.png +++ b/doc/images/qtcreator-valgrind-memcheck-options.png diff --git a/doc/src/analyze/creator-valgrind-overview.qdoc b/doc/src/analyze/creator-valgrind-overview.qdoc index 820b0cb9c7..7f69b554a1 100644 --- a/doc/src/analyze/creator-valgrind-overview.qdoc +++ b/doc/src/analyze/creator-valgrind-overview.qdoc @@ -36,8 +36,8 @@ to use them from \QC. To run the Valgrind tools on a remote host over SSH, select \gui {Analyze - > Valgrind Memory Analyzer (External)} or \gui {Valgrind Function Profiler - (External)}. + > Valgrind Memory Analyzer (Remote)} or \gui {Valgrind Function Profiler + (Remote)}. To stop the currently running analyzer, select \gui {Analyze > Stop}. diff --git a/doc/src/analyze/creator-valgrind.qdoc b/doc/src/analyze/creator-valgrind.qdoc index 00e2edca20..7d00ae9792 100644 --- a/doc/src/analyze/creator-valgrind.qdoc +++ b/doc/src/analyze/creator-valgrind.qdoc @@ -97,6 +97,25 @@ uninitialized memory} check box is selected by default. You can deselect it to make Memcheck run faster. + Memcheck searches for memory leaks when the client application finishes. To + view the amount of leaks that occurred, select \gui {Summary Only} in the + \gui {Check for leaks on finish} field. To also view details of each leak, + select \gui Full. + + \section2 Showing Reachable and Indirectly Lost Blocks + + \e Reachable blocks are blocks that are pointed at by a pointer or chain + of pointers and that might have been freed before the application exited. + \e {Indirectly lost} blocks are considered lost because all the blocks that + point to them are themselves lost. For example, all the children of a lost + root node are indirectly lost. + + By default, Memcheck does not report reachable and indirectly lost blocks. + To have them reported, select the \gui {Show reachable and indirectly lost + blocks}. + + \section2 Suppressing Errors + Memcheck detects numerous problems in the system libraries, such as the C library, which come pre-installed with your OS. As you cannot easily fix them, you want to suppress them. Valgrind reads a list of errors to suppress @@ -252,8 +271,8 @@ \list 1 - \li Select \gui {Analyze > Valgrind Memory Analyzer (External)} or - \gui {Valgrind Function Profiler (External)}. + \li Select \gui {Analyze > Valgrind Memory Analyzer (Remote)} or + \gui {Valgrind Function Profiler (Remote)}. \image qtcreator-valgrind-remote-settings.png "Start Analyzer dialog" diff --git a/doc/src/debugger/creator-debugger.qdoc b/doc/src/debugger/creator-debugger.qdoc index e5b58c126c..ffe1a4d746 100644 --- a/doc/src/debugger/creator-debugger.qdoc +++ b/doc/src/debugger/creator-debugger.qdoc @@ -800,15 +800,13 @@ A snapshot contains the complete state of the debugged program at a time, including the full memory contents. - To create snapshots of a debugged program, select \gui Create in the - context menu in the \gui Snapshot view. + To create snapshots of a debugged program, select \gui {Create Snapshot} in + the context menu in the \gui Snapshots view. - Double-click on entries in the snapshot view to switch between + Double-click on entries in the \gui Snapshots view to switch between snapshots. The debugger views are updated to reflect the state of the program at time of taking the snapshot. - By default, the \gui{Snapshots} view is hidden. - */ @@ -901,7 +899,7 @@ \li There is no GDB to communicate with MSVC compiled applications on Windows. So information can be displayed nicely only in a limited - fashion by using a cdb extension DLL. + fashion by using a CDB extension DLL. \endlist @@ -986,13 +984,13 @@ ]}" \endcode - The value of the \gui{iname} field is the internal name of the object, + The value of the \c iname field is the internal name of the object, constituting a dot-separated list of identifiers, corresponding to the position of the object's representation in the view. If it is not present, is it generated by concatenating the parent object's iname, a dot, and a sequential number. - The value of the\gui{name} field is displayed in the \gui{name} column + The value of the \c name field is displayed in the \gui{Name} column of the view. If it is not specified, a simple number in brackets is used instead. @@ -1003,44 +1001,44 @@ enums, known and unknown structs as well as some convenience functions to handle common situations. - The member functions of the \gui{Dumper} class are the following: + The member functions of the \c Dumper class are the following: \list - \li \gui{__init__(self)} - Initializes the output to an empty string and + \li \c{__init__(self)} - Initializes the output to an empty string and empties the child stack. This should not be used in user code. - \li \gui{put(self, value)} - Low level function to directly append to the + \li \c{put(self, value)} - Low level function to directly append to the output string. That is also the fastest way to append output. - \li \gui{putField(self, name, value)} - Appends a name='value' field. + \li \c{putField(self, name, value)} - Appends a \c{name='value'} field. - \li \gui{childRange(self)} - Returns the range of children specified in + \li \c{childRange(self)} - Returns the range of children specified in the current \c Children scope. - \li \gui{putItemCount(self, count)} - Appends a field + \li \c{putItemCount(self, count)} - Appends a field \c {value='<%d items'} to the output. - \li \gui{putEllipsis(self)} - Appends fields + \li \c{putEllipsis(self)} - Appends fields \c {'{name="<incomplete>",value="",type="",numchild="0"}'}. This is automatically done by \c endChildren if the number of children to print is smaller than the number of actual children. - \li \gui{putName(self, name)} - Appends a \c {name=''} field. + \li \c{putName(self, name)} - Appends a \c {name=''} field. - \li \gui{putType(self, type, priority=0)} - Appends a field \c {type=''} + \li \c{putType(self, type, priority=0)} - Appends a field \c {type=''} unless the \a type coincides with the parent's default child type or \c putType was already called for the current item with a higher value of \c priority. - \li \gui{putBetterType(self, type)} - Overrides the last recorded + \li \c{putBetterType(self, type)} - Overrides the last recorded \c type. - \li \gui{putNumChild(self, numchild)} - Appends a field \c {numchild=''} + \li \c{putNumChild(self, numchild)} - Appends a field \c {numchild=''} unless the \c numchild coincides with the parent's default child numchild value. - \li \gui{putValue(self, value, encoding = None)} - Append a file \c {value=''}, + \li \c{putValue(self, value, encoding = None)} - Append a file \c {value=''}, optionally followed by a field \c {valueencoding=''}. The \c value needs to be convertible to a string entirely consisting of alphanumerical values. The \c encoding parameter can be used to @@ -1072,16 +1070,16 @@ double quotes are added. \endlist - \li \gui{putStringValue(self, value)} - Encodes a QString and calls + \li \c{putStringValue(self, value)} - Encodes a QString and calls \c putValue with the correct \c encoding setting. - \li \gui{putByteArrayValue(self, value)} - Encodes a QByteArray and calls + \li \c{putByteArrayValue(self, value)} - Encodes a QByteArray and calls \c putValue with the correct \c encoding setting. - \li \gui{isExpanded()} - Checks whether the current item + \li \c{isExpanded()} - Checks whether the current item is expanded in the view. - \li \gui{putIntItem(self, name, value)} - Equivalent to: + \li \c{putIntItem(self, name, value)} - Equivalent to: \code with SubItem(self, name): self.putValue(value) @@ -1090,7 +1088,7 @@ self.putNumChild(0) \endcode - \li \gui{putBoolItem(self, name, value)} - Equivalent to: + \li \c{putBoolItem(self, name, value)} - Equivalent to: \code with SubItem(self, name): self.putValue(value) @@ -1098,7 +1096,7 @@ self.putNumChild(0) \endcode - \li \gui{putCallItem(self, name, value, func, *args)} - + \li \c{putCallItem(self, name, value, func, *args)} - Uses GDB to call the function \c func on the value specified by \a {value} and output the resulting item. Use \c{putCallItem} only if there is no other way to access the data. @@ -1107,12 +1105,12 @@ and have the potential to change the state of the debugged program. - \li \gui{putItem(self, value)} - The "master function", handling + \li \c{putItem(self, value)} - The "master function", handling basic types, references, pointers and enums directly, iterates over base classes and class members of compound types and calls \c qdump__* functions whenever appropriate. - \li \gui{putSubItem(self, component, value)} - Equivalent to: + \li \c{putSubItem(self, component, value)} - Equivalent to: \code with SubItem(self, component): self.putItem(value) @@ -1135,7 +1133,7 @@ use \c Children and \c SubItem \e{Context Managers} to create the nested items. - The \c Children constructor \gui{__init__(self, dumper, numChild = 1, + The \c Children constructor \c{__init__(self, dumper, numChild = 1, childType = None, childNumChild = None, maxNumChild = None, addrBase = None, addrStep = None)} uses one mandatory argument and several optional arguments. The mandatory argument refers to the current \c Dumper diff --git a/doc/src/projects/creator-projects-cmake.qdoc b/doc/src/projects/creator-projects-cmake.qdoc index 54638f942a..8443334240 100644 --- a/doc/src/projects/creator-projects-cmake.qdoc +++ b/doc/src/projects/creator-projects-cmake.qdoc @@ -110,6 +110,69 @@ For more information about known issues for the current version, see \l{Known Issues}. + \section1 Deploying CMake Projects to Embedded Linux Devices + + \QC cannot extract files to be installed from a CMake project, and + therefore, only executable targets are automatically added to deployment + files. You must specify all other files in the \c QtCreatorDeployment.txt + file that you create and place in the root directory of the CMake project. + + Use the following syntax in the file: + + \code + <deployment/prefix> + <relative/source/file1>:<relative/destination/dir1> + ... + <relative/source/filen>:<relative/destination/dirn> + \endcode + + Where: + + \list + + \li \c{<deployment/prefix>} is the (absolute) path prefix to where files + are copied on the remote machine. + + \li \c{<relative/source/file>} is the file path relative to the CMake + project root. No directories or wildcards are allowed in this + value. + + \li \c{<relative/destination/dir>} is the destination directory path + relative to \c{deployment/prefix}. + + \endlist + + To automate the creation of \c QtCreatorDeployment.txt file: + + \list 1 + + \li Define the following macros in the top level \c CMakeLists.txt file: + + \code + file(WRITE "${CMAKE_SOURCE_DIR}/QtCreatorDeployment.txt" "<deployment/prefix>\n") + + macro(add_deployment_file SRC DEST) + file(RELATIVE_PATH path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + file(APPEND "${CMAKE_SOURCE_DIR}/QtCreatorDeployment.txt" "${path}/${SRC}:${DEST}\n") + endmacro() + + macro(add_deployment_directory SRC DEST) + file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${SRC}/*") + foreach(filename ${files}) + get_filename_component(path ${filename} PATH) + add_deployment_file("${filename}" "${DEST}/${path}") + endforeach(filename) + endmacro() + \endcode + + \li Use \c {add_deployment_file(<file/name>)} to add files and + \c {add_deployment_directory(<folder/name>)} to add directories + (including subdirectories) to the \c QtCreatorDeployment.txt file. + + \li Re-run \c cmake after you add or remove files using the macros. + + \endlist + \section1 Adding External Libraries to CMake Projects Through external libraries, \QC can support code completion and syntax diff --git a/doc/src/projects/creator-projects-qbs.qdoc b/doc/src/projects/creator-projects-qbs.qdoc index aacf512201..5a489ccdfa 100644 --- a/doc/src/projects/creator-projects-qbs.qdoc +++ b/doc/src/projects/creator-projects-qbs.qdoc @@ -31,7 +31,7 @@ To use Qbs to build a project, you must create a .qbs file for the project. For more information, see the - \l{http://doc-snapshot.qt-project.org/qbs/index.html}{Qbs Manual}. + \l{http://qt-project.org/doc/qbs/index.html}{Qbs Manual}. \section1 Enabling the Qbs Plugin @@ -58,18 +58,20 @@ \list 1 - \li Create a .qbs file for your project. For examples, see the - \c {tests\manual} directory in the qbs repository. - - \li In \QC, select \gui File > \gui {Open File or Project}. - - \li Select the .qbs file for your project. + \li Select \gui File > \gui {New File or Project} > \gui {Plain C + Project (Qbs Build)} or \gui {Plain C++ Project (Qbs Build)} > + \gui Choose, and follow the instructions of the wizard to create a + Qbs project. \image creator-qbs-project.png + \li Edit the .qbs file for you project. Usually, you must add the + \c Depends item for a Qt application. For examples, see the + \c {examples} directory in the qbs repository. + \li Click the \inlineimage qtcreator-run.png - (\gui Run) button to run the application. + (\gui Run) button to build, deploy, and run the application. \endlist diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index aa205c5f54..389f6f91de 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -420,7 +420,7 @@ class LocalItem: ####################################################################### def bbedit(args): - theDumper.bbedit(args.split(",")) + theDumper.bbedit(args) registerCommand("bbedit", bbedit) @@ -706,7 +706,8 @@ class Dumper(DumperBase): if type.find(":") >= 0: type = "'" + type + "'" # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912 - exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg) + #exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg) + exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg) #warn("CALL: %s" % exp) result = None try: @@ -903,6 +904,15 @@ class Dumper(DumperBase): # Try _some_ fallback (good enough for the std::complex dumper) return gdb.parse_and_eval("{%s}%s" % (referencedType, address)) + def setValue(self, address, type, value): + cmd = "set {%s}%s=%s" % (type, address, value) + gdb.execute(cmd) + + def setValues(self, address, type, values): + cmd = "set {%s[%s]}%s={%s}" \ + % (type, len(values), address, ','.join(map(str, values))) + gdb.execute(cmd) + def selectedInferior(self): try: # gdb.Inferior is new in gdb 7.2 @@ -1630,6 +1640,10 @@ class Dumper(DumperBase): return out def threadnames(self, maximalStackDepth): + # FIXME: This needs a proper implementation for MinGW, and only there. + # Linux, Mac and QNX mirror the objectName()to the underlying threads, + # so we get the names already as part of the -thread-info output. + return '[]' out = '[' oldthread = gdb.selected_thread() if oldthread: @@ -1676,22 +1690,25 @@ class Dumper(DumperBase): return namespace - def bbedit(self, type, expr, value): - type = b16decode(type) + def bbedit(self, args): + (typeName, expr, data) = args.split(',') + typeName = b16decode(typeName) ns = self.qtNamespace() - if type.startswith(ns): - type = type[len(ns):] - type = type.replace("::", "__") - pos = type.find('<') + if typeName.startswith(ns): + typeName = typeName[len(ns):] + typeName = typeName.replace("::", "__") + pos = typeName.find('<') if pos != -1: - type = type[0:pos] + typeName = typeName[0:pos] expr = b16decode(expr) - value = b16decode(value) - #warn("EDIT: %s %s %s %s: " % (pos, type, expr, value)) - if self.qqEditable.has_key(type): - self.qqEditable[type](expr, value) + data = b16decode(data) + if typeName in self.qqEditable: + #self.qqEditable[typeName](self, expr, data) + value = gdb.parse_and_eval(expr) + self.qqEditable[typeName](self, value, data) else: - gdb.execute("set (%s)=%s" % (expr, value)) + cmd = "set variable (%s)=%s" % (expr, data) + gdb.execute(cmd) def hasVTable(self, type): fields = type.fields() diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 235c56069b..07b8ead43c 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -618,7 +618,10 @@ class Dumper(DumperBase): self.report('state="inferiorrunfailed"') return self.report('pid="%s"' % self.process.GetProcessID()) - self.report('state="enginerunandinferiorstopok"') + # even if it stops it seems that lldb assumes it is running and later detects that + # it did stop after all, so it is be better to mirror that and wait for the spontaneous + # stop + self.report('state="enginerunandinferiorrunok"') elif len(self.remoteChannel_) > 0: self.process = self.target.ConnectRemote( self.debugger.GetListener(), @@ -626,7 +629,10 @@ class Dumper(DumperBase): if not error.Success(): self.report('state="inferiorrunfailed"') return - self.report('state="enginerunandinferiorstopok"') + # even if it stops it seems that lldb assumes it is running and later detects that + # it did stop after all, so it is be better to mirror that and wait for the spontaneous + # stop + self.report('state="enginerunandinferiorrunok"') else: launchInfo = lldb.SBLaunchInfo(self.processArgs_.split()) launchInfo.SetWorkingDirectory(os.getcwd()) diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index f54757a4e8..cb54d39200 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -165,7 +165,7 @@ def qdump__QModelIndex(d, value): v = val["d"]["data"]["ptr"] d.putStringValue(d.makeValue(ns + 'QString', v)) except: - d.putValue("(invalid)") + d.putValue("") d.putNumChild(rowCount * columnCount) if d.isExpanded(): @@ -1645,17 +1645,10 @@ def qdump__QStandardItem(d, value): d.putPlainChildren(value) -def qedit__QString(expr, value): - cmd = "call (%s).resize(%d)" % (expr, len(value)) - gdb.execute(cmd) - d = gdb.parse_and_eval(expr)["d"]["data"] - cmd = "set {short[%d]}%s={" % (len(value), d.pointerValue(d)) - for i in range(len(value)): - if i != 0: - cmd += ',' - cmd += str(ord(value[i])) - cmd += '}' - gdb.execute(cmd) +def qedit__QString(d, value, data): + d.call(value, "resize", str(len(data))) + (base, size, alloc) = d.stringData(value) + d.setValues(base, "short", [ord(c) for c in data]) def qform__QString(): return "Inline,Separate Window" @@ -1981,15 +1974,19 @@ def qdump__QVariant(d, value): return tdata.type -def qedit__QVector(expr, value): - values = value.split(',') - ob = gdb.parse_and_eval(expr) - cmd = "call (%s).resize(%d)" % (expr, len(values)) - gdb.execute(cmd) - innerType = d.templateArgument(ob.type, 0) - ptr = ob["p"]["array"].cast(d.voidPtrType()) - cmd = "set {%s[%d]}%s={%s}" % (innerType, len(values), d.pointerValue(ptr), value) - gdb.execute(cmd) +def qedit__QVector(d, value, data): + values = data.split(',') + size = len(values) + d.call(value, "resize", str(size)) + innerType = d.templateArgument(value.type, 0) + try: + # Qt 5. Will fail on Qt 4 due to the missing 'offset' member. + offset = value["d"]["offset"] + base = d.pointerValue(value["d"].cast(d.charPtrType()) + offset) + except: + # Qt 4. + base = d.pointerValue(value["p"]["array"]) + d.setValues(base, innerType, values) def qform__QVector(): diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 9f55089f50..8c1c129e2c 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -659,16 +659,16 @@ def qdump__std____debug__unordered_set(d, value): qdump__std__unordered_set(d, value) -def qedit__std__vector(expr, value): - values = value.split(',') +def qedit__std__vector(d, value, data): + import gdb + values = data.split(',') n = len(values) - ob = gdb.parse_and_eval(expr) - innerType = d.templateArgument(ob.type, 0) + innerType = d.templateArgument(value.type, 0) cmd = "set $d = (%s*)calloc(sizeof(%s)*%s,1)" % (innerType, innerType, n) gdb.execute(cmd) - cmd = "set {void*[3]}%s = {$d, $d+%s, $d+%s}" % (ob.address, n, n) + cmd = "set {void*[3]}%s = {$d, $d+%s, $d+%s}" % (value.address, n, n) gdb.execute(cmd) - cmd = "set (%s[%d])*$d={%s}" % (innerType, n, value) + cmd = "set (%s[%d])*$d={%s}" % (innerType, n, data) gdb.execute(cmd) def qdump__std__vector(d, value): @@ -766,12 +766,11 @@ def qdump__std____1__vector(d, value): def qdump__std____debug__vector(d, value): qdump__std__vector(d, value) -def qedit__std__string(expr, value): - cmd = "print (%s).assign(\"%s\")" % (expr, value) - gdb.execute(cmd) +def qedit__std__string(d, value, data): + d.call(value, "assign", '"%s"' % data.replace('"', '\\"')) -def qedit__string(expr, value): - qedit__std__string(expr, value) +def qedit__string(d, expr, value): + qedit__std__string(d, expr, value) def qdump__string(d, value): qdump__std__string(d, value) diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet/qmlpuppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qmlpuppet/qmlpuppetmain.cpp index 4f712d0f86..13ce89e792 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet/qmlpuppetmain.cpp +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet/qmlpuppetmain.cpp @@ -35,6 +35,8 @@ #include <private/qsimulatorconnection_p.h> #endif +#include <iostream> + #include <qt4nodeinstanceclientproxy.h> #ifdef ENABLE_QT_BREAKPAD @@ -60,11 +62,11 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationName("QtProject"); QCoreApplication::setOrganizationDomain("qt-project.org"); QCoreApplication::setApplicationName("QmlPuppet"); - QCoreApplication::setApplicationVersion("1.1.0"); + QCoreApplication::setApplicationVersion("2.0.0"); if (application.arguments().count() == 2 && application.arguments().at(1) == "--version") { - qDebug() << QCoreApplication::applicationVersion(); + std::cout << 2; return 0; } diff --git a/share/qtcreator/templates/html5app/app.pro b/share/qtcreator/templates/html5app/app.pro index 66cddd2618..42c6c9cdba 100644 --- a/share/qtcreator/templates/html5app/app.pro +++ b/share/qtcreator/templates/html5app/app.pro @@ -10,11 +10,6 @@ DEPLOYMENTFOLDERS = folder_01 # TOUCH_OPTIMIZED_NAVIGATION # DEFINES += TOUCH_OPTIMIZED_NAVIGATION -# If your application uses the Qt Mobility libraries, uncomment the following -# lines and add the respective components to the MOBILITY variable. -# CONFIG += mobility -# MOBILITY += - # The .cpp file which was generated for your project. Feel free to hack it. SOURCES += main.cpp diff --git a/share/qtcreator/welcomescreen/widgets/SideBar.qml b/share/qtcreator/welcomescreen/widgets/SideBar.qml index 5183c4b906..20ba652277 100644 --- a/share/qtcreator/welcomescreen/widgets/SideBar.qml +++ b/share/qtcreator/welcomescreen/widgets/SideBar.qml @@ -101,13 +101,13 @@ ColumnLayout { width: parent.width } - Text { + NativeText { text: qsTr("New to Qt?") font.pixelSize: 18 font.bold: false } - Text { + NativeText { text: qsTr("Learn how to develop your own applications and explore Qt Creator.") font.pixelSize: 12 wrapMode: Text.WordWrap diff --git a/src/libs/languageutils/componentversion.cpp b/src/libs/languageutils/componentversion.cpp index b043802a00..99aff4a5b8 100644 --- a/src/libs/languageutils/componentversion.cpp +++ b/src/libs/languageutils/componentversion.cpp @@ -30,6 +30,7 @@ #include "componentversion.h" #include <QString> +#include <QCryptographicHash> #include <limits> @@ -77,7 +78,13 @@ bool ComponentVersion::isValid() const QString ComponentVersion::toString() const { return QString::fromLatin1("%1.%2").arg(QString::number(_major), - QString::number(_minor)); + QString::number(_minor)); +} + +void ComponentVersion::addToHash(QCryptographicHash &hash) const +{ + hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major)); + hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor)); } namespace LanguageUtils { diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h index d1c0bde541..7fe35e8792 100644 --- a/src/libs/languageutils/componentversion.h +++ b/src/libs/languageutils/componentversion.h @@ -32,6 +32,10 @@ #include "languageutils_global.h" +QT_BEGIN_NAMESPACE +class QCryptographicHash; +QT_END_NAMESPACE + namespace LanguageUtils { class LANGUAGEUTILS_EXPORT ComponentVersion @@ -55,6 +59,7 @@ public: bool isValid() const; QString toString() const; + void addToHash(QCryptographicHash &hash) const; }; bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs); diff --git a/src/libs/languageutils/fakemetaobject.cpp b/src/libs/languageutils/fakemetaobject.cpp index a478b2171a..980b69d16f 100644 --- a/src/libs/languageutils/fakemetaobject.cpp +++ b/src/libs/languageutils/fakemetaobject.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include "fakemetaobject.h" +#include <QCryptographicHash> using namespace LanguageUtils; @@ -62,6 +63,24 @@ QStringList FakeMetaEnum::keys() const bool FakeMetaEnum::hasKey(const QString &key) const { return m_keys.contains(key); } +void FakeMetaEnum::addToHash(QCryptographicHash &hash) const +{ + int len = m_name.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar)); + len = m_keys.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QString &key, m_keys) { + len = key.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar)); + } + len = m_values.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (int value, m_values) + hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); +} + FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType) : m_name(name) , m_returnType(returnType) @@ -109,6 +128,33 @@ int FakeMetaMethod::revision() const void FakeMetaMethod::setRevision(int r) { m_revision = r; } +void FakeMetaMethod::addToHash(QCryptographicHash &hash) const +{ + int len = m_name.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar)); + hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess)); + hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy)); + hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision)); + len = m_paramNames.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QString &pName, m_paramNames) { + len = pName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar)); + } + len = m_paramTypes.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QString &pType, m_paramTypes) { + len = pType.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar)); + } + len = m_returnType.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar)); +} + FakeMetaProperty::FakeMetaProperty(const QString &name, const QString &type, bool isList, bool isWritable, bool isPointer, int revision) @@ -138,6 +184,21 @@ bool FakeMetaProperty::isPointer() const int FakeMetaProperty::revision() const { return m_revision; } +void FakeMetaProperty::addToHash(QCryptographicHash &hash) const +{ + int len = m_propertyName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar)); + hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision)); + int flags = (m_isList ? (1 << 0) : 0) + + (m_isPointer ? (1 << 1) : 0) + + (m_isWritable ? (1 << 2) : 0); + hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags)); + len = m_type.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar)); +} + FakeMetaObject::FakeMetaObject() { @@ -226,8 +287,85 @@ QString FakeMetaObject::attachedTypeName() const void FakeMetaObject::setAttachedTypeName(const QString &name) { m_attachedTypeName = name; } +QByteArray FakeMetaObject::calculateFingerprint() const +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + int len = m_className.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar)); + len = m_attachedTypeName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar)); + len = m_defaultPropertyName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar)); + len = m_enumNameToIndex.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + { + QStringList keys(m_enumNameToIndex.keys()); + keys.sort(); + foreach (const QString &key, keys) { + len = key.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar)); + int value = m_enumNameToIndex.value(key); + hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint... + m_enums.at(value).addToHash(hash); + } + } + len = m_exports.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const Export &e, m_exports) + e.addToHash(hash); // normalize order? + len = m_exports.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const FakeMetaMethod &m, m_methods) + m.addToHash(hash); // normalize order? + { + QStringList keys(m_propNameToIdx.keys()); + keys.sort(); + foreach (const QString &key, keys) { + len = key.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar)); + int value = m_propNameToIdx.value(key); + hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint... + m_props.at(value).addToHash(hash); + } + } + len = m_superName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar)); + + QByteArray res = hash.result(); + res.append('F'); + return res; +} + +void FakeMetaObject::updateFingerprint() +{ + m_fingerprint = calculateFingerprint(); +} + +QByteArray FakeMetaObject::fingerprint() const +{ + return m_fingerprint; +} + FakeMetaObject::Export::Export() : metaObjectRevision(0) {} bool FakeMetaObject::Export::isValid() const { return version.isValid() || !package.isEmpty() || !type.isEmpty(); } + +void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const +{ + int len = package.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar)); + len = type.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar)); + version.addToHash(hash); + hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision)); +} diff --git a/src/libs/languageutils/fakemetaobject.h b/src/libs/languageutils/fakemetaobject.h index c0a417a7e1..ed485d9f62 100644 --- a/src/libs/languageutils/fakemetaobject.h +++ b/src/libs/languageutils/fakemetaobject.h @@ -39,6 +39,10 @@ #include <QHash> #include <QSharedPointer> +QT_BEGIN_NAMESPACE +class QCryptographicHash; +QT_END_NAMESPACE + namespace LanguageUtils { class LANGUAGEUTILS_EXPORT FakeMetaEnum { @@ -60,6 +64,7 @@ public: int keyCount() const; QStringList keys() const; bool hasKey(const QString &key) const; + void addToHash(QCryptographicHash &hash) const; }; class LANGUAGEUTILS_EXPORT FakeMetaMethod { @@ -96,6 +101,7 @@ public: int revision() const; void setRevision(int r); + void addToHash(QCryptographicHash &hash) const; private: QString m_name; @@ -125,6 +131,7 @@ public: bool isWritable() const; bool isPointer() const; int revision() const; + void addToHash(QCryptographicHash &hash) const; }; class LANGUAGEUTILS_EXPORT FakeMetaObject { @@ -144,6 +151,7 @@ public: int metaObjectRevision; bool isValid() const; + void addToHash(QCryptographicHash &hash) const; }; private: @@ -157,6 +165,7 @@ private: QList<FakeMetaMethod> m_methods; QString m_defaultPropertyName; QString m_attachedTypeName; + QByteArray m_fingerprint; public: FakeMetaObject(); @@ -195,6 +204,9 @@ public: QString attachedTypeName() const; void setAttachedTypeName(const QString &name); + QByteArray calculateFingerprint() const; + void updateFingerprint(); + QByteArray fingerprint() const; }; } // namespace LanguageUtils diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index d4e83127da..beb80b8fb8 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -39,7 +39,9 @@ HEADERS += \ $$PWD/qmljssimplereader.h \ $$PWD/persistenttrie.h \ $$PWD/qmljsqrcparser.h \ - $$PWD/qmljsconstants.h + $$PWD/qmljsconstants.h \ + $$PWD/qmljsimportdependencies.h \ + $$PWD/qmljsviewercontext.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -70,7 +72,10 @@ SOURCES += \ $$PWD/consoleitem.cpp \ $$PWD/qmljssimplereader.cpp \ $$PWD/persistenttrie.cpp \ - $$PWD/qmljsqrcparser.cpp + $$PWD/qmljsqrcparser.cpp \ + $$PWD/qmljsimportdependencies.cpp \ + $$PWD/qmljsviewercontext.cpp + RESOURCES += \ $$PWD/qmljs.qrc diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs index c79197475f..94968cf313 100644 --- a/src/libs/qmljs/qmljs.qbs +++ b/src/libs/qmljs/qmljs.qbs @@ -35,6 +35,7 @@ QtcLibrary { "qmljsevaluate.cpp", "qmljsevaluate.h", "qmljsicons.cpp", "qmljsicons.h", "qmljsicontextpane.h", + "qmljsimportdependencies.cpp", "qmljsimportdependencies.h", "qmljsindenter.cpp", "qmljsindenter.h", "qmljsinterpreter.cpp", "qmljsinterpreter.h", "qmljslineinfo.cpp", "qmljslineinfo.h", @@ -52,7 +53,8 @@ QtcLibrary { "qmljsstaticanalysismessage.cpp", "qmljsstaticanalysismessage.h", "qmljstypedescriptionreader.cpp", "qmljstypedescriptionreader.h", "qmljsutils.cpp", "qmljsutils.h", - "qmljsvalueowner.cpp", "qmljsvalueowner.h" + "qmljsvalueowner.cpp", "qmljsvalueowner.h", + "qmljsviewercontext.cpp", "qmljsviewercontext.h" ] } diff --git a/src/libs/qmljs/qmljsconstants.h b/src/libs/qmljs/qmljsconstants.h index fcb117050f..6f0f4c7fad 100644 --- a/src/libs/qmljs/qmljsconstants.h +++ b/src/libs/qmljs/qmljsconstants.h @@ -35,14 +35,22 @@ namespace QmlJS { namespace ImportType { enum Enum { Invalid, - ImplicitDirectory, Library, - File, Directory, - QrcFile, + ImplicitDirectory, + File, + UnknownFile, // refers a file/directory that wasn't found (or to an url) QrcDirectory, - ImplicitQrcDirectory, - UnknownFile // refers a file/directory that wasn't found + QrcFile +}; +} + +namespace ImportKind { +enum Enum { + Invalid, + Library, + Path, + QrcPath, }; } diff --git a/src/libs/qmljs/qmljscontext.cpp b/src/libs/qmljs/qmljscontext.cpp index ff601e37a6..c04206c182 100644 --- a/src/libs/qmljs/qmljscontext.cpp +++ b/src/libs/qmljs/qmljscontext.cpp @@ -54,17 +54,20 @@ using namespace QmlJS::AST; QmlJSTextEditorWidget::semanticInfo()::context. */ -ContextPtr Context::create(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports) +ContextPtr Context::create(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext) { - QSharedPointer<Context> result(new Context(snapshot, valueOwner, imports)); + QSharedPointer<Context> result(new Context(snapshot, valueOwner, imports, vContext)); result->_ptr = result; return result; } -Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports) +Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext) : _snapshot(snapshot), _valueOwner(valueOwner), - _imports(imports) + _imports(imports), + _vContext(vContext) { } diff --git a/src/libs/qmljs/qmljscontext.h b/src/libs/qmljs/qmljscontext.h index 63466a2316..ad3d8ed118 100644 --- a/src/libs/qmljs/qmljscontext.h +++ b/src/libs/qmljs/qmljscontext.h @@ -32,6 +32,7 @@ #include "qmljs_global.h" #include "qmljsvalueowner.h" +#include "qmljsviewercontext.h" #include <QSharedPointer> @@ -50,7 +51,8 @@ public: typedef QHash<const Document *, QSharedPointer<const Imports> > ImportsPerDocument; // Context takes ownership of valueOwner - static ContextPtr create(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports); + static ContextPtr create(const Snapshot &snapshot, ValueOwner *valueOwner, + const ImportsPerDocument &imports, const ViewerContext &vContext); ~Context(); ContextPtr ptr() const; @@ -69,11 +71,13 @@ public: private: // Context takes ownership of valueOwner - Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports); + Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports, + const ViewerContext &vContext); Snapshot _snapshot; QSharedPointer<ValueOwner> _valueOwner; ImportsPerDocument _imports; + ViewerContext _vContext; QWeakPointer<const Context> _ptr; }; diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index fe169f795c..68fb5eadd5 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -30,11 +30,17 @@ #include "qmljsdocument.h" #include "qmljsbind.h" #include "qmljsconstants.h" +#include "qmljsimportdependencies.h" #include <qmljs/parser/qmljslexer_p.h> #include <qmljs/parser/qmljsparser_p.h> +#include <utils/qtcassert.h> + +#include <QCryptographicHash> #include <QDir> +#include <algorithm> + using namespace QmlJS; using namespace QmlJS::AST; @@ -204,6 +210,16 @@ void Document::setLanguage(Language::Enum l) _language = l; } +QString Document::importId() const +{ + return _fileName; +} + +QByteArray Document::fingerprint() const +{ + return _fingerprint; +} + AST::UiProgram *Document::qmlProgram() const { return cast<UiProgram *>(_ast); @@ -245,6 +261,9 @@ QString Document::source() const void Document::setSource(const QString &source) { _source = source; + QCryptographicHash sha(QCryptographicHash::Sha1); + sha.addData(source.toUtf8()); + _fingerprint = sha.result(); } int Document::editorRevision() const @@ -373,21 +392,88 @@ LibraryInfo::LibraryInfo(Status status) : _status(status) , _dumpStatus(NoTypeInfo) { + updateFingerprint(); } -LibraryInfo::LibraryInfo(const QmlDirParser &parser) +LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint) : _status(Found) , _components(parser.components().values()) , _plugins(parser.plugins()) , _typeinfos(parser.typeInfos()) + , _fingerprint(fingerprint) , _dumpStatus(NoTypeInfo) { + if (_fingerprint.isEmpty()) + updateFingerprint(); } LibraryInfo::~LibraryInfo() { } +QByteArray LibraryInfo::calculateFingerprint() const +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(reinterpret_cast<const char *>(&_status), sizeof(_status)); + int len = _components.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QmlDirParser::Component &component, _components) { + len = component.fileName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(component.fileName.constData()), len * sizeof(QChar)); + hash.addData(reinterpret_cast<const char *>(&component.majorVersion), sizeof(component.majorVersion)); + hash.addData(reinterpret_cast<const char *>(&component.minorVersion), sizeof(component.minorVersion)); + len = component.typeName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(component.typeName.constData()), component.typeName.size() * sizeof(QChar)); + int flags = (component.singleton ? (1 << 0) : 0) + (component.internal ? (1 << 1) : 0); + hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags)); + } + len = _plugins.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QmlDirParser::Plugin &plugin, _plugins) { + len = plugin.path.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(plugin.path.constData()), len * sizeof(QChar)); + len = plugin.name.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(plugin.name.constData()), len * sizeof(QChar)); + } + len = _typeinfos.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const QmlDirParser::TypeInfo &typeinfo, _typeinfos) { + len = typeinfo.fileName.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(typeinfo.fileName.constData()), len * sizeof(QChar)); + } + len = _metaObjects.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + QList<QByteArray> metaFingerprints; + foreach (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject, _metaObjects) + metaFingerprints.append(metaObject->fingerprint()); + std::sort(metaFingerprints.begin(), metaFingerprints.end()); + foreach (const QByteArray &fp, metaFingerprints) + hash.addData(fp); + hash.addData(reinterpret_cast<const char *>(&_dumpStatus), sizeof(_dumpStatus)); + len = _dumpError.size(); // localization dependent (avoid?) + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(_dumpError.constData()), len * sizeof(QChar)); + + len = _moduleApis.size(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + foreach (const ModuleApiInfo &moduleInfo, _moduleApis) + moduleInfo.addToHash(hash); // make it order independent? + + QByteArray res(hash.result()); + res.append('L'); + return res; +} + +void LibraryInfo::updateFingerprint() +{ + _fingerprint = calculateFingerprint(); +} + Snapshot::Snapshot() { } @@ -396,21 +482,67 @@ Snapshot::~Snapshot() { } +Snapshot::Snapshot(const Snapshot &o) + : _documents(o._documents), + _documentsByPath(o._documentsByPath), + _libraries(o._libraries), + _dependencies(o._dependencies) +{ +} + void Snapshot::insert(const Document::Ptr &document, bool allowInvalid) { if (document && (allowInvalid || document->qmlProgram() || document->jsProgram())) { const QString fileName = document->fileName(); const QString path = document->path(); - remove(fileName); _documentsByPath[path].append(document); _documents.insert(fileName, document); + CoreImport cImport; + cImport.importId = document->importId(); + cImport.language = document->language(); + cImport.possibleExports << Export(ImportKey(ImportType::File, fileName), + QString(), true); + cImport.fingerprint = document->fingerprint(); + _dependencies.addCoreImport(cImport); } } void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info) { + QTC_CHECK(info.fingerprint() == info.calculateFingerprint()); _libraries.insert(QDir::cleanPath(path), info); + if (!info.wasFound()) return; + CoreImport cImport; + cImport.importId = path; + cImport.language = Language::Unknown; + QSet<ImportKey> packages; + foreach (const ModuleApiInfo &moduleInfo, info.moduleApis()) { + ImportKey iKey(ImportType::Library, moduleInfo.uri, moduleInfo.version.majorVersion(), + moduleInfo.version.minorVersion()); + packages.insert(iKey); + } + foreach (const LanguageUtils::FakeMetaObject::ConstPtr &metaO, info.metaObjects()) { + foreach (const LanguageUtils::FakeMetaObject::Export &e, metaO->exports()) { + ImportKey iKey(ImportType::Library, e.package, e.version.majorVersion(), + e.version.minorVersion()); + packages.insert(iKey); + } + } + + QStringList splitPath = path.split(QLatin1Char('/')); + foreach (const ImportKey &importKey, packages) { + QString requiredPath = QStringList(splitPath.mid(0, splitPath.size() - importKey.splitPath.size())) + .join(QLatin1String("/")); + cImport.possibleExports << Export(importKey, requiredPath, true); + } + foreach (const QmlDirParser::Component &component, info.components()) { + foreach (const Export &e, cImport.possibleExports) + // renaming of type name not really represented here... fix? + _dependencies.addExport(component.fileName, e.exportName, e.pathRequired); + } + cImport.fingerprint = info.fingerprint(); + _dependencies.addCoreImport(cImport); } void Snapshot::remove(const QString &fileName) @@ -427,6 +559,16 @@ void Snapshot::remove(const QString &fileName) } } +const QmlJS::ImportDependencies *Snapshot::importDependencies() const +{ + return &_dependencies; +} + +QmlJS::ImportDependencies *Snapshot::importDependencies() +{ + return &_dependencies; +} + Document::MutablePtr Snapshot::documentFromSource( const QString &code, const QString &fileName, Language::Enum language) const @@ -454,3 +596,15 @@ LibraryInfo Snapshot::libraryInfo(const QString &path) const { return _libraries.value(QDir::cleanPath(path)); } + + +void ModuleApiInfo::addToHash(QCryptographicHash &hash) const +{ + int len = uri.length(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(uri.constData()), len * sizeof(QChar)); + version.addToHash(hash); + len = cppName.length(); + hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); + hash.addData(reinterpret_cast<const char *>(cppName.constData()), len * sizeof(QChar)); +} diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 5d22890880..59906c354e 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -39,11 +39,13 @@ #include "parser/qmljsengine_p.h" #include "qmljs_global.h" #include "qmljsconstants.h" +#include "qmljsimportdependencies.h" namespace QmlJS { class Bind; class Snapshot; +class ImportDependencies; class QMLJS_EXPORT Document { @@ -69,6 +71,8 @@ public: Language::Enum language() const; void setLanguage(Language::Enum l); + QString importId() const; + QByteArray fingerprint() const; AST::UiProgram *qmlProgram() const; AST::Program *jsProgram() const; AST::ExpressionNode *expression() const; @@ -111,6 +115,7 @@ private: QString _componentName; QString _source; QWeakPointer<Document> _ptr; + QByteArray _fingerprint; int _editorRevision; Language::Enum _language; bool _parsedCorrectly; @@ -125,6 +130,8 @@ public: QString uri; LanguageUtils::ComponentVersion version; QString cppName; + + void addToHash(QCryptographicHash &hash) const; }; class QMLJS_EXPORT LibraryInfo @@ -152,15 +159,21 @@ private: typedef QList<LanguageUtils::FakeMetaObject::ConstPtr> FakeMetaObjectList; FakeMetaObjectList _metaObjects; QList<ModuleApiInfo> _moduleApis; + QByteArray _fingerprint; PluginTypeInfoStatus _dumpStatus; QString _dumpError; public: explicit LibraryInfo(Status status = NotScanned); - explicit LibraryInfo(const QmlDirParser &parser); + explicit LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint = QByteArray()); ~LibraryInfo(); + QByteArray calculateFingerprint() const; + void updateFingerprint(); + QByteArray fingerprint() const + { return _fingerprint; } + QList<QmlDirParser::Component> components() const { return _components; } @@ -188,6 +201,9 @@ public: bool wasScanned() const { return _status != NotScanned; } + bool wasFound() const + { return _status != NotFound; } + PluginTypeInfoStatus pluginTypeInfoStatus() const { return _dumpStatus; } @@ -204,9 +220,11 @@ class QMLJS_EXPORT Snapshot QHash<QString, Document::Ptr> _documents; QHash<QString, QList<Document::Ptr> > _documentsByPath; QHash<QString, LibraryInfo> _libraries; + ImportDependencies _dependencies; public: Snapshot(); + Snapshot(const Snapshot &o); ~Snapshot(); typedef _Base::iterator iterator; @@ -219,6 +237,9 @@ public: void insertLibraryInfo(const QString &path, const LibraryInfo &info); void remove(const QString &fileName); + const ImportDependencies *importDependencies() const; + ImportDependencies *importDependencies(); + Document::Ptr document(const QString &fileName) const; QList<Document::Ptr> documentsInDirectory(const QString &path) const; LibraryInfo libraryInfo(const QString &path) const; diff --git a/src/libs/qmljs/qmljsimportdependencies.cpp b/src/libs/qmljs/qmljsimportdependencies.cpp new file mode 100644 index 0000000000..dffcf8ee4b --- /dev/null +++ b/src/libs/qmljs/qmljsimportdependencies.cpp @@ -0,0 +1,931 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljsimportdependencies.h" +#include "qmljsinterpreter.h" +#include "qmljsqrcparser.h" + +#include <utils/qtcassert.h> +#include <utils/function.h> + +#include <QCryptographicHash> + +#include <algorithm> + +namespace QmlJS { + +static bool debugImportDependencies = false; + +ImportKind::Enum toImportKind(ImportType::Enum type) +{ + switch (type) { + case ImportType::Invalid: + break; + case ImportType::Library: + return ImportKind::Library; + case ImportType::ImplicitDirectory: + case ImportType::File: + case ImportType::Directory: + case ImportType::UnknownFile: + return ImportKind::Path; + case ImportType::QrcFile: + case ImportType::QrcDirectory: + return ImportKind::QrcPath; + } + return ImportKind::Invalid; +} + +ImportMatchStrength::ImportMatchStrength(QList<int> match) + : m_match(match) +{ } + +int ImportMatchStrength::compareMatch(const ImportMatchStrength &o) const +{ + int len1 = m_match.size(); + int len2 = o.m_match.size(); + int len = ((len1 < len2) ? len1 : len2); + for (int i = 0; i < len; ++ i) { + int v1 = m_match.at(i); + int v2 = o.m_match.at(i); + if (v1 < v2) + return -1; + if (v2 > v1) + return 1; + } + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + return 0; +} + +bool ImportMatchStrength::hasNoMatch() +{ + return m_match.isEmpty(); +} + +bool ImportMatchStrength::hasMatch() +{ + return !m_match.isEmpty(); +} + +bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return m1.m_match == m2.m_match; +} + +bool operator !=(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return !(m1 == m2); +} + +bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2) +{ + return m1.compareMatch(m2) < 0; +} + +ImportKey::ImportKey() + : type(ImportType::Invalid), + majorVersion(LanguageUtils::ComponentVersion::NoVersion), + minorVersion(LanguageUtils::ComponentVersion::NoVersion) +{ } + +ImportKey::ImportKey(const ImportInfo &info) + : type(info.type()) + , majorVersion(info.version().majorVersion()) + , minorVersion(info.version().minorVersion()) +{ + splitPath = QFileInfo(info.path()).canonicalFilePath().split(QLatin1Char('/'), + QString::KeepEmptyParts); +} + +ImportKey::ImportKey(ImportType::Enum type, const QString &path, int majorVersion, int minorVersion) + : type(type) + , majorVersion(majorVersion) + , minorVersion(minorVersion) +{ + switch (type) { + case ImportType::Library: + splitPath = path.split(QLatin1Char('.')); + break; + case ImportType::ImplicitDirectory: + case ImportType::Directory: + splitPath = path.split(QLatin1Char('/')); + if (splitPath.length() > 1 && splitPath.last().isEmpty()) + splitPath.removeLast(); + break; + case ImportType::File: + case ImportType::QrcFile: + splitPath = QrcParser::normalizedQrcFilePath(path).split(QLatin1Char('/')); + break; + case ImportType::QrcDirectory: + splitPath = QrcParser::normalizedQrcDirectoryPath(path).split(QLatin1Char('/')); + if (splitPath.length() > 1 && splitPath.last().isEmpty()) + splitPath.removeLast(); + break; + case ImportType::Invalid: + case ImportType::UnknownFile: + splitPath = path.split(QLatin1Char('/')); + break; + } +} + +void ImportKey::addToHash(QCryptographicHash &hash) const +{ + hash.addData(reinterpret_cast<const char *>(&type), sizeof(type)); + hash.addData(reinterpret_cast<const char *>(&majorVersion), sizeof(majorVersion)); + hash.addData(reinterpret_cast<const char *>(&minorVersion), sizeof(minorVersion)); + foreach (const QString &s, splitPath) { + hash.addData("/", 1); + hash.addData(reinterpret_cast<const char *>(s.constData()), sizeof(QChar) * s.size()); + } + hash.addData("/", 1); +} + +ImportKey ImportKey::flatKey() const { + switch (type) { + case ImportType::Invalid: + return *this; + case ImportType::ImplicitDirectory: + case ImportType::Library: + case ImportType::File: + case ImportType::Directory: + case ImportType::QrcFile: + case ImportType::QrcDirectory: + case ImportType::UnknownFile: + break; + } + QStringList flatPath = splitPath; + int i = 0; + while (i < flatPath.size()) { + if (flatPath.at(i).startsWith(QLatin1Char('+'))) + flatPath.removeAt(i); + else + ++i; + } + if (flatPath.size() == splitPath.size()) + return *this; + ImportKey res = *this; + res.splitPath = flatPath; + return res; +} + +QString ImportKey::path() const +{ + QString res = splitPath.join(QString::fromLatin1("/")); + if (res.isEmpty() && !splitPath.isEmpty()) + return QLatin1String("/"); + return res; +} + +ImportMatchStrength ImportKey::matchImport(const ImportKey &o, const ViewerContext &vContext) const +{ + if (majorVersion != o.majorVersion || minorVersion > o.minorVersion) + return ImportMatchStrength(); + bool dirToFile = false; + switch (o.type) { + case ImportType::Invalid: + return ImportMatchStrength(); + case ImportType::ImplicitDirectory: + case ImportType::Directory: + switch (type) { + case ImportType::File: + case ImportType::UnknownFile: + dirToFile = true; + break; + case ImportType::ImplicitDirectory: + case ImportType::Directory: + break; + default: + return ImportMatchStrength(); + } + break; + case ImportType::Library: + if (type != ImportType::Library) + return ImportMatchStrength(); + break; + case ImportType::QrcDirectory: + switch (type) { + case ImportType::QrcFile: + dirToFile = true; + break; + case ImportType::QrcDirectory: + break; + default: + return ImportMatchStrength(); + } + break; + case ImportType::QrcFile: + if (type != ImportType::QrcFile) + return ImportMatchStrength(); + case ImportType::UnknownFile: + case ImportType::File: + switch (type) { + case ImportType::UnknownFile: + case ImportType::File: + break; + default: + return ImportMatchStrength(); + } + break; + } + + QList<int> res; + int iPath1 = 0; + int lenPath1 = splitPath.size(); + int iPath2 = 0; + int lenPath2 = o.splitPath.size(); + if (dirToFile) + --lenPath1; + int iSelector = 0; + int nSelectors = vContext.selectors.size(); + while (iPath1 < lenPath1) { + if (lenPath2 - iPath2 > lenPath1 - iPath1) + return ImportMatchStrength(); + QString p1 = splitPath.at(iPath1); + if (iPath2 < lenPath2) { + QString p2 = splitPath.at(iPath2); + if (p1 == p2) { + ++iPath1; + ++iPath2; + continue; + } + } + if (!p1.startsWith(QLatin1Char('+'))) + return QList<int>(); + QStringRef selectorAtt(&p1, 1, p1.size()-1); + while (iSelector < nSelectors) { + if (selectorAtt == vContext.selectors.at(iSelector)) + break; + ++iSelector; + } + if (iSelector == nSelectors) + return QList<int>(); + res << (nSelectors - iSelector); + ++iSelector; + ++iPath1; + } + if (iPath2 != lenPath2) + return QList<int>(); + if (res.isEmpty()) + res << 0; + return ImportMatchStrength(res); +} + +int ImportKey::compare(const ImportKey &other) const +{ + ImportKind::Enum k1 = toImportKind(type); + ImportKind::Enum k2 = toImportKind(other.type); + if (k1 < k2) + return -1; + if (k1 > k2) + return 1; + int len1 = splitPath.size(); + int len2 = other.splitPath.size(); + int len = ((len1 < len2) ? len1 : len2); + for (int i = 0; i < len; ++ i) { + QString v1 = splitPath.at(i); + QString v2 = other.splitPath.at(i); + if (v1 < v2) + return -1; + if (v2 > v1) + return 1; + } + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + if (majorVersion < other.majorVersion) + return -1; + if (majorVersion > other.majorVersion) + return 1; + if (minorVersion < other.minorVersion) + return -1; + if (minorVersion > other.minorVersion) + return 1; + if (type < other.type) + return -1; + if (type > other.type) + return 1; + return 0; +} + +bool ImportKey::isDirectoryLike() const +{ + switch (type) { + case ImportType::Directory: + case ImportType::ImplicitDirectory: + case ImportType::QrcDirectory: + return true; + default: + return false; + } +} + +ImportKey::DirCompareInfo ImportKey::compareDir(const ImportKey &superDir) const +{ + // assumes dir/+selectors/file (i.e. no directories inside selectors) + switch (superDir.type) { + case ImportType::UnknownFile: + case ImportType::File: + case ImportType::Directory: + case ImportType::ImplicitDirectory: + if (type != ImportType::File && type != ImportType::ImplicitDirectory + && type != ImportType::Directory && type != ImportType::UnknownFile) + return Incompatible; + break; + case ImportType::QrcDirectory: + case ImportType::QrcFile: + if (type != ImportType::QrcDirectory && type != ImportType::QrcFile) + return Incompatible; + break; + case ImportType::Invalid: + case ImportType::Library: + return Incompatible; + } + bool isDir1 = isDirectoryLike(); + bool isDir2 = superDir.isDirectoryLike(); + int len1 = splitPath.size(); + int len2 = superDir.splitPath.size(); + if (isDir1 && len1 > 0) + --len1; + if (isDir2 && len2 > 0) + --len2; + + int i1 = 0; + int i2 = 0; + while (i1 < len1 && i2 < len2) { + QString p1 = splitPath.at(i1); + QString p2 = superDir.splitPath.at(i2); + if (p1 == p2) { + ++i1; + ++i2; + continue; + } + if (p1.startsWith(QLatin1Char('+'))) { + if (p2.startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + if (p2.startsWith(QLatin1Char('+'))) + return FirstInSecond; + return Different; + } + if (i1 < len1) { + if (splitPath.at(i1).startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + if (i2 < len2) { + if (superDir.splitPath.at(i2).startsWith(QLatin1Char('+'))) + return SameDir; + return SecondInFirst; + } + return SameDir; +} + +QString ImportKey::toString() const +{ + QString res; + switch (type) { + case ImportType::UnknownFile: + case ImportType::File: + res = path(); + break; + case ImportType::Directory: + case ImportType::ImplicitDirectory: + res = path() + QLatin1Char('/'); + break; + case ImportType::QrcDirectory: + res = QLatin1String("qrc:") + path() + QLatin1Char('/'); + break; + case ImportType::QrcFile: + res = QLatin1String("qrc:") + path() + QLatin1Char('/'); + break; + case ImportType::Invalid: + res = path(); + break; + case ImportType::Library: + res = splitPath.join(QLatin1String(".")); + break; + } + + if (majorVersion != LanguageUtils::ComponentVersion::NoVersion + || minorVersion != LanguageUtils::ComponentVersion::NoVersion) + return res + QLatin1Char(' ') + QString::number(majorVersion) + + QLatin1Char('.') + QString::number(minorVersion); + + return res; +} + +uint qHash(const ImportKey &info) +{ + uint res = ::qHash(info.type) ^ + ::qHash(info.majorVersion) ^ ::qHash(info.minorVersion); + foreach (const QString &s, info.splitPath) + res = res ^ ::qHash(s); + return res; +} + +bool operator==(const ImportKey &i1, const ImportKey &i2) +{ + return i1.type == i2.type + && i1.splitPath == i2.splitPath + && i1.majorVersion == i2.majorVersion + && i1.minorVersion == i2.minorVersion; +} + +bool operator !=(const ImportKey &i1, const ImportKey &i2) +{ + return ! (i1 == i2); +} + +bool operator <(const ImportKey &i1, const ImportKey &i2) +{ + return i1.compare(i2) < 0; +} + +Export::Export() + : intrinsic(false) +{ } + +Export::Export(ImportKey exportName, QString pathRequired, bool intrinsic) + : exportName(exportName), pathRequired(pathRequired), intrinsic(intrinsic) +{ } + +bool Export::visibleInVContext(const ViewerContext &vContext) const +{ + return pathRequired.isEmpty() || vContext.paths.contains(pathRequired); +} + +bool operator ==(const Export &i1, const Export &i2) +{ + return i1.exportName == i2.exportName + && i1.pathRequired == i2.pathRequired + && i1.intrinsic == i2.intrinsic; +} + +bool operator !=(const Export &i1, const Export &i2) +{ + return !(i1 == i2); +} + +CoreImport::CoreImport() : language(Language::Qml) { } + +CoreImport::CoreImport(const QString &importId, const QList<Export> &possibleExports, + Language::Enum language, const QByteArray &fingerprint) + : importId(importId), possibleExports(possibleExports), language(language), + fingerprint(fingerprint) +{ } + +bool CoreImport::valid() { + return !fingerprint.isEmpty(); +} + +QByteArray DependencyInfo::calculateFingerprint(const ImportDependencies &deps) +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + rootImport.addToHash(hash); + QStringList coreImports = allCoreImports.toList(); + coreImports.sort(); + foreach (const QString importId, coreImports) { + hash.addData(reinterpret_cast<const char*>(importId.constData()), importId.size() * sizeof(QChar)); + QByteArray coreImportFingerprint = deps.coreImport(importId).fingerprint; + hash.addData(coreImportFingerprint); + } + hash.addData("/", 1); + QList<ImportKey> imports(allImports.toList()); + std::sort(imports.begin(), imports.end()); + foreach (const ImportKey &k, imports) + k.addToHash(hash); + return hash.result(); +} + +MatchedImport::MatchedImport() +{ } + +MatchedImport::MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey, + const QString &coreImportId) + : matchStrength(matchStrength), importKey(importKey), coreImportId(coreImportId) +{ } + +int MatchedImport::compare(const MatchedImport &o) const { + int res = matchStrength.compareMatch(o.matchStrength); + if (res != 0) + return res; + res = importKey.compare(o.importKey); + if (res != 0) + return res; + if (coreImportId < o.coreImportId) + return -1; + if (coreImportId > o.coreImportId) + return 1; + return 0; +} + +bool operator ==(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) == 0; +} + +bool operator !=(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) != 0; +} + +bool operator <(const MatchedImport &m1, const MatchedImport &m2) +{ + return m1.compare(m2) < 0; +} + +ImportDependencies::ImportDependencies() +{ } + +ImportDependencies::~ImportDependencies() +{ } + +void ImportDependencies::filter(const ViewerContext &vContext) +{ + QMap<QString, CoreImport> newCoreImports; + QMap<ImportKey, QStringList> newImportCache; + QMapIterator<QString, CoreImport> j(m_coreImports); + bool hasChanges = false; + while (j.hasNext()) { + j.next(); + const CoreImport &cImport = j.value(); + if (vContext.languageIsCompatible(cImport.language)) { + QList<Export> newExports; + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + newExports.append(e); + QStringList &candidateImports = newImportCache[e.exportName]; + if (!candidateImports.contains(cImport.importId)) + candidateImports.append(cImport.importId); + } + } + if (newExports.size() == cImport.possibleExports.size()) { + newCoreImports.insert(cImport.importId, cImport); + } else if (newExports.length() > 0) { + CoreImport newCImport = cImport; + newCImport.possibleExports = newExports; + newCoreImports.insert(newCImport.importId, newCImport); + hasChanges = true; + } else { + hasChanges = true; + } + } else { + hasChanges = true; + } + } + if (!hasChanges) + return; + m_coreImports = newCoreImports; + m_importCache = newImportCache; +} + +CoreImport ImportDependencies::coreImport(const QString &importId) const +{ + return m_coreImports.value(importId); +} + +void ImportDependencies::iterateOnCandidateImports( + const ImportKey &key, const ViewerContext &vContext, + Utils::function<bool (const ImportMatchStrength &,const Export &,const CoreImport &)> + const &iterF) const +{ + switch (key.type) { + case ImportType::Directory: + case ImportType::QrcDirectory: + case ImportType::ImplicitDirectory: + break; + default: + { + QStringList imp = m_importCache.value(key.flatKey()); + foreach (const QString &cImportName, imp) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(key, vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + return; + } + } + QMap<ImportKey, QStringList>::const_iterator lb = m_importCache.lowerBound(key.flatKey()); + QMap<ImportKey, QStringList>::const_iterator end = m_importCache.constEnd(); + while (lb != end) { + ImportKey::DirCompareInfo c = key.compareDir(lb.key()); + if (c == ImportKey::SameDir) { + foreach (const QString &cImportName, lb.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(key, vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + } else if (c != ImportKey::SecondInFirst) { + break; + } + ++lb; + } +} + +class CollectCandidateImports +{ +public: + ImportDependencies::ImportElements &res; + + CollectCandidateImports(ImportDependencies::ImportElements & res) + : res(res) + { } + + bool operator ()(const ImportMatchStrength &m, const Export &e, const CoreImport &cI) const + { + ImportKey flatName = e.exportName.flatKey(); + res[flatName].append(MatchedImport(m, e.exportName, cI.importId)); + return true; + } +}; + +ImportDependencies::ImportElements ImportDependencies::candidateImports( + const ImportKey &key, + const ViewerContext &vContext) const +{ + ImportDependencies::ImportElements res; + CollectCandidateImports collector(res); + iterateOnCandidateImports(key, vContext, collector); + typedef QMap<ImportKey, QList<MatchedImport> >::iterator iter_t; + iter_t i = res.begin(); + iter_t end = res.end(); + while (i != end) { + std::sort(i.value().begin(), i.value().end()); + ++i; + } + return res; +} + +QList<DependencyInfo::ConstPtr> ImportDependencies::createDependencyInfos( + const ImportKey &mainDoc, const ViewerContext &vContext) const +{ + Q_UNUSED(mainDoc); + Q_UNUSED(vContext); + QList<DependencyInfo::ConstPtr> res; + QTC_CHECK(false); + return res; +} + +void ImportDependencies::addCoreImport(const CoreImport &import) +{ + CoreImport newImport = import; + if (m_coreImports.contains(import.importId)) { + CoreImport oldVal = m_coreImports.value(import.importId); + foreach (const Export &e, oldVal.possibleExports) { + if (e.intrinsic) + removeImportCacheEntry(e.exportName, import.importId); + else + newImport.possibleExports.append(e); + } + } + foreach (const Export &e, import.possibleExports) + m_importCache[e.exportName].append(import.importId); + m_coreImports.insert(newImport.importId, newImport); + if (debugImportDependencies) { + QDebug dbg(qDebug()); + dbg << "added import "<< newImport.importId << " for"; + foreach (const Export &e, newImport.possibleExports) + dbg << " " << e.exportName.toString() << "(" << e.pathRequired << ")"; + } +} + +void ImportDependencies::removeCoreImport(const QString &importId) +{ + if (!m_coreImports.contains(importId)) { + qDebug() << "missing importId in removeCoreImport(" << importId << ")"; + return; + } + CoreImport &cImport = m_coreImports[importId]; + QList<Export> newExports; + foreach (const Export &e, cImport.possibleExports) + if (e.intrinsic) + removeImportCacheEntry(e.exportName, importId); + else + newExports.append(e); + if (newExports.size()>0) + cImport.possibleExports = newExports; + else + m_coreImports.remove(importId); + + if (debugImportDependencies) + qDebug() << "removed import with id:"<< importId; +} + +void ImportDependencies::removeImportCacheEntry(const ImportKey &importKey, const QString &importId) +{ + QStringList &cImp = m_importCache[importKey]; + if (!cImp.removeOne(importId)) { + qDebug() << "missing possibleExport backpointer for " << importKey.toString() << " to " + << importId; + } + if (cImp.isEmpty()) + m_importCache.remove(importKey); +} + +void ImportDependencies::addExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath) +{ + if (!m_coreImports.contains(importId)) { + CoreImport newImport(importId); + newImport.language = Language::Unknown; + newImport.possibleExports.append(Export(importKey, requiredPath, false)); + m_coreImports.insert(newImport.importId, newImport); + m_importCache[importKey].append(importId); + return; + } + CoreImport &importValue = m_coreImports[importId]; + importValue.possibleExports.append(Export(importKey, requiredPath, false)); + m_importCache[importKey].append(importId); + if (debugImportDependencies) + qDebug() << "added export "<< importKey.toString() << " for id " <<importId + << " (" << requiredPath << ")"; +} + +void ImportDependencies::removeExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath) +{ + if (!m_coreImports.contains(importId)) { + qDebug() << "non existing core import for removeExport(" << importId << ", " + << importKey.toString() << ")"; + } else { + CoreImport &importValue = m_coreImports[importId]; + if (!importValue.possibleExports.removeOne(Export(importKey, requiredPath, false))) { + qDebug() << "non existing export for removeExport(" << importId << ", " + << importKey.toString() << ")"; + } + if (importValue.possibleExports.isEmpty() && importValue.fingerprint.isEmpty()) + m_coreImports.remove(importId); + } + if (!m_importCache.contains(importKey)) { + qDebug() << "missing possibleExport for " << importKey.toString() << " when removing export of " + << importId; + } else { + removeImportCacheEntry(importKey, importId); + } + if (debugImportDependencies) + qDebug() << "removed export "<< importKey.toString() << " for id " << importId + << " (" << requiredPath << ")"; +} + +void ImportDependencies::iterateOnCoreImports( + const ViewerContext &vContext, + Utils::function<bool (const CoreImport &)> const &iterF) const +{ + QMapIterator<QString, CoreImport> i(m_coreImports); + while (i.hasNext()) { + i.next(); + if (vContext.languageIsCompatible(i.value().language)) + iterF(i.value()); // check also that at least one export is visible? + } +} + +void ImportDependencies::iterateOnLibraryImports( + const ViewerContext &vContext, + Utils::function<bool (const ImportMatchStrength &, + const Export &, + const CoreImport &)> const &iterF) const +{ + typedef QMap<ImportKey, QStringList>::const_iterator iter_t; + ImportKey firstLib; + firstLib.type = ImportType::Library; + iter_t i = m_importCache.lowerBound(firstLib); + iter_t end = m_importCache.constEnd(); + while (i != end && i.key().type == ImportType::Library) { + if (debugImportDependencies) + qDebug() << "libloop:" << i.key().toString() << i.value(); + foreach (const QString &cImportName, i.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext) && e.exportName.type == ImportType::Library) { + ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext); + if (m.hasMatch()) { + if (debugImportDependencies) + qDebug() << "import iterate:" << e.exportName.toString() + << " (" << e.pathRequired << "), id:" << cImport.importId; + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + ++i; + } +} + +void ImportDependencies::iterateOnSubImports( + const ImportKey &baseKey, + const ViewerContext &vContext, + Utils::function<bool (const ImportMatchStrength &, + const Export &, + const CoreImport &)> const &iterF) const +{ + typedef QMap<ImportKey, QStringList>::const_iterator iter_t; + iter_t i = m_importCache.lowerBound(baseKey); + iter_t end = m_importCache.constEnd(); + while (i != end) { + ImportKey::DirCompareInfo c = baseKey.compareDir(i.key()); + if (c != ImportKey::SameDir && c != ImportKey::SecondInFirst) + break; + foreach (const QString &cImportName, i.value()) { + CoreImport cImport = coreImport(cImportName); + if (vContext.languageIsCompatible(cImport.language)) { + foreach (const Export &e, cImport.possibleExports) { + if (e.visibleInVContext(vContext)) { + ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext); + if (m.hasMatch()) { + if (!iterF(m, e, cImport)) + return; + } + } + } + } + } + ++i; + } +} + +class CollectImportKeys { +public: + QSet<ImportKey> &imports; + CollectImportKeys(QSet<ImportKey> &imports) + : imports(imports) + { } + bool operator()(const ImportMatchStrength &m, + const Export &e, + const CoreImport &cI) const + { + Q_UNUSED(m); + Q_UNUSED(cI); + imports.insert(e.exportName.flatKey()); + return true; + } +}; + +QSet<ImportKey> ImportDependencies::libraryImports(const ViewerContext &viewContext) const +{ + QSet<ImportKey> res; + CollectImportKeys importCollector(res); + iterateOnLibraryImports(viewContext, importCollector); + return res; +} + +QSet<ImportKey> ImportDependencies::subdirImports( + const ImportKey &baseKey, const ViewerContext &viewContext) const +{ + QSet<ImportKey> res; + CollectImportKeys importCollector(res); + iterateOnSubImports(baseKey, viewContext, importCollector); + return res; +} + +} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsimportdependencies.h b/src/libs/qmljs/qmljsimportdependencies.h new file mode 100644 index 0000000000..fc84f0794f --- /dev/null +++ b/src/libs/qmljs/qmljsimportdependencies.h @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLJSIMPORTCACHE_H +#define QMLJSIMPORTCACHE_H + +#include "qmljsviewercontext.h" + +#include <languageutils/componentversion.h> +#include <utils/qtcoverride.h> +#include <utils/function.h> + +#include <QObject> +#include <QString> +#include <QStringList> +#include <QList> +#include <QMap> +#include <QSet> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QCryptographicHash; +QT_END_NAMESPACE + +namespace QmlJS { +class ImportInfo; +namespace Internal { +class ImportDependenciesPrivate; +} +class ImportDependencies; + +// match strenght wrt to the selectors of a ViewerContext +// this is valid only within a ViewerContext +class QMLJS_EXPORT ImportMatchStrength +{ +public: + explicit ImportMatchStrength() {} + ImportMatchStrength(QList<int> match); + + int compareMatch(const ImportMatchStrength &o) const; + + bool hasNoMatch(); + + bool hasMatch(); +private: + friend bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2); + QList<int> m_match; +}; +bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2); +bool operator !=(const ImportMatchStrength &m1, const ImportMatchStrength &m2); +bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2); + +/*! + * \brief The ImportKey class represent an import (or export), and can be used as hash key + * + * This represent only what is to be imported, *not* how (i.e. no as clause) + * + * Order is defined so that files in the same directory are contiguous, and different + * ImportKind are separated. + * This is used to efficiently iterate just on library imports, or just on a directory + * while preserving space. + */ +class QMLJS_EXPORT ImportKey +{ +public: + enum DirCompareInfo { + SameDir, + FirstInSecond, + SecondInFirst, + Different, + Incompatible + }; + + explicit ImportKey(); + explicit ImportKey(const ImportInfo &info); + ImportKey(ImportType::Enum type, const QString &path, + int majorVersion = LanguageUtils::ComponentVersion::NoVersion, + int minorVersion = LanguageUtils::ComponentVersion::NoVersion); + + ImportType::Enum type; + QStringList splitPath; + int majorVersion; + int minorVersion; + + QString path() const; + + void addToHash(QCryptographicHash &hash) const; + ImportKey flatKey() const; + + // wrap QList in a special type? + ImportMatchStrength matchImport(const ImportKey &o, const ViewerContext &vContext) const; + int compare(const ImportKey &other) const; + bool isDirectoryLike() const; + DirCompareInfo compareDir(const ImportKey &other) const; + QString toString() const; +}; + +uint qHash(const ImportKey &info); +bool operator ==(const ImportKey &i1, const ImportKey &i2); +bool operator !=(const ImportKey &i1, const ImportKey &i2); +bool operator <(const ImportKey &i1, const ImportKey &i2); + +class QMLJS_EXPORT Export +{ +public: + Export(); + Export(ImportKey exportName, QString pathRequired, bool intrinsic = false); + ImportKey exportName; + QString pathRequired; + bool intrinsic; + bool visibleInVContext(const ViewerContext &vContext) const; +}; +bool operator ==(const Export &i1, const Export &i2); +bool operator !=(const Export &i1, const Export &i2); + +class QMLJS_EXPORT CoreImport +{ +public: + CoreImport(); + CoreImport(const QString &importId, const QList<Export> &possibleExports = QList<Export>(), + Language::Enum language = Language::Qml, const QByteArray &fingerprint = QByteArray()); + QString importId; + QList<Export> possibleExports; + Language::Enum language; + QByteArray fingerprint; + bool valid(); +}; + +class QMLJS_EXPORT DependencyInfo +{ +public: + typedef QSharedPointer<const DependencyInfo> ConstPtr; + typedef QSharedPointer<DependencyInfo> Ptr; + + QByteArray calculateFingerprint(const ImportDependencies &deps); + + ImportKey rootImport; + QSet<QString> allCoreImports; + QSet<ImportKey> allImports; + QByteArray fingerprint; +}; + +class QMLJS_EXPORT MatchedImport +{ +public: + MatchedImport(); + MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey, + const QString &coreImportId); + + ImportMatchStrength matchStrength; + ImportKey importKey; + QString coreImportId; + int compare(const MatchedImport &o) const; +}; +bool operator ==(const MatchedImport &m1, const MatchedImport &m2); +bool operator !=(const MatchedImport &m1, const MatchedImport &m2); +bool operator <(const MatchedImport &m1, const MatchedImport &m2); + +class QMLJS_EXPORT ImportDependencies +{ +public: + typedef QMap<ImportKey, QList<MatchedImport> > ImportElements; + + explicit ImportDependencies(); + ~ImportDependencies(); + + void filter(const ViewerContext &vContext); + + CoreImport coreImport(const QString &importId) const; + void iterateOnCandidateImports(const ImportKey &key, const ViewerContext &vContext, + Utils::function<bool(const ImportMatchStrength &, + const Export &, + const CoreImport &)> const &iterF) const; + ImportElements candidateImports(const ImportKey &key, const ViewerContext &vContext) const; + + QList<DependencyInfo::ConstPtr> createDependencyInfos(const ImportKey &mainDoc, + const ViewerContext &vContext) const; + + void addCoreImport(const CoreImport &import); + void removeCoreImport(const QString &importId); + + void addExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath); + void removeExport(const QString &importId, const ImportKey &importKey, + const QString &requiredPath); + + void iterateOnCoreImports(const ViewerContext &vContext, + Utils::function<bool(const CoreImport &)> const &iterF) const; + void iterateOnLibraryImports(const ViewerContext &vContext, + Utils::function<bool(const ImportMatchStrength &, + const Export &, + const CoreImport &)> const &iterF) const; + void iterateOnSubImports(const ImportKey &baseKey, const ViewerContext &vContext, + Utils::function<bool(const ImportMatchStrength &, + const Export &, + const CoreImport &)> const &iterF) const; + + QSet<ImportKey> libraryImports(const ViewerContext &viewContext) const; + QSet<ImportKey> subdirImports(const ImportKey &baseKey, const ViewerContext &viewContext) const; +private: + void removeImportCacheEntry(const ImportKey &importKey, const QString &importId); + + QMap<ImportKey, QStringList> m_importCache; + QMap<QString, CoreImport> m_coreImports; +}; + +} // namespace QmlJS + +#endif // QMLJSIMPORTCACHE_H diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index cf8d22a384..ef5577a90c 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -33,6 +33,7 @@ #include <qmljs/qmljsdocument.h> #include <qmljs/qmljs_global.h> #include <qmljs/qmljsconstants.h> +#include <qmljs/qmljsimportdependencies.h> #include <QFileInfoList> #include <QList> @@ -912,6 +913,7 @@ public: // const! ObjectValue *object; ImportInfo info; + DependencyInfo::ConstPtr deps; // uri imports: path to library, else empty QString libraryPath; // whether the import succeeded diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 950d238f1b..01100454bc 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -34,7 +34,8 @@ #include "qmljsbind.h" #include "qmljsutils.h" #include "qmljsmodelmanagerinterface.h" -#include <qmljs/qmljsqrcparser.h> +#include "qmljsqrcparser.h" +#include "qmljsconstants.h" #include <QDir> #include <QDebug> @@ -83,6 +84,7 @@ public: ValueOwner *valueOwner; QStringList importPaths; LibraryInfo builtins; + ViewerContext vContext; QHash<ImportCacheKey, Import> importCache; @@ -131,13 +133,14 @@ public: \l{QmlJSEditor::SemanticInfo} of a \l{QmlJSEditor::QmlJSTextEditorWidget}. */ -Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const LibraryInfo &builtins) +Link::Link(const Snapshot &snapshot, const ViewerContext &vContext, const LibraryInfo &builtins) : d(new LinkPrivate) { d->valueOwner = new ValueOwner; d->snapshot = snapshot; - d->importPaths = importPaths; + d->importPaths = vContext.paths; d->builtins = builtins; + d->vContext = vContext; d->diagnosticMessages = 0; d->allDiagnosticMessages = 0; @@ -173,14 +176,14 @@ Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const Libra ContextPtr Link::operator()(QHash<QString, QList<DiagnosticMessage> > *messages) { d->allDiagnosticMessages = messages; - return Context::create(d->snapshot, d->valueOwner, d->linkImports()); + return Context::create(d->snapshot, d->valueOwner, d->linkImports(), d->vContext); } ContextPtr Link::operator()(const Document::Ptr &doc, QList<DiagnosticMessage> *messages) { d->document = doc; d->diagnosticMessages = messages; - return Context::create(d->snapshot, d->valueOwner, d->linkImports()); + return Context::create(d->snapshot, d->valueOwner, d->linkImports(), d->vContext); } Link::~Link() diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 7d7875e421..dbb2904e0d 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -48,7 +48,7 @@ class QMLJS_EXPORT Link Q_DECLARE_TR_FUNCTIONS(QmlJS::Link) public: - Link(const Snapshot &snapshot, const QStringList &importPaths, const LibraryInfo &builtins); + Link(const Snapshot &snapshot, const ViewerContext &vContext, const LibraryInfo &builtins); // Link all documents in snapshot, collecting all diagnostic messages (if messages != 0) ContextPtr operator()(QHash<QString, QList<DiagnosticMessage> > *messages = 0); diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index cdcaef362d..45a7c87991 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -33,7 +33,8 @@ #include "qmljs_global.h" #include "qmljsdocument.h" #include "qmljsbundle.h" - +#include "qmljsconstants.h" +#include "qmljsviewercontext.h" #include <utils/environment.h> #include <QObject> @@ -175,6 +176,12 @@ public: virtual LibraryInfo builtins(const Document::Ptr &doc) const = 0; + virtual ViewerContext completeVContext(const ViewerContext &vCtx, + const Document::Ptr &doc = Document::Ptr(0)) const = 0; + virtual ViewerContext defaultVContext(bool autoComplete = true, + const Document::Ptr &doc = Document::Ptr(0)) const = 0; + virtual void setDefaultVContext(const ViewerContext &vContext) = 0; + // Blocks until all parsing threads are done. Used for testing. virtual void joinAllThreads() = 0; diff --git a/src/libs/qmljs/qmljstypedescriptionreader.cpp b/src/libs/qmljs/qmljstypedescriptionreader.cpp index 6cfb5124dd..695ef5b5c9 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.cpp +++ b/src/libs/qmljs/qmljstypedescriptionreader.cpp @@ -223,6 +223,7 @@ void TypeDescriptionReader::readComponent(UiObjectDefinition *ast) // ### add implicit export into the package of c++ types fmo->addExport(fmo->className(), QmlJS::CppQmlTypes::cppPackage, ComponentVersion()); + fmo->updateFingerprint(); _objects->insert(fmo->className(), fmo); } diff --git a/src/libs/qmljs/qmljsviewercontext.cpp b/src/libs/qmljs/qmljsviewercontext.cpp new file mode 100644 index 0000000000..06f5dd1806 --- /dev/null +++ b/src/libs/qmljs/qmljsviewercontext.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljsviewercontext.h" + +namespace QmlJS { +/*! + \class QmlJS::ViewerContext + \brief The ViewerContext class encapsulate selector and paths for a given viewer. + + Using a a different viewer context can emulate (the pure qml part) of a device. + This allows checking how a given qml would be interpreted on another platform/viewer. + + Screen information will also most likely need to be added here. +*/ +ViewerContext::ViewerContext() + : language(Language::Qml), flags(AddAllPaths) +{ } + +ViewerContext::ViewerContext(QStringList selectors, QStringList paths, + QmlJS::Language::Enum language, + QmlJS::ViewerContext::Flags flags) + : selectors(selectors), paths(paths), language(language), + flags(flags) +{ } + + +/* + which languages might be imported in this context + */ +bool ViewerContext::languageIsCompatible(Language::Enum l) const +{ + switch (language) { + case Language::JavaScript: + case Language::Json: + case Language::QmlProject: + case Language::QmlQbs: + case Language::QmlTypeInfo: + return language == l; + case Language::Qml: + return l == Language::Qml || l == Language::QmlQtQuick1 || l == Language::QmlQtQuick2 + || Language::JavaScript; + case Language::QmlQtQuick1: + return l == Language::Qml || l == Language::QmlQtQuick1 || Language::JavaScript; + case Language::QmlQtQuick2: + return l == Language::Qml || l == Language::QmlQtQuick2 || Language::JavaScript; + case Language::Unknown: // ? + break; + } + return true; +} + +} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsviewercontext.h b/src/libs/qmljs/qmljsviewercontext.h new file mode 100644 index 0000000000..97620b5353 --- /dev/null +++ b/src/libs/qmljs/qmljsviewercontext.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VIEWERCONTEXT_H +#define VIEWERCONTEXT_H + +#include "qmljs_global.h" +#include "qmljsconstants.h" + +#include <QStringList> + +namespace QmlJS { + +class QMLJS_EXPORT ViewerContext +{ +public: + enum Flags { + Complete, + AddAllPaths, + AddQtPath + }; + + ViewerContext(); + ViewerContext(QStringList selectors, QStringList paths, + Language::Enum language = Language::Qml, + Flags flags = AddAllPaths); + + bool languageIsCompatible(Language::Enum l) const; + + QStringList selectors; + QStringList paths; + Language::Enum language; + Flags flags; +}; + +} // namespace QmlJS +#endif // VIEWERCONTEXT_H diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 5b4c95bf31..91e7b478a5 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -422,7 +422,7 @@ AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(ProjectExplorer::Proje return info; } - AndroidDeviceDialog dialog(apiLevel, abi); + AndroidDeviceDialog dialog(apiLevel, abi, Core::ICore::mainWindow()); if (dialog.exec() == QDialog::Accepted) { AndroidDeviceInfo info = dialog.device(); if (dialog.saveDeviceSelection()) { @@ -496,9 +496,9 @@ QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *erro return devices; } -QString AndroidConfigurations::createAVD(int minApiLevel, QString targetArch) const +QString AndroidConfigurations::createAVD(QWidget *parent, int minApiLevel, QString targetArch) const { - QDialog d; + QDialog d(parent); Ui::AddNewAVDDialog avdDialog; avdDialog.setupUi(&d); // NOTE: adb list targets does actually include information on which abis are supported per apilevel diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 09c00d6b11..f69a830aed 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -102,7 +102,7 @@ public: Utils::FileName zipalignPath() const; Utils::FileName stripPath(ProjectExplorer::Abi::Architecture architecture, const QString &ndkToolChainVersion) const; Utils::FileName readelfPath(ProjectExplorer::Abi::Architecture architecture, const QString &ndkToolChainVersion) const; - QString createAVD(int minApiLevel = 0, QString targetArch = QString()) const; + QString createAVD(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const; QString createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize) const; bool removeAVD(const QString &name) const; QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const; diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 53f1b148af..c4a5adc67a 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -388,7 +388,7 @@ bool AndroidDeployQtStep::fromMap(const QVariantMap &map) m_deployAction = AndroidDeployQtAction(map.value(QLatin1String(DeployActionKey), BundleLibrariesDeployment).toInt()); m_keystorePath = Utils::FileName::fromString(map.value(KeystoreLocationKey).toString()); - m_signPackage = map.value(SignPackageKey).toBool(); + m_signPackage = false; // don't restore this m_buildTargetSdk = map.value(BuildTargetSdkKey).toString(); m_verbose = map.value(VerboseOutputKey).toBool(); m_inputFile = map.value(InputFile).toString(); diff --git a/src/plugins/android/androiddeployqtwidget.cpp b/src/plugins/android/androiddeployqtwidget.cpp index a1dee1ddba..7cc2e1561c 100644 --- a/src/plugins/android/androiddeployqtwidget.cpp +++ b/src/plugins/android/androiddeployqtwidget.cpp @@ -82,9 +82,11 @@ AndroidDeployQtWidget::AndroidDeployQtWidget(AndroidDeployQtStep *step) } // signing + m_ui->signPackageCheckBox->setChecked(m_step->signPackage()); m_ui->KeystoreLocationLineEdit->setText(m_step->keystorePath().toUserOutput()); m_ui->signingDebugWarningIcon->hide(); m_ui->signingDebugWarningLabel->hide(); + signPackageCheckBoxToggled(m_step->signPackage()); m_ui->verboseOutputCheckBox->setChecked(m_step->verboseOutput()); m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation()); diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp index 66f4c1ea98..6b3fb90bd5 100644 --- a/src/plugins/android/androiddevicedialog.cpp +++ b/src/plugins/android/androiddevicedialog.cpp @@ -446,7 +446,7 @@ void AndroidDeviceDialog::refreshDeviceList() void AndroidDeviceDialog::createAvd() { - QString avd = AndroidConfigurations::instance().createAVD(m_apiLevel, m_abi); + QString avd = AndroidConfigurations::instance().createAVD(this, m_apiLevel, m_abi); if (avd.isEmpty()) return; refreshDeviceList(); diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index fc0a01c22a..4ab8228d0b 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -402,14 +402,16 @@ void AndroidSettingsWidget::browseOpenJDKLocation() void AndroidSettingsWidget::addAVD() { - AndroidConfigurations::instance().createAVD(); + AndroidConfigurations::instance().createAVD(this); m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); + avdActivated(m_ui->AVDTableView->currentIndex()); } void AndroidSettingsWidget::removeAVD() { AndroidConfigurations::instance().removeAVD(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); m_AVDModel.setAvdList(AndroidConfigurations::instance().androidVirtualDevices()); + avdActivated(m_ui->AVDTableView->currentIndex()); } void AndroidSettingsWidget::startAVD() diff --git a/src/plugins/android/createandroidmanifestwizard.cpp b/src/plugins/android/createandroidmanifestwizard.cpp index 84def92065..be51fb1c77 100644 --- a/src/plugins/android/createandroidmanifestwizard.cpp +++ b/src/plugins/android/createandroidmanifestwizard.cpp @@ -76,6 +76,7 @@ ChooseProFilePage::ChooseProFilePage(CreateAndroidManifestWizard *wizard, const foreach (QmakeProFileNode *node, nodes) m_comboBox->addItem(node->displayName(), QVariant::fromValue(static_cast<void *>(node))); // TODO something more? + nodeSelected(0); connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(nodeSelected(int))); diff --git a/src/plugins/baremetal/baremetalruncontrolfactory.cpp b/src/plugins/baremetal/baremetalruncontrolfactory.cpp index 3ba17b73bf..50edf9a152 100644 --- a/src/plugins/baremetal/baremetalruncontrolfactory.cpp +++ b/src/plugins/baremetal/baremetalruncontrolfactory.cpp @@ -89,7 +89,7 @@ DebuggerStartParameters BareMetalRunControlFactory::startParameters(const BareMe params.languages |= CppLanguage; params.processArgs = runConfig->arguments(); params.startMode = AttachToRemoteServer; - params.executable = Utils::HostOsInfo::withExecutableSuffix(runConfig->localExecutableFilePath()); + params.executable = runConfig->localExecutableFilePath(); params.remoteSetupNeeded = false; //FIXME probably start debugserver with that, how? params.displayName = runConfig->displayName(); if (const Project *project = target->project()) { diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index 4c08a10556..a08ec4e96d 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -1551,7 +1551,7 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName QFile::rename(absPath + QLatin1String(".hijack"), absPath); } - if ((!response.error || response.stdOut.contains(QLatin1String("already checked out"))) + if ((!response.error || response.stdErr.contains(QLatin1String("already checked out"))) && !m_settings.disableIndexer) { setStatus(absPath, FileStatus::CheckedOut); } @@ -1683,7 +1683,7 @@ bool ClearCasePlugin::ccFileOp(const QString &workingDir, const QString &title, runCleartool(workingDir, args, m_settings.timeOutMS(), ShowStdOutInLogWindow | FullySynchronously); if (coResponse.error) { - if (coResponse.stdOut.contains(QLatin1String("already checked out"))) + if (coResponse.stdErr.contains(QLatin1String("already checked out"))) noCheckout = true; else return false; diff --git a/src/plugins/coreplugin/core.qrc b/src/plugins/coreplugin/core.qrc index d54182e37b..6fd7523c22 100644 --- a/src/plugins/coreplugin/core.qrc +++ b/src/plugins/coreplugin/core.qrc @@ -3,6 +3,7 @@ <file>images/clean_pane_small.png</file> <file>images/clear.png</file> <file>images/closebutton.png</file> + <file>images/compile_error_taskbar.png</file> <file>images/dir.png</file> <file>images/editcopy.png</file> <file>images/editcut.png</file> diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index fb7620213f..253662a78f 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -114,8 +114,6 @@ using namespace Utils; //===================EditorManager===================== -EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0; - EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent) : QWidget(parent), m_mode(mode) { @@ -129,16 +127,17 @@ EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *p EditorManagerPlaceHolder::~EditorManagerPlaceHolder() { - if (m_current == this) { - EditorManager::instance()->setParent(0); - EditorManager::instance()->hide(); + // EditorManager will be deleted in ~MainWindow() + EditorManager *em = EditorManager::instance(); + if (em && em->parent() == this) { + em->hide(); + em->setParent(0); } } void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode) { if (m_mode == mode) { - m_current = this; QWidget *previousFocus = 0; if (EditorManager::instance()->focusWidget() && EditorManager::instance()->focusWidget()->hasFocus()) previousFocus = EditorManager::instance()->focusWidget(); @@ -146,16 +145,9 @@ void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode) EditorManager::instance()->show(); if (previousFocus) previousFocus->setFocus(); - } else if (m_current == this) { - m_current = 0; } } -EditorManagerPlaceHolder* EditorManagerPlaceHolder::current() -{ - return m_current; -} - // ---------------- EditorManager namespace Core { diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 4b58092b41..9e554b4a46 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -79,12 +79,10 @@ class CORE_EXPORT EditorManagerPlaceHolder : public QWidget public: explicit EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent = 0); ~EditorManagerPlaceHolder(); - static EditorManagerPlaceHolder* current(); private slots: void currentModeChanged(Core::IMode *); private: Core::IMode *m_mode; - static EditorManagerPlaceHolder* m_current; }; class CORE_EXPORT EditorManager : public QWidget diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/plugins/coreplugin/fileiconprovider.cpp index 6bba67ba06..4e56cd96b9 100644 --- a/src/plugins/coreplugin/fileiconprovider.cpp +++ b/src/plugins/coreplugin/fileiconprovider.cpp @@ -75,7 +75,7 @@ public: : m_unknownFileIcon(qApp->style()->standardIcon(QStyle::SP_FileIcon)) {} - QIcon icon(const QFileInfo &info) const; + QIcon icon(const QFileInfo &info); using QFileIconProvider::icon; void registerIconOverlayForSuffix(const QIcon &icon, const QString &suffix) @@ -113,23 +113,26 @@ QFileIconProvider *iconProvider() return instance(); } -QIcon FileIconProviderImplementation::icon(const QFileInfo &fileInfo) const +QIcon FileIconProviderImplementation::icon(const QFileInfo &fileInfo) { if (debug) qDebug() << "FileIconProvider::icon" << fileInfo.absoluteFilePath(); // Check for cached overlay icons by file suffix. - if (!m_cache.isEmpty() && !fileInfo.isDir()) { - const QString suffix = fileInfo.suffix(); - if (!suffix.isEmpty() && m_cache.contains(suffix)) { + bool isDir = fileInfo.isDir(); + QString suffix = !isDir ? fileInfo.suffix() : QString(); + if (!m_cache.isEmpty() && !isDir && !suffix.isEmpty()) { + if (m_cache.contains(suffix)) return m_cache.value(suffix); - } } // Get icon from OS. + QIcon icon; if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) - return QFileIconProvider::icon(fileInfo); - - // File icons are unknown on linux systems. - return fileInfo.isDir() ? QFileIconProvider::icon(fileInfo) : m_unknownFileIcon; + icon = QFileIconProvider::icon(fileInfo); + else // File icons are unknown on linux systems. + icon = isDir ? QFileIconProvider::icon(fileInfo) : m_unknownFileIcon; + if (!isDir && !suffix.isEmpty()) + m_cache.insert(suffix, icon); + return icon; } /*! diff --git a/src/plugins/coreplugin/images/compile_error_taskbar.png b/src/plugins/coreplugin/images/compile_error_taskbar.png Binary files differnew file mode 100644 index 0000000000..71dd4ede42 --- /dev/null +++ b/src/plugins/coreplugin/images/compile_error_taskbar.png diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp index 39ad2439de..1f04e55d47 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp @@ -122,7 +122,7 @@ void Core::Internal::ProgressManagerPrivate::doSetApplicationLabel(const QString if (text.isEmpty()) { pITask->SetOverlayIcon(winId, NULL, NULL); } else { - QPixmap pix = QPixmap(QLatin1String(":/projectexplorer/images/compile_error.png")); + QPixmap pix = QPixmap(QLatin1String(":/core/images/compile_error_taskbar.png")); QPainter p(&pix); p.setPen(Qt::white); QFont font = p.font(); diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 8feffc6579..ab8a947940 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.cpp +++ b/src/plugins/cppeditor/cppdoxygen_test.cpp @@ -51,6 +51,9 @@ using namespace CPlusPlus; using namespace CppEditor::Internal; namespace { + +typedef QByteArray _; + /** * Encapsulates the whole process of setting up an editor, * pressing ENTER and checking the result. @@ -140,205 +143,149 @@ void TestCase::run(const QByteArray &expected, int undoCount) } } // anonymous namespace -void CppEditorPlugin::test_doxygen_comments_qt_style() +void CppEditorPlugin::test_doxygen_comments_data() { - const QByteArray given = + QTest::addColumn<QByteArray>("given"); + QTest::addColumn<QByteArray>("expected"); + + QTest::newRow("qt_style") << _( "bool preventFolding;\n" "/*!|\n" "int a;\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "/*!\n" " * \\brief a\n" " */\n" "int a;\n" - ; + ); - TestCase data(given); - data.run(expected); -} - -void CppEditorPlugin::test_doxygen_comments_qt_style_continuation() -{ - const QByteArray given = + QTest::newRow("qt_style_continuation") << _( "bool preventFolding;\n" "/*!\n" " * \\brief a|\n" " */\n" "int a;\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "/*!\n" " * \\brief a\n" - " *\n" + " * \n" " */\n" "int a;\n" - ; + ); - TestCase data(given); - data.run(expected); -} - -void CppEditorPlugin::test_doxygen_comments_java_style() -{ - const QByteArray given = + QTest::newRow("java_style") << _( "bool preventFolding;\n" "/**|\n" "int a;\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "/**\n" " * @brief a\n" " */\n" "int a;\n" - ; - - TestCase data(given); - data.run(expected); -} + ); -void CppEditorPlugin::test_doxygen_comments_java_style_continuation() -{ - const QByteArray given = + QTest::newRow("java_style_continuation") << _( "bool preventFolding;\n" "/**\n" " * @brief a|\n" " */\n" "int a;\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "/**\n" " * @brief a\n" - " *\n" + " * \n" " */\n" "int a;\n" - ; - - TestCase data(given); - data.run(expected); -} + ); -void CppEditorPlugin::test_doxygen_comments_cpp_styleA() -{ - const QByteArray given = + QTest::newRow("cpp_styleA") << _( "bool preventFolding;\n" "///|\n" "int a;\n" - ; - - const QByteArray expected = + ) << _( "bool preventFolding;\n" "///\n" "/// \\brief a\n" "///\n" "int a;\n" - ; - TestCase data(given); - data.run(expected); -} + ); -void CppEditorPlugin::test_doxygen_comments_cpp_styleB() -{ - const QByteArray given = + QTest::newRow("cpp_styleB") << _( "bool preventFolding;\n" "//!|\n" "int a;\n" - ; - - const QByteArray expected = + ) << _( "bool preventFolding;\n" "//!\n" "//! \\brief a\n" "//!\n" "int a;\n" - ; - TestCase data(given); - data.run(expected); -} + ); -void CppEditorPlugin::test_doxygen_comments_cpp_styleA_continuation() -{ - const QByteArray given = + QTest::newRow("cpp_styleA_continuation") << _( "bool preventFolding;\n" "///\n" "/// \\brief a|\n" "///\n" "int a;\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "///\n" "/// \\brief a\n" - "///\n" + "/// \n" "///\n" "int a;\n" - ; - - TestCase data(given); - data.run(expected); -} + ); -/// test cpp style doxygen comment when inside a indented scope -void CppEditorPlugin::test_doxygen_comments_cpp_styleA_indented() -{ - const QByteArray given = + /// test cpp style doxygen comment when inside a indented scope + QTest::newRow("cpp_styleA_indented") << _( " bool preventFolding;\n" " ///|\n" " int a;\n" - ; - - const QByteArray expected = + ) << _( " bool preventFolding;\n" " ///\n" " /// \\brief a\n" " ///\n" " int a;\n" - ; - TestCase data(given); - data.run(expected); -} + ); -/// test cpp style doxygen comment continuation when inside a indented scope -void CppEditorPlugin::test_doxygen_comments_cpp_styleA_indented_continuation() -{ - const QByteArray given = + /// test cpp style doxygen comment continuation when inside a indented scope + QTest::newRow("cpp_styleA_indented_continuation") << _( " bool preventFolding;\n" " ///\n" " /// \\brief a|\n" " ///\n" " int a;\n" - ; - const QByteArray expected = + ) << _( " bool preventFolding;\n" " ///\n" " /// \\brief a\n" - " ///\n" + " /// \n" " ///\n" " int a;\n" - ; + ); - TestCase data(given); - data.run(expected); -} - -void CppEditorPlugin::test_doxygen_comments_cpp_styleA_corner_case() -{ - const QByteArray given = + QTest::newRow("cpp_styleA_corner_case") << _( "bool preventFolding;\n" "///\n" "void d(); ///|\n" - ; - const QByteArray expected = + ) << _( "bool preventFolding;\n" "///\n" "void d(); ///\n" "\n" - ; + ); +} +void CppEditorPlugin::test_doxygen_comments() +{ + QFETCH(QByteArray, given); + QFETCH(QByteArray, expected); TestCase data(given); data.run(expected); } diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 331c66946c..63d5bc2e5b 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -430,6 +430,7 @@ bool handleDoxygenCppStyleContinuation(QTextCursor &cursor, const QString commentMarker = text.mid(offset, 3); newLine.append(commentMarker); + newLine.append(QLatin1Char(' ')); cursor.insertText(newLine); e->accept(); @@ -481,12 +482,13 @@ bool handleDoxygenContinuation(QTextCursor &cursor, c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset); newLine.append(c.selectedText()); if (text.at(offset) == QLatin1Char('/')) { - newLine.append(QLatin1String(" *")); + newLine.append(QLatin1String(" * ")); } else { int start = offset; while (offset < blockPos && text.at(offset) == QLatin1Char('*')) ++offset; newLine.append(QString(offset - start, QLatin1Char('*'))); + newLine.append(QLatin1Char(' ')); } cursor.insertText(newLine); e->accept(); diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index a05dc7bdd7..0989225538 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -116,16 +116,8 @@ private slots: void test_FollowSymbolUnderCursor_virtualFunctionCall(); void test_FollowSymbolUnderCursor_virtualFunctionCall_multipleDocuments(); - void test_doxygen_comments_qt_style(); - void test_doxygen_comments_qt_style_continuation(); - void test_doxygen_comments_java_style(); - void test_doxygen_comments_java_style_continuation(); - void test_doxygen_comments_cpp_styleA(); - void test_doxygen_comments_cpp_styleB(); - void test_doxygen_comments_cpp_styleA_indented(); - void test_doxygen_comments_cpp_styleA_continuation(); - void test_doxygen_comments_cpp_styleA_indented_continuation(); - void test_doxygen_comments_cpp_styleA_corner_case(); + void test_doxygen_comments_data(); + void test_doxygen_comments(); void test_quickfix_CompleteSwitchCaseStatement_basic1(); void test_quickfix_CompleteSwitchCaseStatement_basic2(); diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index 15a2429f82..3efde4c154 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -36,6 +36,7 @@ #include "cppincludehierarchymodel.h" #include "cppincludehierarchytreeview.h" +#include <coreplugin/editormanager/editormanager.h> #include <cplusplus/CppDocument.h> #include <utils/annotateditemdelegate.h> @@ -89,7 +90,8 @@ CppIncludeHierarchyWidget::CppIncludeHierarchyWidget() : m_treeView(0), m_model(0), m_delegate(0), - m_includeHierarchyInfoLabel(0) + m_includeHierarchyInfoLabel(0), + m_editor(0) { m_inspectedFile = new CppIncludeLabel(this); m_inspectedFile->setMargin(5); @@ -118,6 +120,9 @@ CppIncludeHierarchyWidget::CppIncludeHierarchyWidget() : setLayout(layout); connect(CppEditorPlugin::instance(), SIGNAL(includeHierarchyRequested()), SLOT(perform())); + connect(Core::EditorManager::instance(), SIGNAL(editorsClosed(QList<Core::IEditor *>)), + this, SLOT(editorsClosed(QList<Core::IEditor *>))); + } CppIncludeHierarchyWidget::~CppIncludeHierarchyWidget() @@ -128,15 +133,16 @@ void CppIncludeHierarchyWidget::perform() { showNoIncludeHierarchyLabel(); - CPPEditor *editor = qobject_cast<CPPEditor *>(Core::EditorManager::currentEditor()); - if (!editor) + m_editor = qobject_cast<CPPEditor *>(Core::EditorManager::currentEditor()); + if (!m_editor) return; - CPPEditorWidget *widget = qobject_cast<CPPEditorWidget *>(editor->widget()); + + CPPEditorWidget *widget = qobject_cast<CPPEditorWidget *>(m_editor->widget()); if (!widget) return; m_model->clear(); - m_model->buildHierarchy(widget->editorDocument()->filePath()); + m_model->buildHierarchy(m_editor, widget->editorDocument()->filePath()); if (m_model->isEmpty()) return; @@ -162,6 +168,14 @@ void CppIncludeHierarchyWidget::onItemClicked(const QModelIndex &index) Constants::CPPEDITOR_ID); } +void CppIncludeHierarchyWidget::editorsClosed(QList<Core::IEditor *> editors) +{ + foreach (Core::IEditor *editor, editors) { + if (m_editor == editor) + perform(); + } +} + void CppIncludeHierarchyWidget::showNoIncludeHierarchyLabel() { m_inspectedFile->hide(); diff --git a/src/plugins/cppeditor/cppincludehierarchy.h b/src/plugins/cppeditor/cppincludehierarchy.h index fd8d31cbde..cb7720db5e 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.h +++ b/src/plugins/cppeditor/cppincludehierarchy.h @@ -55,6 +55,7 @@ class FileName; namespace CppEditor { namespace Internal { +class CPPEditor; class CPPEditorWidget; class CppInclude; class CppIncludeLabel; @@ -73,6 +74,7 @@ public slots: private slots: void onItemClicked(const QModelIndex &index); + void editorsClosed(QList<Core::IEditor *> editors); private: void showNoIncludeHierarchyLabel(); @@ -84,6 +86,7 @@ private: Utils::AnnotatedItemDelegate *m_delegate; CppIncludeLabel *m_inspectedFile; QLabel *m_includeHierarchyInfoLabel; + CPPEditor *m_editor; }; // @todo: Pretty much the same design as the OutlineWidgetStack. Maybe we can generalize the diff --git a/src/plugins/cppeditor/cppincludehierarchy_test.cpp b/src/plugins/cppeditor/cppincludehierarchy_test.cpp index e52273743c..7e641e7494 100644 --- a/src/plugins/cppeditor/cppincludehierarchy_test.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy_test.cpp @@ -30,6 +30,8 @@ #include "cppeditorplugin.h" #include "cppincludehierarchymodel.h" +#include <coreplugin/editormanager/editormanager.h> +#include <cppeditor/cppeditor.h> #include <cpptools/cppmodelmanagerinterface.h> #include <utils/fileutils.h> @@ -47,6 +49,7 @@ class TestCase public: TestCase(const QList<QByteArray> &sourceList) : m_cmm(CppModelManagerInterface::instance()) + , m_editor(0) { QStringList filePaths; const int sourceListSize = sourceList.size(); @@ -82,22 +85,30 @@ public: ~TestCase() { + // Close editor + if (m_editor) + Core::EditorManager::closeEditor(m_editor, false); + m_cmm->GC(); QVERIFY(m_cmm->snapshot().isEmpty()); } - void run(int includesCount, int includedByCount) const + void run(int includesCount, int includedByCount) { const QString fileName = QDir::tempPath() + QLatin1String("/file1.h"); + m_editor = qobject_cast<CPPEditor *>(Core::EditorManager::openEditor(fileName)); + QVERIFY(m_editor); + CppIncludeHierarchyModel model(0); - model.buildHierarchy(fileName); + model.buildHierarchy(m_editor, fileName); QCOMPARE(model.rowCount(model.index(0, 0)), includesCount); QCOMPARE(model.rowCount(model.index(1, 0)), includedByCount); } private: CppModelManagerInterface *m_cmm; + CPPEditor *m_editor; }; } diff --git a/src/plugins/cppeditor/cppincludehierarchymodel.cpp b/src/plugins/cppeditor/cppincludehierarchymodel.cpp index 7709bed2d5..06fe941cd9 100644 --- a/src/plugins/cppeditor/cppincludehierarchymodel.cpp +++ b/src/plugins/cppeditor/cppincludehierarchymodel.cpp @@ -35,10 +35,12 @@ #include <cplusplus/CppDocument.h> #include <cppeditor/cppeditor.h> #include <cpptools/cppmodelmanagerinterface.h> +#include <cpptools/cpptoolseditorsupport.h> #include <QSet> using namespace CPlusPlus; +using namespace CppTools; namespace CppEditor { namespace Internal { @@ -48,7 +50,7 @@ CppIncludeHierarchyModel::CppIncludeHierarchyModel(QObject *parent) , m_rootItem(new CppIncludeHierarchyItem(QString())) , m_includesItem(new CppIncludeHierarchyItem(tr("Includes"), m_rootItem)) , m_includedByItem(new CppIncludeHierarchyItem(tr("Included by"), m_rootItem)) - + , m_editor(0) { m_rootItem->appendChild(m_includesItem); m_rootItem->appendChild(m_includedByItem); @@ -199,6 +201,15 @@ void CppIncludeHierarchyModel::clear() endResetModel(); } +void CppIncludeHierarchyModel::buildHierarchy(CPPEditor *editor, const QString &filePath) +{ + m_editor = editor; + beginResetModel(); + buildHierarchyIncludes(filePath); + buildHierarchyIncludedBy(filePath); + endResetModel(); +} + bool CppIncludeHierarchyModel::hasChildren(const QModelIndex &parent) const { if (!parent.isValid()) @@ -210,14 +221,6 @@ bool CppIncludeHierarchyModel::hasChildren(const QModelIndex &parent) const return parentItem->hasChildren(); } -void CppIncludeHierarchyModel::buildHierarchy(const QString &filePath) -{ - beginResetModel(); - buildHierarchyIncludes(filePath); - buildHierarchyIncludedBy(filePath); - endResetModel(); -} - bool CppIncludeHierarchyModel::isEmpty() const { return !m_includesItem->hasChildren() && !m_includedByItem->hasChildren(); @@ -233,7 +236,11 @@ void CppIncludeHierarchyModel::buildHierarchyIncludes_helper(const QString &file CppIncludeHierarchyItem *parent, QSet<QString> *cyclic, bool recursive) { - const Snapshot &snapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); + if (!m_editor) + return; + + CppModelManagerInterface *cppMM = CppModelManagerInterface::instance(); + const Snapshot &snapshot = cppMM->cppEditorSupport(m_editor)->snapshotUpdater()->snapshot(); Document::Ptr doc = snapshot.document(filePath); if (!doc) return; diff --git a/src/plugins/cppeditor/cppincludehierarchymodel.h b/src/plugins/cppeditor/cppincludehierarchymodel.h index cc9cbef050..afb4c7ceb8 100644 --- a/src/plugins/cppeditor/cppincludehierarchymodel.h +++ b/src/plugins/cppeditor/cppincludehierarchymodel.h @@ -41,9 +41,14 @@ enum ItemRole { } // Anonymous +namespace Core { +class IEditor; +} + namespace CppEditor { namespace Internal { +class CPPEditor; class CppIncludeHierarchyItem; class CppIncludeHierarchyModel : public QAbstractItemModel @@ -63,7 +68,7 @@ public: bool hasChildren(const QModelIndex &parent) const; void clear(); - void buildHierarchy(const QString &filePath); + void buildHierarchy(CPPEditor *editor, const QString &filePath); bool isEmpty() const; private: @@ -77,6 +82,7 @@ private: CppIncludeHierarchyItem *m_rootItem; CppIncludeHierarchyItem *m_includesItem; CppIncludeHierarchyItem *m_includedByItem; + CPPEditor *m_editor; }; } // namespace Internal diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp index 89e787d081..85c304cad6 100644 --- a/src/plugins/cpptools/cpptoolseditorsupport.cpp +++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp @@ -169,6 +169,7 @@ CppEditorSupport::~CppEditorSupport() m_highlighter.cancel(); m_futureSemanticInfo.cancel(); + m_documentParser.waitForFinished(); m_highlighter.waitForFinished(); m_futureSemanticInfo.waitForFinished(); } diff --git a/src/plugins/debugger/debuggerstartparameters.h b/src/plugins/debugger/debuggerstartparameters.h index 2b46a94b93..f928fd40e3 100644 --- a/src/plugins/debugger/debuggerstartparameters.h +++ b/src/plugins/debugger/debuggerstartparameters.h @@ -64,6 +64,7 @@ public: attachPID(-1), useTerminal(false), breakOnMain(false), + continueAfterAttach(false), multiProcess(false), languages(AnyLanguage), qmlServerAddress(QLatin1String("127.0.0.1")), @@ -102,6 +103,7 @@ public: qint64 attachPID; bool useTerminal; bool breakOnMain; + bool continueAfterAttach; bool multiProcess; DebuggerLanguages languages; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index ae83e0801f..882dfaac9d 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3603,6 +3603,7 @@ void GdbEngine::handleStackSelectThread(const GdbResponse &) void GdbEngine::reloadFullStack() { PENDING_DEBUG("RELOAD FULL STACK"); + resetLocation(); postCommand("-stack-list-frames", Discardable, CB(handleStackListFrames), QVariant::fromValue<StackCookie>(StackCookie(true, true))); } @@ -4307,10 +4308,8 @@ void GdbEngine::assignValueInDebugger(const WatchData *data, + value.toString().toUtf8().toHex(); postCommand(cmd, Discardable, CB(handleVarAssign)); } else { - postCommand("-var-delete assign"); - postCommand("-var-create assign * " + expression.toLatin1()); - postCommand("-var-assign assign " + - GdbMi::escapeCString(value.toString().toLatin1()), + postCommand("set variable (" + expression.toLatin1() + ")=" + + GdbMi::escapeCString(value.toString().toLatin1()), Discardable, CB(handleVarAssign)); } } diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 7e740f0bd8..c2c7b69141 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -83,7 +83,7 @@ static QByteArray tooltipIName(const QString &exp) /////////////////////////////////////////////////////////////////////// LldbEngine::LldbEngine(const DebuggerStartParameters &startParameters) - : DebuggerEngine(startParameters) + : DebuggerEngine(startParameters), m_continueAtNextSpontaneousStop(false) { m_lastAgentId = 0; m_lastToken = 0; @@ -197,13 +197,19 @@ void LldbEngine::setupInferior() cmd.arg("executable", QFileInfo(sp.executable).absoluteFilePath()); cmd.arg("startMode", sp.startMode); // directly relying on this is brittle wrt. insertions, so check it here cmd.arg("processArgs", sp.processArgs); - cmd.arg("attachPid", ((sp.startMode == AttachCrashedExternal || sp.startMode == AttachExternal) - ? sp.attachPID : 0)); + + QTC_CHECK(!sp.attachPID || (sp.startMode == AttachCrashedExternal + || sp.startMode == AttachExternal)); + cmd.arg("attachPid", sp.attachPID); cmd.arg("sysRoot", sp.sysRoot); cmd.arg("remoteChannel", ((sp.startMode == AttachToRemoteProcess || sp.startMode == AttachToRemoteServer) ? sp.remoteChannel : QString())); cmd.arg("platform", sp.platform); + QTC_CHECK(!sp.continueAfterAttach || (sp.startMode == AttachToRemoteProcess + || sp.startMode == AttachExternal + || sp.startMode == AttachToRemoteServer)); + m_continueAtNextSpontaneousStop = false; runCommand(cmd); updateLocals(); // update display options } @@ -1014,9 +1020,13 @@ void LldbEngine::refreshState(const GdbMi &reportedState) notifyInferiorRunOk(); else if (newState == "inferiorrunfailed") notifyInferiorRunFailed(); - else if (newState == "stopped") + else if (newState == "stopped") { notifyInferiorSpontaneousStop(); - else if (newState == "inferiorstopok") + if (m_continueAtNextSpontaneousStop) { + m_continueAtNextSpontaneousStop = false; + continueInferior(); + } + } else if (newState == "inferiorstopok") notifyInferiorStopOk(); else if (newState == "inferiorstopfailed") notifyInferiorStopFailed(); @@ -1028,9 +1038,11 @@ void LldbEngine::refreshState(const GdbMi &reportedState) notifyEngineRunFailed(); else if (newState == "inferiorsetupok") notifyInferiorSetupOk(); - else if (newState == "enginerunandinferiorrunok") + else if (newState == "enginerunandinferiorrunok") { + if (startParameters().continueAfterAttach) + m_continueAtNextSpontaneousStop = true; notifyEngineRunAndInferiorRunOk(); - else if (newState == "enginerunandinferiorstopok") + } else if (newState == "enginerunandinferiorstopok") notifyEngineRunAndInferiorStopOk(); else if (newState == "inferiorshutdownok") notifyInferiorShutdownOk(); diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 22d05cb293..dc4e174768 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -201,6 +201,7 @@ private: // FIXME: Make generic. int m_lastAgentId; int m_lastToken; + int m_continueAtNextSpontaneousStop; QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents; QMap<QPointer<MemoryAgent>, int> m_memoryAgents; QHash<int, QPointer<QObject> > m_memoryAgentTokens; diff --git a/src/plugins/debugger/threadshandler.cpp b/src/plugins/debugger/threadshandler.cpp index 4996d34752..63b1e1375e 100644 --- a/src/plugins/debugger/threadshandler.cpp +++ b/src/plugins/debugger/threadshandler.cpp @@ -36,12 +36,11 @@ #include <utils/qtcassert.h> #include <QDebug> -#include <QSortFilterProxyModel> namespace Debugger { namespace Internal { -void mergeThreadData(ThreadData &data, const ThreadData &other) +static void mergeThreadData(ThreadData &data, const ThreadData &other) { if (!other.core.isEmpty()) data.core = other.core; @@ -67,6 +66,40 @@ void mergeThreadData(ThreadData &data, const ThreadData &other) data.lineNumber = other.lineNumber; } +static QVariant threadPart(const ThreadData &thread, int column) +{ + switch (column) { + case ThreadData::IdColumn: + return thread.id.raw(); + case ThreadData::FunctionColumn: + return thread.function; + case ThreadData::FileColumn: + return thread.fileName.isEmpty() ? thread.module : thread.fileName; + case ThreadData::LineColumn: + return thread.lineNumber >= 0 + ? QString::number(thread.lineNumber) : QString(); + case ThreadData::AddressColumn: + return thread.address > 0 + ? QLatin1String("0x") + QString::number(thread.address, 16) + : QString(); + case ThreadData::CoreColumn: + return thread.core; + case ThreadData::StateColumn: + return thread.state; + case ThreadData::TargetIdColumn: + if (thread.targetId.startsWith(QLatin1String("Thread "))) + return thread.targetId.mid(7); + return thread.targetId; + case ThreadData::NameColumn: + return thread.name; + case ThreadData::DetailsColumn: + return thread.details; + case ThreadData::ComboNameColumn: + return QString::fromLatin1("#%1 %2").arg(thread.id.raw()).arg(thread.name); + } + return QVariant(); +} + //////////////////////////////////////////////////////////////////////// // // ThreadsHandler @@ -137,8 +170,6 @@ ThreadsHandler::ThreadsHandler() { m_resetLocationScheduled = false; setObjectName(QLatin1String("ThreadsModel")); - m_proxyModel = new QSortFilterProxyModel(this); - m_proxyModel->setSourceModel(this); } int ThreadsHandler::currentThreadIndex() const @@ -168,35 +199,7 @@ QVariant ThreadsHandler::data(const QModelIndex &index, int role) const switch (role) { case Qt::DisplayRole: - switch (index.column()) { - case ThreadData::IdColumn: - return thread.id.raw(); - case ThreadData::FunctionColumn: - return thread.function; - case ThreadData::FileColumn: - return thread.fileName.isEmpty() ? thread.module : thread.fileName; - case ThreadData::LineColumn: - return thread.lineNumber >= 0 - ? QString::number(thread.lineNumber) : QString(); - case ThreadData::AddressColumn: - return thread.address > 0 - ? QLatin1String("0x") + QString::number(thread.address, 16) - : QString(); - case ThreadData::CoreColumn: - return thread.core; - case ThreadData::StateColumn: - return thread.state; - case ThreadData::TargetIdColumn: - if (thread.targetId.startsWith(QLatin1String("Thread "))) - return thread.targetId.mid(7); - return thread.targetId; - case ThreadData::NameColumn: - return thread.name; - case ThreadData::DetailsColumn: - return thread.details; - case ThreadData::ComboNameColumn: - return QString::fromLatin1("#%1 %2").arg(thread.id.raw()).arg(thread.name); - } + return threadPart(thread, index.column()); case Qt::ToolTipRole: return threadToolTip(thread); case Qt::DecorationRole: @@ -250,6 +253,33 @@ Qt::ItemFlags ThreadsHandler::flags(const QModelIndex &index) const return stopped ? QAbstractTableModel::flags(index) : Qt::ItemFlags(0); } +struct Sorter +{ + Sorter(int column, Qt::SortOrder order) + : m_column(column), m_order(order) + {} + + bool operator()(const ThreadData &t1, const ThreadData &t2) const + { + const QVariant v1 = threadPart(t1, m_column); + const QVariant v2 = threadPart(t2, m_column); + if (v1 == v2) + return false; + // FIXME: Use correct toXXX(); + return (v1.toString() < v2.toString()) ^ (m_order == Qt::DescendingOrder); + } + + int m_column; + Qt::SortOrder m_order; +}; + +void ThreadsHandler::sort(int column, Qt::SortOrder order) +{ + layoutAboutToBeChanged(); + qSort(m_threads.begin(), m_threads.end(), Sorter(column, order)); + layoutChanged(); +} + ThreadId ThreadsHandler::currentThread() const { return m_currentId; @@ -490,7 +520,7 @@ void ThreadsHandler::resetLocation() QAbstractItemModel *ThreadsHandler::model() { - return m_proxyModel; + return this; } } // namespace Internal diff --git a/src/plugins/debugger/threadshandler.h b/src/plugins/debugger/threadshandler.h index 1bf9ef6da9..30f458ee36 100644 --- a/src/plugins/debugger/threadshandler.h +++ b/src/plugins/debugger/threadshandler.h @@ -30,14 +30,10 @@ #ifndef THREADSHANDLER_H #define THREADSHANDLER_H -#include <QAbstractTableModel> -#include <QIcon> - #include "threaddata.h" -QT_BEGIN_NAMESPACE -class QSortFilterProxyModel; -QT_END_NAMESPACE +#include <QAbstractTableModel> +#include <QIcon> //////////////////////////////////////////////////////////////////////// // @@ -92,6 +88,7 @@ private: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; Qt::ItemFlags flags(const QModelIndex &index) const; + void sort(int, Qt::SortOrder); void updateThreadBox(); void threadDataChanged(ThreadId id); @@ -101,7 +98,6 @@ private: const QIcon m_emptyIcon; bool m_resetLocationScheduled; - QSortFilterProxyModel *m_proxyModel; }; } // namespace Internal diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 5c3050f81d..388e60c7eb 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -784,6 +784,12 @@ static const QMap<QString, int> &vimKeyNames() return k; } +bool isControlModifier(const Qt::KeyboardModifiers &mods) +{ + static const int ctrl = HostOsInfo::controlModifier(); + return (mods & ctrl) == ctrl; +} + Range::Range() : beginPos(-1), endPos(-1), rangemode(RangeCharMode) @@ -938,12 +944,12 @@ public: bool is(int c) const { - return m_xkey == c && m_modifiers != HostOsInfo::controlModifier(); + return m_xkey == c && !isControl(); } bool isControl() const { - return m_modifiers == HostOsInfo::controlModifier(); + return isControlModifier(m_modifiers); } bool isControl(int c) const @@ -2215,7 +2221,7 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev) } // We are interested in overriding most Ctrl key combinations. - if (mods == HostOsInfo::controlModifier() + if (isControlModifier(mods) && !config(ConfigPassControlKey).toBool() && ((key >= Key_A && key <= Key_Z && key != Key_K) || key == Key_BracketLeft || key == Key_BracketRight)) { diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index c37ff7c12e..1b3d521559 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -1443,7 +1443,7 @@ void GitClient::show(const QString &source, const QString &id, GitDiffHandler *handler = new GitDiffHandler(diffEditor, gitBinaryPath(), - workingDirectory, + findRepositoryForDirectory(workingDirectory), processEnvironment(), settings()->intValue(GitSettings::timeoutKey)); handler->show(id); diff --git a/src/plugins/ios/iosdebugsupport.cpp b/src/plugins/ios/iosdebugsupport.cpp index 0d761320b9..32d69447af 100644 --- a/src/plugins/ios/iosdebugsupport.cpp +++ b/src/plugins/ios/iosdebugsupport.cpp @@ -84,6 +84,8 @@ RunControl *IosDebugSupport::createDebugRunControl(IosRunConfiguration *runConfi } params.displayName = runConfig->appName(); params.remoteSetupNeeded = true; + if (!params.breakOnMain) + params.continueAfterAttach = true; Debugger::DebuggerRunConfigurationAspect *aspect = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>(); diff --git a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp index bb93d20043..169afe0cac 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp @@ -249,9 +249,6 @@ void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line, if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH ="))) return; proFile << nextLine << endl; - } else if (line.contains(QLatin1String("# HARMATTAN_BOOSTABLE"))) { - QString nextLine = proFileTemplate.readLine(); // eats '# CONFIG += qdeclarative-boostable' - proFile << nextLine << endl; } } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 9ed39b9f63..677d13bd81 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -119,6 +119,9 @@ void FormEditorItem::setHighlightBoundingRect(bool highlight) void FormEditorItem::blurContent(bool blurContent) { + if (!scene()) + return; + if (m_blurContent != blurContent) { m_blurContent = blurContent; update(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 8358c18608..1205aae5ae 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -510,7 +510,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) if ((valid || entry.forceImport()) && (entry.requiredImport().isEmpty() - || model->hasImport(entryToImport(entry), true, false) || entry.forceImport())) { + || model->hasImport(entryToImport(entry), true, true) || entry.forceImport())) { QString itemSectionName = entry.category(); ItemLibrarySectionModel *sectionModel; ItemLibraryItemModel *itemModel; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index ad7edd745c..99e92d88ea 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -367,13 +367,10 @@ void ItemLibraryWidget::startDragAndDrop(int itemLibId) { QMimeData *mimeData = m_itemLibraryModel->getMimeData(itemLibId); QDrag *drag = new QDrag(this); - const QImage image = qvariant_cast<QImage>(mimeData->imageData()); drag->setPixmap(m_itemLibraryModel->getIcon(itemLibId).pixmap(32, 32)); drag->setMimeData(mimeData); - QQuickItem *rootItem = qobject_cast<QQuickItem*>(m_itemsView->rootObject()); - drag->exec(); } diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 8e4334d87c..3d6e903d93 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -99,6 +99,8 @@ public: QList<Import> possibleImports() const; QList<Import> usedImports() const; void changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importsToBeRemoved); + void setPossibleImports(const QList<Import> &possibleImports); + void setUsedImports(const QList<Import> &usedImports); bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false); QString pathForImport(const Import &import); diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 2405ef8412..bf845dcc81 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -75,6 +75,8 @@ static inline QStringList importPaths() { static inline bool checkIfDerivedFromItem(const QString &fileName) { + return true; + QmlJS::Snapshot snapshot; @@ -101,7 +103,7 @@ static inline bool checkIfDerivedFromItem(const QString &fileName) snapshot.insert(document); - QmlJS::Link link(snapshot, modelManager->importPaths(), QmlJS::ModelManagerInterface::instance()->builtins(document)); + QmlJS::Link link(snapshot, modelManager->defaultVContext(), QmlJS::ModelManagerInterface::instance()->builtins(document)); QList<QmlJS::DiagnosticMessage> diagnosticLinkMessages; QmlJS::ContextPtr context = link(document, &diagnosticLinkMessages); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index df895a4048..b2c2019bb7 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1731,25 +1731,12 @@ QList<Import> Model::imports() const QList<Import> Model::possibleImports() const { - QList<Import> possibleImportList; - - possibleImportList.append(Import::createLibraryImport("QtQuick.Controls", "1.0")); - possibleImportList.append(Import::createLibraryImport("QtQuick.Layouts", "1.0")); - possibleImportList.append(Import::createLibraryImport("QtQuick.Window", "2.0")); - - return possibleImportList; + return d->m_possibleImportList; } QList<Import> Model::usedImports() const { - QList<Import> usedImportList; - - usedImportList.append(Import::createLibraryImport("QtQuick", "1.0")); - usedImportList.append(Import::createLibraryImport("QtQuick", "1.1")); - usedImportList.append(Import::createLibraryImport("QtQuick", "2.0")); - usedImportList.append(Import::createLibraryImport("QtQuick", "2.1")); - - return usedImportList; + return d->m_usedImportList; } void Model::changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importsToBeRemoved) @@ -1757,6 +1744,16 @@ void Model::changeImports(const QList<Import> &importsToBeAdded, const QList<Imp d->changeImports(importsToBeAdded, importsToBeRemoved); } +void Model::setPossibleImports(const QList<Import> &possibleImports) +{ + d->m_possibleImportList = possibleImports; +} + +void Model::setUsedImports(const QList<Import> &usedImports) +{ + d->m_usedImportList = usedImports; +} + static bool compareVersions(const QString &version1, const QString &version2, bool allowHigherVersion) { diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 7dcc150491..8b2653ed9c 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -236,6 +236,8 @@ private: Model *m_q; MetaInfo m_metaInfo; QList<Import> m_imports; + QList<Import> m_possibleImportList; + QList<Import> m_usedImportList; QList<QWeakPointer<AbstractView> > m_viewList; QList<InternalNodePointer> m_selectedInternalNodeList; QHash<QString,InternalNodePointer> m_idNodeHash; diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 20eec5c8e1..7b0b170d23 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -48,6 +48,7 @@ #include <qmljs/qmljsutils.h> #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsqrcparser.h> +#include <qmljs/qmljsinterpreter.h> #include <utils/qtcassert.h> @@ -306,10 +307,10 @@ class ReadingContext { public: ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc, - const QStringList importPaths) + const ViewerContext &vContext) : m_snapshot(snapshot) , m_doc(doc) - , m_link(snapshot, importPaths, + , m_link(snapshot, vContext, QmlJS::ModelManagerInterface::instance()->builtins(doc)) , m_context(m_link(doc, &m_diagnosticLinkMessages)) , m_scopeChain(doc, m_context) @@ -726,6 +727,67 @@ void TextToModelMerger::setupImports(const Document::Ptr &doc, differenceHandler.importAbsentInQMl(import); } +void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext) +{ + QList<Import> possibleImports; + + QSet<ImportKey> possibleImportKeys = snapshot.importDependencies()->libraryImports(viewContext); + + QHash<QString, ImportKey> filteredPossibleImportKeys; + foreach (const ImportKey &importKey, possibleImportKeys) { + if (!filteredPossibleImportKeys.contains(importKey.path()) + || filteredPossibleImportKeys.value(importKey.path()).majorVersion < importKey.majorVersion + || (filteredPossibleImportKeys.value(importKey.path()).majorVersion == importKey.majorVersion + && filteredPossibleImportKeys.value(importKey.path()).minorVersion < importKey.minorVersion)) + filteredPossibleImportKeys.insert(importKey.path(), importKey); + } + + filteredPossibleImportKeys.remove(QLatin1String("<cpp>")); + filteredPossibleImportKeys.remove(QLatin1String("QML")); + filteredPossibleImportKeys.remove(QLatin1String("QtQml")); + filteredPossibleImportKeys.remove(QLatin1String("QtQuick/PrivateWidgets")); + + QList<QmlJS::Import> allImports = m_scopeChain->context()->imports(m_document.data())->all(); + + foreach (const QmlJS::Import &import, allImports) { + filteredPossibleImportKeys.remove(import.info.path()); + } + + foreach (const ImportKey &importKey, filteredPossibleImportKeys) { + QString libraryName = importKey.splitPath.join(QLatin1Char('.')); + QString version = QString(QStringLiteral("%1.%2").arg(importKey.majorVersion).arg(importKey.minorVersion)); + possibleImports.append(Import::createLibraryImport(libraryName, version)); + } + + if ( m_rewriterView->isAttached()) + m_rewriterView->model()->setPossibleImports(possibleImports); +} + +void TextToModelMerger::setupUsedImports() +{ + QList<QmlJS::Import> allImports = m_scopeChain->context()->imports(m_document.data())->all(); + + QList<Import> usedImports; + + foreach (const QmlJS::Import &import, allImports) { + if (import.used) { + if (import.info.type() == ImportType::Library) { + usedImports.append(Import::createLibraryImport(import.info.name(), import.info.version().toString(), import.info.as())); + } else if (import.info.type() == ImportType::Directory || import.info.type() == ImportType::File) { + usedImports.append(Import::createFileImport(import.info.name(), import.info.version().toString(), import.info.as())); + } + } + } + // even if not explicitly used we probably want to keep QtQuick imports + usedImports.append(Import::createLibraryImport("QtQuick", "1.0")); + usedImports.append(Import::createLibraryImport("QtQuick", "1.1")); + usedImports.append(Import::createLibraryImport("QtQuick", "2.0")); + usedImports.append(Import::createLibraryImport("QtQuick", "2.1")); + + if (m_rewriterView->isAttached()) + m_rewriterView->model()->setUsedImports(usedImports); +} + bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceHandler) { // qDebug() << "TextToModelMerger::load with data:" << data; @@ -751,7 +813,11 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH return false; } snapshot.insert(doc); - ReadingContext ctxt(snapshot, doc, importPaths); + QmlJS::ViewerContext vContext; + vContext.language = QmlJS::Language::Qml; + vContext.paths = importPaths; + vContext.flags = QmlJS::ViewerContext::Complete; + ReadingContext ctxt(snapshot, doc, vContext); m_scopeChain = QSharedPointer<const ScopeChain>( new ScopeChain(ctxt.scopeChain())); m_document = doc; @@ -764,6 +830,8 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH } setupImports(doc, differenceHandler); + setupPossibleImports(snapshot, vContext); + setupUsedImports(); if (m_rewriterView->model()->imports().isEmpty()) { const QmlJS::DiagnosticMessage diagnosticMessage(QmlJS::Severity::Error, AST::SourceLocation(0, 0, 0, 0), QCoreApplication::translate("QmlDesigner::TextToModelMerger", "No import statements found")); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index 8c98376db2..d82bf851fe 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -59,6 +59,8 @@ public: bool isActive() const; void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler); + void setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext); + void setupUsedImports(); bool load(const QString &data, DifferenceHandler &differenceHandler); RewriterView *view() const diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index a8b7d70914..48a16d0de0 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -831,7 +831,7 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future, QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); - Link link(snapshot, modelManager->importPaths(), modelManager->builtins(doc)); + Link link(snapshot, modelManager->defaultVContext(), modelManager->builtins(doc)); ContextPtr context = link(); ScopeChain scopeChain(doc, context); diff --git a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp index 2e4a73b57e..30b3422cc5 100644 --- a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp +++ b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp @@ -122,7 +122,7 @@ QmlJSTools::SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const QmlJS::D ModelManagerInterface *modelManager = ModelManagerInterface::instance(); - Link link(semanticInfo.snapshot, modelManager->importPaths(), modelManager->builtins(doc)); + Link link(semanticInfo.snapshot, modelManager->defaultVContext(), modelManager->builtins(doc)); semanticInfo.context = link(doc, &semanticInfo.semanticMessages); ScopeChain *scopeChain = new ScopeChain(doc, semanticInfo.context); diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index 42f78bec19..40d19319aa 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -92,13 +92,13 @@ static QList<ProjectExplorer::Task> convertToTasks(const QList<StaticAnalysis::M void QmlTaskManager::collectMessages( QFutureInterface<FileErrorMessages> &future, Snapshot snapshot, QList<ModelManagerInterface::ProjectInfo> projectInfos, - QStringList importPaths, bool updateSemantic) + ViewerContext vContext, bool updateSemantic) { foreach (const ModelManagerInterface::ProjectInfo &info, projectInfos) { QHash<QString, QList<DiagnosticMessage> > linkMessages; ContextPtr context; if (updateSemantic) { - Link link(snapshot, importPaths, snapshot.libraryInfo(info.qtImportsPath)); + Link link(snapshot, vContext, snapshot.libraryInfo(info.qtImportsPath)); context = link(&linkMessages); } @@ -161,7 +161,7 @@ void QmlTaskManager::updateMessagesNow(bool updateSemantic) QFuture<FileErrorMessages> future = QtConcurrent::run<FileErrorMessages>( &collectMessages, modelManager->newestSnapshot(), modelManager->projectInfos(), - modelManager->importPaths(), updateSemantic); + modelManager->defaultVContext(), updateSemantic); m_messageCollector.setFuture(future); } diff --git a/src/plugins/qmljseditor/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h index 986a2e65f3..f7aaf0ab74 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.h +++ b/src/plugins/qmljseditor/qmltaskmanager.h @@ -85,7 +85,7 @@ private: static void collectMessages(QFutureInterface<FileErrorMessages> &future, QmlJS::Snapshot snapshot, QList<QmlJS::ModelManagerInterface::ProjectInfo> projectInfos, - QStringList importPaths, + QmlJS::ViewerContext vContext, bool updateSemantic); private: diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp index 0d2bd263f5..13c88da73f 100644 --- a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp +++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp @@ -609,14 +609,12 @@ static LanguageUtils::FakeMetaObject::Ptr buildFakeMetaObject( BaseClass *base = klass->baseClassAt(0); if (!base->name()) return fmo; - const QString baseClassName = namePrinter.prettyName(base->name()); fmo->setSuperclassName(baseClassName); Class *baseClass = lookupClass(baseClassName, klass, typeOf); if (!baseClass) return fmo; - buildFakeMetaObject(baseClass, fakeMetaObjects, typeOf); } @@ -735,8 +733,10 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) // convert to list of FakeMetaObject::ConstPtr m_exportedTypes.reserve(fakeMetaObjects.size()); - foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) + foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) { + fmo->updateFingerprint(); m_exportedTypes += fmo; + } } QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 46beb6a345..61470f40c2 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -49,6 +49,7 @@ #include <qtsupport/qmldumptool.h> #include <qtsupport/qtsupportconstants.h> #include <utils/hostosinfo.h> +#include <utils/function.h> #include <extensionsystem/pluginmanager.h> #include <QDir> @@ -360,7 +361,7 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles, QFuture<void> result = QtConcurrent::run(&ModelManager::parse, workingCopy(), sourceFiles, - this, + this, Language::Qml, emitDocumentOnDiskChanged); if (m_synchronizer.futures().size() > 10) { @@ -386,7 +387,7 @@ void ModelManager::fileChangedOnDisk(const QString &path) { QtConcurrent::run(&ModelManager::parse, workingCopy(), QStringList() << path, - this, true); + this, Language::Unknown, true); } void ModelManager::removeFiles(const QStringList &files) @@ -700,7 +701,8 @@ static bool findNewQmlLibraryInPath(const QString &path, ModelManager *modelManager, QStringList *importedFiles, QSet<QString> *scannedPaths, - QSet<QString> *newLibraries) + QSet<QString> *newLibraries, + bool ignoreMissing) { // if we know there is a library, done const LibraryInfo &existingInfo = snapshot.libraryInfo(path); @@ -715,8 +717,10 @@ static bool findNewQmlLibraryInPath(const QString &path, const QDir dir(path); QFile qmldirFile(dir.filePath(QLatin1String("qmldir"))); if (!qmldirFile.exists()) { - LibraryInfo libraryInfo(LibraryInfo::NotFound); - modelManager->updateLibraryInfo(path, libraryInfo); + if (!ignoreMissing) { + LibraryInfo libraryInfo(LibraryInfo::NotFound); + modelManager->updateLibraryInfo(path, libraryInfo); + } return false; } @@ -765,18 +769,18 @@ static void findNewQmlLibrary( QString::number(version.minorVersion())); findNewQmlLibraryInPath( libraryPath, snapshot, modelManager, - importedFiles, scannedPaths, newLibraries); + importedFiles, scannedPaths, newLibraries, false); libraryPath = QString::fromLatin1("%1.%2").arg( path, QString::number(version.majorVersion())); findNewQmlLibraryInPath( libraryPath, snapshot, modelManager, - importedFiles, scannedPaths, newLibraries); + importedFiles, scannedPaths, newLibraries, false); findNewQmlLibraryInPath( path, snapshot, modelManager, - importedFiles, scannedPaths, newLibraries); + importedFiles, scannedPaths, newLibraries, false); } static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snapshot, @@ -785,7 +789,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap { // scan current dir findNewQmlLibraryInPath(doc->path(), snapshot, modelManager, - importedFiles, scannedPaths, newLibraries); + importedFiles, scannedPaths, newLibraries, false); // scan dir and lib imports const QStringList importPaths = modelManager->importPaths(); @@ -793,7 +797,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap if (import.type() == ImportType::Directory) { const QString targetPath = import.path(); findNewQmlLibraryInPath(targetPath, snapshot, modelManager, - importedFiles, scannedPaths, newLibraries); + importedFiles, scannedPaths, newLibraries, false); } if (import.type() == ImportType::Library) { @@ -808,24 +812,18 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap } } -void ModelManager::parse(QFutureInterface<void> &future, - WorkingCopy workingCopy, - QStringList files, - ModelManager *modelManager, - bool emitDocChangedOnDisk) +void ModelManager::parseLoop(QSet<QString> &scannedPaths, + QSet<QString> &newLibraries, + WorkingCopy workingCopy, + QStringList files, + ModelManager *modelManager, + Language::Enum mainLanguage, + bool emitDocChangedOnDisk, + Utils::function<bool(qreal)> reportProgress) { - int progressRange = files.size(); - future.setProgressRange(0, progressRange); - - // paths we have scanned for files and added to the files list - QSet<QString> scannedPaths; - // libraries we've found while scanning imports - QSet<QString> newLibraries; - for (int i = 0; i < files.size(); ++i) { - if (future.isCanceled()) - break; - future.setProgressValue(qreal(i) / files.size() * progressRange); + if (!reportProgress(qreal(i) / files.size())) + return; const QString fileName = files.at(i); @@ -835,7 +833,9 @@ void ModelManager::parse(QFutureInterface<void> &future, modelManager->updateQrcFile(fileName); continue; } - + if (language == Language::Qml + && (mainLanguage == Language::QmlQtQuick1 || Language::QmlQtQuick2)) + language = mainLanguage; QString contents; int documentRevision = 0; @@ -878,7 +878,116 @@ void ModelManager::parse(QFutureInterface<void> &future, if (emitDocChangedOnDisk) modelManager->emitDocumentChangedOnDisk(doc); } +} + +class FutureReporter +{ +public: + FutureReporter(QFutureInterface<void> &future, int multiplier = 100, int base = 0) + :future(future), multiplier(multiplier), base(base) + { } + bool operator()(qreal val) + { + if (future.isCanceled()) + return false; + future.setProgressValue(int(base + multiplier * val)); + return true; + } +private: + QFutureInterface<void> &future; + int multiplier; + int base; +}; + +void ModelManager::parse(QFutureInterface<void> &future, + WorkingCopy workingCopy, + QStringList files, + ModelManager *modelManager, + Language::Enum mainLanguage, + bool emitDocChangedOnDisk) +{ + FutureReporter reporter(future); + future.setProgressRange(0, 100); + + // paths we have scanned for files and added to the files list + QSet<QString> scannedPaths; + // libraries we've found while scanning imports + QSet<QString> newLibraries; + parseLoop(scannedPaths, newLibraries, workingCopy, files, modelManager, mainLanguage, + emitDocChangedOnDisk, reporter); + future.setProgressValue(100); +} + +struct ScanItem { + QString path; + int depth; + ScanItem(QString path = QString(), int depth = 0) + : path(path), depth(depth) + { } +}; + +void ModelManager::importScan(QFutureInterface<void> &future, + ModelManagerInterface::WorkingCopy workingCopy, + QStringList paths, ModelManager *modelManager, + Language::Enum language, + bool emitDocChangedOnDisk) +{ + // paths we have scanned for files and added to the files list + QSet<QString> scannedPaths = modelManager->m_scannedPaths; + // libraries we've found while scanning imports + QSet<QString> newLibraries; + QVector<ScanItem> pathsToScan; + pathsToScan.reserve(paths.size()); + foreach (const QString &path, paths) { + QString cPath = QDir::cleanPath(path); + if (modelManager->m_scannedPaths.contains(cPath)) + continue; + pathsToScan.append(ScanItem(cPath)); + } + const int maxScanDepth = 5; + int progressRange = pathsToScan.size() * (1 << (2 + maxScanDepth)); + int totalWork(progressRange), workDone(0); + future.setProgressRange(0, progressRange); // update max length while iterating? + const bool libOnly = true; // FIXME remove when tested more + while (!pathsToScan.isEmpty() && !future.isCanceled()) { + ScanItem toScan = pathsToScan.last(); + pathsToScan.pop_back(); + int pathBudget = (maxScanDepth + 2 - toScan.depth); + if (!scannedPaths.contains(toScan.path)) { + QStringList importedFiles; + const Snapshot snapshot = modelManager->snapshot(); + if (!findNewQmlLibraryInPath(toScan.path, snapshot, modelManager, &importedFiles, + &scannedPaths, &newLibraries, true) + && !libOnly && snapshot.documentsInDirectory(toScan.path).isEmpty()) + importedFiles += qmlFilesInDirectory(toScan.path); + workDone += 1; + future.setProgressValue(progressRange * workDone / totalWork); + if (!importedFiles.isEmpty()) { + FutureReporter reporter(future, progressRange * pathBudget / (4 * totalWork), + progressRange * workDone / totalWork); + parseLoop(scannedPaths, newLibraries, workingCopy, importedFiles, modelManager, + language, emitDocChangedOnDisk, reporter); // run in parallel?? + importedFiles.clear(); + } + workDone += pathBudget / 4 - 1; + future.setProgressValue(progressRange * workDone / totalWork); + } else { + workDone += pathBudget / 4; + } + // always descend tree, as we might have just scanned with a smaller depth + if (toScan.depth < maxScanDepth) { + QDir dir(toScan.path); + QStringList subDirs(dir.entryList(QDir::Dirs)); + workDone += 1; + totalWork += pathBudget / 2 * subDirs.size() - pathBudget * 3 / 4 + 1; + foreach (const QString path, subDirs) + pathsToScan.append(ScanItem(dir.absoluteFilePath(path), toScan.depth + 1)); + } else { + workDone += pathBudget *3 / 4; + } + future.setProgressValue(progressRange * workDone / totalWork); + } future.setProgressValue(progressRange); } @@ -993,6 +1102,33 @@ void ModelManager::updateImportPaths() findNewLibraryImports(doc, snapshot, this, &importedFiles, &scannedPaths, &newLibraries); updateSourceFiles(importedFiles, true); + + QStringList pathToScan; + foreach (QString importPath, allImportPaths) + if (!m_scannedPaths.contains(importPath)) + pathToScan.append(importPath); + + if (pathToScan.count() > 1) { + QFuture<void> result = QtConcurrent::run(&ModelManager::importScan, + workingCopy(), pathToScan, + this, Language::Qml, + true); + + if (m_synchronizer.futures().size() > 10) { + QList<QFuture<void> > futures = m_synchronizer.futures(); + + m_synchronizer.clearFutures(); + + foreach (const QFuture<void> &future, futures) { + if (! (future.isFinished() || future.isCanceled())) + m_synchronizer.addFuture(future); + } + } + + m_synchronizer.addFuture(result); + + ProgressManager::addTask(result, tr("Qml import scan"), Constants::TASK_IMPORT_SCAN); + } } void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &importPath, @@ -1121,6 +1257,35 @@ LibraryInfo ModelManager::builtins(const Document::Ptr &doc) const return _validSnapshot.libraryInfo(info.qtImportsPath); } +ViewerContext ModelManager::completeVContext(const ViewerContext &vCtx, + const Document::Ptr &doc) const +{ + Q_UNUSED(doc); + ViewerContext res = vCtx; + switch (res.flags) { + case ViewerContext::Complete: + break; + case ViewerContext::AddQtPath: + case ViewerContext::AddAllPaths: + res.paths << importPaths(); + } + res.flags = ViewerContext::Complete; + return res; +} + +ViewerContext ModelManager::defaultVContext(bool autoComplete, const Document::Ptr &doc) const +{ + if (autoComplete) + return completeVContext(m_vContext, doc); + else + return m_vContext; +} + +void ModelManager::setDefaultVContext(const ViewerContext &vContext) +{ + m_vContext = vContext; +} + void ModelManager::joinAllThreads() { foreach (QFuture<void> future, m_synchronizer.futures()) diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h index 04cb3d5d4e..6d30d536e2 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.h +++ b/src/plugins/qmljstools/qmljsmodelmanager.h @@ -34,6 +34,7 @@ #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsqrcparser.h> +#include <qmljs/qmljsconstants.h> #include <cplusplus/CppDocument.h> #include <utils/qtcoverride.h> @@ -115,6 +116,14 @@ public: QmlJS::LibraryInfo builtins(const QmlJS::Document::Ptr &doc) const QTC_OVERRIDE; + QmlJS::ViewerContext completeVContext( + const QmlJS::ViewerContext &vCtx, + const QmlJS::Document::Ptr &doc = QmlJS::Document::Ptr(0)) const QTC_OVERRIDE; + QmlJS::ViewerContext defaultVContext( + bool autoComplete = true, + const QmlJS::Document::Ptr &doc = QmlJS::Document::Ptr(0)) const QTC_OVERRIDE; + void setDefaultVContext(const QmlJS::ViewerContext &vContext) QTC_OVERRIDE; + void joinAllThreads() QTC_OVERRIDE; public slots: @@ -127,11 +136,22 @@ protected: QFuture<void> refreshSourceFiles(const QStringList &sourceFiles, bool emitDocumentOnDiskChanged); + static void parseLoop(QSet<QString> &scannedPaths, QSet<QString> &newLibraries, + WorkingCopy workingCopy, QStringList files, ModelManager *modelManager, + QmlJS::Language::Enum mainLanguage, bool emitDocChangedOnDisk, + Utils::function<bool (qreal)> reportProgress); static void parse(QFutureInterface<void> &future, WorkingCopy workingCopy, QStringList files, ModelManager *modelManager, + QmlJS::Language::Enum mainLanguage, bool emitDocChangedOnDisk); + static void importScan(QFutureInterface<void> &future, + WorkingCopy workingCopy, + QStringList paths, + ModelManager *modelManager, + QmlJS::Language::Enum mainLanguage, + bool emitDocChangedOnDisk); void loadQmlTypeDescriptions(); void loadQmlTypeDescriptions(const QString &path); @@ -158,6 +178,8 @@ private: QStringList m_defaultImportPaths; QmlJS::QmlLanguageBundles m_activeBundles; QmlJS::QmlLanguageBundles m_extendedBundles; + QmlJS::ViewerContext m_vContext; + QSet<QString> m_scannedPaths; QTimer *m_updateCppQmlTypesTimer; QTimer *m_asyncResetTimer; diff --git a/src/plugins/qmljstools/qmljsplugindumper.cpp b/src/plugins/qmljstools/qmljsplugindumper.cpp index bb8a4a8835..d0fb8a7b46 100644 --- a/src/plugins/qmljstools/qmljsplugindumper.cpp +++ b/src/plugins/qmljstools/qmljsplugindumper.cpp @@ -339,6 +339,7 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode) if (!warning.isEmpty()) printParseWarnings(libraryPath, warning); } + libraryInfo.updateFingerprint(); m_modelManager->updateLibraryInfo(libraryPath, libraryInfo); } @@ -361,6 +362,7 @@ void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError) const Snapshot snapshot = m_modelManager->snapshot(); LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath); libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages)); + libraryInfo.updateFingerprint(); m_modelManager->updateLibraryInfo(libraryPath, libraryInfo); } } @@ -418,6 +420,7 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths, if (!warnings.isEmpty()) printParseWarnings(libraryPath, warnings.join(QLatin1String("\n"))); + libraryInfo.updateFingerprint(); m_modelManager->updateLibraryInfo(libraryPath, libraryInfo); } @@ -456,6 +459,7 @@ void PluginDumper::dump(const Plugin &plugin) } libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, errorMessage); + libraryInfo.updateFingerprint(); m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo); return; } diff --git a/src/plugins/qmljstools/qmljstools_test.cpp b/src/plugins/qmljstools/qmljstools_test.cpp index e4336b0146..010da935dc 100644 --- a/src/plugins/qmljstools/qmljstools_test.cpp +++ b/src/plugins/qmljstools/qmljstools_test.cpp @@ -53,7 +53,7 @@ void QmlJSTools::Internal::QmlJSToolsPlugin::test_basic() Document::Ptr doc = snapshot.document(welcomescreenRootPath); QVERIFY(doc && doc->isQmlDocument()); - ContextPtr context = Link(snapshot, QStringList(), LibraryInfo())(); + ContextPtr context = Link(snapshot, ViewerContext(), LibraryInfo())(); QVERIFY(context); const CppComponentValue *rectangleValue = context->valueOwner()->cppQmlTypes().objectByQualifiedName( diff --git a/src/plugins/qmljstools/qmljstoolsconstants.h b/src/plugins/qmljstools/qmljstoolsconstants.h index 4662204fe0..2a0d7644f1 100644 --- a/src/plugins/qmljstools/qmljstoolsconstants.h +++ b/src/plugins/qmljstools/qmljstoolsconstants.h @@ -43,6 +43,7 @@ const char JS_MIMETYPE[] = "application/javascript"; const char JSON_MIMETYPE[] = "application/json"; const char TASK_INDEX[] = "QmlJSEditor.TaskIndex"; +const char TASK_IMPORT_SCAN[] = "QmlJSEditor.TaskImportScan"; const char QML_JS_CODE_STYLE_SETTINGS_ID[] = "A.Code Style"; const char QML_JS_CODE_STYLE_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("QmlJSTools", "Code Style"); diff --git a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp b/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp index 9107e53d48..3503807896 100644 --- a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp +++ b/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp @@ -92,6 +92,13 @@ void QmlProfilerCanvas::componentComplete() QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0); } QQuickItem::componentComplete(); + requestRedraw(); +} + +void QmlProfilerCanvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChanged(newGeometry, oldGeometry); + requestRedraw(); } } diff --git a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h b/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h index b320601396..4a360d012c 100644 --- a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h +++ b/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h @@ -61,6 +61,7 @@ private slots: protected: virtual void paint(QPainter *); virtual void componentComplete(); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); private: Context2D *m_context2d; diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml index f067422819..9b2864c19b 100644 --- a/src/plugins/qmlprofiler/qml/MainView.qml +++ b/src/plugins/qmlprofiler/qml/MainView.qml @@ -330,6 +330,7 @@ Rectangle { contentWidth: 0 height: labels.height + labelsTail.height flickableDirection: Flickable.HorizontalFlick + boundsBehavior: Flickable.StopAtBounds onContentXChanged: view.updateZoomControl() clip:true diff --git a/src/plugins/qmlprofiler/qml/SelectionRange.qml b/src/plugins/qmlprofiler/qml/SelectionRange.qml index 86fdb2a7c6..e3930c7d16 100644 --- a/src/plugins/qmlprofiler/qml/SelectionRange.qml +++ b/src/plugins/qmlprofiler/qml/SelectionRange.qml @@ -69,6 +69,11 @@ RangeMover { } function setPos(pos) { + if (pos < 0) + pos = 0; + else if (pos > width) + pos = width; + switch (creationState) { case 1: { setLeft(pos); diff --git a/src/plugins/qmlprofiler/qml/TimeDisplay.qml b/src/plugins/qmlprofiler/qml/TimeDisplay.qml index 81bc47d337..5a6a63b9e4 100644 --- a/src/plugins/qmlprofiler/qml/TimeDisplay.qml +++ b/src/plugins/qmlprofiler/qml/TimeDisplay.qml @@ -38,17 +38,6 @@ Canvas2D { property real endTime : 0 property real timePerPixel: 0 - - Component.onCompleted: { - requestRedraw(); - } - onWidthChanged: { - requestRedraw(); - } - onHeightChanged: { - requestRedraw(); - } - Connections { target: zoomControl onRangeChanged: { diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml index ee06d501d8..6d48cbb014 100644 --- a/src/plugins/qmlprofiler/qml/TimeMarks.qml +++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml @@ -38,21 +38,6 @@ Canvas2D { property real endTime property real timePerPixel - Component.onCompleted: { - requestRedraw(); - } - - onWidthChanged: { - requestRedraw(); - } - onHeightChanged: { - requestRedraw(); - } - - onYChanged: { - requestRedraw(); - } - Connections { target: labels onHeightChanged: { requestRedraw(); } diff --git a/src/plugins/qnx/blackberryconfiguration.cpp b/src/plugins/qnx/blackberryconfiguration.cpp index 397c82ab94..9125b07a62 100644 --- a/src/plugins/qnx/blackberryconfiguration.cpp +++ b/src/plugins/qnx/blackberryconfiguration.cpp @@ -73,27 +73,27 @@ BlackBerryConfiguration::BlackBerryConfiguration(const FileName &ndkEnvFile, boo m_qnxEnv = QnxUtils::qnxEnvironmentFromNdkFile(m_ndkEnvFile.toString()); QString ndkTarget; - QString qnxHost; foreach (const Utils::EnvironmentItem &item, m_qnxEnv) { if (item.name == QLatin1String("QNX_TARGET")) ndkTarget = item.value; else if (item.name == QLatin1String("QNX_HOST")) - qnxHost = item.value; + m_qnxHost = item.value; } - QString sep = QString::fromLatin1("%1qnx6").arg(QDir::separator()); - m_targetName = ndkTarget.split(sep).first().split(QDir::separator()).last(); + // The QNX_TARGET value is using Unix-like separator on all platforms. + QString sep = QString::fromLatin1("/qnx6"); + m_targetName = ndkTarget.split(sep).first().split(QLatin1Char('/')).last(); if (QDir(ndkTarget).exists()) m_sysRoot = FileName::fromString(ndkTarget); - FileName qmake4Path = QnxUtils::executableWithExtension(FileName::fromString(qnxHost + QLatin1String("/usr/bin/qmake"))); - FileName qmake5Path = QnxUtils::executableWithExtension(FileName::fromString(qnxHost + QLatin1String("/usr/bin/qt5/qmake"))); - FileName gccPath = QnxUtils::executableWithExtension(FileName::fromString(qnxHost + QLatin1String("/usr/bin/qcc"))); - FileName deviceGdbPath = QnxUtils::executableWithExtension(FileName::fromString(qnxHost + QLatin1String("/usr/bin/ntoarm-gdb"))); - FileName simulatorGdbPath = QnxUtils::executableWithExtension(FileName::fromString(qnxHost + QLatin1String("/usr/bin/ntox86-gdb"))); + FileName qmake4Path = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qmake"))); + FileName qmake5Path = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qt5/qmake"))); + FileName gccPath = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qcc"))); + FileName deviceGdbPath = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/ntoarm-gdb"))); + FileName simulatorGdbPath = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/ntox86-gdb"))); if (qmake4Path.toFileInfo().exists()) m_qmake4BinaryFile = qmake4Path; @@ -126,6 +126,11 @@ QString BlackBerryConfiguration::targetName() const return m_targetName; } +QString BlackBerryConfiguration::qnxHost() const +{ + return m_qnxHost; +} + bool BlackBerryConfiguration::isAutoDetected() const { return m_isAutoDetected; diff --git a/src/plugins/qnx/blackberryconfiguration.h b/src/plugins/qnx/blackberryconfiguration.h index 51473a5b0a..1b31d186b3 100644 --- a/src/plugins/qnx/blackberryconfiguration.h +++ b/src/plugins/qnx/blackberryconfiguration.h @@ -66,6 +66,7 @@ public: QString ndkPath() const; QString displayName() const; QString targetName() const; + QString qnxHost() const; bool isAutoDetected() const; bool isActive() const; bool isValid() const; @@ -81,6 +82,7 @@ public: private: QString m_displayName; QString m_targetName; + QString m_qnxHost; bool m_isAutoDetected; Utils::FileName m_ndkEnvFile; Utils::FileName m_qmake4BinaryFile; diff --git a/src/plugins/qnx/blackberryinstallwizardpages.cpp b/src/plugins/qnx/blackberryinstallwizardpages.cpp index b854475ff5..b2bd3f5dc0 100644 --- a/src/plugins/qnx/blackberryinstallwizardpages.cpp +++ b/src/plugins/qnx/blackberryinstallwizardpages.cpp @@ -64,10 +64,13 @@ NdkPathChooser::NdkPathChooser(Mode mode, QWidget *parent) : Utils::PathChooser(parent) , m_mode(mode) { - if (m_mode == NdkPathChooser::InstallMode) + if (m_mode == NdkPathChooser::InstallMode) { setExpectedKind(Utils::PathChooser::Directory); - else + } else { setExpectedKind(Utils::PathChooser::File); + setPromptDialogFilter(Utils::HostOsInfo::isWindowsHost() ? QLatin1String("*.bat") : + QLatin1String("*.sh")); + } } bool NdkPathChooser::validatePath(const QString &path, QString *errorMessage) @@ -80,7 +83,10 @@ bool NdkPathChooser::validatePath(const QString &path, QString *errorMessage) return !(QnxUtils::sdkInstallerPath(path).isEmpty()); QFileInfo fi(path); - return (fi.suffix() == QLatin1String("sh") || fi.suffix() == QLatin1String("bat")); + if (Utils::HostOsInfo::isWindowsHost()) + return fi.suffix() == QLatin1String("bat"); + + return fi.suffix() == QLatin1String("sh"); } //------------------------------------------------------------------ diff --git a/src/plugins/qnx/blackberryndksettingswidget.cpp b/src/plugins/qnx/blackberryndksettingswidget.cpp index 9ca62b4aa6..a1982539e7 100644 --- a/src/plugins/qnx/blackberryndksettingswidget.cpp +++ b/src/plugins/qnx/blackberryndksettingswidget.cpp @@ -144,15 +144,18 @@ void BlackBerryNDKSettingsWidget::updateInfoTable(QTreeWidgetItem* currentItem) if (!config) return; - foreach (const NdkInstallInformation &ndkInfo, QnxUtils::installedNdks()) - { - if (ndkInfo.name == config->displayName()) { - m_ui->baseNameLabel->setText(ndkInfo.name); - m_ui->ndkPathLabel->setText(ndkInfo.path); - m_ui->versionLabel->setText(ndkInfo.version); - m_ui->hostLabel->setText(ndkInfo.host); - m_ui->targetLabel->setText(ndkInfo.target); - break; + m_ui->baseNameLabel->setText(config->displayName()); + m_ui->ndkPathLabel->setText(QDir::toNativeSeparators(config->ndkPath())); + m_ui->hostLabel->setText(QDir::toNativeSeparators(config->qnxHost())); + m_ui->targetLabel->setText(QDir::toNativeSeparators(config->sysRoot().toString())); + m_ui->versionLabel->clear(); + // TODO: Add a versionNumber attribute for the BlackBerryConfiguration class + if (config->isAutoDetected()) { + foreach (const NdkInstallInformation &ndkInfo, QnxUtils::installedNdks()) { + if (ndkInfo.name == config->displayName()) { + m_ui->versionLabel->setText(ndkInfo.version); + break; + } } } diff --git a/src/plugins/qnx/cascadesimport/cascadesimportwizard.cpp b/src/plugins/qnx/cascadesimport/cascadesimportwizard.cpp index 3291e0c762..d21fa5af86 100644 --- a/src/plugins/qnx/cascadesimport/cascadesimportwizard.cpp +++ b/src/plugins/qnx/cascadesimport/cascadesimportwizard.cpp @@ -129,11 +129,6 @@ CascadesImportWizard::~CascadesImportWizard() { } -Core::FeatureSet CascadesImportWizard::requiredFeatures() const -{ - return Core::FeatureSet(Constants::QNX_BB_FEATURE); -} - Core::BaseFileWizard::ExtensionList CascadesImportWizard::selectExtensions() { return Core::BaseFileWizard::ExtensionList(); diff --git a/src/plugins/qnx/cascadesimport/cascadesimportwizard.h b/src/plugins/qnx/cascadesimport/cascadesimportwizard.h index 599be858e6..9a60db994e 100644 --- a/src/plugins/qnx/cascadesimport/cascadesimportwizard.h +++ b/src/plugins/qnx/cascadesimport/cascadesimportwizard.h @@ -68,7 +68,6 @@ public: CascadesImportWizard(QObject *parent = 0); virtual ~CascadesImportWizard(); - Core::FeatureSet requiredFeatures() const; protected: ExtensionList selectExtensions(); QWizard* createWizardDialog(QWidget *parent, diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp index 19211acdbb..a894b2f092 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.cpp +++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp @@ -75,7 +75,8 @@ Highlighter::Highlighter(QTextDocument *parent) : << TextEditor::C_TEXT // TODO : add style for error (eg. red underline) << TextEditor::C_FUNCTION << TextEditor::C_TEXT - << TextEditor::C_TEXT; + << TextEditor::C_TEXT + << TextEditor::C_LOCAL; } setTextFormatCategories(categories); @@ -113,6 +114,7 @@ KateFormatMap::KateFormatMap() m_ids.insert(QLatin1String("dsFunction"), Highlighter::Function); m_ids.insert(QLatin1String("dsRegionMarker"), Highlighter::RegionMarker); m_ids.insert(QLatin1String("dsOthers"), Highlighter::Others); + m_ids.insert(QLatin1String("dsIdentifier"), Highlighter::Identifier); } Q_GLOBAL_STATIC(KateFormatMap, kateFormatMap) diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h index 968a48a6c1..ef0498036a 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.h +++ b/src/plugins/texteditor/generichighlighter/highlighter.h @@ -83,7 +83,8 @@ public: Error, Function, RegionMarker, - Others + Others, + Identifier }; void setTabSettings(const TabSettings &ts); |