Merge remote-tracking branch 'origin/3.5'
Change-Id: I7b3ef276d438ff0f184a649153e8aeec08a9f8c9
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 17 KiB |
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
*/
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,6 @@ Project {
|
||||
"QT_NO_CAST_FROM_ASCII"
|
||||
].concat(testsEnabled ? ["WITH_TESTS"] : [])
|
||||
qbsSearchPaths: "qbs"
|
||||
property bool enableQbsJavaSupport: false
|
||||
|
||||
references: [
|
||||
"src/src.qbs",
|
||||
|
@@ -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
|
||||
# define signals public __attribute__((annotate("qt_signal")))
|
||||
# define slots __attribute__((annotate("qt_slot")))
|
||||
# endif
|
||||
#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
|
||||
#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 slots
|
||||
# define slots __attribute__((annotate("qt_slot")))
|
||||
#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_SIGNALS
|
||||
# define Q_SIGNALS public __attribute__((annotate("qt_signal")))
|
||||
#endif
|
||||
|
||||
#define SIGNAL(a) #a
|
||||
#define SLOT(a) #a
|
||||
#ifdef Q_SLOTS
|
||||
# define Q_SLOTS slots __attribute__((annotate("qt_slot")))
|
||||
#endif
|
||||
|
||||
#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
|
@@ -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)
|
||||
|
@@ -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):
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ Product {
|
||||
qbs.installSourceBase: "qtcreator"
|
||||
prefix: "qtcreator/"
|
||||
files: [
|
||||
"cplusplus/**/*",
|
||||
"debugger/**/*",
|
||||
"designer/**/*",
|
||||
"generic-highlighter/**/*",
|
||||
|
3
src/libs/3rdparty/cplusplus/Scope.cpp
vendored
@@ -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];
|
||||
}
|
||||
|
@@ -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
|
||||
|
114
src/libs/clangbackendipc/clangbackendipcdebugutils.cpp
Normal 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
|
@@ -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
|
@@ -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_;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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_;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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_;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
};
|
||||
|
@@ -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();
|
||||
|
@@ -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() << ")";
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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,12 +1256,14 @@ LookupScopePrivate *LookupScopePrivate::nestedType(const Name *name, LookupScope
|
||||
if (baseTemplate)
|
||||
break;
|
||||
}
|
||||
if (LookupScopePrivate *specialization =
|
||||
findSpecialization(baseTemplate, templId, specializations, origin)) {
|
||||
reference = specialization;
|
||||
if (Q_UNLIKELY(debug)) {
|
||||
Overview oo;
|
||||
qDebug() << "picked specialization" << oo(specialization->_name);
|
||||
if (baseTemplate) {
|
||||
if (LookupScopePrivate *specialization =
|
||||
findSpecialization(baseTemplate, templId, specializations, origin)) {
|
||||
reference = specialization;
|
||||
if (Q_UNLIKELY(debug)) {
|
||||
Overview oo;
|
||||
qDebug() << "picked specialization" << oo(specialization->_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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 \
|
||||
|
@@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
const QString &functionName)
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
QTC_ASSERT(m_modelManagerSupport, return);
|
||||
m_modelManagerSupport->ipcCommunicator().unregisterFilesForCodeCompletion(
|
||||
{ClangBackEnd::FileContainer(filePath(), projectFilePath)});
|
||||
if (m_projectPart) {
|
||||
QTC_ASSERT(m_modelManagerSupport, return);
|
||||
m_modelManagerSupport->ipcCommunicator().unregisterFilesForCodeCompletion(
|
||||
{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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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.";
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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>
|
||||
|
@@ -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"
|
||||
|
@@ -57,6 +57,7 @@ private slots:
|
||||
void testCompleteGlobals();
|
||||
void testCompleteMembers();
|
||||
void testCompleteFunctions();
|
||||
void testCompleteConstructorAndFallbackToGlobalCompletion();
|
||||
|
||||
void testProjectDependentCompletion();
|
||||
void testChangingProjectDependentCompletion();
|
||||
|
@@ -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()));
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
int globalVariable;
|
||||
|
||||
struct GlobalClassWithCustomConstructor {
|
||||
GlobalClassWithCustomConstructor(int) {}
|
||||
};
|
||||
|
||||
void f() {
|
||||
GlobalClassWithCustomConstructor foo( /* COMPLETE HERE */
|
||||
}
|
@@ -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>
|
||||
|
@@ -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,
|
||||
|
@@ -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"
|
||||
|
@@ -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,
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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")
|
||||
|
@@ -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:
|
||||
|
@@ -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;
|
||||
|
@@ -47,6 +47,7 @@ public:
|
||||
static BaseEditorDocumentParser *get(const QString &filePath);
|
||||
|
||||
struct Configuration {
|
||||
bool stickToPreviousProjectPart = true;
|
||||
bool usePrecompiledHeaders = false;
|
||||
QByteArray editorDefines;
|
||||
ProjectPart::Ptr manuallySetProjectPart;
|
||||
|
@@ -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";
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|