Merge remote-tracking branch 'origin/3.5'

Change-Id: I7b3ef276d438ff0f184a649153e8aeec08a9f8c9
This commit is contained in:
Eike Ziller
2015-08-04 13:15:51 +02:00
203 changed files with 6590 additions and 12601 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -44,7 +44,9 @@
\l{http://clang-analyzer.llvm.org/available_checks.html}{Available Checkers}.
To suppress diagnostics, select \uicontrol {Suppress This Diagnostic} in the
context menu.
context menu. To view the suppression list for a project and to remove
diagnostics from it, select \uicontrol {Clang Static Analyzer Settings} in
the \uicontrol Projects mode.
\section1 Setting Up Clang Static Analyzer

View File

@@ -67,6 +67,8 @@
\l{http://developer.android.com/tools/sdk/ndk/index.html}
{Android NDK} from Google.
\note Android Studio is currently not supported by \QC.
\li On Windows, you also need the following:
\list

View File

@@ -291,6 +291,7 @@
\li In the \uicontrol Application group, you can give the application a name and
select an icon for it. The three icon fields can contain different versions
of the icon with low, medium, and high DPI values, from left to right.
You can also give an activity a name and select the activity to run.
\li In the \uicontrol Permissions field, you can specify the permissions that your
application needs. Users are asked to grant the permissions when they

View File

@@ -55,20 +55,22 @@
developers working on the same projects.
For example, if you work on a project and need to switch to another project
for a while, you can save your workspace as a session and then close the
project and all the files in it. Everything that you open after saving the
session becomes a part of a new session. When you want to return to working
on the first project, open the saved session. \QC opens the projects and
files that belong to the session.
for a while, you can clone the current session to save it under a name and
create a new session for the other project. Everything that you open after
switching to the new session becomes a part of it. When you want to return
to working on the first project, switch to the saved session. \QC opens the
projects and files that belong to the session.
To create a new session or remove existing sessions, select \uicontrol File >
\uicontrol{Session Manager}.
To save a session under a new name, select \uicontrol File >
\uicontrol {Session Manager} > \uicontrol Clone.
To create a new session, select \uicontrol File >
\uicontrol {Session Manager} > \uicontrol New.
\image qtcreator-session-manager.png
To switch between sessions, choose \uicontrol {File > Session Manager}. If you do
not create or select a session, \QC always uses the default session, which
was created the last time you exited \QC.
To switch between sessions, choose \uicontrol {File > Session Manager} >
\uicontrol {Switch to}.
When you launch \QC, a list of existing sessions is displayed in the
\uicontrol Welcome mode.

View File

@@ -486,6 +486,11 @@
To determine whether the keywords in the whole project or in the current
file are displayed by default, select \uicontrol {Scanning scope}.
To exclude files from scanning, select \uicontrol {To-Do Settings} in the
\uicontrol Projects mode. Select \uicontrol Add and enter a regular
expression that matches the path to files to exclude. Use a forward slash
(/) as a separator in the path also on Windows.
The Todo plugin is disabled by default. To enable the plugin, select
\uicontrol Help > \uicontrol {About Plugins} > \uicontrol Utilities >
\uicontrol Todo and restart

View File

@@ -722,6 +722,12 @@
deselect the \uicontrol Load check box for the \uicontrol Perforce plugin in the
\uicontrol {Version Control} group.
In the Perforce options, you can specify workspace details:
\uicontrol {P4 user}, \uicontrol {P4 client}, and \uicontrol {P4 port}. To
specify the details individually for several projects, use configuration
files instead. Create a \c {p4config.txt} configuration file for each
project in the top level project directory.
The \uicontrol Perforce submenu contains the following additional items:
\table

View File

@@ -39,13 +39,20 @@
Since \QC 1.1, CMake configuration files are supported. Since \QC 1.3, the
Microsoft tool chain is supported if the CMake version is at least 2.8.
\section1 Setting the Path for CMake
\QC automatically detects the CMake executable specified in the \c PATH.
You can add paths to other CMake executables and use them in different
build and run \l{glossary-buildandrun-kit}{kits}.
You can set the path for the CMake executable in \uicontrol Tools >
\uicontrol Options > \uicontrol {Build & Run} > \uicontrol CMake.
\section1 Adding CMake Tools
To specify paths to CMake executables, select \uicontrol Tools >
\uicontrol Options > \uicontrol {Build & Run} > \uicontrol CMake >
\uicontrol Add.
\image qtcreator-cmakeexecutable.png
\section1 Opening CMake Projects
\note Before you open a CMake project, you must modify the \c {PATH}
environment variable to include the bin folders of \c mingw and Qt.
@@ -60,8 +67,6 @@
C:\Qt\Tools\QtCreator\qtcreator.exe
\endcode
\section1 Opening CMake Projects
To open a CMake project:
\list 1
@@ -103,6 +108,9 @@
\QC supports multiple build configurations. You can change the build
directory after the initial import.
The build and run kit that you select determines which CMake tool is used
for building. For more information, see \l {Adding Kits}.
\section1 Running CMake Projects
\QC automatically adds \uicontrol {Run Configurations} for all targets

View File

@@ -155,8 +155,8 @@
\image qtcreator-options-qbs.png
To add keys and values to a build profile that is associated with a build
and run kit:
You can modify a build profile that is associated with a build and run kit
by editing the values of the keys generated by \QC and by adding new values:
\list 1
@@ -166,12 +166,21 @@
\li Select \uicontrol Edit to edit the profile associated with the kit.
\li Select \uicontrol Add to add keys and values to the profile.
Spefify keys as: \c <module_name>.<property_name>. Specify the
values as JSON literals.
\li Select \uicontrol Add to add keys and values to the profile or to
modify existing values.
\li In the \uicontrol Key column, spefify the key to add or modify as:
\c <module_name>.<property_name>.
\li In the \uicontrol Value column, specify a value as a JSON literal.
\li Click \uicontrol OK.
\endlist
The values that you have modified are displayed in red in the
\uicontrol {Profile properties} field.
For a list of available keys and values, see the
\l{http://doc.qt.io/qbs/list-of-modules.html}{List of Modules} in the
Qbs Manual.

View File

@@ -114,6 +114,10 @@
the build step and add a custom build step that specifies another shell
command.
To generate debug symbols also for applications compiled in release mode,
select the \uicontrol {Generate separate debug info} check box. For more
information, see \l{Using the CPU Usage Analyzer}.
\QC is integrated to \l{http://doc.qt.io/QtQuickCompiler/}
{Qt Quick Compiler} (commercial only) that enables you to compile QML source
code into the final binary. This

View File

@@ -88,6 +88,11 @@
\li \l{Specifying Dependencies}{Dependencies}
\li \l{Using Clang Static Analyzer}{Clang Static Analyzer Settings}
(commercial only)
\li \l{To-Do List}{To-Do Settings} (experimental)
\endlist
Use the \uicontrol Build and \uicontrol Run buttons to switch between the build and

View File

@@ -114,6 +114,11 @@
configuration that should be used by qmake. If you leave this field
empty, the default mkspec of the selected Qt version is used.
\li In the \uicontrol {CMake Tool} field, select the CMake tool to use
for building the project. Select \uicontrol Manage to add installed
CMake tools to the list. For more information, see
\l{Adding CMake Tools}.
\endlist
*/

View File

@@ -469,7 +469,7 @@
\section2 Previewing Component Size
The width and height of the root item in a QML file determine the size of
the component. You can reuse component, such as buttons, in different
the component. You can reuse components, such as buttons, in different
sizes in other QML files and design screens for use with different device
profiles, screen resolution, or screen orientation. The component size
might also be zero (0,0) if its final size is determined by property

View File

@@ -39,7 +39,12 @@
\note GIMP does not support grouping, and therefore, each layer is exported
as an item in GIMP.
You can open the QML file in \QC for editing. If you edit the file in Adobe
You can open the QML file in \QC for editing. By default, the export scripts
generate Qt Quick 1 files. To edit the files in \QMLD, change the import
statements in the export scripts to import Qt Quick 2. Or, you can change
the import statement in each file after generating the files.
If you edit the file in Adobe
Photoshop and export it to the same directory again, any changes you made in
\QC are overwritten. However, you can re-export graphical assets without
recreating the QML code.
@@ -114,13 +119,21 @@
\list 1
\li Download the export script, \e{Export QML.jx}, from
\l{https://code.qt.io/cgit/qt-labs/photoshop-qmlexporter.git/}
{code.qt.io}.
\li Clone the repository that contains the export script,
\e{Export QML.jx}, from
\l{https://codereview.qt-project.org/#/admin/projects/qt-labs/photoshop-qmlexporter}
{Qt Code Review}.
\note Read the README.txt file in the repository for latest
information about the script.
\li To generate QML files that you can edit in \QMLD, edit the import
statement in \e {Export QML.jx}. For example:
\code
qmlfile.write("import QtQuick 2.5\n");
\endcode
\li Double-click the export script to add the export command to the
\uicontrol Scripts menu. You can also copy the script file to the Adobe
Photoshop scripts directory (typically, \c{\Presets\Scripts} in the
@@ -160,21 +173,12 @@
The script has been tested to work on GIMP 2. You can download GIMP 2 from
\l{http://www.gimp.org/downloads/}{GIMP Downloads}.
To use the export script on Microsoft Windows, you also need to install the
GIMP Python extension (Python, PyCairo, PyGobject, PyGTK). However, GIMP is
not officially supported on Windows, so we cannot guarantee that this will
work.
\list 1
\li On Microsoft Windows, you must first add Python support to your GIMP
installation, as instructed in
\l {http://www.gimpusers.com/tutorials/install-python-for-gimp-2-6-windows}
{Tutorial: Installing Python for GIMP 2.6 (Windows)}.
\li Download the export script, \e qmlexporter.py, from
\l{https://code.qt.io/cgit/qt-labs/gimp-qmlexporter.git/}
{code.qt.io}.
\li Clone the repository that contains the export script,
\e qmlexporter.py, from
\l{https://codereview.qt-project.org/#/admin/projects/qt-labs/gimp-qmlexporter}
{Qt Code Review}.
\note Read the INSTALL.txt in the repository for latest information
about the script.
@@ -186,6 +190,13 @@
On Linux, run the following command: \c {chmod u+rx}
\li To generate QML files that you can edit in \QMLD, edit the import
statement in \e qmlexporter.py. For example:
\code
f.write('import QtQuick 2.5\n')
\endcode
\li Restart GIMP to have the export command added to the \uicontrol File menu.
\li Choose \uicontrol {File > Export to QML} to export the design to a QML

View File

@@ -1,4 +1,17 @@
function llvmConfig(qbs)
var File = loadExtension("qbs.File")
var MinimumLLVMVersion = "3.6.0"
function isSuitableLLVMConfig(llvmConfigCandidate, qtcFunctions, processOutputReader)
{
if (File.exists(llvmConfigCandidate)) {
var candidateVersion = version(llvmConfigCandidate, processOutputReader);
if (candidateVersion && candidateVersion.length)
return qtcFunctions.versionIsAtLeast(candidateVersion, MinimumLLVMVersion)
}
return false;
}
function llvmConfig(qbs, qtcFunctions, processOutputReader)
{
var llvmInstallDirFromEnv = qbs.getEnv("LLVM_INSTALL_DIR")
var llvmConfigVariants = [
@@ -10,7 +23,7 @@ function llvmConfig(qbs)
if (llvmInstallDirFromEnv) {
for (var i = 0; i < llvmConfigVariants.length; ++i) {
var variant = llvmInstallDirFromEnv + "/bin/" + llvmConfigVariants[i];
if (File.exists(variant))
if (isSuitableLLVMConfig(variant, qtcFunctions, processOutputReader))
return variant;
}
}
@@ -22,7 +35,7 @@ function llvmConfig(qbs)
for (var i = 0; i < llvmConfigVariants.length; ++i) {
for (var j = 0; j < pathList.length; ++j) {
var variant = pathList[j] + "/" + llvmConfigVariants[i];
if (File.exists(variant))
if (isSuitableLLVMConfig(variant, qtcFunctions, processOutputReader))
return variant;
}
}

View File

@@ -58,7 +58,6 @@ Project {
"QT_NO_CAST_FROM_ASCII"
].concat(testsEnabled ? ["WITH_TESTS"] : [])
qbsSearchPaths: "qbs"
property bool enableQbsJavaSupport: false
references: [
"src/src.qbs",

View File

@@ -28,44 +28,35 @@
**
****************************************************************************/
#define QT_NO_META_MACROS
// Include qobjectdefs.h from Qt ...
#include_next <qobjectdefs.h>
#if defined(QT_NO_KEYWORDS)
# define QT_NO_EMIT
#else
# ifndef QT_NO_SIGNALS_SLOTS_KEYWORDS
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmacro-redefined"
// ...and redefine macros for tagging signals/slots
#ifdef signals
# define signals public __attribute__((annotate("qt_signal")))
#endif
#ifdef slots
# define slots __attribute__((annotate("qt_slot")))
# endif
#endif
#define Q_SIGNALS public __attribute__((annotate("qt_signal")))
#define Q_SLOTS slots __attribute__((annotate("qt_slot")))
#define Q_SIGNAL __attribute__((annotate("qt_signal")))
#define Q_SLOT __attribute__((annotate("qt_slot")))
#define Q_PRIVATE_SLOT(d, signature)
#define Q_EMIT
#ifndef QT_NO_EMIT
# define emit
#ifdef Q_SIGNALS
# define Q_SIGNALS public __attribute__((annotate("qt_signal")))
#endif
#define Q_CLASSINFO(name, value)
#define Q_PLUGIN_METADATA(x)
#define Q_INTERFACES(x)
#define Q_PROPERTY(text)
#define Q_PRIVATE_PROPERTY(d, text)
#define Q_REVISION(v)
#define Q_OVERRIDE(text)
#define Q_ENUMS(x)
#define Q_FLAGS(x)
#define Q_ENUM(x)
#define Q_FLAG(x)
#define Q_SCRIPTABLE
#define Q_INVOKABLE
#define Q_GADGET \
public: \
static const QMetaObject staticMetaObject; \
private:
#ifdef Q_SLOTS
# define Q_SLOTS slots __attribute__((annotate("qt_slot")))
#endif
#define SIGNAL(a) #a
#define SLOT(a) #a
#ifdef Q_SIGNAL
# define Q_SIGNAL __attribute__((annotate("qt_signal")))
#endif
#ifdef Q_SLOT
# define Q_SLOT __attribute__((annotate("qt_slot")))
#endif
#pragma clang diagnostic pop

View File

@@ -41,7 +41,7 @@ def qdump__boost__bimaps__bimap(d, value):
def qdump__boost__optional(d, value):
if int(value["m_initialized"]) == 0:
d.putValue("<uninitialized>")
d.putSpecialValue(SpecialUninitializedValue)
d.putNumChild(0)
else:
type = d.templateArgument(value.type, 0)

View File

@@ -46,8 +46,11 @@ def extractPointerType(d, value):
try:
return readLiteral(d, value["_name"]) + postfix
except:
if str(value.type.unqualified().target()) == "CPlusPlus::IntegerType":
typeName = str(value.type.unqualified().target())
if typeName == "CPlusPlus::IntegerType":
return "int" + postfix
elif typeName == "CPlusPlus::VoidType":
return "void" + postfix
return "<unsupported>"
def readTemplateName(d, value):

View File

@@ -129,8 +129,18 @@ Hex2EncodedFloat4, \
Hex2EncodedFloat8, \
IPv6AddressAndHexScopeId, \
Hex2EncodedUtf8WithoutQuotes, \
DateTimeInternal \
= range(30)
DateTimeInternal, \
SpecialEmptyValue, \
SpecialUninitializedValue, \
SpecialInvalidValue, \
SpecialNotAccessibleValue, \
SpecialItemCountValue, \
SpecialMinimumItemCountValue, \
SpecialNotCallableValue, \
SpecialNullReferenceValue, \
SpecialOptimizedOutValue, \
SpecialEmptyStructureValue, \
= range(40)
# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h
StopDisplay, \
@@ -292,7 +302,7 @@ class Children:
if self.d.passExceptions:
showException("CHILDREN", exType, exValue, exTraceBack)
self.d.putNumChild(0)
self.d.putValue("<not accessible>")
self.d.putSpecialValue(SpecialNotAccessibleValue)
if not self.d.currentMaxNumChild is None:
if self.d.currentMaxNumChild < self.d.currentNumChild:
self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
@@ -692,7 +702,7 @@ class DumperBase:
self.putItem(result)
except:
with SubItem(self, name):
self.putValue("<not callable>")
self.putSpecialValue(SpecialNotCallableValue);
self.putNumChild(0)
def call(self, value, func, *args):
@@ -816,9 +826,9 @@ class DumperBase:
def putItemCount(self, count, maximum = 1000000000):
# This needs to override the default value, so don't use 'put' directly.
if count > maximum:
self.putValue('<>%s items>' % maximum)
self.putSpeciaValue(SpecialMinimumItemCountValue, maximum)
else:
self.putValue('<%s items>' % count)
self.putSpecialValue(SpecialItemCountValue, count)
self.putNumChild(count)
def putField(self, name, value):
@@ -837,6 +847,9 @@ class DumperBase:
if priority >= self.currentValue.priority:
self.currentValue = ReportItem(value, encoding, priority, elided)
def putSpecialValue(self, encoding, value = ""):
self.putValue(value, encoding)
def putEmptyValue(self, priority = -10):
if priority >= self.currentValue.priority:
self.currentValue = ReportItem("", None, priority, None)
@@ -896,21 +909,28 @@ class DumperBase:
p = None
displayFormat = self.currentItemFormat()
n = int(arrayType.sizeof / ts)
arrayByteSize = arrayType.sizeof
if arrayByteSize == 0:
# This should not happen. But it does, see QTCREATORBUG-14755.
# GDB/GCC produce sizeof == 0 for QProcess arr[3]
s = str(value.type)
arrayByteSize = int(s[s.find('[')+1:s.find(']')]) * ts;
n = int(arrayByteSize / ts)
if displayFormat != RawFormat:
if innerTypeName == "char":
# Use Latin1 as default for char [].
blob = self.readMemory(self.addressOf(value), arrayType.sizeof)
blob = self.readMemory(self.addressOf(value), arrayByteSize)
self.putValue(blob, Hex2EncodedLatin1)
elif innerTypeName == "wchar_t":
blob = self.readMemory(self.addressOf(value), arrayType.sizeof)
blob = self.readMemory(self.addressOf(value), arrayByteSize)
if innerType.sizeof == 2:
self.putValue(blob, Hex4EncodedLittleEndian)
else:
self.putValue(blob, Hex8EncodedLittleEndian)
elif p:
self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName, displayFormat, arrayType.sizeof)
self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName,
displayFormat, arrayByteSize)
self.putNumChild(n)
if self.isExpanded():
@@ -1413,7 +1433,7 @@ class DumperBase:
else:
connections = connections.dereference()
connections = connections.cast(self.directBaseClass(connections.type))
self.putValue('<>0 items>')
self.putSpecialValue(SpecialMinimumItemCountValue, 0)
self.putNumChild(1)
if self.isExpanded():
pp = 0

View File

@@ -465,7 +465,7 @@ class Dumper(DumperBase):
if self.passExceptions:
showException("SUBITEM", exType, exValue, exTraceBack)
self.putNumChild(0)
self.putValue("<not accessible>")
self.putSpecialValue(SpecialNotAccessibleValue)
try:
if self.currentType.value:
typeName = self.stripClassTag(self.currentType.value)
@@ -473,7 +473,8 @@ class Dumper(DumperBase):
self.put('type="%s",' % typeName) # str(type.unqualified()) ?
if self.currentValue.value is None:
self.put('value="<not accessible>",numchild="0",')
self.put('value="",encoding="%d","numchild="0",'
% SpecialNotAccessibleValue)
else:
if not self.currentValue.encoding is None:
self.put('valueencoded="%d",' % self.currentValue.encoding)
@@ -930,7 +931,7 @@ class Dumper(DumperBase):
if value is None:
# Happens for non-available watchers in gdb versions that
# need to use gdb.execute instead of gdb.parse_and_eval
self.putValue("<not available>")
self.putSpecialValue(SpecialNotAvailableValue)
self.putType("<unknown>")
self.putNumChild(0)
return
@@ -939,7 +940,7 @@ class Dumper(DumperBase):
typeName = str(typeobj)
if value.is_optimized_out:
self.putValue("<optimized out>")
self.putSpecialValue(SpecialOptimizedOutValue)
self.putType(typeName)
self.putNumChild(0)
return
@@ -960,7 +961,7 @@ class Dumper(DumperBase):
try:
# Try to recognize null references explicitly.
if toInteger(value.address) == 0:
self.putValue("<null reference>")
self.putSpecialValue(SpecialNullReferenceValue)
self.putType(typeName)
self.putNumChild(0)
return
@@ -988,7 +989,7 @@ class Dumper(DumperBase):
self.putBetterType("%s &" % self.currentType.value)
return
except RuntimeError:
self.putValue("<optimized out reference>")
self.putSpecialValue(SpecialOptimizedOutValue)
self.putType(typeName)
self.putNumChild(0)
return
@@ -1069,7 +1070,7 @@ class Dumper(DumperBase):
# Anonymous union. We need a dummy name to distinguish
# multiple anonymous unions in the struct.
self.putType(typeobj)
self.putValue("{...}")
self.putSpecialValue(SpecialEmptyStructureValue)
self.anonNumber += 1
with Children(self, 1):
self.listAnonymous(value, "#%d" % self.anonNumber, typeobj)
@@ -1155,7 +1156,13 @@ class Dumper(DumperBase):
def putFields(self, value, dumpBase = True):
fields = value.type.fields()
if self.sortStructMembers:
fields.sort(key = lambda field: "%d%s" % (not field.is_base_class, field.name))
def sortOrder(field):
if field.is_base_class:
return 0
if field.name and field.name.startswith("_vptr."):
return 1
return 2
fields.sort(key = lambda field: "%d%s" % (sortOrder(field), field.name))
#warn("TYPE: %s" % value.type)
#warn("FIELDS: %s" % fields)
@@ -1706,7 +1713,7 @@ class CliDumper(Dumper):
if self.passExceptions:
showException("SUBITEM", exType, exValue, exTraceBack)
self.putNumChild(0)
self.putValue("<not accessible>")
self.putSpecialValue(SpecialNotAccessibleValue)
try:
if self.currentType.value:
typeName = self.stripClassTag(self.currentType.value)

View File

@@ -266,14 +266,15 @@ class Dumper(DumperBase):
if self.passExceptions:
showException("SUBITEM", exType, exValue, exTraceBack)
self.putNumChild(0)
self.putValue("<not accessible>")
self.putSpecialValue(SpecialNotAccessibleValue)
try:
if self.currentType.value:
typeName = self.currentType.value
if len(typeName) > 0 and typeName != self.currentChildType:
self.put('type="%s",' % typeName) # str(type.unqualified()) ?
if self.currentValue.value is None:
self.put('value="<not accessible>",numchild="0",')
self.put('value="",encoding="%d",numchild="0",'
% SpecialNotAccessibleValue)
else:
if not self.currentValue.encoding is None:
self.put('valueencoded="%s",' % self.currentValue.encoding)

View File

@@ -58,6 +58,7 @@ def qform__QByteArray():
def qdump__QByteArray(d, value):
data, size, alloc = d.byteArrayData(value)
d.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 100)
d.putNumChild(size)
elided, p = d.encodeByteArrayHelper(d.extractPointer(value), d.displayStringLimit)
displayFormat = d.currentItemFormat()
@@ -78,6 +79,7 @@ def qdump__QByteArray(d, value):
def qdump__QByteArrayData(d, value):
data, size, alloc = d.byteArrayDataHelper(d.addressOf(value))
d.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 100)
d.putValue(d.readMemory(data, size), Hex2EncodedLatin1)
d.putNumChild(1)
if d.isExpanded():
@@ -1552,7 +1554,7 @@ def qdump__QRegExp(d, value):
def qdump__QRegion(d, value):
p = value["d"].dereference()["qt_rgn"]
if d.isNull(p):
d.putValue("<empty>")
d.putSpecialValue(SpecialEmptyValue)
d.putNumChild(0)
else:
# struct QRegionPrivate:

View File

@@ -60,4 +60,8 @@ ScrollViewStyle {
decrementControl: Item {}
incrementControl: Item {}
corner: Item {}
//Even if the platform style reports touch support a scrollview should not be flickable.
Component.onCompleted: control.flickableItem.interactive = false
transientScrollBars: false
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@ Product {
qbs.installSourceBase: "qtcreator"
prefix: "qtcreator/"
files: [
"cplusplus/**/*",
"debugger/**/*",
"designer/**/*",
"generic-highlighter/**/*",

View File

@@ -117,6 +117,7 @@ void SymbolTable::enterSymbol(Symbol *symbol)
_allocatedSymbols = DefaultInitialSize;
_symbols = reinterpret_cast<Symbol **>(realloc(_symbols, sizeof(Symbol *) * _allocatedSymbols));
memset(_symbols + _symbolCount, 0, sizeof(Symbol *) * (_allocatedSymbols - _symbolCount));
}
symbol->_index = _symbolCount;
@@ -214,7 +215,7 @@ unsigned SymbolTable::symbolCount() const
Symbol *SymbolTable::symbolAt(unsigned index) const
{
if (! _symbols)
if (! _symbols || index >= symbolCount())
return 0;
return _symbols[index];
}

View File

@@ -36,7 +36,8 @@ SOURCES += $$PWD/ipcserverinterface.cpp \
$$PWD/codecompletionchunk.cpp \
$$PWD/projectpartcontainer.cpp \
$$PWD/projectpartsdonotexistcommand.cpp \
$$PWD/lineprefixer.cpp
$$PWD/lineprefixer.cpp \
$$PWD/clangbackendipcdebugutils.cpp
HEADERS += \
$$PWD/ipcserverinterface.h \
@@ -67,6 +68,7 @@ HEADERS += \
$$PWD/projectpartsdonotexistcommand.h \
$$PWD/container_common.h \
$$PWD/clangbackendipc_global.h \
$$PWD/lineprefixer.h
$$PWD/lineprefixer.h \
$$PWD/clangbackendipcdebugutils.h
contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols

View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "clangbackendipcdebugutils.h"
#include "filecontainer.h"
#include <utf8string.h>
#include <QDir>
#include <QLoggingCategory>
#include <QString>
#include <QTemporaryDir>
#include <QTemporaryFile>
namespace {
Q_LOGGING_CATEGORY(timersLog, "qtc.clangbackend.timers");
class DebugInspectionDir : public QTemporaryDir
{
public:
DebugInspectionDir()
: QTemporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangbackendipc-XXXXXX"))
{
setAutoRemove(false); // Keep around for later inspection.
}
};
class DebugInspectionFile : public QTemporaryFile
{
public:
DebugInspectionFile(const QString &directoryPath,
const Utf8String &id,
const Utf8String &fileContent)
: QTemporaryFile(directoryPath + QString::fromUtf8("/%1-XXXXXX").arg(id.toString()))
{
setAutoRemove(false); // Keep around for later inspection.
m_isValid = open() && write(fileContent.constData(), fileContent.byteSize());
}
bool isValid() const
{
return m_isValid;
}
private:
bool m_isValid = false;
};
}
namespace ClangBackEnd {
Utf8String debugWriteFileForInspection(const Utf8String &fileContent, const Utf8String &id)
{
static DebugInspectionDir debugInspectionDir;
if (!debugInspectionDir.isValid())
return Utf8String();
DebugInspectionFile file(debugInspectionDir.path(), id, fileContent);
if (file.isValid())
return Utf8String::fromString(file.fileName());
return Utf8String();
}
Utf8String debugId(const FileContainer &fileContainer)
{
const Utf8String filePath = fileContainer.filePath();
Utf8String id(Utf8StringLiteral("unsavedfilecontent-"));
id.append(QFileInfo(filePath).fileName());
return id;
}
VerboseScopeDurationTimer::VerboseScopeDurationTimer(const char *id)
: id(id)
{
if (timersLog().isDebugEnabled())
timer.start();
}
VerboseScopeDurationTimer::~VerboseScopeDurationTimer()
{
qCDebug(timersLog) << id << "needed" << timer.elapsed() << "ms";
}
} // namespace ClangBackEnd

View File

@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company
** Contact: info@kdab.com
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
@@ -28,29 +28,34 @@
**
****************************************************************************/
#ifndef PROJECTEXPLORER_DEVICETYPEKITCHOOSER_H
#define PROJECTEXPLORER_DEVICETYPEKITCHOOSER_H
#ifndef CLANGBACKENDIPCDEBUGUTILS_H
#define CLANGBACKENDIPCDEBUGUTILS_H
#include "../kitchooser.h"
#include "../projectexplorer_export.h"
#include "clangbackendipc_global.h"
#include <coreplugin/id.h>
#include <QElapsedTimer>
namespace ProjectExplorer {
class FileContainer;
class Utf8String;
class PROJECTEXPLORER_EXPORT DeviceTypeKitChooser : public KitChooser
namespace ClangBackEnd {
Utf8String debugWriteFileForInspection(const Utf8String &fileContent, const Utf8String &id);
Utf8String debugId(const FileContainer &fileContainer);
class CMBIPC_EXPORT VerboseScopeDurationTimer
{
Q_OBJECT
public:
explicit DeviceTypeKitChooser(Core::Id deviceType, QWidget *parent = 0);
protected:
bool kitMatches(const Kit *k) const;
VerboseScopeDurationTimer(const char *id = 0);
~VerboseScopeDurationTimer();
private:
Core::Id m_deviceType;
const char * const id;
QElapsedTimer timer;
};
} // namespace ProjectExplorer
} // namespace ClangBackEnd
#endif // PROJECTEXPLORER_DEVICETYPEKITCHOOSER_H
#define TIME_SCOPE_DURATION(id) ClangBackEnd::VerboseScopeDurationTimer scopeDurationTimer(id)
#endif // CLANGBACKENDIPCDEBUGUTILS_H

View File

@@ -37,13 +37,13 @@
namespace ClangBackEnd {
CodeCompletedCommand::CodeCompletedCommand(const QVector<CodeCompletion> &codeCompletions, quint64 ticketNumber)
CodeCompletedCommand::CodeCompletedCommand(const CodeCompletions &codeCompletions, quint64 ticketNumber)
: codeCompletions_(codeCompletions),
ticketNumber_(ticketNumber)
{
}
const QVector<CodeCompletion> &CodeCompletedCommand::codeCompletions() const
const CodeCompletions &CodeCompletedCommand::codeCompletions() const
{
return codeCompletions_;
}

View File

@@ -48,14 +48,14 @@ class CMBIPC_EXPORT CodeCompletedCommand
friend void PrintTo(const CodeCompletedCommand &command, ::std::ostream* os);
public:
CodeCompletedCommand() = default;
CodeCompletedCommand(const QVector<CodeCompletion> &codeCompletions, quint64 ticketNumber);
CodeCompletedCommand(const CodeCompletions &codeCompletions, quint64 ticketNumber);
const QVector<CodeCompletion> &codeCompletions() const;
const CodeCompletions &codeCompletions() const;
quint64 ticketNumber() const;
private:
QVector<CodeCompletion> codeCompletions_;
CodeCompletions codeCompletions_;
quint64 ticketNumber_ = 0;
};

View File

@@ -70,12 +70,12 @@ CodeCompletion::Kind CodeCompletion::completionKind() const
return completionKind_;
}
void CodeCompletion::setChunks(const QVector<CodeCompletionChunk> &chunks)
void CodeCompletion::setChunks(const CodeCompletionChunks &chunks)
{
chunks_ = chunks;
}
const QVector<CodeCompletionChunk> &CodeCompletion::chunks() const
const CodeCompletionChunks &CodeCompletion::chunks() const
{
return chunks_;
}

View File

@@ -37,9 +37,13 @@
#include <utf8string.h>
#include <QMetaType>
#include <QVector>
namespace ClangBackEnd {
class CodeCompletion;
using CodeCompletions = QVector<CodeCompletion>;
class CMBIPC_EXPORT CodeCompletion
{
friend CMBIPC_EXPORT QDataStream &operator<<(QDataStream &out, const CodeCompletion &command);
@@ -91,8 +95,8 @@ public:
void setCompletionKind(Kind completionKind);
Kind completionKind() const;
void setChunks(const QVector<CodeCompletionChunk> &chunks);
const QVector<CodeCompletionChunk> &chunks() const;
void setChunks(const CodeCompletionChunks &chunks);
const CodeCompletionChunks &chunks() const;
void setAvailability(Availability availability);
Availability availability() const;
@@ -109,7 +113,7 @@ private:
private:
Utf8String text_;
QVector<CodeCompletionChunk> chunks_;
CodeCompletionChunks chunks_;
quint32 priority_ = 0;
Kind completionKind_ = Other;
Availability availability_ = NotAvailable;

View File

@@ -44,7 +44,7 @@ CodeCompletionChunk::CodeCompletionChunk()
CodeCompletionChunk::CodeCompletionChunk(CodeCompletionChunk::Kind kind,
const Utf8String &text,
const QVector<CodeCompletionChunk> &optionalChunks)
const CodeCompletionChunks &optionalChunks)
: text_(text),
optionalChunks_(optionalChunks),
kind_(kind)
@@ -61,7 +61,7 @@ const Utf8String &CodeCompletionChunk::text() const
return text_;
}
const QVector<CodeCompletionChunk> &CodeCompletionChunk::optionalChunks() const
const CodeCompletionChunks &CodeCompletionChunk::optionalChunks() const
{
return optionalChunks_;
}

View File

@@ -39,6 +39,9 @@
namespace ClangBackEnd {
class CodeCompletionChunk;
using CodeCompletionChunks = QVector<CodeCompletionChunk>;
class CMBIPC_EXPORT CodeCompletionChunk
{
friend CMBIPC_EXPORT QDataStream &operator<<(QDataStream &out, const CodeCompletionChunk &chunk);
@@ -73,18 +76,18 @@ public:
CodeCompletionChunk();
CodeCompletionChunk(Kind kind,
const Utf8String &text,
const QVector<CodeCompletionChunk> &optionalChunks = QVector<CodeCompletionChunk>());
const CodeCompletionChunks &optionalChunks = CodeCompletionChunks());
Kind kind() const;
const Utf8String &text() const;
const QVector<CodeCompletionChunk> &optionalChunks() const;
const CodeCompletionChunks &optionalChunks() const;
private:
quint32 &kindAsInt();
private:
Utf8String text_;
QVector<CodeCompletionChunk> optionalChunks_;
CodeCompletionChunks optionalChunks_;
Kind kind_ = Invalid;
};

View File

@@ -30,6 +30,7 @@
#include "connectionclient.h"
#include "clangbackendipcdebugutils.h"
#include "cmbcompletecodecommand.h"
#include "cmbregistertranslationunitsforcodecompletioncommand.h"
#include "cmbunregistertranslationunitsforcodecompletioncommand.h"
@@ -55,8 +56,8 @@ QString connectionName()
ConnectionClient::ConnectionClient(IpcClientInterface *client)
: serverProxy_(client, &localSocket),
isAliveTimerResetted(false),
stdErrPrefixer("ClangBackEnd-StdErr: "),
stdOutPrefixer("ClangBackEnd: ")
stdErrPrefixer("clangbackend.stderr: "),
stdOutPrefixer("clangbackend.stdout: ")
{
processAliveTimer.setInterval(10000);
@@ -76,6 +77,8 @@ ConnectionClient::~ConnectionClient()
bool ConnectionClient::connectToServer()
{
TIME_SCOPE_DURATION("ConnectionClient::connectToServer");
startProcess();
resetProcessAliveTimer();
const bool isConnected = connectToLocalSocket();
@@ -123,6 +126,8 @@ void ConnectionClient::setProcessAliveTimerInterval(int processTimerInterval)
void ConnectionClient::startProcess()
{
TIME_SCOPE_DURATION("ConnectionClient::startProcess");
if (!isProcessIsRunning()) {
connectProcessFinished();
connectStandardOutputAndError();
@@ -216,6 +221,8 @@ void ConnectionClient::printStandardError()
void ConnectionClient::finishProcess()
{
TIME_SCOPE_DURATION("ConnectionClient::finishProcess");
processAliveTimer.stop();
disconnectProcessFinished();

View File

@@ -30,6 +30,8 @@
#include "filecontainer.h"
#include "clangbackendipcdebugutils.h"
#include <QDataStream>
#include <QDebug>
@@ -108,9 +110,13 @@ QDebug operator<<(QDebug debug, const FileContainer &container)
<< ", "
<< container.projectPartId();
if (container.hasUnsavedFileContent())
if (container.hasUnsavedFileContent()) {
const Utf8String fileWithContent = debugWriteFileForInspection(
container.unsavedFileContent(),
debugId(container));
debug.nospace() << ", "
<< container.unsavedFileContent();
<< "<" << fileWithContent << ">";
}
debug.nospace() << ")";

View File

@@ -30,9 +30,10 @@
#include "projectpartcontainer.h"
#include <QDebug>
#include "clangbackendipcdebugutils.h"
#include <QDataStream>
#include <QDebug>
#include <ostream>
@@ -91,10 +92,15 @@ static Utf8String quotedArguments(const Utf8StringVector &arguments)
QDebug operator<<(QDebug debug, const ProjectPartContainer &container)
{
const Utf8String arguments = quotedArguments(container.arguments());
const Utf8String fileWithArguments = debugWriteFileForInspection(
arguments,
Utf8StringLiteral("projectpartargs-"));
debug.nospace() << "ProjectPartContainer("
<< container.projectPartId()
<< ","
<< quotedArguments(container.arguments())
<< "<" << fileWithArguments << ">"
<< ")";
return debug;

View File

@@ -65,8 +65,8 @@ void ASTPath::dump(const QList<AST *> nodes)
bool ASTPath::preVisit(AST *ast)
{
unsigned firstToken = ast->firstToken();
unsigned lastToken = ast->lastToken();
const unsigned firstToken = firstNonGeneratedToken(ast);
const unsigned lastToken = lastNonGeneratedToken(ast);
if (firstToken > 0) {
if (lastToken <= firstToken)
@@ -89,3 +89,24 @@ bool ASTPath::preVisit(AST *ast)
return false;
}
unsigned ASTPath::firstNonGeneratedToken(AST *ast) const
{
const unsigned lastTokenIndex = ast->lastToken();
unsigned tokenIndex = ast->firstToken();
while (tokenIndex <= lastTokenIndex && tokenAt(tokenIndex).generated())
++tokenIndex;
return tokenIndex;
}
unsigned ASTPath::lastNonGeneratedToken(AST *ast) const
{
const unsigned firstTokenIndex = ast->firstToken();
const unsigned lastTokenIndex = ast->lastToken();
unsigned tokenIndex = lastTokenIndex;
while (firstTokenIndex <= tokenIndex && tokenAt(tokenIndex).generated())
--tokenIndex;
return tokenIndex != lastTokenIndex
? tokenIndex + 1
: tokenIndex;
}

View File

@@ -62,7 +62,11 @@ public:
#endif
protected:
virtual bool preVisit(AST *ast);
bool preVisit(AST *ast) override;
private:
unsigned firstNonGeneratedToken(AST *ast) const;
unsigned lastNonGeneratedToken(AST *ast) const;
private:
Document::Ptr _doc;

View File

@@ -41,6 +41,7 @@
#include <cplusplus/Names.h>
#include <cplusplus/Scope.h>
#include <cplusplus/Control.h>
#include <cplusplus/cppassert.h>
#include <QStack>
#include <QHash>
@@ -1255,6 +1256,7 @@ LookupScopePrivate *LookupScopePrivate::nestedType(const Name *name, LookupScope
if (baseTemplate)
break;
}
if (baseTemplate) {
if (LookupScopePrivate *specialization =
findSpecialization(baseTemplate, templId, specializations, origin)) {
reference = specialization;
@@ -1264,6 +1266,7 @@ LookupScopePrivate *LookupScopePrivate::nestedType(const Name *name, LookupScope
}
}
}
}
// let's instantiation be instantiation
nonConstTemplId->setIsSpecialization(false);
}
@@ -1970,9 +1973,11 @@ FullySpecifiedType CreateBindings::resolveTemplateArgument(Clone &cloner,
unsigned index)
{
FullySpecifiedType ty;
CPP_ASSERT(specialization && instantiation, return ty);
const TypenameArgument *tParam
= specialization->templateParameterAt(index)->asTypenameArgument();
const TypenameArgument *tParam = 0;
if (Symbol *tArgument = specialization->templateParameterAt(index))
tParam = tArgument->asTypenameArgument();
if (!tParam)
return ty;

View File

@@ -115,27 +115,7 @@ SQLITE_EXPORT QDataStream &operator>>(QDataStream &datastream, Utf8String &text)
SQLITE_EXPORT QDebug operator<<(QDebug debug, const Utf8String &text);
SQLITE_EXPORT void PrintTo(const Utf8String &text, ::std::ostream* os);
#if defined(Q_COMPILER_LAMBDA)
# define Utf8StringLiteral(str) \
([]() -> Utf8String { \
enum { Size = sizeof(str) - 1 }; \
static const QStaticByteArrayData<Size> qbytearray_literal = { \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER(Size), \
str }; \
QByteArrayDataPtr holder = { qbytearray_literal.data_ptr() }; \
const QByteArray byteArray(holder); \
return Utf8String::fromByteArray(byteArray); \
}()) \
/**/
#endif
#ifndef Utf8StringLiteral
// no lambdas, not GCC, just return a temporary QByteArray
# define Utf8StringLiteral(str) Utf8String(str, sizeof(str) - 1)
#endif
#define Utf8StringLiteral(str) Utf8String::fromByteArray(QByteArrayLiteral(str))
Q_DECLARE_METATYPE(Utf8String)

View File

@@ -46,24 +46,10 @@ using namespace Utils;
namespace Analyzer {
namespace Internal {
class SshKitChooser : public KitChooser
{
public:
SshKitChooser(QWidget *parent = 0) : KitChooser(parent) { }
private:
bool kitMatches(const Kit *kit) const {
if (!KitChooser::kitMatches(kit))
return false;
const IDevice::ConstPtr device = DeviceKitInformation::device(kit);
return device && !device->sshParameters().host.isEmpty();
}
};
class StartRemoteDialogPrivate
{
public:
SshKitChooser *kitChooser;
KitChooser *kitChooser;
QLineEdit *executable;
QLineEdit *arguments;
QLineEdit *workingDirectory;
@@ -79,7 +65,11 @@ StartRemoteDialog::StartRemoteDialog(QWidget *parent)
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Start Remote Analysis"));
d->kitChooser = new Internal::SshKitChooser(this);
d->kitChooser = new KitChooser(this);
d->kitChooser->setKitMatcher([](const Kit *kit) {
const IDevice::ConstPtr device = DeviceKitInformation::device(kit);
return kit->isValid() && device && !device->sshParameters().host.isEmpty();
});
d->executable = new QLineEdit(this);
d->arguments = new QLineEdit(this);
d->workingDirectory = new QLineEdit(this);

View File

@@ -109,6 +109,7 @@ private:
class AndroidDeviceModelDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
AndroidDeviceModelDelegate(QObject * parent = 0)
: QStyledItemDelegate(parent)
@@ -239,6 +240,7 @@ public:
class AndroidDeviceModel : public QAbstractItemModel
{
Q_OBJECT
public:
AndroidDeviceModel(int apiLevel, const QString &abi, AndroidConfigurations::Options options);
QModelIndex index(int row, int column,
@@ -663,3 +665,5 @@ void AndroidDeviceDialog::defaultDeviceClear()
m_ui->lookingForDeviceCancel->setVisible(false);
m_defaultDevice.clear();
}
#include "androiddevicedialog.moc"

View File

@@ -48,9 +48,9 @@ Utils::FileName Android::AndroidQtSupport::apkPath(ProjectExplorer::Target *targ
QString apkPath;
if (buildApkStep->useGradle())
apkPath = QLatin1String("/build/outputs/apk/android-build-");
apkPath = QLatin1String("build/outputs/apk/android-build-");
else
apkPath = QLatin1String("/bin/QtApp-");
apkPath = QLatin1String("bin/QtApp-");
if (buildApkStep->signPackage())
apkPath += QLatin1String("release.apk");
else

View File

@@ -0,0 +1,272 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "activationsequencecontextprocessor.h"
#include "activationsequenceprocessor.h"
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/SimpleLexer.h>
#include <QRegExp>
#include <QTextDocument>
namespace ClangCodeModel {
namespace Internal {
ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *assistInterface)
: m_textCursor(assistInterface->textDocument()),
m_assistInterface(assistInterface),
m_positionInDocument(assistInterface->position()),
m_startOfNamePosition(m_positionInDocument),
m_operatorStartPosition(m_positionInDocument)
{
m_textCursor.setPosition(m_positionInDocument);
process();
}
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
{
return m_completionKind;
}
const QTextCursor &ActivationSequenceContextProcessor::textCursor_forTestOnly() const
{
return m_textCursor;
}
int ActivationSequenceContextProcessor::startOfNamePosition() const
{
return m_startOfNamePosition;
}
int ActivationSequenceContextProcessor::operatorStartPosition() const
{
return m_operatorStartPosition;
}
void ActivationSequenceContextProcessor::process()
{
goBackToStartOfName();
processActivationSequence();
if (m_completionKind != CPlusPlus::T_EOF_SYMBOL) {
processStringLiteral();
processComma();
generateTokens();
processDoxygenComment();
processComment();
processInclude();
processSlashOutsideOfAString();
processLeftParen();
processPreprocessorInclude();
}
resetPositionsForEOFCompletionKind();
}
void ActivationSequenceContextProcessor::processActivationSequence()
{
const int nonSpacePosition = skipPrecedingWhitespace(m_assistInterface, m_startOfNamePosition);
const auto activationSequence = m_assistInterface->textAt(nonSpacePosition - 3, 3);
ActivationSequenceProcessor activationSequenceProcessor(activationSequence,
nonSpacePosition,
true);
m_completionKind = activationSequenceProcessor.completionKind();
m_operatorStartPosition = activationSequenceProcessor.operatorStartPosition();
}
void ActivationSequenceContextProcessor::processStringLiteral()
{
if (m_completionKind == CPlusPlus::T_STRING_LITERAL) {
QTextCursor selectionTextCursor = m_textCursor;
selectionTextCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString selection = selectionTextCursor.selectedText();
if (selection.indexOf(QLatin1Char('"')) < selection.length() - 1)
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
}
void ActivationSequenceContextProcessor::processComma()
{
if (m_completionKind == CPlusPlus::T_COMMA) {
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(m_assistInterface->languageFeatures());
if (expressionUnderCursor.startOfFunctionCall(m_textCursor) == -1)
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
}
void ActivationSequenceContextProcessor::generateTokens()
{
CPlusPlus::SimpleLexer tokenize;
tokenize.setLanguageFeatures(m_assistInterface->languageFeatures());
tokenize.setSkipComments(false);
auto state = CPlusPlus::BackwardsScanner::previousBlockState(m_textCursor.block());
m_tokens = tokenize(m_textCursor.block().text(), state);
int leftOfCursorTokenIndex = std::max(0, m_textCursor.positionInBlock() - 1);
m_tokenIndex= CPlusPlus::SimpleLexer::tokenBefore(m_tokens, leftOfCursorTokenIndex); // get the token at the left of the cursor
if (m_tokenIndex > -1)
m_token = m_tokens.at(m_tokenIndex);
}
void ActivationSequenceContextProcessor::processDoxygenComment()
{
if (m_completionKind == CPlusPlus::T_DOXY_COMMENT
&& !(m_token.is(CPlusPlus::T_DOXY_COMMENT)
|| m_token.is(CPlusPlus::T_CPP_DOXY_COMMENT)))
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
void ActivationSequenceContextProcessor::processComment()
{
if (m_token.is(CPlusPlus::T_COMMENT) || m_token.is(CPlusPlus::T_CPP_COMMENT))
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
void ActivationSequenceContextProcessor::processInclude()
{
if (m_token.isLiteral() && !isCompletionKindStringLiteralOrSlash())
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
void ActivationSequenceContextProcessor::processSlashOutsideOfAString()
{
if (m_completionKind ==CPlusPlus::T_SLASH
&& (m_token.isNot(CPlusPlus::T_STRING_LITERAL)
&& m_token.isNot(CPlusPlus::T_ANGLE_STRING_LITERAL)))
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
void ActivationSequenceContextProcessor::processLeftParen()
{
if (m_completionKind == CPlusPlus::T_LPAREN) {
if (m_tokenIndex > 0) {
// look at the token at the left of T_LPAREN
const CPlusPlus::Token &previousToken = m_tokens.at(m_tokenIndex - 1);
switch (previousToken.kind()) {
case CPlusPlus::T_IDENTIFIER:
case CPlusPlus::T_GREATER:
case CPlusPlus::T_SIGNAL:
case CPlusPlus::T_SLOT:
break; // good
default:
// that's a bad token :)
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
}
}
}
bool ActivationSequenceContextProcessor::isCompletionKindStringLiteralOrSlash() const
{
return m_completionKind == CPlusPlus::T_STRING_LITERAL
|| m_completionKind == CPlusPlus::T_ANGLE_STRING_LITERAL
|| m_completionKind == CPlusPlus::T_SLASH;
}
bool ActivationSequenceContextProcessor::isProbablyPreprocessorIncludeDirective() const
{
return m_tokens.size() >= 3
&& m_tokens.at(0).is(CPlusPlus::T_POUND)
&& m_tokens.at(1).is(CPlusPlus::T_IDENTIFIER)
&& (m_tokens.at(2).is(CPlusPlus::T_STRING_LITERAL)
|| m_tokens.at(2).is(CPlusPlus::T_ANGLE_STRING_LITERAL));
}
void ActivationSequenceContextProcessor::processPreprocessorInclude()
{
if (isCompletionKindStringLiteralOrSlash()) {
if (isProbablyPreprocessorIncludeDirective()) {
const CPlusPlus::Token &directiveToken = m_tokens.at(1);
QString directive = m_textCursor.block().text().mid(directiveToken.bytesBegin(),
directiveToken.bytes());
if (directive != QStringLiteral("include")
&& directive != QStringLiteral("include_next")
&& directive != QStringLiteral("import"))
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
} else {
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
}
}
}
void ActivationSequenceContextProcessor::resetPositionsForEOFCompletionKind()
{
if (m_completionKind == CPlusPlus::T_EOF_SYMBOL)
m_operatorStartPosition = m_positionInDocument;
}
int ActivationSequenceContextProcessor::skipPrecedingWhitespace(
const TextEditor::AssistInterface *assistInterface,
int startPosition)
{
int position = startPosition;
while (assistInterface->characterAt(position - 1).isSpace())
--position;
return position;
}
static bool isValidIdentifierChar(const QChar &character)
{
return character.isLetterOrNumber()
|| character == QLatin1Char('_')
|| character.isHighSurrogate()
|| character.isLowSurrogate();
}
int ActivationSequenceContextProcessor::findStartOfName(
const TextEditor::AssistInterface *assistInterface,
int startPosition)
{
int position = startPosition;
QChar character;
do {
character = assistInterface->characterAt(--position);
} while (isValidIdentifierChar(character));
return position + 1;
}
void ActivationSequenceContextProcessor::goBackToStartOfName()
{
m_startOfNamePosition = findStartOfName(m_assistInterface, m_positionInDocument);
if (m_startOfNamePosition != m_positionInDocument)
m_textCursor.setPosition(m_startOfNamePosition);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef CLANGCODEMODEL_INTERNAL_ACTIVATIONSEQUENCECONTEXTPROCESSOR_H
#define CLANGCODEMODEL_INTERNAL_ACTIVATIONSEQUENCECONTEXTPROCESSOR_H
#include <clangcodemodel/clangcompletionassistinterface.h>
#include <cplusplus/Token.h>
#include <QTextCursor>
QT_BEGIN_NAMESPACE
class QTextDocument;
QT_END_NAMESPACE
namespace ClangCodeModel {
namespace Internal {
class ActivationSequenceContextProcessor
{
public:
ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *assistInterface);
CPlusPlus::Kind completionKind() const;
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"
int operatorStartPosition() const; // e.g. points to '.' for "foo.bar<CURSOR>"
const QTextCursor &textCursor_forTestOnly() const;
static int findStartOfName(const TextEditor::AssistInterface *assistInterface,
int startPosition);
static int skipPrecedingWhitespace(const TextEditor::AssistInterface *assistInterface,
int startPosition);
protected:
void process();
void goBackToStartOfName();
void processActivationSequence();
void processStringLiteral();
void processComma();
void generateTokens();
void processDoxygenComment();
void processComment();
void processInclude();
void processSlashOutsideOfAString();
void processLeftParen();
void processPreprocessorInclude();
void resetPositionsForEOFCompletionKind();
bool isCompletionKindStringLiteralOrSlash() const;
bool isProbablyPreprocessorIncludeDirective() const;
private:
QVector<CPlusPlus::Token> m_tokens;
QTextCursor m_textCursor;
CPlusPlus::Token m_token;
const ClangCompletionAssistInterface *m_assistInterface;
int m_tokenIndex;
const int m_positionInDocument;
int m_startOfNamePosition;
int m_operatorStartPosition;
CPlusPlus::Kind m_completionKind;
};
} // namespace Internal
} // namespace ClangCodeModel
#endif // CLANGCODEMODEL_INTERNAL_ACTIVATIONSEQUENCECONTEXTPROCESSOR_H

View File

@@ -71,7 +71,7 @@ int ActivationSequenceProcessor::offset() const
return m_offset;
}
int ActivationSequenceProcessor::position() const
int ActivationSequenceProcessor::operatorStartPosition() const
{
return m_positionInDocument - m_offset;
}
@@ -164,8 +164,8 @@ void ActivationSequenceProcessor::processArrowStar()
void ActivationSequenceProcessor::processDoxyGenComment()
{
if ((m_char2 == QLatin1Char('\\') || m_char2 == QLatin1Char('@'))
&& (m_char3.isNull() || m_char3.isSpace())) {
if ((m_char2.isNull() || m_char2.isSpace())
&& (m_char3 == QLatin1Char('\\') || m_char3 == QLatin1Char('@'))) {
m_completionKind = CPlusPlus::T_DOXY_COMMENT;
m_offset = 1;
}

View File

@@ -47,7 +47,7 @@ public:
CPlusPlus::Kind completionKind() const;
int offset() const;
int position() const;
int operatorStartPosition() const; // e.g. points to '.' for "foo.bar<CURSOR>"
private:
void extractCharactersBeforePosition(const QString &activationString);

View File

@@ -54,8 +54,6 @@ bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
applies = QString::fromLatin1("(,").contains(typedChar);
else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
applies = (typedChar == QLatin1Char('/')) && text().endsWith(QLatin1Char('/'));
else if (!isCodeCompletion())
applies = (typedChar == QLatin1Char('(')); /* && data().canConvert<CompleteFunctionDeclaration>()*/ //###
else if (codeCompletion().completionKind() == CodeCompletion::ObjCMessageCompletionKind)
applies = QString::fromLatin1(";.,").contains(typedChar);
else
@@ -247,18 +245,14 @@ void ClangAssistProposalItem::addOverload(const CodeCompletion &ccr)
m_overloads.append(ccr);
}
CodeCompletion ClangAssistProposalItem::codeCompletion() const
void ClangAssistProposalItem::setCodeCompletion(const CodeCompletion &codeCompletion)
{
const QVariant &value = data();
if (value.canConvert<CodeCompletion>())
return value.value<CodeCompletion>();
else
return CodeCompletion();
m_codeCompletion = codeCompletion;
}
bool ClangAssistProposalItem::isCodeCompletion() const
const ClangBackEnd::CodeCompletion &ClangAssistProposalItem::codeCompletion() const
{
return data().canConvert<CodeCompletion>();
return m_codeCompletion;
}
} // namespace Internal

View File

@@ -52,13 +52,14 @@ public:
bool isOverloaded() const;
void addOverload(const ClangBackEnd::CodeCompletion &ccr);
ClangBackEnd::CodeCompletion codeCompletion() const;
void setCodeCompletion(const ClangBackEnd::CodeCompletion &codeCompletion);
const ClangBackEnd::CodeCompletion &codeCompletion() const;
bool isCodeCompletion() const;
private:
ClangBackEnd::CodeCompletion m_codeCompletion;
QList<ClangBackEnd::CodeCompletion> m_overloads;
unsigned m_completionOperator;
mutable QChar m_typedChar;
QList<ClangBackEnd::CodeCompletion> m_overloads;
};
} // namespace Internal

View File

@@ -145,8 +145,11 @@ void IpcReceiver::codeCompleted(const CodeCompletedCommand &command)
const quint64 ticket = command.ticketNumber();
QScopedPointer<ClangCompletionAssistProcessor> processor(m_assistProcessorsTable.take(ticket));
if (processor)
processor->asyncCompletionsAvailable(command.codeCompletions());
if (processor) {
const bool finished = processor->handleAvailableAsyncCompletions(command.codeCompletions());
if (!finished)
processor.take();
}
}
void IpcReceiver::translationUnitDoesNotExist(const TranslationUnitDoesNotExistCommand &command)
@@ -233,13 +236,10 @@ static bool areCommandsRegistered = false;
void IpcCommunicator::initializeBackend()
{
// TODO: Add a asynchron API to ConnectionClient, otherwise we might hang here
if (!areCommandsRegistered) {
areCommandsRegistered = true;
Commands::registerCommands();
}
QElapsedTimer timer; timer.start();
const QString clangBackEndProcessPath = backendProcessPath();
qCDebug(log) << "Starting" << clangBackEndProcessPath;
@@ -251,12 +251,9 @@ void IpcCommunicator::initializeBackend()
connect(&m_connection, &ConnectionClient::processRestarted,
this, &IpcCommunicator::onBackendRestarted);
if (m_connection.connectToServer()) {
qCDebug(log) << "...started and connected in" << timer.elapsed() << "ms.";
// TODO: Add a asynchron API to ConnectionClient, otherwise we might hang here
if (m_connection.connectToServer())
initializeBackendWithCurrentData();
} else {
qCDebug(log) << "...failed.";
}
}
void IpcCommunicator::registerEmptyProjectForProjectLessFiles()
@@ -310,8 +307,7 @@ static ClangBackEnd::ProjectPartContainer toProjectPartContainer(
const CppTools::ProjectPart::Ptr &projectPart)
{
const QStringList arguments = projectPartCommandLine(projectPart);
return ClangBackEnd::ProjectPartContainer(projectPart->projectFile,
Utf8StringVector(arguments));
return ClangBackEnd::ProjectPartContainer(projectPart->id(), Utf8StringVector(arguments));
}
static QVector<ClangBackEnd::ProjectPartContainer> toProjectPartContainers(
@@ -339,13 +335,13 @@ void IpcCommunicator::updateUnsavedFileFromCppEditorDocument(const QString &file
void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArray &contents)
{
const QString projectFilePath = Utils::projectFilePathForFile(filePath);
const QString projectPartId = Utils::projectPartIdForFile(filePath);
const bool hasUnsavedContent = true;
// TODO: Send new only if changed
registerFilesForCodeCompletion({
ClangBackEnd::FileContainer(filePath,
projectFilePath,
projectPartId,
Utf8String::fromByteArray(contents),
hasUnsavedContent)
});
@@ -432,12 +428,12 @@ void IpcCommunicator::registerProjectPartsForCodeCompletion(
m_ipcSender->registerProjectPartsForCodeCompletion(command);
}
void IpcCommunicator::unregisterProjectPartsForCodeCompletion(const QStringList &filePaths)
void IpcCommunicator::unregisterProjectPartsForCodeCompletion(const QStringList &projectPartIds)
{
if (m_sendMode == IgnoreSendRequests)
return;
const UnregisterProjectPartsForCodeCompletionCommand command((Utf8StringVector(filePaths)));
const UnregisterProjectPartsForCodeCompletionCommand command((Utf8StringVector(projectPartIds)));
qCDebug(log) << ">>>" << command;
m_ipcSender->unregisterProjectPartsForCodeCompletion(command);
}

View File

@@ -112,7 +112,7 @@ public:
void registerFilesForCodeCompletion(const FileContainers &fileContainers);
void unregisterFilesForCodeCompletion(const FileContainers &fileContainers);
void registerProjectPartsForCodeCompletion(const ProjectPartContainers &projectPartContainers);
void unregisterProjectPartsForCodeCompletion(const QStringList &filePaths);
void unregisterProjectPartsForCodeCompletion(const QStringList &projectPartIds);
void completeCode(ClangCompletionAssistProcessor *assistProcessor, const QString &filePath,
quint32 line,
quint32 column,

View File

@@ -12,6 +12,7 @@ DEFINES += "\"CLANG_RESOURCE_DIR=\\\"$${LLVM_LIBDIR}/clang/$${LLVM_VERSION}/incl
unix:QMAKE_LFLAGS += -Wl,-rpath,\'$$LLVM_LIBDIR\'
SOURCES += \
activationsequencecontextprocessor.cpp \
activationsequenceprocessor.cpp \
clangassistproposal.cpp \
clangassistproposalitem.cpp \
@@ -48,6 +49,7 @@ SOURCES += \
HEADERS += \
activationsequencecontextprocessor.h \
activationsequenceprocessor.h \
clangassistproposal.h \
clangassistproposalitem.h \
@@ -107,13 +109,10 @@ equals(TEST, 1) {
test/clang_tests_database.qrc
HEADERS += \
test/clangcodecompletion_test.h \
test/clangcompletioncontextanalyzertest.h
test/clangcodecompletion_test.h
SOURCES += \
test/clangcodecompletion_test.cpp \
test/clangcompletioncontextanalyzertest.cpp
test/clangcodecompletion_test.cpp
DISTFILES += \
test/mysource.cpp \

View File

@@ -1,6 +1,7 @@
import qbs
import qbs.File
import QtcClangInstallation as Clang
import QtcFunctions
import QtcProcessOutputReader
QtcPlugin {
@@ -23,7 +24,7 @@ QtcPlugin {
property bool clangHighlighting: true
property bool clangIndexing: false
property string llvmConfig: Clang.llvmConfig(qbs)
property string llvmConfig: Clang.llvmConfig(qbs, QtcFunctions, QtcProcessOutputReader)
property string llvmIncludeDir: Clang.includeDir(llvmConfig, QtcProcessOutputReader)
property string llvmLibDir: Clang.libDir(llvmConfig, QtcProcessOutputReader)
property string llvmLibs: Clang.libraries(qbs.targetOS)
@@ -53,6 +54,8 @@ QtcPlugin {
name: "Completion support"
condition: product.clangCompletion
files: [
"activationsequencecontextprocessor.cpp",
"activationsequencecontextprocessor.h",
"activationsequenceprocessor.cpp",
"activationsequenceprocessor.h",
"clangassistproposal.cpp",
@@ -104,8 +107,6 @@ QtcPlugin {
"clang_tests_database.qrc",
"clangcodecompletion_test.cpp",
"clangcodecompletion_test.h",
"clangcompletioncontextanalyzertest.cpp",
"clangcompletioncontextanalyzertest.h",
]
}

View File

@@ -36,7 +36,6 @@
#ifdef WITH_TESTS
# include "test/clangcodecompletion_test.h"
# include "test/clangcompletioncontextanalyzertest.h"
#endif
#include <cpptools/cppmodelmanager.h>
@@ -93,7 +92,6 @@ QList<QObject *> ClangCodeModelPlugin::createTestObjects() const
{
return {
new Tests::ClangCodeCompletionTest,
new Tests::ClangCompletionContextAnalyzerTest
};
}
#endif

View File

@@ -1,7 +1,11 @@
INCLUDEPATH += $$PWD
SOURCES += $$PWD/completionchunkstotextconverter.cpp \
$$PWD/activationsequenceprocessor.cpp
$$PWD/activationsequenceprocessor.cpp \
$$PWD/activationsequencecontextprocessor.cpp \
$$PWD/clangcompletioncontextanalyzer.cpp
HEADERS += $$PWD/completionchunkstotextconverter.h \
$$PWD/activationsequenceprocessor.h
$$PWD/activationsequenceprocessor.h \
$$PWD/activationsequencecontextprocessor.h \
$$PWD/clangcompletioncontextanalyzer.h

View File

@@ -61,6 +61,7 @@
namespace ClangCodeModel {
namespace Internal {
using ClangBackEnd::CodeCompletion;
using TextEditor::AssistProposalItem;
namespace {
@@ -72,8 +73,6 @@ QList<AssistProposalItem *> toAssistProposalItems(const CodeCompletions &complet
{
static CPlusPlus::Icons m_icons; // de-deduplicate
QList<AssistProposalItem *> result;
bool signalCompletion = false; // TODO
bool slotCompletion = false; // TODO
@@ -105,11 +104,10 @@ QList<AssistProposalItem *> toAssistProposalItems(const CodeCompletions &complet
if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind)
item->setDetail(CompletionChunksToTextConverter::convertToToolTip(ccr.chunks()));
item->setData(QVariant::fromValue(ccr));
item->setCodeCompletion(ccr);
}
// FIXME: show the effective accessebility instead of availability
using ClangBackEnd::CodeCompletion;
using CPlusPlus::Icons;
switch (ccr.completionKind()) {
@@ -179,10 +177,11 @@ QList<AssistProposalItem *> toAssistProposalItems(const CodeCompletions &complet
}
}
foreach (ClangAssistProposalItem *item, items.values())
result.append(item);
QList<AssistProposalItem *> results;
results.reserve(items.size());
std::copy(items.cbegin(), items.cend(), std::back_inserter(results));
return result;
return results;
}
bool isFunctionHintLikeCompletion(CodeCompletion::Kind kind)
@@ -194,10 +193,10 @@ bool isFunctionHintLikeCompletion(CodeCompletion::Kind kind)
|| kind == CodeCompletion::SlotCompletionKind;
}
QVector<CodeCompletion> matchingFunctionCompletions(const QVector<CodeCompletion> completions,
CodeCompletions matchingFunctionCompletions(const CodeCompletions completions,
const QString &functionName)
{
QVector<CodeCompletion> matching;
CodeCompletions matching;
foreach (const CodeCompletion &completion, completions) {
if (isFunctionHintLikeCompletion(completion.completionKind())
@@ -227,25 +226,32 @@ IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface *
{
m_interface.reset(static_cast<const ClangCompletionAssistInterface *>(interface));
if (interface->reason() != ExplicitlyInvoked && !accepts())
if (interface->reason() != ExplicitlyInvoked && !accepts()) {
setPerformWasApplicable(false);
return 0;
}
return startCompletionHelper(); // == 0 if results are calculated asynchronously
}
void ClangCompletionAssistProcessor::asyncCompletionsAvailable(const CodeCompletions &completions)
bool ClangCompletionAssistProcessor::handleAvailableAsyncCompletions(
const CodeCompletions &completions)
{
bool handled = true;
switch (m_sentRequestType) {
case CompletionRequestType::NormalCompletion:
onCompletionsAvailable(completions);
handleAvailableCompletions(completions);
break;
case CompletionRequestType::FunctionHintCompletion:
onFunctionHintCompletionsAvailable(completions);
handled = handleAvailableFunctionHintCompletions(completions);
break;
default:
QTC_CHECK(!"Unhandled ClangCompletionAssistProcessor::CompletionRequestType");
break;
}
return handled;
}
const TextEditorWidget *ClangCompletionAssistProcessor::textEditorWidget() const
@@ -302,8 +308,6 @@ static QByteArray modifyInput(QTextDocument *doc, int endOfExpression) {
IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper()
{
sendFileContent(Utils::projectFilePathForFile(m_interface->fileName()), QByteArray()); // TODO: Remoe
ClangCompletionContextAnalyzer analyzer(m_interface.data(), m_interface->languageFeatures());
analyzer.analyze();
m_completionOperator = analyzer.completionOperator();
@@ -361,7 +365,7 @@ int ClangCompletionAssistProcessor::startOfOperator(int positionInDocument,
*kind = activationSequenceProcessor.completionKind();
int start = activationSequenceProcessor.position();
int start = activationSequenceProcessor.operatorStartPosition();
if (start != positionInDocument) {
QTextCursor tc(m_interface->textDocument());
tc.setPosition(positionInDocument);
@@ -648,34 +652,44 @@ void ClangCompletionAssistProcessor::addCompletionItem(const QString &text,
m_completions.append(item);
}
void ClangCompletionAssistProcessor::sendFileContent(const QString &projectFilePath,
const QByteArray &modifiedFileContent)
ClangCompletionAssistProcessor::UnsavedFileContentInfo
ClangCompletionAssistProcessor::unsavedFileContent(const QByteArray &customFileContent) const
{
const QString filePath = m_interface->fileName();
const QByteArray unsavedContent = modifiedFileContent.isEmpty()
? m_interface->textDocument()->toPlainText().toUtf8()
: modifiedFileContent;
const bool hasUnsavedContent = true; // TODO
const bool hasCustomModification = !customFileContent.isEmpty();
UnsavedFileContentInfo info;
info.isDocumentModified = hasCustomModification || m_interface->textDocument()->isModified();
info.unsavedContent = hasCustomModification
? customFileContent
: m_interface->textDocument()->toPlainText().toUtf8();
return info;
}
void ClangCompletionAssistProcessor::sendFileContent(const QString &projectPartId,
const QByteArray &customFileContent)
{
// TODO: Revert custom modification after the completions
const UnsavedFileContentInfo info = unsavedFileContent(customFileContent);
IpcCommunicator &ipcCommunicator = m_interface->ipcCommunicator();
ipcCommunicator.registerFilesForCodeCompletion(
{ClangBackEnd::FileContainer(filePath,
projectFilePath,
Utf8String::fromByteArray(unsavedContent),
hasUnsavedContent)});
{ClangBackEnd::FileContainer(m_interface->fileName(),
projectPartId,
Utf8String::fromByteArray(info.unsavedContent),
info.isDocumentModified)});
}
void ClangCompletionAssistProcessor::sendCompletionRequest(int position,
const QByteArray &modifiedFileContent)
const QByteArray &customFileContent)
{
int line, column;
TextEditor::Convenience::convertPosition(m_interface->textDocument(), position, &line, &column);
++column;
const QString filePath = m_interface->fileName();
const QString projectFilePath = Utils::projectFilePathForFile(filePath);
sendFileContent(projectFilePath, modifiedFileContent);
m_interface->ipcCommunicator().completeCode(this, filePath, line, column, projectFilePath);
const QString projectPartId = Utils::projectPartIdForFile(filePath);
sendFileContent(projectPartId, customFileContent);
m_interface->ipcCommunicator().completeCode(this, filePath, line, column, projectPartId);
}
TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal() const
@@ -685,7 +699,7 @@ TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal() co
return new ClangAssistProposal(m_positionForProposal, model);
}
void ClangCompletionAssistProcessor::onCompletionsAvailable(const CodeCompletions &completions)
void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeCompletions &completions)
{
QTC_CHECK(m_completions.isEmpty());
@@ -696,20 +710,24 @@ void ClangCompletionAssistProcessor::onCompletionsAvailable(const CodeCompletion
setAsyncProposalAvailable(createProposal());
}
void ClangCompletionAssistProcessor::onFunctionHintCompletionsAvailable(
bool ClangCompletionAssistProcessor::handleAvailableFunctionHintCompletions(
const CodeCompletions &completions)
{
QTC_CHECK(!m_functionName.isEmpty());
const auto relevantCompletions = matchingFunctionCompletions(completions, m_functionName);
if (!relevantCompletions.isEmpty()) {
TextEditor::IFunctionHintProposalModel *model = new ClangFunctionHintModel(relevantCompletions);
TextEditor::FunctionHintProposal *proposal = new FunctionHintProposal(m_positionForProposal, model);
auto *model = new ClangFunctionHintModel(relevantCompletions);
auto *proposal = new FunctionHintProposal(m_positionForProposal, model);
setAsyncProposalAvailable(proposal);
return true;
} else {
QTC_CHECK(!"Function completion failed. Would fallback to global completion here...");
// TODO: If we need this, the processor can't be deleted in IpcClient.
m_addSnippets = false;
m_functionName.clear();
m_sentRequestType = NormalCompletion;
sendCompletionRequest(m_interface->position(), QByteArray());
return false; // We are not yet finished.
}
}

View File

@@ -34,20 +34,16 @@
#include "clangcompletionassistinterface.h"
#include <cpptools/cppcompletionassistprocessor.h>
#include <texteditor/texteditor.h>
#include <QCoreApplication>
#include <clangbackendipc/codecompletion.h>
namespace ClangBackEnd {
class CodeCompletion;
}
#include <QCoreApplication>
namespace ClangCodeModel {
namespace Internal {
using CodeCompletions = QVector<ClangBackEnd::CodeCompletion>;
using ClangBackEnd::CodeCompletion;
using ClangBackEnd::CodeCompletions;
class ClangCompletionAssistProcessor : public CppTools::CppCompletionAssistProcessor
{
@@ -59,7 +55,7 @@ public:
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
void asyncCompletionsAvailable(const CodeCompletions &completions);
bool handleAvailableAsyncCompletions(const CodeCompletions &completions);
const TextEditor::TextEditorWidget *textEditorWidget() const;
@@ -81,11 +77,17 @@ private:
int order = 0,
const QVariant &data = QVariant());
void sendFileContent(const QString &projectFilePath, const QByteArray &modifiedFileContent);
void sendCompletionRequest(int position, const QByteArray &modifiedFileContent);
struct UnsavedFileContentInfo {
QByteArray unsavedContent;
bool isDocumentModified = false;
};
UnsavedFileContentInfo unsavedFileContent(const QByteArray &customFileContent) const;
void onCompletionsAvailable(const CodeCompletions &completions);
void onFunctionHintCompletionsAvailable(const CodeCompletions &completions);
void sendFileContent(const QString &projectPartId, const QByteArray &customFileContent);
void sendCompletionRequest(int position, const QByteArray &customFileContent);
void handleAvailableCompletions(const CodeCompletions &completions);
bool handleAvailableFunctionHintCompletions(const CodeCompletions &completions);
private:
QScopedPointer<const ClangCompletionAssistInterface> m_interface;

View File

@@ -31,6 +31,9 @@
#include "clangcompletioncontextanalyzer.h"
#include "activationsequenceprocessor.h"
#include "activationsequencecontextprocessor.h"
#include <texteditor/codeassist/assistinterface.h>
#include <cplusplus/BackwardsScanner.h>
@@ -47,80 +50,6 @@ using namespace CPlusPlus;
namespace {
int activationSequenceChar(const QChar &ch, const QChar &ch2, const QChar &ch3,
unsigned *kind, bool wantFunctionCall)
{
int referencePosition = 0;
int completionKind = T_EOF_SYMBOL;
switch (ch.toLatin1()) {
case '.':
if (ch2 != QLatin1Char('.')) {
completionKind = T_DOT;
referencePosition = 1;
}
break;
case ',':
completionKind = T_COMMA;
referencePosition = 1;
break;
case '(':
if (wantFunctionCall) {
completionKind = T_LPAREN;
referencePosition = 1;
}
break;
case ':':
if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
completionKind = T_COLON_COLON;
referencePosition = 2;
}
break;
case '>':
if (ch2 == QLatin1Char('-')) {
completionKind = T_ARROW;
referencePosition = 2;
}
break;
case '*':
if (ch2 == QLatin1Char('.')) {
completionKind = T_DOT_STAR;
referencePosition = 2;
} else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) {
completionKind = T_ARROW_STAR;
referencePosition = 3;
}
break;
case '\\':
case '@':
if (ch2.isNull() || ch2.isSpace()) {
completionKind = T_DOXY_COMMENT;
referencePosition = 1;
}
break;
case '<':
completionKind = T_ANGLE_STRING_LITERAL;
referencePosition = 1;
break;
case '"':
completionKind = T_STRING_LITERAL;
referencePosition = 1;
break;
case '/':
completionKind = T_SLASH;
referencePosition = 1;
break;
case '#':
completionKind = T_POUND;
referencePosition = 1;
break;
}
if (kind)
*kind = completionKind;
return referencePosition;
}
bool isTokenForIncludePathCompletion(unsigned tokenKind)
{
return tokenKind == T_STRING_LITERAL
@@ -143,7 +72,7 @@ namespace ClangCodeModel {
namespace Internal {
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
const TextEditor::AssistInterface *assistInterface,
const ClangCompletionAssistInterface *assistInterface,
CPlusPlus::LanguageFeatures languageFeatures)
: m_interface(assistInterface)
, m_languageFeatures(languageFeatures)
@@ -153,79 +82,33 @@ ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
void ClangCompletionContextAnalyzer::analyze()
{
QTC_ASSERT(m_interface, return);
const int startOfName = findStartOfName();
m_positionForProposal = startOfName;
setActionAndClangPosition(PassThroughToLibClang, -1);
const int endOfOperator = skipPrecedingWhitespace(startOfName);
m_completionOperator = T_EOF_SYMBOL;
m_positionEndOfExpression = startOfOperator(endOfOperator, &m_completionOperator,
/*want function call =*/ true);
ActivationSequenceContextProcessor activationSequenceContextProcessor(m_interface);
m_completionOperator = activationSequenceContextProcessor.completionKind();
int afterOperatorPosition = activationSequenceContextProcessor.startOfNamePosition();
m_positionEndOfExpression = activationSequenceContextProcessor.operatorStartPosition();
m_positionForProposal = activationSequenceContextProcessor.startOfNamePosition();
if (isTokenForPassThrough(m_completionOperator)) {
setActionAndClangPosition(PassThroughToLibClang, endOfOperator);
return;
} else if (m_completionOperator == T_DOXY_COMMENT) {
setActionAndClangPosition(CompleteDoxygenKeyword, -1);
return;
} else if (m_completionOperator == T_POUND) {
// TODO: Check if libclang can complete preprocessor directives
setActionAndClangPosition(CompletePreprocessorDirective, -1);
return;
} else if (isTokenForIncludePathCompletion(m_completionOperator)) {
setActionAndClangPosition(CompleteIncludePath, -1);
return;
const bool actionIsSet = handleNonFunctionCall(afterOperatorPosition);
if (!actionIsSet) {
handleCommaInFunctionCall();
handleFunctionCall(afterOperatorPosition);
}
ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
QTextCursor textCursor(m_interface->textDocument());
if (m_completionOperator == T_COMMA) { // For function hints
textCursor.setPosition(m_positionEndOfExpression);
const int start = expressionUnderCursor.startOfFunctionCall(textCursor);
QTC_ASSERT(start != -1, setActionAndClangPosition(PassThroughToLibClang, startOfName); return);
m_positionEndOfExpression = start;
m_positionForProposal = start + 1; // After '(' of function call
m_completionOperator = T_LPAREN;
}
if (m_completionOperator == T_LPAREN) {
textCursor.setPosition(m_positionEndOfExpression);
const QString expression = expressionUnderCursor(textCursor);
if (expression.endsWith(QLatin1String("SIGNAL"))) {
setActionAndClangPosition(CompleteSignal, endOfOperator);
} else if (expression.endsWith(QLatin1String("SLOT"))) {
setActionAndClangPosition(CompleteSlot, endOfOperator);
} else if (m_interface->position() != endOfOperator) {
// No function completion if cursor is not after '(' or ','
m_positionForProposal = startOfName;
setActionAndClangPosition(PassThroughToLibClang, endOfOperator);
} else {
const FunctionInfo functionInfo = analyzeFunctionCall(endOfOperator);
m_functionName = functionInfo.functionName;
setActionAndClangPosition(PassThroughToLibClangAfterLeftParen,
functionInfo.functionNamePosition);
}
return;
}
QTC_CHECK(!"Unexpected completion context");
setActionAndClangPosition(PassThroughToLibClang, startOfName);
return;
}
ClangCompletionContextAnalyzer::FunctionInfo ClangCompletionContextAnalyzer::analyzeFunctionCall(
int endOfOperator) const
ClangCompletionContextAnalyzer::FunctionInfo
ClangCompletionContextAnalyzer::analyzeFunctionCall(int endOfOperator) const
{
int index = skipPrecedingWhitespace(endOfOperator);
int index = ActivationSequenceContextProcessor::skipPrecedingWhitespace(m_interface,
endOfOperator);
QTextCursor textCursor(m_interface->textDocument());
textCursor.setPosition(index);
ExpressionUnderCursor euc(m_languageFeatures);
index = euc.startOfFunctionCall(textCursor);
const int functionNameStart = findStartOfName(index);
const int functionNameStart = ActivationSequenceContextProcessor::findStartOfName(m_interface,
index);
QTextCursor textCursor2(m_interface->textDocument());
textCursor2.setPosition(functionNameStart);
@@ -237,127 +120,6 @@ ClangCompletionContextAnalyzer::FunctionInfo ClangCompletionContextAnalyzer::ana
return info;
}
int ClangCompletionContextAnalyzer::findStartOfName(int position) const
{
if (position == -1)
position = m_interface->position();
QChar chr;
do {
chr = m_interface->characterAt(--position);
// TODO: Check also chr.isHighSurrogate() / ch.isLowSurrogate()?
// See also CppTools::isValidFirstIdentifierChar
} while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
return position + 1;
}
int ClangCompletionContextAnalyzer::skipPrecedingWhitespace(int position) const
{
QTC_ASSERT(position >= 0, return position);
while (m_interface->characterAt(position - 1).isSpace())
--position;
return position;
}
int ClangCompletionContextAnalyzer::startOfOperator(int pos,
unsigned *kind,
bool wantFunctionCall) const
{
const QChar ch = pos > -1 ? m_interface->characterAt(pos - 1) : QChar();
const QChar ch2 = pos > 0 ? m_interface->characterAt(pos - 2) : QChar();
const QChar ch3 = pos > 1 ? m_interface->characterAt(pos - 3) : QChar();
int start = pos - activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall);
if (start != pos) {
QTextCursor tc(m_interface->textDocument());
tc.setPosition(pos);
// Include completion: make sure the quote character is the first one on the line
if (*kind == T_STRING_LITERAL) {
QTextCursor s = tc;
s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString sel = s.selectedText();
if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) {
*kind = T_EOF_SYMBOL;
start = pos;
}
} else if (*kind == T_COMMA) {
ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
*kind = T_EOF_SYMBOL;
start = pos;
}
}
SimpleLexer tokenize;
tokenize.setLanguageFeatures(m_languageFeatures);
tokenize.setSkipComments(false);
const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); // get the token at the left of the cursor
const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
*kind = T_EOF_SYMBOL;
start = pos;
}
// Don't complete in comments or strings, but still check for include completion
else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT) ||
(tk.isLiteral() && (*kind != T_STRING_LITERAL
&& *kind != T_ANGLE_STRING_LITERAL
&& *kind != T_SLASH))) {
*kind = T_EOF_SYMBOL;
start = pos;
}
// Include completion: can be triggered by slash, but only in a string
else if (*kind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
*kind = T_EOF_SYMBOL;
start = pos;
}
else if (*kind == T_LPAREN) {
if (tokenIdx > 0) {
const Token &previousToken = tokens.at(tokenIdx - 1); // look at the token at the left of T_LPAREN
switch (previousToken.kind()) {
case T_IDENTIFIER:
case T_GREATER:
case T_SIGNAL:
case T_SLOT:
break; // good
default:
// that's a bad token :)
*kind = T_EOF_SYMBOL;
start = pos;
}
}
}
// Check for include preprocessor directive
else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL || *kind == T_SLASH) {
bool include = false;
if (tokens.size() >= 3) {
if (tokens.at(0).is(T_POUND) && tokens.at(1).is(T_IDENTIFIER) && (tokens.at(2).is(T_STRING_LITERAL) ||
tokens.at(2).is(T_ANGLE_STRING_LITERAL))) {
const Token &directiveToken = tokens.at(1);
QString directive = tc.block().text().mid(directiveToken.bytesBegin(),
directiveToken.bytes());
if (directive == QLatin1String("include") ||
directive == QLatin1String("include_next") ||
directive == QLatin1String("import")) {
include = true;
}
}
}
if (!include) {
*kind = T_EOF_SYMBOL;
start = pos;
}
}
}
return start;
}
void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction action,
int position)
{
@@ -366,5 +128,69 @@ void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction
m_positionForClang = position;
}
void
ClangCompletionContextAnalyzer::setAction(ClangCompletionContextAnalyzer::CompletionAction action)
{
setActionAndClangPosition(action, -1);
}
void ClangCompletionContextAnalyzer::handleCommaInFunctionCall()
{
if (m_completionOperator == T_COMMA) {
ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
QTextCursor textCursor(m_interface->textDocument());
textCursor.setPosition(m_positionEndOfExpression);
const int start = expressionUnderCursor.startOfFunctionCall(textCursor);
m_positionEndOfExpression = start;
m_positionForProposal = start + 1; // After '(' of function call
m_completionOperator = T_LPAREN;
}
}
void ClangCompletionContextAnalyzer::handleFunctionCall(int afterOperatorPosition)
{
if (m_completionOperator == T_LPAREN) {
ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
QTextCursor textCursor(m_interface->textDocument());
textCursor.setPosition(m_positionEndOfExpression);
const QString expression = expressionUnderCursor(textCursor);
if (expression.endsWith(QLatin1String("SIGNAL"))) {
setActionAndClangPosition(CompleteSignal, afterOperatorPosition);
} else if (expression.endsWith(QLatin1String("SLOT"))) {
setActionAndClangPosition(CompleteSlot, afterOperatorPosition);
} else if (m_interface->position() != afterOperatorPosition) {
// No function completion if cursor is not after '(' or ','
m_positionForProposal = afterOperatorPosition;
setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition);
} else {
const FunctionInfo functionInfo = analyzeFunctionCall(afterOperatorPosition);
m_functionName = functionInfo.functionName;
setActionAndClangPosition(PassThroughToLibClangAfterLeftParen,
functionInfo.functionNamePosition);
}
}
}
bool ClangCompletionContextAnalyzer::handleNonFunctionCall(int position)
{
if (isTokenForPassThrough(m_completionOperator)) {
setActionAndClangPosition(PassThroughToLibClang, position);
return true;
} else if (m_completionOperator == T_DOXY_COMMENT) {
setAction(CompleteDoxygenKeyword);
return true;
} else if (m_completionOperator == T_POUND) {
// TODO: Check if libclang can complete preprocessor directives
setAction(CompletePreprocessorDirective);
return true;
} else if (isTokenForIncludePathCompletion(m_completionOperator)) {
setAction(CompleteIncludePath);
return true;
}
return false;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -40,10 +40,12 @@ namespace TextEditor { class AssistInterface; }
namespace ClangCodeModel {
namespace Internal {
class ClangCompletionAssistInterface;
class ClangCompletionContextAnalyzer
{
public:
ClangCompletionContextAnalyzer(const TextEditor::AssistInterface *assistInterface,
ClangCompletionContextAnalyzer(const ClangCompletionAssistInterface *assistInterface,
CPlusPlus::LanguageFeatures languageFeatures);
void analyze();
@@ -70,18 +72,20 @@ private:
struct FunctionInfo { int functionNamePosition; QString functionName; };
FunctionInfo analyzeFunctionCall(int endOfExpression) const;
int findStartOfName(int position = -1) const;
int skipPrecedingWhitespace(int position) const;
int startOfOperator(int position, unsigned *kind, bool wantFunctionCall) const;
void setActionAndClangPosition(CompletionAction action, int position);
void setAction(CompletionAction action);
const TextEditor::AssistInterface * const m_interface; // Not owned
bool handleNonFunctionCall(int position);
void handleCommaInFunctionCall();
void handleFunctionCall(int endOfOperator);
private:
const ClangCompletionAssistInterface *m_interface; // Not owned
const CPlusPlus::LanguageFeatures m_languageFeatures; // TODO: Get from assistInterface?!
// Results
CompletionAction m_completionAction = PassThroughToLibClang;
unsigned m_completionOperator = CPlusPlus::T_EOF_SYMBOL;
CPlusPlus::Kind m_completionOperator = CPlusPlus::T_EOF_SYMBOL;
int m_positionForProposal = -1;
int m_positionForClang = -1;
int m_positionEndOfExpression = -1;

View File

@@ -86,6 +86,9 @@ ClangEditorDocumentParser::ClangEditorDocumentParser(const QString &filePath)
: BaseEditorDocumentParser(filePath)
, m_marker(new ClangCodeModel::SemanticMarker)
{
BaseEditorDocumentParser::Configuration config = configuration();
config.stickToPreviousProjectPart = false;
setConfiguration(config);
}
void ClangEditorDocumentParser::updateHelper(const BaseEditorDocumentParser::InMemoryInfo &info)

View File

@@ -44,10 +44,6 @@
#include <utils/qtcassert.h>
#include <utils/QtConcurrentTools>
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(log, "qtc.clangcodemodel.clangeditordocumentprocessor")
namespace {
typedef CPlusPlus::Document::DiagnosticMessage CppToolsDiagnostic;
@@ -59,8 +55,6 @@ QList<CppToolsDiagnostic> toCppToolsDiagnostics(
QList<CppToolsDiagnostic> converted;
foreach (const ClangCodeModel::Diagnostic &d, diagnostics) {
qCDebug(log) << "diagnostic" << d.severityAsString() << d.location() << d.spelling();
if (d.location().fileName() != filePath)
continue;
@@ -112,6 +106,9 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::semanticInfoUpdated,
this, &ClangEditorDocumentProcessor::semanticInfoUpdated);
connect(CppTools::CppModelManager::instance(), &CppTools::CppModelManager::projectPartsRemoved,
this, &ClangEditorDocumentProcessor::onProjectPartsRemoved);
m_semanticHighlighter.setHighlightingRunner(
[this]() -> QFuture<TextEditor::HighlightingResult> {
const int firstLine = 1;
@@ -129,16 +126,11 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
m_parserWatcher.cancel();
m_parserWatcher.waitForFinished();
const CppTools::ProjectPart::Ptr projectPart = m_parser.projectPart();
QTC_ASSERT(projectPart, return);
QString projectFilePath;
if (Utils::isProjectPartValid(projectPart))
projectFilePath = projectPart->projectFile; // OK, Project Part is still loaded
if (m_projectPart) {
QTC_ASSERT(m_modelManagerSupport, return);
m_modelManagerSupport->ipcCommunicator().unregisterFilesForCodeCompletion(
{ClangBackEnd::FileContainer(filePath(), projectFilePath)});
{ClangBackEnd::FileContainer(filePath(), m_projectPart->id())});
}
}
void ClangEditorDocumentProcessor::run()
@@ -192,6 +184,25 @@ bool ClangEditorDocumentProcessor::isParserRunning() const
return m_parserWatcher.isRunning();
}
CppTools::ProjectPart::Ptr ClangEditorDocumentProcessor::projectPart() const
{
return m_projectPart;
}
ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath)
{
return qobject_cast<ClangEditorDocumentProcessor *>(BaseEditorDocumentProcessor::get(filePath));
}
void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForCompletion()
{
const CppTools::ProjectPart::Ptr projectPart = m_parser.projectPart();
QTC_ASSERT(projectPart, return);
updateTranslationUnitForCompletion(*projectPart.data());
m_projectPart = projectPart;
}
void ClangEditorDocumentProcessor::onParserFinished()
{
if (revision() != m_parserRevision)
@@ -208,6 +219,34 @@ void ClangEditorDocumentProcessor::onParserFinished()
// Run semantic highlighter
m_semanticHighlighter.run();
updateProjectPartAndTranslationUnitForCompletion();
}
void ClangEditorDocumentProcessor::onProjectPartsRemoved(const QStringList &projectPartIds)
{
if (m_projectPart && projectPartIds.contains(m_projectPart->id()))
m_projectPart.clear();
}
void ClangEditorDocumentProcessor::updateTranslationUnitForCompletion(
CppTools::ProjectPart &projectPart)
{
QTC_ASSERT(m_modelManagerSupport, return);
IpcCommunicator &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
if (m_projectPart) {
if (projectPart.id() != m_projectPart->id()) {
auto container1 = {ClangBackEnd::FileContainer(filePath(), m_projectPart->id())};
ipcCommunicator.unregisterFilesForCodeCompletion(container1);
auto container2 = {ClangBackEnd::FileContainer(filePath(), projectPart.id())};
ipcCommunicator.registerFilesForCodeCompletion(container2);
}
} else {
auto container = {ClangBackEnd::FileContainer(filePath(), projectPart.id())};
ipcCommunicator.registerFilesForCodeCompletion(container);
}
}
} // namespace Internal

View File

@@ -63,13 +63,23 @@ public:
CPlusPlus::Snapshot snapshot() override;
bool isParserRunning() const override;
CppTools::ProjectPart::Ptr projectPart() const;
public:
static ClangEditorDocumentProcessor *get(const QString &filePath);
private slots:
void onParserFinished();
void onProjectPartsRemoved(const QStringList &projectPartIds);
private:
void updateProjectPartAndTranslationUnitForCompletion();
void updateTranslationUnitForCompletion(CppTools::ProjectPart &projectPart);
QPointer<ModelManagerSupportClang> m_modelManagerSupport;
ClangEditorDocumentParser m_parser;
CppTools::ProjectPart::Ptr m_projectPart;
QFutureWatcher<void> m_parserWatcher;
unsigned m_parserRevision;

View File

@@ -39,7 +39,7 @@ namespace Internal {
using namespace CPlusPlus;
ClangFunctionHintModel::ClangFunctionHintModel(const CodeCompletions &functionSymbols)
ClangFunctionHintModel::ClangFunctionHintModel(const ClangBackEnd::CodeCompletions &functionSymbols)
: m_functionSymbols(functionSymbols)
, m_currentArg(-1)
{

View File

@@ -38,12 +38,10 @@
namespace ClangCodeModel {
namespace Internal {
using CodeCompletions = QVector<ClangBackEnd::CodeCompletion>;
class ClangFunctionHintModel : public TextEditor::IFunctionHintProposalModel
{
public:
ClangFunctionHintModel(const CodeCompletions &functionSymbols);
ClangFunctionHintModel(const ClangBackEnd::CodeCompletions &functionSymbols);
void reset() override;
int size() const override;
@@ -51,7 +49,7 @@ public:
int activeArgument(const QString &prefix) const override;
private:
CodeCompletions m_functionSymbols;
ClangBackEnd::CodeCompletions m_functionSymbols;
mutable int m_currentArg;
};

View File

@@ -159,9 +159,9 @@ void ModelManagerSupportClang::onAbstractEditorSupportRemoved(const QString &fil
{
QTC_ASSERT(!filePath.isEmpty(), return);
if (!cppModelManager()->cppEditorDocument(filePath)) {
const QString projectFilePath = Utils::projectFilePathForFile(filePath);
const QString projectPartId = Utils::projectPartIdForFile(filePath);
m_ipcCommunicator.unregisterFilesForCodeCompletion(
{ClangBackEnd::FileContainer(filePath, projectFilePath)});
{ClangBackEnd::FileContainer(filePath, projectPartId)});
}
}
@@ -173,9 +173,10 @@ void ModelManagerSupportClang::onProjectPartsUpdated(ProjectExplorer::Project *p
m_ipcCommunicator.registerProjectsParts(projectInfo.projectParts());
}
void ModelManagerSupportClang::onProjectPartsRemoved(const QStringList &projectFiles)
void ModelManagerSupportClang::onProjectPartsRemoved(const QStringList &projectPartIds)
{
m_ipcCommunicator.unregisterProjectPartsForCodeCompletion(projectFiles);
if (!projectPartIds.isEmpty())
m_ipcCommunicator.unregisterProjectPartsForCodeCompletion(projectPartIds);
}
#ifdef QT_TESTLIB_LIB

View File

@@ -73,7 +73,7 @@ private:
void onAbstractEditorSupportRemoved(const QString &filePath);
void onProjectPartsUpdated(ProjectExplorer::Project *project);
void onProjectPartsRemoved(const QStringList &projectFiles);
void onProjectPartsRemoved(const QStringList &projectPartIds);
IpcCommunicator m_ipcCommunicator;
ClangCompletionAssistProvider m_completionAssistProvider;

View File

@@ -114,53 +114,27 @@ static bool maybeIncludeBorlandExtensions()
class LibClangOptionsBuilder : public CompilerOptionsBuilder
{
public:
static QStringList build(const ProjectPart::Ptr &pPart, ProjectFile::Kind fileKind)
static QStringList build(const ProjectPart::Ptr &projectPart, ProjectFile::Kind fileKind)
{
if (pPart.isNull())
if (projectPart.isNull())
return QStringList();
LibClangOptionsBuilder optionsBuilder(pPart);
LibClangOptionsBuilder optionsBuilder(projectPart);
if (verboseRunLog().isDebugEnabled())
optionsBuilder.add(QLatin1String("-v"));
optionsBuilder.addLanguageOption(fileKind);
optionsBuilder.addOptionsForLanguage(maybeIncludeBorlandExtensions());
optionsBuilder.addToolchainAndProjectDefines();
static const QString resourceDir = getResourceDir();
if (!resourceDir.isEmpty()) {
optionsBuilder.add(QLatin1String("-nostdlibinc"));
optionsBuilder.add(QLatin1String("-I") + resourceDir);
optionsBuilder.add(QLatin1String("-undef"));
}
optionsBuilder.addResourceDirOptions();
optionsBuilder.addWrappedQtHeadersIncludePath();
optionsBuilder.addHeaderPathOptions();
optionsBuilder.addProjectConfigFileInclude();
// Inject header file
static const QString injectedHeader = ICore::instance()->resourcePath()
+ QLatin1String("/cplusplus/qt%1-qobjectdefs-injected.h");
// if (pPart->qtVersion == ProjectPart::Qt4) {
// builder.addOption(QLatin1String("-include"));
// builder.addOption(injectedHeader.arg(QLatin1Char('4')));
// }
if (pPart->qtVersion == ProjectPart::Qt5) {
optionsBuilder.add(QLatin1String("-include"));
optionsBuilder.add(injectedHeader.arg(QLatin1Char('5')));
}
if (!pPart->projectConfigFile.isEmpty()) {
optionsBuilder.add(QLatin1String("-include"));
optionsBuilder.add(pPart->projectConfigFile);
}
optionsBuilder.add(QLatin1String("-fmessage-length=0"));
optionsBuilder.add(QLatin1String("-fdiagnostics-show-note-include-stack"));
optionsBuilder.add(QLatin1String("-fmacro-backtrace-limit=0"));
optionsBuilder.add(QLatin1String("-fretain-comments-from-system-headers"));
// TODO: -Xclang -ferror-limit -Xclang 0 ?
optionsBuilder.addExtraOptions();
return optionsBuilder.options();
}
@@ -175,6 +149,44 @@ private:
{
return path.contains(QLatin1String("lib/gcc/i686-apple-darwin"));
}
void addResourceDirOptions()
{
static const QString resourceDir = getResourceDir();
if (!resourceDir.isEmpty()) {
add(QLatin1String("-nostdlibinc"));
add(QLatin1String("-I") + resourceDir);
add(QLatin1String("-undef"));
}
}
void addWrappedQtHeadersIncludePath()
{
static const QString wrappedQtHeaders = ICore::instance()->resourcePath()
+ QLatin1String("/cplusplus/wrappedQtHeaders");
if (m_projectPart->qtVersion != ProjectPart::NoQt) {
add(QLatin1String("-I") + wrappedQtHeaders);
add(QLatin1String("-I") + wrappedQtHeaders + QLatin1String("/QtCore"));
}
}
void addProjectConfigFileInclude()
{
if (!m_projectPart->projectConfigFile.isEmpty()) {
add(QLatin1String("-include"));
add(m_projectPart->projectConfigFile);
}
}
void addExtraOptions()
{
add(QLatin1String("-fmessage-length=0"));
add(QLatin1String("-fdiagnostics-show-note-include-stack"));
add(QLatin1String("-fmacro-backtrace-limit=0"));
add(QLatin1String("-fretain-comments-from-system-headers"));
// TODO: -Xclang -ferror-limit -Xclang 0 ?
}
};
/**
@@ -218,16 +230,16 @@ ProjectPart::Ptr projectPartForFile(const QString &filePath)
bool isProjectPartValid(const ProjectPart::Ptr projectPart)
{
if (projectPart)
return CppModelManager::instance()->projectPartForProjectFile(projectPart->projectFile);
return CppModelManager::instance()->projectPartForId(projectPart->id());
return false;
}
QString projectFilePathForFile(const QString &filePath)
QString projectPartIdForFile(const QString &filePath)
{
const ProjectPart::Ptr projectPart = projectPartForFile(filePath);
if (isProjectPartValid(projectPart))
return projectPart->projectFile; // OK, Project Part is still loaded
return projectPart->id(); // OK, Project Part is still loaded
return QString();
}

View File

@@ -55,7 +55,7 @@ QStringList createPCHInclusionOptions(const QString &pchFile);
CppTools::ProjectPart::Ptr projectPartForFile(const QString &filePath);
bool isProjectPartValid(const CppTools::ProjectPart::Ptr projectPart);
QString projectFilePathForFile(const QString &filePath);
QString projectPartIdForFile(const QString &filePath);
} // namespace Utils
} // namespace Clang

View File

@@ -36,7 +36,7 @@
namespace ClangCodeModel {
namespace Internal {
void CompletionChunksToTextConverter::parseChunks(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks)
void CompletionChunksToTextConverter::parseChunks(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
m_text.clear();
m_placeholderPositions.clear();
@@ -94,7 +94,7 @@ bool CompletionChunksToTextConverter::hasPlaceholderPositions() const
return m_placeholderPositions.size() > 0;
}
QString CompletionChunksToTextConverter::convertToFunctionSignature(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks)
QString CompletionChunksToTextConverter::convertToFunctionSignature(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
CompletionChunksToTextConverter converter;
converter.setAddPlaceHolderText(true);
@@ -105,7 +105,7 @@ QString CompletionChunksToTextConverter::convertToFunctionSignature(const QVecto
return converter.text();
}
QString CompletionChunksToTextConverter::convertToName(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks)
QString CompletionChunksToTextConverter::convertToName(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
CompletionChunksToTextConverter converter;
@@ -114,7 +114,7 @@ QString CompletionChunksToTextConverter::convertToName(const QVector<ClangBackEn
return converter.text();
}
QString CompletionChunksToTextConverter::convertToToolTip(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks)
QString CompletionChunksToTextConverter::convertToToolTip(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
CompletionChunksToTextConverter converter;
converter.setAddPlaceHolderText(true);
@@ -196,7 +196,7 @@ void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces()
addExtraVerticalSpaceBetweenBraces(m_codeCompletionChunks.begin());
}
void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces(const QVector<ClangBackEnd::CodeCompletionChunk>::iterator &begin)
void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces(const ClangBackEnd::CodeCompletionChunks::iterator &begin)
{
using ClangBackEnd::CodeCompletionChunk;

View File

@@ -45,7 +45,7 @@ namespace Internal {
class CompletionChunksToTextConverter
{
public:
void parseChunks(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks);
void parseChunks(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
void setAddPlaceHolderText(bool addPlaceHolderText);
void setAddPlaceHolderPositions(bool addPlaceHolderPositions);
@@ -57,9 +57,9 @@ public:
const std::vector<int> &placeholderPositions() const;
bool hasPlaceholderPositions() const;
static QString convertToFunctionSignature(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks);
static QString convertToName(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks);
static QString convertToToolTip(const QVector<ClangBackEnd::CodeCompletionChunk> &codeCompletionChunks);
static QString convertToFunctionSignature(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
static QString convertToName(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
static QString convertToToolTip(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
private:
void parse(const ClangBackEnd::CodeCompletionChunk & codeCompletionChunk);
void parseResultType(const Utf8String &text);
@@ -69,13 +69,13 @@ private:
void parseLeftParen(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parseLeftBrace(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void addExtraVerticalSpaceBetweenBraces();
void addExtraVerticalSpaceBetweenBraces(const QVector<ClangBackEnd::CodeCompletionChunk>::iterator &);
void addExtraVerticalSpaceBetweenBraces(const ClangBackEnd::CodeCompletionChunks::iterator &);
bool canAddSpace() const;
private:
std::vector<int> m_placeholderPositions;
QVector<ClangBackEnd::CodeCompletionChunk> m_codeCompletionChunks;
ClangBackEnd::CodeCompletionChunks m_codeCompletionChunks;
ClangBackEnd::CodeCompletionChunk m_previousCodeCompletionChunk;
QString m_text;
bool m_addPlaceHolderText = false;

View File

@@ -32,6 +32,7 @@
#include "cppcreatemarkers.h"
#include <cplusplus/CppDocument.h>
#include <utils/executeondestruction.h>
#include <utils/runextensions.h>
#include <QCoreApplication>
@@ -77,6 +78,9 @@ CreateMarkers::~CreateMarkers()
void CreateMarkers::run()
{
QMutexLocker lock(m_marker->mutex());
::Utils::ExecuteOnDestruction reportFinishedOnDestruction([this]() { reportFinished(); });
if (isCanceled())
return;
@@ -86,23 +90,18 @@ void CreateMarkers::run()
m_usages.clear();
if (isCanceled()) {
reportFinished();
if (isCanceled())
return;
}
const QList<ClangCodeModel::SourceMarker> markers
= m_marker->sourceMarkersInRange(m_firstLine, m_lastLine);
foreach (const ClangCodeModel::SourceMarker &m, markers)
addUse(SourceMarker(m.location().line(), m.location().column(), m.length(), m.kind()));
if (isCanceled()) {
reportFinished();
if (isCanceled())
return;
}
flush();
reportFinished();
qCDebug(log) << "Creating markers took" << t.elapsed() << "ms in total.";
}

View File

@@ -33,6 +33,7 @@
#include "utils_p.h"
#include "cxraii.h"
#include <utils/mimetypes/mimedatabase.h>
#include <utils/qtcassert.h>
using namespace ClangCodeModel;
@@ -131,12 +132,23 @@ static void appendDiagnostic(const CXDiagnostic &diag,
}
}
static bool isBlackListedDiagnostic(const Utils::MimeType &mimeType, const QString &diagnostic)
{
static QString pragmaOnceInMainFile = QLatin1String("#pragma once in main file");
return diagnostic == pragmaOnceInMainFile
&& mimeType.inherits(QLatin1String("text/x-chdr"));
}
QList<Diagnostic> SemanticMarker::diagnostics() const
{
QList<Diagnostic> diagnostics;
if (!m_unit || !m_unit->isLoaded())
return diagnostics;
Utils::MimeDatabase mimeDatabase;
const Utils::MimeType mimeType = mimeDatabase.mimeTypeForFile(fileName());
const unsigned diagCount = m_unit->getNumDiagnostics();
for (unsigned i = 0; i < diagCount; ++i) {
ScopedCXDiagnostic diag(m_unit->getDiagnostic(i));
@@ -148,6 +160,9 @@ QList<Diagnostic> SemanticMarker::diagnostics() const
CXSourceLocation cxLocation = clang_getDiagnosticLocation(diag);
QString spelling = Internal::getQString(clang_getDiagnosticSpelling(diag));
if (isBlackListedDiagnostic(mimeType, spelling))
continue;
// Attach messages with Diagnostic::Note severity
ScopedCXDiagnosticSet cxChildren(clang_getChildDiagnostics(diag));
const unsigned numChildren = clang_getNumDiagnosticsInSet(cxChildren);
@@ -333,9 +348,8 @@ QList<SourceMarker> SemanticMarker::sourceMarkersInRange(unsigned firstLine,
unsigned lastLine)
{
QList<SourceMarker> result;
QTC_ASSERT(m_unit, return result);
if (!m_unit->isLoaded())
if (!m_unit || !m_unit->isLoaded())
return result;
// Highlighting called asynchronously, and a few lines at the end can be deleted for this time.

View File

@@ -20,5 +20,6 @@
<file>exampleIncludeDir/otherFile.h</file>
<file>exampleIncludeDir/mylib/mylib.h</file>
<file>globalCompletion.cpp</file>
<file>constructorCompletion.cpp</file>
</qresource>
</RCC>

View File

@@ -34,6 +34,7 @@
#include "../clangcompletionassistinterface.h"
#include "../clangmodelmanagersupport.h"
#include <clangcodemodel/clangeditordocumentprocessor.h>
#include <clangcodemodel/constants.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -80,6 +81,13 @@ QString qrcPath(const QByteArray relativeFilePath)
QString fileName(const QString &filePath)
{ return QFileInfo(filePath).fileName(); }
CppTools::Tests::TemporaryDir *globalTemporaryDir()
{
static CppTools::Tests::TemporaryDir dir;
QTC_CHECK(dir.isValid());
return &dir;
}
struct LogOutput
{
LogOutput(const QString &text) : text(text.toUtf8()) {}
@@ -469,11 +477,13 @@ public:
OpenEditorAtCursorPosition(const TestDocument &testDocument);
~OpenEditorAtCursorPosition(); // Close editor
bool succeeded() const { return m_editor; }
bool succeeded() const { return m_editor && m_backendIsNotified; }
bool waitUntilBackendIsNotified(int timeout = 10000);
TextEditor::BaseTextEditor *editor() const { return m_editor; }
private:
TextEditor::BaseTextEditor *m_editor;
bool m_backendIsNotified = false;
};
OpenEditorAtCursorPosition::OpenEditorAtCursorPosition(const TestDocument &testDocument)
@@ -483,6 +493,8 @@ OpenEditorAtCursorPosition::OpenEditorAtCursorPosition(const TestDocument &testD
QTC_CHECK(m_editor);
if (m_editor && testDocument.hasValidCursorPosition())
m_editor->setCursorPosition(testDocument.cursorPosition);
m_backendIsNotified = waitUntilBackendIsNotified();
QTC_CHECK(m_backendIsNotified);
}
OpenEditorAtCursorPosition::~OpenEditorAtCursorPosition()
@@ -491,6 +503,30 @@ OpenEditorAtCursorPosition::~OpenEditorAtCursorPosition()
Core::EditorManager::closeEditor(m_editor, /* askAboutModifiedEditors= */ false);
}
bool OpenEditorAtCursorPosition::waitUntilBackendIsNotified(int timeout)
{
QTC_ASSERT(m_editor, return false);
const QString filePath = m_editor->document()->filePath().toString();
QTime time;
time.start();
forever {
if (time.elapsed() > timeout)
return false;
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
if (processor && processor->projectPart())
return true;
QCoreApplication::processEvents();
QThread::msleep(20);
}
return false;
}
CppTools::ProjectPart::Ptr createProjectPart(const QStringList &files,
const QString &defines)
{
@@ -576,7 +612,7 @@ public:
CppTools::Tests::TestCase garbageCollectionGlobalSnapshot;
QVERIFY(garbageCollectionGlobalSnapshot.succeededSoFar());
const TestDocument testDocument(testFileName);
const TestDocument testDocument(testFileName, globalTemporaryDir());
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openEditor(testDocument);
@@ -838,6 +874,15 @@ void ClangCodeCompletionTest::testCompleteFunctions()
QVERIFY(hasItem(t.proposal, "TType<QString> f(bool)"));
}
void ClangCodeCompletionTest::testCompleteConstructorAndFallbackToGlobalCompletion()
{
ProjectLessCompletionTest t("constructorCompletion.cpp");
QVERIFY(hasItem(t.proposal, "globalVariable"));
QVERIFY(hasItem(t.proposal, "GlobalClassWithCustomConstructor"));
QVERIFY(!hasSnippet(t.proposal, "class"));
}
void ClangCodeCompletionTest::testProjectDependentCompletion()
{
const TestDocument testDocument("completionWithProject.cpp");
@@ -1029,23 +1074,27 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart()
CppTools::Tests::ProjectOpenerAndCloser projectManager;
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
QVERIFY(projectInfo.isValid());
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
// ...and a file of the project
const QString completionFile = testDir.absolutePath("mainwindow.cpp");
const TestDocument testDocument = TestDocument::fromExistingFile(completionFile);
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openSource(testDocument);
QVERIFY(openSource.succeeded());
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
// Check commands that would have been sent
QVERIFY(compare(LogOutput(spy.senderLog),
LogOutput(
"RegisterTranslationUnitForCodeCompletionCommand\n"
" Path: myheader.h ProjectPart: \n"
"RegisterProjectPartsForCodeCompletionCommand\n"
" ProjectPartContainer id: qt-widgets-app.pro\n"
" ProjectPartContainer id: qt-widgets-app.pro qt-widgets-app\n"
"RegisterTranslationUnitForCodeCompletionCommand\n"
" Path: ui_mainwindow.h ProjectPart: \n"
"RegisterTranslationUnitForCodeCompletionCommand\n"
" Path: myheader.h ProjectPart: \n"
"RegisterTranslationUnitForCodeCompletionCommand\n"
" Path: ui_mainwindow.h ProjectPart: \n"
" Path: mainwindow.cpp ProjectPart: qt-widgets-app.pro qt-widgets-app\n"
)));
spy.senderLog.clear();
@@ -1062,7 +1111,7 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart()
"RegisterProjectPartsForCodeCompletionCommand\n"
" ProjectPartContainer id: \n"
"RegisterProjectPartsForCodeCompletionCommand\n"
" ProjectPartContainer id: qt-widgets-app.pro\n"
" ProjectPartContainer id: qt-widgets-app.pro qt-widgets-app\n"
"RegisterTranslationUnitForCodeCompletionCommand\n"
" Path: myheader.h ProjectPart: \n"
"RegisterTranslationUnitForCodeCompletionCommand\n"

View File

@@ -57,6 +57,7 @@ private slots:
void testCompleteGlobals();
void testCompleteMembers();
void testCompleteFunctions();
void testCompleteConstructorAndFallbackToGlobalCompletion();
void testProjectDependentCompletion();
void testChangingProjectDependentCompletion();

View File

@@ -1,234 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "clangcompletioncontextanalyzertest.h"
#include <clangcodemodel/clangcompletioncontextanalyzer.h>
#include <texteditor/codeassist/assistinterface.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QTest>
#include <QTextDocument>
using namespace CPlusPlus;
using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal;
using namespace ClangCodeModel::Internal::Tests;
Q_DECLARE_METATYPE(ClangCodeModel::Internal::ClangCompletionContextAnalyzer::CompletionAction)
QT_BEGIN_NAMESPACE
namespace QTest {
template<> char *toString(const ClangCompletionContextAnalyzer::CompletionAction &action)
{
using CCA = ClangCompletionContextAnalyzer;
switch (action) {
case CCA::PassThroughToLibClang:
return qstrdup("PassThroughToLibClang");
case CCA::PassThroughToLibClangAfterLeftParen:
return qstrdup("PassThroughToLibClangAfterLeftParen");
case CCA::CompleteDoxygenKeyword:
return qstrdup("CompleteDoxygenKeyword");
case CCA::CompleteIncludePath:
return qstrdup("CompleteIncludePath");
case CCA::CompletePreprocessorDirective:
return qstrdup("CompletePreprocessorDirective");
case CCA::CompleteSignal:
return qstrdup("CompleteSignal");
case CCA::CompleteSlot:
return qstrdup("CompleteSlot");
}
return qstrdup("Unexpected Value");
}
} // namespace QTest
QT_END_NAMESPACE
namespace {
typedef QByteArray _;
class DummyAssistInterface : public TextEditor::AssistInterface
{
public:
DummyAssistInterface(const QByteArray &text, int position)
: AssistInterface(new QTextDocument(QString::fromUtf8(text)),
position,
QLatin1String("<testdocument>"),
TextEditor::ActivationCharacter)
{}
~DummyAssistInterface() { delete textDocument(); }
};
class TestDocument
{
public:
TestDocument(const QByteArray &theSource)
: source(theSource)
, position(theSource.lastIndexOf('@')) // Use 'lastIndexOf' due to doxygen: "//! @keyword"
{
QTC_CHECK(position != -1);
source.remove(position, 1);
}
QByteArray source;
int position;
};
bool isAPassThroughToLibClangAction(ClangCompletionContextAnalyzer::CompletionAction action)
{
return action == ClangCompletionContextAnalyzer::PassThroughToLibClang
|| action == ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen;
}
ClangCompletionContextAnalyzer runAnalyzer(const TestDocument &testDocument)
{
DummyAssistInterface assistInterface(testDocument.source, testDocument.position);
ClangCompletionContextAnalyzer analyzer(&assistInterface, LanguageFeatures::defaultFeatures());
analyzer.analyze();
return analyzer;
}
} // anonymous namespace
void ClangCompletionContextAnalyzerTest::testPassThroughToClangAndSignalSlotRecognition_data()
{
QTest::addColumn<QByteArray>("givenSource");
QTest::addColumn<ClangCompletionContextAnalyzer::CompletionAction>("expectedCompletionAction");
QTest::addColumn<int>("expectedDiffBetweenCursorAndCalculatedClangPosition");
QTest::addColumn<int>("expectedDiffBetweenCursorAndCalculatedProposalPosition");
using CCA = ClangCompletionContextAnalyzer;
QTest::newRow("members - dot 1") << _("o.mem@") << CCA::PassThroughToLibClang << -3 << -3;
QTest::newRow("members - dot 2") << _("o. mem@") << CCA::PassThroughToLibClang << -4 << -3;
QTest::newRow("members - dot 3") << _("o.@mem") << CCA::PassThroughToLibClang << 0 << 0;
QTest::newRow("members - dot 4") << _("o. @ mem") << CCA::PassThroughToLibClang << -1 << 0;
QTest::newRow("members - arrow 1") << _("o->mem@") << CCA::PassThroughToLibClang << -3 << -3;
QTest::newRow("members - arrow 2") << _("o-> mem@") << CCA::PassThroughToLibClang << -4 << -3;
QTest::newRow("members - arrow 3") << _("o->@mem") << CCA::PassThroughToLibClang << 0 << 0;
QTest::newRow("members - arrow 4") << _("o-> @ mem") << CCA::PassThroughToLibClang << -1 << 0;
QTest::newRow("call 1") << _("f(@") << CCA::PassThroughToLibClangAfterLeftParen << -2 << 0;
QTest::newRow("call 2") << _("f(1,@") << CCA::PassThroughToLibClangAfterLeftParen << -4 << -2;
QTest::newRow("call 3") << _("f(1, @") << CCA::PassThroughToLibClang << -1 << 0;
QTest::newRow("qt4 signals 1") << _("SIGNAL(@") << CCA::CompleteSignal << 0 << 0;
QTest::newRow("qt4 signals 2") << _("SIGNAL(foo@") << CCA::CompleteSignal << -3 << -3;
QTest::newRow("qt4 slots 1") << _("SLOT(@") << CCA::CompleteSlot << 0 << 0;
QTest::newRow("qt4 slots 2") << _("SLOT(foo@") << CCA::CompleteSlot << -3 << -3;
}
void ClangCompletionContextAnalyzerTest::testPassThroughToClangAndSignalSlotRecognition()
{
QFETCH(QByteArray, givenSource);
QFETCH(ClangCompletionContextAnalyzer::CompletionAction, expectedCompletionAction);
QFETCH(int, expectedDiffBetweenCursorAndCalculatedClangPosition);
QFETCH(int, expectedDiffBetweenCursorAndCalculatedProposalPosition);
const TestDocument testDocument(givenSource);
ClangCompletionContextAnalyzer analyzer = runAnalyzer(testDocument);
QCOMPARE(analyzer.completionAction(), expectedCompletionAction);
QCOMPARE(analyzer.positionForClang() - testDocument.position,
expectedDiffBetweenCursorAndCalculatedClangPosition);
QCOMPARE(analyzer.positionForProposal() - testDocument.position,
expectedDiffBetweenCursorAndCalculatedProposalPosition);
}
void ClangCompletionContextAnalyzerTest::testSpecialCompletionRecognition_data()
{
QTest::addColumn<QByteArray>("givenSource");
QTest::addColumn<ClangCompletionContextAnalyzer::CompletionAction>("expectedCompletionAction");
QTest::addColumn<int>("expectedDiffBetweenCursorAndCalculatedProposalPosition");
using CCA = ClangCompletionContextAnalyzer;
QTest::newRow("doxygen keywords 1") << _("//! \\@") << CCA::CompleteDoxygenKeyword << 0;
QTest::newRow("doxygen keywords 3") << _("//! @@") << CCA::CompleteDoxygenKeyword << 0;
QTest::newRow("doxygen keywords 2") << _("//! \\par@") << CCA::CompleteDoxygenKeyword << -3;
QTest::newRow("pp directives 1") << _("#@") << CCA::CompletePreprocessorDirective << 0;
QTest::newRow("pp directives 2") << _("#if@") << CCA::CompletePreprocessorDirective << -2;
QTest::newRow("pp include path 1") << _("#include \"foo@\"") << CCA::CompleteIncludePath << -3;
QTest::newRow("pp include path 2") << _("#include <foo@>") << CCA::CompleteIncludePath << -3;
QTest::newRow("pp include path 3") << _("#include <foo/@>") << CCA::CompleteIncludePath << 0;
}
void ClangCompletionContextAnalyzerTest::testSpecialCompletionRecognition()
{
QFETCH(QByteArray, givenSource);
QFETCH(ClangCompletionContextAnalyzer::CompletionAction, expectedCompletionAction);
QFETCH(int, expectedDiffBetweenCursorAndCalculatedProposalPosition);
const TestDocument testDocument(givenSource);
ClangCompletionContextAnalyzer analyzer = runAnalyzer(testDocument);
QCOMPARE(analyzer.completionAction(), expectedCompletionAction);
QCOMPARE(analyzer.positionForClang(), -1);
QCOMPARE(analyzer.positionForProposal() - testDocument.position,
expectedDiffBetweenCursorAndCalculatedProposalPosition);
}
void ClangCompletionContextAnalyzerTest::testAvoidSpecialCompletionRecognition_data()
{
QTest::addColumn<QByteArray>("givenSource");
QTest::newRow("no special completion for literals 1") << _("\"@");
QTest::newRow("no special completion for literals 2") << _(" \"@");
QTest::newRow("no special completion for literals 3") << _("\"text\"@");
QTest::newRow("no special completion for literals 4") << _("\"hello cruel@ world\"");
QTest::newRow("no special completion for literals 5") << _("'@'");
QTest::newRow("no special completion for literals 6") << _("'a@'");
QTest::newRow("no special completion for comma operator") << _("a = b,@\"");
QTest::newRow("no special completion for doxygen marker not in doxygen comment 1") << _("@@");
QTest::newRow("no special completion for doxygen marker not in doxygen comment 2") << _("\\@");
QTest::newRow("no special completion in comments 1") << _("// text@");
QTest::newRow("no special completion in comments 2") << _("/* text@ */");
QTest::newRow("no special completion for slash") << _("5 /@");
QTest::newRow("no special completion for '(' 1") << _("(@");
QTest::newRow("no special completion for '(' 2") << _("((@");
QTest::newRow("no special completion for '(' 3") << _("*(@");
}
void ClangCompletionContextAnalyzerTest::testAvoidSpecialCompletionRecognition()
{
QFETCH(QByteArray, givenSource);
const TestDocument testDocument(givenSource);
ClangCompletionContextAnalyzer analyzer = runAnalyzer(testDocument);
QVERIFY(isAPassThroughToLibClangAction(analyzer.completionAction()));
}

View File

@@ -0,0 +1,9 @@
int globalVariable;
struct GlobalClassWithCustomConstructor {
GlobalClassWithCustomConstructor(int) {}
};
void f() {
GlobalClassWithCustomConstructor foo( /* COMPLETE HERE */
}

View File

@@ -9,16 +9,12 @@
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<string>MainWindow</string>
</property>
<widget class="QMenuBar" name="menuBar" />
<widget class="QToolBar" name="mainToolBar" />
<widget class="QWidget" name="centralWidget" />
<widget class="QStatusBar" name="statusBar" />
</widget>
<layoutDefault spacing="6" margin="11" />
<pixmapfunction></pixmapfunction>
<resources/>
<connections/>
</ui>

View File

@@ -47,6 +47,7 @@ CMakeKitInformation::CMakeKitInformation()
{
setObjectName(QLatin1String("CMakeKitInformation"));
setId(CMakeKitInformation::id());
setPriority(20000);
//make sure the default value is set if a selected CMake is removed
connect(CMakeToolManager::instance(), &CMakeToolManager::cmakeRemoved,

View File

@@ -378,6 +378,7 @@ void CMakeToolItemConfigWidget::load(const CMakeToolTreeItem *item)
class CMakeToolConfigWidget : public QWidget
{
Q_OBJECT
public:
CMakeToolConfigWidget() : m_currentItem(0)
{
@@ -572,3 +573,5 @@ void CMakeSettingsPage::finish()
} // namespace Internal
} // namespace CMakeProjectManager
#include "cmakesettingspage.moc"

View File

@@ -254,13 +254,13 @@ void CppEditorDocument::updatePreprocessorSettings()
return;
const QString prefix = QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX);
const QString &projectFile = ProjectExplorer::SessionManager::value(
const QString &projectPartId = ProjectExplorer::SessionManager::value(
prefix + filePath().toString()).toString();
const QString directivesKey = projectFile + QLatin1Char(',') + filePath().toString();
const QString directivesKey = projectPartId + QLatin1Char(',') + filePath().toString();
const QByteArray additionalDirectives = ProjectExplorer::SessionManager::value(
directivesKey).toString().toUtf8();
setPreprocessorSettings(mm()->projectPartForProjectFile(projectFile), additionalDirectives);
setPreprocessorSettings(mm()->projectPartForId(projectPartId), additionalDirectives);
}
void CppEditorDocument::setPreprocessorSettings(const CppTools::ProjectPart::Ptr &projectPart,

View File

@@ -213,8 +213,17 @@ private slots:
void test_includehierarchy_data();
void test_includehierarchy();
// The following tests depend on the projects that are loaded on startup
// and will be skipped in case no projects are loaded.
// The following tests operate on a project and require special invocation:
//
// Ensure that the project is properly configured for a given settings path:
// $ ./qtcreator -settingspath /your/settings/path /path/to/project
//
// ...and that it builds, which might prevent blocking dialogs for not
// existing files (e.g. ui_*.h).
//
// Run a test:
// $ export QTC_TEST_WAIT_FOR_LOADED_PROJECT=1
// $ ./qtcreator -settingspath /your/settings/path -test CppEditor,test_openEachFile /path/to/project
void test_openEachFile();
void test_switchHeaderSourceOnEachFile();
void test_moveTokenWiseThroughEveryFile();

View File

@@ -70,7 +70,7 @@ CppPreProcessorDialog::CppPreProcessorDialog(QWidget *parent, const QString &fil
ProjectPartAddition addition;
addition.projectPart = projectPart;
addition.additionalDirectives = ProjectExplorer::SessionManager::value(
projectPart->projectFile + QLatin1Char(',') + m_filePath).toString();
projectPart->id() + QLatin1Char(',') + m_filePath).toString();
if (projectPart->id() == projectPartIdToUse)
currentIndex = m_ui->projectComboBox->count() - 1;
m_partAdditions << addition;
@@ -96,16 +96,16 @@ int CppPreProcessorDialog::exec()
ProjectExplorer::SessionManager::setValue(
QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX) + m_filePath,
m_partAdditions[m_ui->projectComboBox->currentIndex()].projectPart->projectFile);
m_partAdditions[m_ui->projectComboBox->currentIndex()].projectPart->id());
foreach (ProjectPartAddition partAddition, m_partAdditions) {
const QString &previousDirectives = ProjectExplorer::SessionManager::value(
partAddition.projectPart->projectFile
partAddition.projectPart->id()
+ QLatin1Char(',')
+ m_filePath).toString();
if (previousDirectives != partAddition.additionalDirectives) {
ProjectExplorer::SessionManager::setValue(
partAddition.projectPart->projectFile + QLatin1Char(',') + m_filePath,
partAddition.projectPart->id() + QLatin1Char(',') + m_filePath,
partAddition.additionalDirectives);
}
}

View File

@@ -1720,6 +1720,23 @@ void CppEditorPlugin::test_quickfix_data()
" f2(str);\n"
"}\n");
QTest::newRow("ConvertToPointerWithMacro")
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
<< _("#define BAR bar\n"
"void func()\n"
"{\n"
" int @foo = 42;\n"
" int bar;\n"
" BAR = foo;\n"
"}\n")
<< _("#define BAR bar\n"
"void func()\n"
"{\n"
" int *foo = 42;\n"
" int bar;\n"
" BAR = *foo;\n"
"}\n");
QTest::newRow("InsertQtPropertyMembers_noTriggerInvalidCode")
<< CppQuickFixFactoryPtr(new InsertQtPropertyMembers)
<< _("class C { @Q_PROPERTY(typeid foo READ foo) };\n")

View File

@@ -118,17 +118,8 @@ private:
CppEditor *editor, const Actions &tokenActions);
static void undoAllChangesAndCloseAllEditors();
/// This function expects:
/// (1) Only Qt4 projects are loaded (qmake in PATH should point to Qt4/bin).
/// (2) No *.pro.user file exists for the projects.
static void configureAllProjects(const QList<QPointer<ProjectExplorer::Project> > &projects);
static bool allProjectsConfigured;
};
bool TestActionsTestCase::allProjectsConfigured = false;
typedef TestActionsTestCase::Actions Actions;
typedef TestActionsTestCase::ActionPointer ActionPointer;
@@ -137,18 +128,35 @@ Actions singleAction(const ActionPointer &action)
return Actions() << action;
}
static bool waitUntilAProjectIsLoaded(int timeOutInMs = 30000)
{
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < timeOutInMs) {
if (!CppModelManager::instance()->projectInfos().isEmpty())
return true;
QCoreApplication::processEvents();
QThread::msleep(20);
}
return false;
}
TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Actions &fileActions)
: Tests::TestCase(/*runGarbageCollector=*/false)
{
QVERIFY(succeededSoFar());
if (qgetenv("QTC_TEST_WAIT_FOR_LOADED_PROJECT") != "1")
QSKIP("Environment variable QTC_TEST_WAIT_FOR_LOADED_PROJECT=1 not set.");
QVERIFY(waitUntilAProjectIsLoaded());
// Collect files to process
QStringList filesToOpen;
QList<QPointer<ProjectExplorer::Project> > projects;
const QList<ProjectInfo> projectInfos
= m_modelManager->projectInfos();
if (projectInfos.isEmpty())
QSKIP("No project(s) loaded. Test operates only on loaded projects.");
const QList<ProjectInfo> projectInfos = m_modelManager->projectInfos();
foreach (const ProjectInfo &info, projectInfos) {
QPointer<ProjectExplorer::Project> project = info.project();
@@ -160,12 +168,6 @@ TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Acti
filesToOpen << sourceFile;
}
// Configure all projects on first execution of this function (= very first test)
if (!TestActionsTestCase::allProjectsConfigured) {
configureAllProjects(projects);
TestActionsTestCase::allProjectsConfigured = true;
}
Utils::sort(filesToOpen);
// Process all files from the projects
@@ -311,15 +313,6 @@ void TestActionsTestCase::undoAllChangesAndCloseAllEditors()
QCOMPARE(DocumentModel::openedDocuments().size(), 0);
}
void TestActionsTestCase::configureAllProjects(const QList<QPointer<ProjectExplorer::Project> >
&projects)
{
foreach (const QPointer<ProjectExplorer::Project> &project, projects) {
qDebug() << "*** Configuring project" << project->displayName();
project->configureAsExampleProject(QStringList());
}
}
class NoOpTokenAction : public TestActionsTestCase::AbstractAction
{
public:

View File

@@ -126,7 +126,7 @@ ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart(const QString &f
CppModelManager *cmm = CppModelManager::instance();
QList<ProjectPart::Ptr> projectParts = cmm->projectPart(filePath);
if (projectParts.isEmpty()) {
if (projectPart)
if (projectPart && config.stickToPreviousProjectPart)
// File is not directly part of any project, but we got one before. We will re-use it,
// because re-calculating this can be expensive when the dependency table is big.
return projectPart;

View File

@@ -47,6 +47,7 @@ public:
static BaseEditorDocumentParser *get(const QString &filePath);
struct Configuration {
bool stickToPreviousProjectPart = true;
bool usePrecompiledHeaders = false;
QByteArray editorDefines;
ProjectPart::Ptr manuallySetProjectPart;

View File

@@ -491,7 +491,7 @@ void Dumper::dumpProjectInfos( const QList<ProjectInfo> &projectInfos)
}
if (!part->projectConfigFile.isEmpty())
m_out << i3 << "Project Config File: " << part->projectConfigFile << "\n";
m_out << i2 << "Project Part \"" << part->projectFile << "\"{{{3\n";
m_out << i2 << "Project Part \"" << part->id() << "\"{{{3\n";
m_out << i3 << "Project Part Name : " << part->displayName << "\n";
m_out << i3 << "Project Name : " << projectName << "\n";
m_out << i3 << "Project File : " << projectFilePath << "\n";

View File

@@ -54,6 +54,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
@@ -137,7 +138,7 @@ public:
mutable QMutex m_projectMutex;
QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
QMap<QString, ProjectPart::Ptr> m_projectFileToProjectPart;
QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
// The members below are cached/(re)calculated from the projects and/or their project parts
bool m_dirty;
QStringList m_projectFiles;
@@ -710,13 +711,11 @@ void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove
d->m_snapshot.remove(i.next());
}
static QStringList projectFilePaths(const QSet<ProjectPart::Ptr> &projectParts)
static QSet<QString> projectPartIds(const QSet<ProjectPart::Ptr> &projectParts)
{
QStringList result;
QSetIterator<ProjectPart::Ptr> it(projectParts);
while (it.hasNext())
result << it.next()->projectFile;
return result;
return Utils::transform(projectParts, [](const ProjectPart::Ptr &projectPart) {
return projectPart->id();
});
}
class ProjectInfoComparer
@@ -750,9 +749,9 @@ public:
QStringList removedProjectParts()
{
QSet<ProjectPart::Ptr> removed = m_old.projectParts().toSet();
removed.subtract(m_new.projectParts().toSet());
return projectFilePaths(removed);
QSet<QString> removed = projectPartIds(m_old.projectParts().toSet());
removed.subtract(projectPartIds(m_new.projectParts().toSet()));
return removed.toList();
}
/// Returns a list of common files that have a changed timestamp.
@@ -781,13 +780,13 @@ private:
};
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
void CppModelManager::recalculateProjectPartMappings()
{
d->m_projectFileToProjectPart.clear();
d->m_projectPartIdToProjectProjectPart.clear();
d->m_fileToProjectParts.clear();
foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
foreach (const ProjectFile &cxxFile, projectPart->files)
d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
projectPart);
@@ -883,7 +882,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
// Update Project/ProjectInfo and File/ProjectPart table
d->m_projectToProjectsInfo.insert(project, newProjectInfo);
recalculateFileToProjectParts();
recalculateProjectPartMappings();
} // Mutex scope
@@ -908,9 +907,9 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
return updateSourceFiles(filesToReindex, ForcedProgressNotification);
}
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
ProjectPart::Ptr CppModelManager::projectPartForId(const QString &projectPartId) const
{
return d->m_projectFileToProjectPart.value(projectFile);
return d->m_projectPartIdToProjectProjectPart.value(projectPartId);
}
QList<ProjectPart::Ptr> CppModelManager::projectPart(const Utils::FileName &fileName) const
@@ -981,17 +980,17 @@ void CppModelManager::delayedGC()
d->m_delayedGcTimer.start(500);
}
static QStringList pathsOfAllProjectParts(const ProjectInfo &projectInfo)
static QStringList idsOfAllProjectParts(const ProjectInfo &projectInfo)
{
QStringList projectPaths;
foreach (const ProjectPart::Ptr &part, projectInfo.projectParts())
projectPaths << part->projectFile;
projectPaths << part->id();
return projectPaths;
}
void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
{
QStringList projectFilePaths;
QStringList projectPartIds;
{
QMutexLocker locker(&d->m_projectMutex);
@@ -999,14 +998,14 @@ void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
// Save paths
const ProjectInfo projectInfo = d->m_projectToProjectsInfo.value(project, ProjectInfo());
projectFilePaths = pathsOfAllProjectParts(projectInfo);
projectPartIds = idsOfAllProjectParts(projectInfo);
d->m_projectToProjectsInfo.remove(project);
recalculateFileToProjectParts();
recalculateProjectPartMappings();
}
if (!projectFilePaths.isEmpty())
emit projectPartsRemoved(projectFilePaths);
if (!projectPartIds.isEmpty())
emit projectPartsRemoved(projectPartIds);
delayedGC();
}
@@ -1085,7 +1084,7 @@ void CppModelManager::onAboutToUnloadSession()
do {
QMutexLocker locker(&d->m_projectMutex);
d->m_projectToProjectsInfo.clear();
recalculateFileToProjectParts();
recalculateProjectPartMappings();
d->m_dirty = true;
} while (0);
}

View File

@@ -94,7 +94,7 @@ public:
QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo);
/// \return The project part with the given project file
ProjectPart::Ptr projectPartForProjectFile(const QString &projectFile) const;
ProjectPart::Ptr projectPartForId(const QString &projectPartId) const;
/// \return All project parts that mention the given file name as one of the sources/headers.
QList<ProjectPart::Ptr> projectPart(const Utils::FileName &fileName) const;
QList<ProjectPart::Ptr> projectPart(const QString &fileName) const
@@ -170,7 +170,7 @@ signals:
void sourceFilesRefreshed(const QSet<QString> &files);
void projectPartsUpdated(ProjectExplorer::Project *project);
void projectPartsRemoved(const QStringList &projectFiles);
void projectPartsRemoved(const QStringList &projectPartIds);
void globalSnapshotChanged();
@@ -198,7 +198,7 @@ private slots:
private:
void delayedGC();
void recalculateFileToProjectParts();
void recalculateProjectPartMappings();
void updateCppEditorDocuments() const;
void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot);

View File

@@ -146,7 +146,10 @@ ProjectPart::Ptr ProjectPart::copy() const
QString ProjectPart::id() const
{
return QDir::fromNativeSeparators(projectFile) + QLatin1Char(' ') + displayName;
QString projectPartId = QDir::fromNativeSeparators(projectFile);
if (!displayName.isEmpty())
projectPartId.append(QLatin1Char(' ') + displayName);
return projectPartId;
}
QByteArray ProjectPart::readProjectConfigFile(const ProjectPart::Ptr &part)
@@ -669,13 +672,15 @@ void CompilerOptionsBuilder::addOptionsForLanguage(bool checkForBorlandExtension
opts << (gnuExtensions ? QLatin1String("-std=gnu++98") : QLatin1String("-std=c++98"));
break;
case ProjectPart::CXX03:
// Clang 3.6 does not know -std=gnu++03.
opts << QLatin1String("-std=c++03");
break;
case ProjectPart::CXX14:
opts << QLatin1String("-std=c++1y"); // TODO: change to c++14 after 3.5
opts << (gnuExtensions ? QLatin1String("-std=gnu++14") : QLatin1String("-std=c++14"));
break;
case ProjectPart::CXX17:
opts << QLatin1String("-std=c++1z"); // TODO: change to c++17 at some point in the future
// TODO: Change to (probably) "gnu++17"/"c++17" at some point in the future.
opts << (gnuExtensions ? QLatin1String("-std=gnu++1z") : QLatin1String("-std=c++1z"));
break;
}

View File

@@ -1331,6 +1331,8 @@ BreakpointItem::~BreakpointItem()
void BreakpointItem::destroyMarker()
{
if (m_engine)
m_engine->updateBreakpointMarkers();
if (m_marker) {
BreakpointMarker *m = m_marker;
m->m_bp = 0;

Some files were not shown because too many files have changed in this diff Show More