summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2013-12-10 14:37:32 +0100
committerhjk <hjk121@nokiamail.com>2013-12-20 17:05:09 +0100
commit5beb74fd9d11b31b360e0a336e269b81cbca1f5a (patch)
tree6b1bad7c9798e29c694e216f1196e0f47b77ceff
parent93b7528431857d67aa0ffdc60f835d987aa7b101 (diff)
downloadqt-creator-5beb74fd9d11b31b360e0a336e269b81cbca1f5a.tar.gz
Add experimental clang code-model plug-in.
Previously known as the wip/clang branch. Contributors (in alphabetical order): - Christian Kamm <christian.d.kamm@nokia.com> - Erik Verbruggen <erik.verbruggen@digia.com> - Leandro Melo <leandro.melo@nokia.com> - Peter Kuemmel <syntheticpp@gmx.net> - Sergey Shambir <sergey.shambir.auto@gmail.com> Change-Id: I4c3ff600a19b6732641c1d5ef28236bf2cc17737 Reviewed-by: hjk <hjk121@nokiamail.com>
-rw-r--r--share/qtcreator/cplusplus/qt5-qobjectdefs-injected.h60
-rw-r--r--share/qtcreator/static.pro3
-rw-r--r--src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in21
-rw-r--r--src/plugins/clangcodemodel/clang_global.h42
-rw-r--r--src/plugins/clangcodemodel/clang_installation.pri74
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel.pro124
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel_dependencies.pri7
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.cpp96
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.h76
-rw-r--r--src/plugins/clangcodemodel/clangcompleter.cpp208
-rw-r--r--src/plugins/clangcodemodel/clangcompleter.h223
-rw-r--r--src/plugins/clangcodemodel/clangcompletion.cpp1221
-rw-r--r--src/plugins/clangcodemodel/clangcompletion.h144
-rw-r--r--src/plugins/clangcodemodel/clanghighlightingsupport.cpp99
-rw-r--r--src/plugins/clangcodemodel/clanghighlightingsupport.h69
-rw-r--r--src/plugins/clangcodemodel/clangindexer.cpp169
-rw-r--r--src/plugins/clangcodemodel/clangindexer.h94
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp69
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.h64
-rw-r--r--src/plugins/clangcodemodel/clangprojectsettings.cpp108
-rw-r--r--src/plugins/clangcodemodel/clangprojectsettings.h82
-rw-r--r--src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp165
-rw-r--r--src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h72
-rw-r--r--src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui87
-rw-r--r--src/plugins/clangcodemodel/clangsymbolsearcher.cpp155
-rw-r--r--src/plugins/clangcodemodel/clangsymbolsearcher.h69
-rw-r--r--src/plugins/clangcodemodel/clangutils.cpp322
-rw-r--r--src/plugins/clangcodemodel/clangutils.h58
-rw-r--r--src/plugins/clangcodemodel/completionproposalsbuilder.cpp748
-rw-r--r--src/plugins/clangcodemodel/completionproposalsbuilder.h88
-rw-r--r--src/plugins/clangcodemodel/constants.h61
-rw-r--r--src/plugins/clangcodemodel/cppcreatemarkers.cpp201
-rw-r--r--src/plugins/clangcodemodel/cppcreatemarkers.h106
-rw-r--r--src/plugins/clangcodemodel/cxprettyprinter.cpp550
-rw-r--r--src/plugins/clangcodemodel/cxprettyprinter.h75
-rw-r--r--src/plugins/clangcodemodel/cxraii.h145
-rw-r--r--src/plugins/clangcodemodel/dependencygraph.cpp208
-rw-r--r--src/plugins/clangcodemodel/dependencygraph.h235
-rw-r--r--src/plugins/clangcodemodel/diagnostic.cpp63
-rw-r--r--src/plugins/clangcodemodel/diagnostic.h82
-rw-r--r--src/plugins/clangcodemodel/fastindexer.cpp36
-rw-r--r--src/plugins/clangcodemodel/fastindexer.h49
-rw-r--r--src/plugins/clangcodemodel/index.cpp491
-rw-r--r--src/plugins/clangcodemodel/index.h87
-rw-r--r--src/plugins/clangcodemodel/indexer.cpp1286
-rw-r--r--src/plugins/clangcodemodel/indexer.h103
-rw-r--r--src/plugins/clangcodemodel/liveunitsmanager.cpp91
-rw-r--r--src/plugins/clangcodemodel/liveunitsmanager.h78
-rw-r--r--src/plugins/clangcodemodel/pchinfo.cpp62
-rw-r--r--src/plugins/clangcodemodel/pchinfo.h80
-rw-r--r--src/plugins/clangcodemodel/pchmanager.cpp433
-rw-r--r--src/plugins/clangcodemodel/pchmanager.h100
-rw-r--r--src/plugins/clangcodemodel/raii/scopedclangoptions.cpp103
-rw-r--r--src/plugins/clangcodemodel/raii/scopedclangoptions.h72
-rw-r--r--src/plugins/clangcodemodel/semanticmarker.cpp506
-rw-r--r--src/plugins/clangcodemodel/semanticmarker.h90
-rw-r--r--src/plugins/clangcodemodel/sourcelocation.cpp79
-rw-r--r--src/plugins/clangcodemodel/sourcelocation.h69
-rw-r--r--src/plugins/clangcodemodel/sourcemarker.cpp41
-rw-r--r--src/plugins/clangcodemodel/sourcemarker.h97
-rw-r--r--src/plugins/clangcodemodel/symbol.cpp117
-rw-r--r--src/plugins/clangcodemodel/symbol.h77
-rw-r--r--src/plugins/clangcodemodel/test/clang_tests_database.qrc20
-rw-r--r--src/plugins/clangcodemodel/test/clangcompletion_test.cpp392
-rw-r--r--src/plugins/clangcodemodel/test/completiontesthelper.cpp148
-rw-r--r--src/plugins/clangcodemodel/test/completiontesthelper.h80
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_1.cpp44
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_2.cpp50
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_3.cpp68
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_4.cpp41
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_5.cpp61
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_6.cpp50
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_7.cpp47
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_8.cpp46
-rw-r--r--src/plugins/clangcodemodel/test/cxx_regression_9.cpp54
-rw-r--r--src/plugins/clangcodemodel/test/cxx_snippets_1.cpp48
-rw-r--r--src/plugins/clangcodemodel/test/cxx_snippets_2.cpp44
-rw-r--r--src/plugins/clangcodemodel/test/cxx_snippets_3.cpp52
-rw-r--r--src/plugins/clangcodemodel/test/cxx_snippets_4.cpp60
-rw-r--r--src/plugins/clangcodemodel/test/objc_messages_1.mm57
-rw-r--r--src/plugins/clangcodemodel/test/objc_messages_2.mm42
-rw-r--r--src/plugins/clangcodemodel/test/objc_messages_3.mm54
-rw-r--r--src/plugins/clangcodemodel/unit.cpp455
-rw-r--r--src/plugins/clangcodemodel/unit.h195
-rw-r--r--src/plugins/clangcodemodel/unsavedfiledata.cpp61
-rw-r--r--src/plugins/clangcodemodel/unsavedfiledata.h65
-rw-r--r--src/plugins/clangcodemodel/utils.cpp89
-rw-r--r--src/plugins/clangcodemodel/utils.h65
-rw-r--r--src/plugins/clangcodemodel/utils_p.cpp124
-rw-r--r--src/plugins/clangcodemodel/utils_p.h66
-rw-r--r--src/plugins/cppeditor/cppeditor.cpp2
-rw-r--r--src/plugins/cpptools/cppchecksymbols.h11
-rw-r--r--src/plugins/cpptools/cppcodemodelsettingspage.cpp2
-rw-r--r--src/plugins/cpptools/cpphighlightingsupport.h3
-rw-r--r--src/plugins/plugins.pro6
95 files changed, 13289 insertions, 3 deletions
diff --git a/share/qtcreator/cplusplus/qt5-qobjectdefs-injected.h b/share/qtcreator/cplusplus/qt5-qobjectdefs-injected.h
new file mode 100644
index 0000000000..1fe53e6c95
--- /dev/null
+++ b/share/qtcreator/cplusplus/qt5-qobjectdefs-injected.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#define QT_NO_META_MACROS
+
+#define signals public __attribute__((annotate("qt_signal")))
+#define slots __attribute__((annotate("qt_slot")))
+#define Q_SIGNALS signals
+#define Q_SLOTS slots
+#define Q_SIGNAL __attribute__((annotate("qt_signal")))
+#define Q_SLOT __attribute__((annotate("qt_slot")))
+# define Q_PRIVATE_SLOT(d, signature)
+
+#define Q_EMIT
+#define emit
+#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_SCRIPTABLE
+#define Q_INVOKABLE
+
+#define Q_GADGET \
+public: \
+ static const QMetaObject staticMetaObject; \
+private:
+
+#define SIGNAL(a) #a
+#define SLOT(a) #a
diff --git a/share/qtcreator/static.pro b/share/qtcreator/static.pro
index 7795d9cec3..7e417cd9bc 100644
--- a/share/qtcreator/static.pro
+++ b/share/qtcreator/static.pro
@@ -38,7 +38,8 @@ DATA_DIRS = \
qml \
qml-type-descriptions \
generic-highlighter \
- glsl
+ glsl \
+ cplusplus
macx: DATA_DIRS += scripts
for(data_dir, DATA_DIRS) {
diff --git a/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in b/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in
new file mode 100644
index 0000000000..6ec7131af0
--- /dev/null
+++ b/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in
@@ -0,0 +1,21 @@
+<plugin name=\"ClangCodeModel\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\" experimental=\"true\">
+ <vendor>Digia Plc</vendor>
+ <copyright>(C) 2013 Digia Plc</copyright>
+ <license>
+Commercial Usage
+
+Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia.
+
+GNU Lesser General Public License Usage
+
+Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ </license>
+ <category>C++</category>
+ <description>Clang Code Model plugin.</description>
+ <url>http://www.qt-project.org</url>
+ <dependencyList>
+ <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
+ </dependencyList>
+</plugin>
diff --git a/src/plugins/clangcodemodel/clang_global.h b/src/plugins/clangcodemodel/clang_global.h
new file mode 100644
index 0000000000..a5af285d05
--- /dev/null
+++ b/src/plugins/clangcodemodel/clang_global.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_GLOBAL_H
+#define CLANG_GLOBAL_H
+
+#include <qglobal.h>
+
+#if defined(CLANGCODEMODEL_LIBRARY)
+# define CLANG_EXPORT Q_DECL_EXPORT
+#else
+# define CLANG_EXPORT Q_DECL_IMPORT
+#endif
+
+
+#endif // CLANG_GLOBAL_H
diff --git a/src/plugins/clangcodemodel/clang_installation.pri b/src/plugins/clangcodemodel/clang_installation.pri
new file mode 100644
index 0000000000..c2df14d34b
--- /dev/null
+++ b/src/plugins/clangcodemodel/clang_installation.pri
@@ -0,0 +1,74 @@
+isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR)
+
+DEFINES += CLANG_COMPLETION
+DEFINES += CLANG_HIGHLIGHTING
+#DEFINES += CLANG_INDEXING
+
+defineReplace(findLLVMConfig) {
+ LLVM_CONFIG_VARIANTS = \
+ llvm-config llvm-config-3.2 llvm-config-3.3 llvm-config-3.4 \
+ llvm-config-3.5 llvm-config-3.6 llvm-config-4.0 llvm-config-4.1
+
+ ENV_PATH = $$(PATH)
+ win32 {
+ ENV_PATH = $$split($$ENV_PATH, ;)
+ } else {
+ ENV_PATH = $$split($$ENV_PATH, :)
+ }
+ for (variant, LLVM_CONFIG_VARIANTS) {
+ !isEmpty(LLVM_INSTALL_DIR) {
+ variant=$$LLVM_INSTALL_DIR/bin/$$variant
+ exists($$variant) {
+ return($$variant)
+ }
+ } else {
+ for (path, ENV_PATH) {
+ subvariant = $$path/$$variant
+ exists($$subvariant) {
+ return($$subvariant)
+ }
+ }
+ }
+ }
+ return(llvm-config)
+}
+
+win32 {
+ LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include
+ LLVM_LIBS = -L$$LLVM_INSTALL_DIR/bin \
+ -L$$LLVM_INSTALL_DIR/lib \
+ -lclang
+
+ LLVM_LIBS += -ladvapi32 -lshell32
+}
+
+unix {
+ LLVM_CONFIG = $$findLLVMConfig()
+
+ LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir)
+ isEmpty(LLVM_INCLUDEPATH):LLVM_INCLUDEPATH=$$LLVM_INSTALL_DIR/include
+ LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir)
+ isEmpty(LLVM_LIBDIR):LLVM_LIBDIR=$$LLVM_INSTALL_DIR/lib
+
+ exists ($${LLVM_LIBDIR}/libclang.*) {
+ #message("LLVM was build with autotools")
+ CLANG_LIB = clang
+ } else {
+ exists ($${LLVM_LIBDIR}/liblibclang.*) {
+ #message("LLVM was build with CMake")
+ CLANG_LIB = libclang
+ } else {
+ exists ($${LLVM_INSTALL_DIR}/lib/libclang.*) {
+ #message("libclang placed separately from LLVM")
+ CLANG_LIB = clang
+ LLVM_LIBDIR = $${LLVM_INSTALL_DIR}/lib
+ LLVM_INCLUDEPATH=$${LLVM_INSTALL_DIR}/include
+ } else {
+ error("Cannot find Clang shared library!")
+ }
+ }
+ }
+
+ LLVM_LIBS = -L$${LLVM_LIBDIR}
+ LLVM_LIBS += -l$${CLANG_LIB}
+}
diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro
new file mode 100644
index 0000000000..a3b4e550b8
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcodemodel.pro
@@ -0,0 +1,124 @@
+include(../../qtcreatorplugin.pri)
+include(clang_installation.pri)
+
+message("Building with Clang from $$LLVM_INSTALL_DIR")
+
+LIBS += $$LLVM_LIBS
+INCLUDEPATH += $$LLVM_INCLUDEPATH
+DEFINES += CLANGCODEMODEL_LIBRARY
+
+unix:QMAKE_LFLAGS += -Wl,-rpath,\'$$LLVM_LIBDIR\'
+
+contains(DEFINES, CLANG_COMPLETION) {
+ HEADERS += clangcompletion.h clangcompleter.h completionproposalsbuilder.h
+ SOURCES += clangcompletion.cpp clangcompleter.cpp completionproposalsbuilder.cpp
+}
+
+contains(DEFINES, CLANG_HIGHLIGHTING) {
+ HEADERS += cppcreatemarkers.h clanghighlightingsupport.h
+ SOURCES += cppcreatemarkers.cpp clanghighlightingsupport.cpp
+}
+
+HEADERS += clangutils.h \
+ cxprettyprinter.h
+
+SOURCES += clangutils.cpp \
+ cxprettyprinter.cpp
+
+SOURCES += \
+ $$PWD/clangcodemodelplugin.cpp \
+ $$PWD/sourcemarker.cpp \
+ $$PWD/symbol.cpp \
+ $$PWD/sourcelocation.cpp \
+ $$PWD/unit.cpp \
+ $$PWD/utils.cpp \
+ $$PWD/utils_p.cpp \
+ $$PWD/liveunitsmanager.cpp \
+ $$PWD/semanticmarker.cpp \
+ $$PWD/diagnostic.cpp \
+ $$PWD/unsavedfiledata.cpp \
+ $$PWD/fastindexer.cpp \
+ $$PWD/pchinfo.cpp \
+ $$PWD/pchmanager.cpp \
+ $$PWD/clangprojectsettings.cpp \
+ $$PWD/clangprojectsettingspropertiespage.cpp \
+ $$PWD/raii/scopedclangoptions.cpp \
+ $$PWD/clangmodelmanagersupport.cpp
+
+HEADERS += \
+ $$PWD/clangcodemodelplugin.h \
+ $$PWD/clang_global.h \
+ $$PWD/sourcemarker.h \
+ $$PWD/constants.h \
+ $$PWD/symbol.h \
+ $$PWD/cxraii.h \
+ $$PWD/sourcelocation.h \
+ $$PWD/unit.h \
+ $$PWD/utils.h \
+ $$PWD/utils_p.h \
+ $$PWD/liveunitsmanager.h \
+ $$PWD/semanticmarker.h \
+ $$PWD/diagnostic.h \
+ $$PWD/unsavedfiledata.h \
+ $$PWD/fastindexer.h \
+ $$PWD/pchinfo.h \
+ $$PWD/pchmanager.h \
+ $$PWD/clangprojectsettings.h \
+ $$PWD/clangprojectsettingspropertiespage.h \
+ $$PWD/raii/scopedclangoptions.h \
+ $$PWD/clangmodelmanagersupport.h
+
+contains(DEFINES, CLANG_INDEXING) {
+ HEADERS += \
+ $$PWD/clangindexer.h \
+ $$PWD/clangsymbolsearcher.h \
+ $$PWD/index.h \
+ $$PWD/indexer.h
+# $$PWD/dependencygraph.h \
+
+ SOURCES += \
+ $$PWD/clangindexer.cpp \
+ $$PWD/clangsymbolsearcher.cpp \
+ $$PWD/index.cpp \
+ $$PWD/indexer.cpp
+# $$PWD/dependencygraph.cpp \
+}
+
+equals(TEST, 1) {
+ RESOURCES += \
+ $$PWD/test/clang_tests_database.qrc
+
+ HEADERS += \
+ $$PWD/test/completiontesthelper.h
+
+ SOURCES += \
+ $$PWD/test/completiontesthelper.cpp \
+ $$PWD/test/clangcompletion_test.cpp
+
+ OTHER_FILES += \
+ $$PWD/test/cxx_regression_1.cpp \
+ $$PWD/test/cxx_regression_2.cpp \
+ $$PWD/test/cxx_regression_3.cpp \
+ $$PWD/test/cxx_regression_4.cpp \
+ $$PWD/test/cxx_regression_5.cpp \
+ $$PWD/test/cxx_regression_6.cpp \
+ $$PWD/test/cxx_regression_7.cpp \
+ $$PWD/test/cxx_regression_8.cpp \
+ $$PWD/test/cxx_regression_9.cpp \
+ $$PWD/test/cxx_snippets_1.cpp \
+ $$PWD/test/cxx_snippets_2.cpp \
+ $$PWD/test/cxx_snippets_3.cpp \
+ test/cxx_snippets_4.cpp \
+ test/objc_messages_1.mm \
+ test/objc_messages_2.mm \
+ test/objc_messages_3.mm
+}
+
+FORMS += $$PWD/clangprojectsettingspropertiespage.ui
+
+macx {
+ LIBCLANG_VERSION=3.3
+ POSTL = install_name_tool -change "@executable_path/../lib/libclang.$${LIBCLANG_VERSION}.dylib" "$$LLVM_INSTALL_DIR/lib/libclang.$${LIBCLANG_VERSION}.dylib" "\"$${DESTDIR}/lib$${TARGET}.dylib\"" $$escape_expand(\\n\\t)
+ !isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK
+ QMAKE_POST_LINK = $$POSTL $$QMAKE_POST_LINK
+}
diff --git a/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri
new file mode 100644
index 0000000000..dea57152d0
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri
@@ -0,0 +1,7 @@
+QTC_PLUGIN_NAME = ClangCodeModel
+QTC_LIB_DEPENDS += \
+ utils
+QTC_PLUGIN_DEPENDS += \
+ coreplugin \
+ cpptools \
+ texteditor
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
new file mode 100644
index 0000000000..6f21da3850
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangcodemodelplugin.h"
+#include "clangprojectsettingspropertiespage.h"
+#include "fastindexer.h"
+#include "pchmanager.h"
+#include "utils.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/id.h>
+
+#include <cpptools/cppmodelmanager.h>
+
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+
+#include <QtPlugin>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage)
+{
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorMessage)
+
+ addAutoReleasedObject(new ClangProjectSettingsPanelFactory);
+
+ ClangCodeModel::Internal::initializeClang();
+
+ connect(Core::EditorManager::instance(), SIGNAL(editorAboutToClose(Core::IEditor*)),
+ &m_liveUnitsManager, SLOT(editorAboutToClose(Core::IEditor*)));
+ connect(Core::EditorManager::instance(), SIGNAL(editorOpened(Core::IEditor*)),
+ &m_liveUnitsManager, SLOT(editorOpened(Core::IEditor*)));
+
+ PCHManager *pchManager = new PCHManager(this);
+ FastIndexer *fastIndexer = 0;
+
+#ifdef CLANG_INDEXING
+ m_indexer.reset(new ClangIndexer);
+ fastIndexer = m_indexer.data();
+ CppTools::CppModelManagerInterface::instance()->setIndexingSupport(m_indexer->indexingSupport());
+#endif // CLANG_INDEXING
+
+ // wire up the pch manager
+ QObject *session = ProjectExplorer::SessionManager::instance();
+ connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
+ pchManager, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
+ connect(CppTools::CppModelManagerInterface::instance(), SIGNAL(projectPartsUpdated(ProjectExplorer::Project*)),
+ pchManager, SLOT(onProjectPartsUpdated(ProjectExplorer::Project*)));
+
+ m_modelManagerSupport.reset(new ModelManagerSupport(fastIndexer));
+ CppTools::CppModelManagerInterface::instance()->addModelManagerSupport(
+ m_modelManagerSupport.data());
+
+ return true;
+}
+
+void ClangCodeModelPlugin::extensionsInitialized()
+{
+}
+
+} // namespace Internal
+} // namespace Clang
+
+Q_EXPORT_PLUGIN(ClangCodeModel::Internal::ClangCodeModelPlugin)
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h
new file mode 100644
index 0000000000..53603d2ed7
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGPLUGIN_H
+#define CLANGPLUGIN_H
+
+#include "clangmodelmanagersupport.h"
+#include "liveunitsmanager.h"
+
+#ifdef CLANG_INDEXING
+# include "clangindexer.h"
+#endif // CLANG_INDEXING
+
+#include <extensionsystem/iplugin.h>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class ClangCodeModelPlugin: public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClangCodeModel.json")
+
+public:
+ bool initialize(const QStringList &arguments, QString *errorMessage);
+
+ void extensionsInitialized();
+
+private:
+ LiveUnitsManager m_liveUnitsManager;
+ QScopedPointer<ModelManagerSupport> m_modelManagerSupport;
+#ifdef CLANG_INDEXING
+ QScopedPointer<ClangIndexer> m_indexer;
+#endif // CLANG_INDEXING
+
+#ifdef WITH_TESTS
+private slots:
+ void test_CXX_regressions();
+ void test_CXX_regressions_data();
+ void test_CXX_snippets();
+ void test_CXX_snippets_data();
+ void test_ObjC_hints();
+ void test_ObjC_hints_data();
+#endif
+};
+
+} // namespace Internal
+} // namespace Clang
+
+#endif // CLANGPLUGIN_H
diff --git a/src/plugins/clangcodemodel/clangcompleter.cpp b/src/plugins/clangcodemodel/clangcompleter.cpp
new file mode 100644
index 0000000000..31dcf7546b
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcompleter.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangcompleter.h"
+#include "sourcemarker.h"
+#include "unsavedfiledata.h"
+#include "utils_p.h"
+#include "completionproposalsbuilder.h"
+#include "raii/scopedclangoptions.h"
+#include "unit.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QTime>
+
+#include <clang-c/Index.h>
+
+//#define TIME_COMPLETION
+
+class ClangCodeModel::ClangCompleter::PrivateData
+{
+public:
+ PrivateData()
+ : m_mutex(QMutex::Recursive)
+ , m_isSignalSlotCompletion(false)
+ {
+ }
+
+ ~PrivateData()
+ {
+ }
+
+ bool parseFromFile(const Internal::UnsavedFiles &unsavedFiles)
+ {
+ Q_ASSERT(!m_unit.isLoaded());
+ if (m_unit.fileName().isEmpty())
+ return false;
+
+ unsigned opts = clang_defaultEditingTranslationUnitOptions();
+#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5)
+ opts |= CXTranslationUnit_CacheCompletionResults;
+ opts |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
+#endif
+ m_unit.setManagementOptions(opts);
+
+ m_unit.setUnsavedFiles(unsavedFiles);
+ m_unit.parse();
+ return m_unit.isLoaded();
+ }
+
+public:
+ QMutex m_mutex;
+ Internal::Unit m_unit;
+ bool m_isSignalSlotCompletion;
+};
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+/**
+ * @brief Constructs with highest possible priority
+ */
+CodeCompletionResult::CodeCompletionResult()
+ : m_priority(SHRT_MAX)
+ , m_completionKind(Other)
+ , m_availability(Available)
+ , m_hasParameters(false)
+{}
+
+/**
+ * @brief Constructs with given priority
+ * @param priority Will be reversed, because clang's highest priority is 0,
+ * but inside QtCreator it is the lowest priority
+ */
+CodeCompletionResult::CodeCompletionResult(unsigned priority)
+ : m_priority(SHRT_MAX - priority)
+ , m_completionKind(Other)
+ , m_availability(Available)
+ , m_hasParameters(false)
+{
+}
+
+ClangCompleter::ClangCompleter()
+ : d(new PrivateData)
+{
+}
+
+ClangCompleter::~ClangCompleter()
+{
+}
+
+QString ClangCompleter::fileName() const
+{
+ return d->m_unit.fileName();
+}
+
+void ClangCompleter::setFileName(const QString &fileName)
+{
+ if (d->m_unit.fileName() != fileName) {
+ d->m_unit = Internal::Unit(fileName);
+ }
+}
+
+QStringList ClangCompleter::options() const
+{
+ return d->m_unit.compilationOptions();
+}
+
+void ClangCompleter::setOptions(const QStringList &options) const
+{
+ if (d->m_unit.compilationOptions() != options) {
+ d->m_unit.setCompilationOptions(options);
+ d->m_unit.unload();
+ }
+}
+
+bool ClangCompleter::isSignalSlotCompletion() const
+{
+ return d->m_isSignalSlotCompletion;
+}
+
+void ClangCompleter::setSignalSlotCompletion(bool isSignalSlot)
+{
+ d->m_isSignalSlotCompletion = isSignalSlot;
+}
+
+bool ClangCompleter::reparse(const UnsavedFiles &unsavedFiles)
+{
+ if (!d->m_unit.isLoaded())
+ return d->parseFromFile(unsavedFiles);
+
+ d->m_unit.setUnsavedFiles(unsavedFiles);
+ d->m_unit.reparse();
+ return d->m_unit.isLoaded();
+}
+
+QList<CodeCompletionResult> ClangCompleter::codeCompleteAt(unsigned line,
+ unsigned column,
+ const UnsavedFiles &unsavedFiles)
+{
+#ifdef TIME_COMPLETION
+ QTime t;t.start();
+#endif // TIME_COMPLETION
+
+ if (!d->m_unit.isLoaded())
+ if (!d->parseFromFile(unsavedFiles))
+ return QList<CodeCompletionResult>();
+
+ ScopedCXCodeCompleteResults results;
+ d->m_unit.setUnsavedFiles(unsavedFiles);
+ d->m_unit.codeCompleteAt(line, column, results);
+
+ QList<CodeCompletionResult> completions;
+ if (results) {
+ const quint64 contexts = clang_codeCompleteGetContexts(results);
+ CompletionProposalsBuilder builder(completions, contexts, d->m_isSignalSlotCompletion);
+ for (unsigned i = 0; i < results.size(); ++i)
+ builder(results.completionAt(i));
+ }
+
+#ifdef TIME_COMPLETION
+ qDebug() << "Completion timing:" << completions.size() << "results in" << t.elapsed() << "ms.";
+#endif // TIME_COMPLETION
+
+ return completions;
+}
+
+bool ClangCompleter::objcEnabled() const
+{
+ static const QString objcppOption = QLatin1String("-ObjC++");
+ static const QString objcOption = QLatin1String("-ObjC");
+
+ QStringList options = d->m_unit.compilationOptions();
+ return options.contains(objcOption) || options.contains(objcppOption);
+}
+
+QMutex *ClangCompleter::mutex() const
+{
+ return &d->m_mutex;
+}
diff --git a/src/plugins/clangcodemodel/clangcompleter.h b/src/plugins/clangcodemodel/clangcompleter.h
new file mode 100644
index 0000000000..49e95a7c70
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcompleter.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGCOMPLETER_H
+#define CLANGCOMPLETER_H
+
+#include "clang_global.h"
+#include "diagnostic.h"
+#include "sourcelocation.h"
+#include "utils.h"
+
+#include <QList>
+#include <QMap>
+#include <QMutex>
+#include <QPair>
+#include <QSharedPointer>
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+namespace ClangCodeModel {
+
+class SourceMarker;
+
+class CLANG_EXPORT CodeCompletionResult
+{
+public:
+ enum Kind {
+ Other = 0,
+ FunctionCompletionKind,
+ ConstructorCompletionKind,
+ DestructorCompletionKind,
+ VariableCompletionKind,
+ ClassCompletionKind,
+ EnumCompletionKind,
+ EnumeratorCompletionKind,
+ NamespaceCompletionKind,
+ PreProcessorCompletionKind,
+ SignalCompletionKind,
+ SlotCompletionKind,
+ ObjCMessageCompletionKind,
+ KeywordCompletionKind,
+ ClangSnippetKind
+ };
+
+ enum Availability {
+ Available,
+ Deprecated,
+ NotAvailable,
+ NotAccessible
+ };
+
+public:
+ CodeCompletionResult();
+ CodeCompletionResult(unsigned priority);
+
+ unsigned priority() const
+ { return m_priority; }
+
+ bool isValid() const
+ { return !m_text.isEmpty(); }
+
+ QString text() const
+ { return m_text; }
+ void setText(const QString &text)
+ { m_text = text; }
+
+ QString hint() const
+ { return m_hint; }
+ void setHint(const QString &hint)
+ { m_hint = hint; }
+
+ QString snippet() const
+ { return m_snippet; }
+ void setSnippet(const QString &snippet)
+ { m_snippet = snippet; }
+
+ Kind completionKind() const
+ { return m_completionKind; }
+ void setCompletionKind(Kind type)
+ { m_completionKind = type; }
+
+ int compare(const CodeCompletionResult &other) const
+ {
+ if (m_priority < other.m_priority)
+ return -1;
+ else if (m_priority > other.m_priority)
+ return 1;
+
+ if (m_completionKind < other.m_completionKind)
+ return -1;
+ else if (m_completionKind > other.m_completionKind)
+ return 1;
+
+ if (m_text < other.m_text)
+ return -1;
+ else if (m_text > other.m_text)
+ return 1;
+
+ if (m_hint < other.m_hint)
+ return -1;
+ else if (m_hint > other.m_hint)
+ return 1;
+
+ if (!m_hasParameters && other.m_hasParameters)
+ return -1;
+ else if (m_hasParameters && !other.m_hasParameters)
+ return 1;
+
+ if (m_availability < other.m_availability)
+ return -1;
+ else if (m_availability > other.m_availability)
+ return 1;
+
+ return 0;
+ }
+
+ bool hasParameters() const
+ { return m_hasParameters; }
+ void setHasParameters(bool hasParameters)
+ { m_hasParameters = hasParameters; }
+
+ Availability availability() const
+ { return m_availability; }
+ void setAvailability(Availability availability)
+ { m_availability = availability; }
+
+private:
+ unsigned m_priority;
+ Kind m_completionKind;
+ QString m_text;
+ QString m_hint;
+ QString m_snippet;
+ Availability m_availability;
+ bool m_hasParameters;
+};
+
+inline CLANG_EXPORT uint qHash(const CodeCompletionResult &ccr)
+{ return ccr.completionKind() ^ qHash(ccr.text()); }
+
+inline CLANG_EXPORT bool operator==(const CodeCompletionResult &ccr1, const CodeCompletionResult &ccr2)
+{ return ccr1.compare(ccr2) == 0; }
+
+inline CLANG_EXPORT bool operator<(const CodeCompletionResult &ccr1, const CodeCompletionResult &ccr2)
+{
+ return ccr1.compare(ccr2) < 0;
+}
+
+class CLANG_EXPORT ClangCompleter
+{
+ Q_DISABLE_COPY(ClangCompleter)
+
+ class PrivateData;
+
+public: // data structures
+ typedef QSharedPointer<ClangCompleter> Ptr;
+
+public: // methods
+ ClangCompleter();
+ ~ClangCompleter();
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ QStringList options() const;
+ void setOptions(const QStringList &options) const;
+
+ bool isSignalSlotCompletion() const;
+ void setSignalSlotCompletion(bool isSignalSlot);
+
+ bool reparse(const Internal::UnsavedFiles &unsavedFiles);
+
+ /**
+ * Do code-completion at the specified position.
+ *
+ * \param line The line number on which to do code-completion. The first
+ * line of a file has line number 1.
+ * \param column The column number where to do code-completion. Column
+ * numbers start with 1.
+ */
+ QList<CodeCompletionResult> codeCompleteAt(unsigned line,
+ unsigned column,
+ const Internal::UnsavedFiles &unsavedFiles);
+
+ bool objcEnabled() const;
+
+ QMutex *mutex() const;
+
+private: // instance fields
+ QScopedPointer<PrivateData> d;
+};
+
+} // namespace Clang
+
+Q_DECLARE_METATYPE(ClangCodeModel::CodeCompletionResult)
+
+#endif // CLANGCOMPLETER_H
diff --git a/src/plugins/clangcodemodel/clangcompletion.cpp b/src/plugins/clangcodemodel/clangcompletion.cpp
new file mode 100644
index 0000000000..0a1a2ad578
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcompletion.cpp
@@ -0,0 +1,1221 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangcompletion.h"
+#include "clangutils.h"
+#include "pchmanager.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/idocument.h>
+#include <coreplugin/mimedatabase.h>
+
+#include <cplusplus/BackwardsScanner.h>
+#include <cplusplus/ExpressionUnderCursor.h>
+#include <cplusplus/Token.h>
+#include <cplusplus/MatchingText.h>
+
+#include <cppeditor/cppeditorconstants.h>
+
+#include <cpptools/cppdoxygen.h>
+#include <cpptools/cppmodelmanagerinterface.h>
+
+#include <texteditor/basetexteditor.h>
+#include <texteditor/convenience.h>
+#include <texteditor/codeassist/basicproposalitemlistmodel.h>
+#include <texteditor/codeassist/basicproposalitem.h>
+#include <texteditor/codeassist/functionhintproposal.h>
+#include <texteditor/codeassist/genericproposal.h>
+#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
+#include <texteditor/texteditorsettings.h>
+#include <texteditor/completionsettings.h>
+
+#include <QCoreApplication>
+#include <QDirIterator>
+#include <QTextCursor>
+#include <QTextDocument>
+
+static const bool DebugTiming = !qgetenv("QTC_CLANG_VERBOSE").isEmpty();
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace CPlusPlus;
+using namespace CppTools;
+using namespace TextEditor;
+
+static const char SNIPPET_ICON_PATH[] = ":/texteditor/images/snippet.png";
+
+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;
+}
+
+static QList<CodeCompletionResult> unfilteredCompletion(const ClangCompletionAssistInterface* interface,
+ const QString &fileName,
+ unsigned line, unsigned column,
+ QByteArray modifiedInput = QByteArray(),
+ bool isSignalSlotCompletion = false)
+{
+ ClangCompleter::Ptr wrapper = interface->clangWrapper();
+ QMutexLocker lock(wrapper->mutex());
+ //### TODO: check if we're cancelled after we managed to acquire the mutex
+
+ wrapper->setFileName(fileName);
+ wrapper->setOptions(interface->options());
+ wrapper->setSignalSlotCompletion(isSignalSlotCompletion);
+ UnsavedFiles unsavedFiles = interface->unsavedFiles();
+ if (!modifiedInput.isEmpty())
+ unsavedFiles.insert(fileName, modifiedInput);
+
+ QTime t;
+ if (DebugTiming) {
+ qDebug() << "Here we go with ClangCompletionAssistProcessor....";
+ t.start();
+ }
+
+ QList<CodeCompletionResult> result = wrapper->codeCompleteAt(line, column + 1, unsavedFiles);
+ qSort(result);
+
+ if (DebugTiming)
+ qDebug() << "... Completion done in" << t.elapsed() << "ms, with" << result.count() << "items.";
+
+ return result;
+}
+
+} // Anonymous
+
+namespace ClangCodeModel {
+namespace Internal {
+
+// -----------------------------
+// ClangCompletionAssistProvider
+// -----------------------------
+ClangCompletionAssistProvider::ClangCompletionAssistProvider()
+ : m_clangCompletionWrapper(new ClangCodeModel::ClangCompleter)
+{
+}
+
+IAssistProcessor *ClangCompletionAssistProvider::createProcessor() const
+{
+ return new ClangCompletionAssistProcessor;
+}
+
+IAssistInterface *ClangCompletionAssistProvider::createAssistInterface(
+ ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor,
+ QTextDocument *document, int position, AssistReason reason) const
+{
+ Q_UNUSED(project);
+
+ QString fileName = editor->document()->filePath();
+ CppModelManagerInterface *modelManager = CppModelManagerInterface::instance();
+ QList<ProjectPart::Ptr> parts = modelManager->projectPart(fileName);
+ if (parts.isEmpty())
+ parts += modelManager->fallbackProjectPart();
+ QStringList includePaths, frameworkPaths, options;
+ PchInfo::Ptr pchInfo;
+ foreach (ProjectPart::Ptr part, parts) {
+ if (part.isNull())
+ continue;
+ options = ClangCodeModel::Utils::createClangOptions(part, fileName);
+ pchInfo = PCHManager::instance()->pchInfo(part);
+ if (!pchInfo.isNull())
+ options.append(ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName()));
+ includePaths = part->includePaths;
+ frameworkPaths = part->frameworkPaths;
+ break;
+ }
+
+ return new ClangCodeModel::ClangCompletionAssistInterface(
+ m_clangCompletionWrapper,
+ document, position, fileName, reason,
+ options, includePaths, frameworkPaths, pchInfo);
+}
+
+// ------------------------
+// ClangAssistProposalModel
+// ------------------------
+class ClangAssistProposalModel : public TextEditor::BasicProposalItemListModel
+{
+public:
+ ClangAssistProposalModel()
+ : TextEditor::BasicProposalItemListModel()
+ , m_sortable(false)
+ , m_completionOperator(T_EOF_SYMBOL)
+ , m_replaceDotForArrow(false)
+ {}
+
+ virtual bool isSortable(const QString &prefix) const;
+ bool m_sortable;
+ unsigned m_completionOperator;
+ bool m_replaceDotForArrow;
+};
+
+// -------------------
+// ClangAssistProposal
+// -------------------
+class ClangAssistProposal : public TextEditor::GenericProposal
+{
+public:
+ ClangAssistProposal(int cursorPos, TextEditor::IGenericProposalModel *model)
+ : TextEditor::GenericProposal(cursorPos, model)
+ , m_replaceDotForArrow(static_cast<ClangAssistProposalModel *>(model)->m_replaceDotForArrow)
+ {}
+
+ virtual bool isCorrective() const { return m_replaceDotForArrow; }
+ virtual void makeCorrection(BaseTextEditor *editor)
+ {
+ editor->setCursorPosition(basePosition() - 1);
+ editor->replace(1, QLatin1String("->"));
+ moveBasePosition(1);
+ }
+
+private:
+ bool m_replaceDotForArrow;
+};
+
+// ----------------------
+// ClangFunctionHintModel
+// ----------------------
+class ClangFunctionHintModel : public TextEditor::IFunctionHintProposalModel
+{
+public:
+ ClangFunctionHintModel(const QList<CodeCompletionResult> functionSymbols)
+ : m_functionSymbols(functionSymbols)
+ , m_currentArg(-1)
+ {}
+
+ virtual void reset() {}
+ virtual int size() const { return m_functionSymbols.size(); }
+ virtual QString text(int index) const;
+ virtual int activeArgument(const QString &prefix) const;
+
+private:
+ QList<ClangCodeModel::CodeCompletionResult> m_functionSymbols;
+ mutable int m_currentArg;
+};
+
+QString ClangFunctionHintModel::text(int index) const
+{
+#if 0
+ // TODO: add the boldening to the result
+ Overview overview;
+ overview.setShowReturnTypes(true);
+ overview.setShowArgumentNames(true);
+ overview.setMarkedArgument(m_currentArg + 1);
+ Function *f = m_functionSymbols.at(index);
+
+ const QString prettyMethod = overview(f->type(), f->name());
+ const int begin = overview.markedArgumentBegin();
+ const int end = overview.markedArgumentEnd();
+
+ QString hintText;
+ hintText += Qt::escape(prettyMethod.left(begin));
+ hintText += "<b>";
+ hintText += Qt::escape(prettyMethod.mid(begin, end - begin));
+ hintText += "</b>";
+ hintText += Qt::escape(prettyMethod.mid(end));
+ return hintText;
+#endif
+ return m_functionSymbols.at(index).hint();
+}
+
+int ClangFunctionHintModel::activeArgument(const QString &prefix) const
+{
+ int argnr = 0;
+ int parcount = 0;
+ SimpleLexer tokenize;
+ QList<CPlusPlus::Token> tokens = tokenize(prefix);
+ for (int i = 0; i < tokens.count(); ++i) {
+ const CPlusPlus::Token &tk = tokens.at(i);
+ if (tk.is(T_LPAREN))
+ ++parcount;
+ else if (tk.is(T_RPAREN))
+ --parcount;
+ else if (! parcount && tk.is(T_COMMA))
+ ++argnr;
+ }
+
+ if (parcount < 0)
+ return -1;
+
+ if (argnr != m_currentArg)
+ m_currentArg = argnr;
+
+ return argnr;
+}
+
+class ClangAssistProposalItem : public TextEditor::BasicProposalItem
+{
+public:
+ ClangAssistProposalItem() {}
+
+ virtual bool prematurelyApplies(const QChar &c) const;
+ virtual void applyContextualContent(TextEditor::BaseTextEditor *editor,
+ int basePosition) const;
+
+ void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
+
+ bool isOverloaded() const
+ { return !m_overloads.isEmpty(); }
+ void addOverload(const CodeCompletionResult &ccr)
+ { m_overloads.append(ccr); }
+
+ CodeCompletionResult originalItem() const
+ {
+ const QVariant &v = data();
+ if (v.canConvert<CodeCompletionResult>())
+ return v.value<CodeCompletionResult>();
+ else
+ return CodeCompletionResult();
+ }
+
+ bool isCodeCompletionResult() const
+ { return data().canConvert<CodeCompletionResult>(); }
+
+private:
+ unsigned m_completionOperator;
+ mutable QChar m_typedChar;
+ QList<CodeCompletionResult> m_overloads;
+};
+
+/// @return True, because clang always returns priorities for sorting
+bool ClangAssistProposalModel::isSortable(const QString &prefix) const
+{
+ Q_UNUSED(prefix)
+ return true;
+}
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
+{
+ bool ok = false;
+
+ if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
+ ok = QString::fromLatin1("(,").contains(typedChar);
+ else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
+ ok = (typedChar == QLatin1Char('/')) && text().endsWith(QLatin1Char('/'));
+ else if (!isCodeCompletionResult())
+ ok = (typedChar == QLatin1Char('(')); /* && data().canConvert<CompleteFunctionDeclaration>()*/ //###
+ else if (originalItem().completionKind() == CodeCompletionResult::ObjCMessageCompletionKind)
+ ok = QString::fromLatin1(";.,").contains(typedChar);
+ else
+ ok = QString::fromLatin1(";.,:(").contains(typedChar);
+
+ if (ok)
+ m_typedChar = typedChar;
+
+ return ok;
+}
+
+void ClangAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor,
+ int basePosition) const
+{
+ const CodeCompletionResult ccr = originalItem();
+
+ QString toInsert = text();
+ QString extraChars;
+ int extraLength = 0;
+ int cursorOffset = 0;
+
+ bool autoParenthesesEnabled = true;
+ if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
+ extraChars += QLatin1Char(')');
+ if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis
+ m_typedChar = QChar();
+ } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
+ if (!toInsert.endsWith(QLatin1Char('/'))) {
+ extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
+ } else {
+ if (m_typedChar == QLatin1Char('/')) // Eat the slash
+ m_typedChar = QChar();
+ }
+ } else if (ccr.isValid()) {
+ const CompletionSettings &completionSettings =
+ TextEditorSettings::instance()->completionSettings();
+ const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
+
+ if (autoInsertBrackets &&
+ (ccr.completionKind() == CodeCompletionResult::FunctionCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::DestructorCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::SignalCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::SlotCompletionKind)) {
+ // When the user typed the opening parenthesis, he'll likely also type the closing one,
+ // in which case it would be annoying if we put the cursor after the already automatically
+ // inserted closing parenthesis.
+ const bool skipClosingParenthesis = m_typedChar != QLatin1Char('(');
+
+ if (completionSettings.m_spaceAfterFunctionName)
+ extraChars += QLatin1Char(' ');
+ extraChars += QLatin1Char('(');
+ if (m_typedChar == QLatin1Char('('))
+ m_typedChar = QChar();
+
+ // If the function doesn't return anything, automatically place the semicolon,
+ // unless we're doing a scope completion (then it might be function definition).
+ const QChar characterAtCursor = editor->textDocument()->characterAt(editor->position());
+ bool endWithSemicolon = m_typedChar == QLatin1Char(';')/*
+ || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON)*/; //###
+ const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar;
+
+ if (endWithSemicolon && characterAtCursor == semicolon) {
+ endWithSemicolon = false;
+ m_typedChar = QChar();
+ }
+
+ // If the function takes no arguments, automatically place the closing parenthesis
+ if (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) {
+ extraChars += QLatin1Char(')');
+ if (endWithSemicolon) {
+ extraChars += semicolon;
+ m_typedChar = QChar();
+ }
+ } else if (autoParenthesesEnabled) {
+ const QChar lookAhead = editor->textDocument()->characterAt(editor->position() + 1);
+ if (MatchingText::shouldInsertMatchingText(lookAhead)) {
+ extraChars += QLatin1Char(')');
+ --cursorOffset;
+ if (endWithSemicolon) {
+ extraChars += semicolon;
+ --cursorOffset;
+ m_typedChar = QChar();
+ }
+ }
+ }
+ }
+
+#if 0
+ if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
+ if (m_typedChar == QLatin1Char('('))
+ m_typedChar = QChar();
+
+ // everything from the closing parenthesis on are extra chars, to
+ // make sure an auto-inserted ")" gets replaced by ") const" if necessary
+ int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
+ extraChars = toInsert.mid(closingParen);
+ toInsert.truncate(closingParen);
+ }
+#endif
+ }
+
+ // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
+ if (!m_typedChar.isNull()) {
+ extraChars += m_typedChar;
+ if (cursorOffset != 0)
+ --cursorOffset;
+ }
+
+ // Avoid inserting characters that are already there
+ const int endsPosition = editor->position(TextEditor::ITextEditor::EndOfLine);
+ const QString existingText = editor->textDocument()->textAt(editor->position(),
+ endsPosition - editor->position());
+ int existLength = 0;
+ if (!existingText.isEmpty()) {
+ // Calculate the exist length in front of the extra chars
+ existLength = toInsert.length() - (editor->position() - basePosition);
+ while (!existingText.startsWith(toInsert.right(existLength))) {
+ if (--existLength == 0)
+ break;
+ }
+ }
+ for (int i = 0; i < extraChars.length(); ++i) {
+ const QChar a = extraChars.at(i);
+ const QChar b = editor->textDocument()->characterAt(editor->position() + i + existLength);
+ if (a == b)
+ ++extraLength;
+ else
+ break;
+ }
+ toInsert += extraChars;
+
+ // Insert the remainder of the name
+ const int length = editor->position() - basePosition + existLength + extraLength;
+ editor->setCursorPosition(basePosition);
+ editor->replace(length, toInsert);
+ if (cursorOffset)
+ editor->setCursorPosition(editor->position() + cursorOffset);
+}
+
+bool ClangCompletionAssistInterface::objcEnabled() const
+{
+ return m_clangWrapper->objcEnabled();
+}
+
+ClangCompletionAssistInterface::ClangCompletionAssistInterface(ClangCompleter::Ptr clangWrapper,
+ QTextDocument *document,
+ int position,
+ const QString &fileName,
+ AssistReason reason,
+ const QStringList &options,
+ const QStringList &includePaths,
+ const QStringList &frameworkPaths,
+ const PchInfo::Ptr &pchInfo)
+ : DefaultAssistInterface(document, position, fileName, reason)
+ , m_clangWrapper(clangWrapper)
+ , m_options(options)
+ , m_includePaths(includePaths)
+ , m_frameworkPaths(frameworkPaths)
+ , m_savedPchPointer(pchInfo)
+{
+ Q_ASSERT(!clangWrapper.isNull());
+
+ CppModelManagerInterface *mmi = CppModelManagerInterface::instance();
+ Q_ASSERT(mmi);
+ m_unsavedFiles = Utils::createUnsavedFiles(mmi->workingCopy());
+}
+
+ClangCompletionAssistProcessor::ClangCompletionAssistProcessor()
+ : m_preprocessorCompletions(QStringList()
+ << QLatin1String("define")
+ << QLatin1String("error")
+ << QLatin1String("include")
+ << QLatin1String("line")
+ << QLatin1String("pragma")
+ << QLatin1String("pragma once")
+ << QLatin1String("pragma omp atomic")
+ << QLatin1String("pragma omp parallel")
+ << QLatin1String("pragma omp for")
+ << QLatin1String("pragma omp ordered")
+ << QLatin1String("pragma omp parallel for")
+ << QLatin1String("pragma omp section")
+ << QLatin1String("pragma omp sections")
+ << QLatin1String("pragma omp parallel sections")
+ << QLatin1String("pragma omp single")
+ << QLatin1String("pragma omp master")
+ << QLatin1String("pragma omp critical")
+ << QLatin1String("pragma omp barrier")
+ << QLatin1String("pragma omp flush")
+ << QLatin1String("pragma omp threadprivate")
+ << QLatin1String("undef")
+ << QLatin1String("if")
+ << QLatin1String("ifdef")
+ << QLatin1String("ifndef")
+ << QLatin1String("elif")
+ << QLatin1String("else")
+ << QLatin1String("endif"))
+ , m_model(new ClangAssistProposalModel)
+ , m_hintProposal(0)
+
+{
+}
+
+ClangCompletionAssistProcessor::~ClangCompletionAssistProcessor()
+{
+}
+
+IAssistProposal *ClangCompletionAssistProcessor::perform(const IAssistInterface *interface)
+{
+ m_interface.reset(static_cast<const ClangCompletionAssistInterface *>(interface));
+
+ if (interface->reason() != ExplicitlyInvoked && !accepts())
+ return 0;
+
+ int index = startCompletionHelper();
+ if (index != -1) {
+ if (m_hintProposal)
+ return m_hintProposal;
+
+ m_model->m_sortable = (m_model->m_completionOperator != T_EOF_SYMBOL);
+ return createContentProposal();
+ }
+
+ return 0;
+}
+
+int ClangCompletionAssistProcessor::startCompletionHelper()
+{
+ //### TODO: clean-up this method, some calculated values might not be used anymore.
+
+ Q_ASSERT(m_model);
+
+ const int startOfName = findStartOfName();
+ m_startPosition = startOfName;
+ m_model->m_completionOperator = T_EOF_SYMBOL;
+
+ int endOfOperator = m_startPosition;
+
+ // Skip whitespace preceding this position
+ while (m_interface->characterAt(endOfOperator - 1).isSpace())
+ --endOfOperator;
+
+ const QString fileName = m_interface->fileName();
+
+ int endOfExpression = startOfOperator(endOfOperator,
+ &m_model->m_completionOperator,
+ /*want function call =*/ true);
+
+ if (m_model->m_completionOperator == T_EOF_SYMBOL) {
+ endOfOperator = m_startPosition;
+ } else if (m_model->m_completionOperator == T_DOXY_COMMENT) {
+ for (int i = 1; i < T_DOXY_LAST_TAG; ++i)
+ addCompletionItem(QString::fromLatin1(doxygenTagSpell(i)),
+ m_icons.keywordIcon());
+ return m_startPosition;
+ }
+
+ // Pre-processor completion
+ //### TODO: check if clang can do pp completion
+ if (m_model->m_completionOperator == T_POUND) {
+ completePreprocessor();
+ m_startPosition = startOfName;
+ return m_startPosition;
+ }
+
+ // Include completion
+ if (m_model->m_completionOperator == T_STRING_LITERAL
+ || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
+ || m_model->m_completionOperator == T_SLASH) {
+
+ QTextCursor c(m_interface->textDocument());
+ c.setPosition(endOfExpression);
+ if (completeInclude(c))
+ m_startPosition = startOfName;
+ return m_startPosition;
+ }
+
+ ExpressionUnderCursor expressionUnderCursor;
+ QTextCursor tc(m_interface->textDocument());
+
+ if (m_model->m_completionOperator == T_COMMA) {
+ tc.setPosition(endOfExpression);
+ const int start = expressionUnderCursor.startOfFunctionCall(tc);
+ if (start == -1) {
+ m_model->m_completionOperator = T_EOF_SYMBOL;
+ return -1;
+ }
+
+ endOfExpression = start;
+ m_startPosition = start + 1;
+ m_model->m_completionOperator = T_LPAREN;
+ }
+
+ QString expression;
+ int startOfExpression = m_interface->position();
+ tc.setPosition(endOfExpression);
+
+ if (m_model->m_completionOperator) {
+ expression = expressionUnderCursor(tc);
+ startOfExpression = endOfExpression - expression.length();
+
+ if (m_model->m_completionOperator == T_LPAREN) {
+ if (expression.endsWith(QLatin1String("SIGNAL")))
+ m_model->m_completionOperator = T_SIGNAL;
+
+ else if (expression.endsWith(QLatin1String("SLOT")))
+ m_model->m_completionOperator = T_SLOT;
+
+ else if (m_interface->position() != endOfOperator) {
+ // We don't want a function completion when the cursor isn't at the opening brace
+ expression.clear();
+ m_model->m_completionOperator = T_EOF_SYMBOL;
+ m_startPosition = startOfName;
+ startOfExpression = m_interface->position();
+ }
+ }
+ } else if (expression.isEmpty()) {
+ while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
+ --startOfExpression;
+ }
+
+ int line = 0, column = 0;
+// Convenience::convertPosition(m_interface->document(), startOfExpression, &line, &column);
+ Convenience::convertPosition(m_interface->textDocument(), endOfOperator, &line, &column);
+ return startCompletionInternal(fileName, line, column, endOfOperator);
+}
+
+int ClangCompletionAssistProcessor::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;
+ }
+ }
+
+ if (*kind == T_COMMA) {
+ ExpressionUnderCursor expressionUnderCursor;
+ if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
+ *kind = T_EOF_SYMBOL;
+ start = pos;
+ }
+ }
+
+ SimpleLexer tokenize;
+ LanguageFeatures lf = tokenize.languageFeatures();
+ lf.qtMocRunEnabled = true;
+ lf.objCEnabled = true;
+ tokenize.setLanguageFeatures(lf);
+ tokenize.setSkipComments(false);
+ const QList<CPlusPlus::Token> &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 CPlusPlus::Token tk = (tokenIdx == -1) ? CPlusPlus::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 CPlusPlus::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 CPlusPlus::Token &directiveToken = tokens.at(1);
+ QString directive = tc.block().text().mid(directiveToken.begin(),
+ directiveToken.length());
+ if (directive == QLatin1String("include") ||
+ directive == QLatin1String("include_next") ||
+ directive == QLatin1String("import")) {
+ include = true;
+ }
+ }
+ }
+
+ if (!include) {
+ *kind = T_EOF_SYMBOL;
+ start = pos;
+ }
+ }
+ }
+
+ return start;
+}
+
+int ClangCompletionAssistProcessor::findStartOfName(int pos) const
+{
+ if (pos == -1)
+ pos = m_interface->position();
+ QChar chr;
+
+ // Skip to the start of a name
+ do {
+ chr = m_interface->characterAt(--pos);
+ } while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
+
+ return pos + 1;
+}
+
+bool ClangCompletionAssistProcessor::accepts() const
+{
+ const int pos = m_interface->position();
+ unsigned token = T_EOF_SYMBOL;
+
+ const int start = startOfOperator(pos, &token, /*want function call=*/ true);
+ if (start != pos) {
+ if (token == T_POUND) {
+ const int column = pos - m_interface->textDocument()->findBlock(start).position();
+ if (column != 1)
+ return false;
+ }
+
+ return true;
+ } else {
+ // Trigger completion after three characters of a name have been typed, when not editing an existing name
+ QChar characterUnderCursor = m_interface->characterAt(pos);
+ if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) {
+ const int startOfName = findStartOfName(pos);
+ if (pos - startOfName >= 3) {
+ const QChar firstCharacter = m_interface->characterAt(startOfName);
+ if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
+ // Finally check that we're not inside a comment or string (code copied from startOfOperator)
+ QTextCursor tc(m_interface->textDocument());
+ tc.setPosition(pos);
+
+ SimpleLexer tokenize;
+ LanguageFeatures lf = tokenize.languageFeatures();
+ lf.qtMocRunEnabled = true;
+ lf.objCEnabled = true;
+ tokenize.setLanguageFeatures(lf);
+ tokenize.setSkipComments(false);
+ const QList<CPlusPlus::Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
+ const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
+ const CPlusPlus::Token tk = (tokenIdx == -1) ? CPlusPlus::Token() : tokens.at(tokenIdx);
+
+ if (!tk.isComment() && !tk.isLiteral()) {
+ return true;
+ } else if (tk.isLiteral()
+ && tokens.size() == 3
+ && tokens.at(0).kind() == T_POUND
+ && tokens.at(1).kind() == T_IDENTIFIER) {
+ const QString &line = tc.block().text();
+ const CPlusPlus::Token &idToken = tokens.at(1);
+ const QStringRef &identifier =
+ line.midRef(idToken.begin(), idToken.end() - idToken.begin());
+ if (identifier == QLatin1String("include")
+ || identifier == QLatin1String("include_next")
+ || (m_interface->objcEnabled() && identifier == QLatin1String("import"))) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+IAssistProposal *ClangCompletionAssistProcessor::createContentProposal()
+{
+ m_model->loadContent(m_completions);
+ return new ClangAssistProposal(m_startPosition, m_model.take());
+}
+
+/// Seach backwards in the document starting from pos to find the first opening
+/// parenthesis. Nested parenthesis are skipped.
+static int findOpenParen(QTextDocument *doc, int start)
+{
+ unsigned parenCount = 1;
+ for (int pos = start; pos >= 0; --pos) {
+ const QChar ch = doc->characterAt(pos);
+ if (ch == QLatin1Char('(')) {
+ --parenCount;
+ if (parenCount == 0)
+ return pos;
+ } else if (ch == QLatin1Char(')')) {
+ ++parenCount;
+ }
+ }
+ return -1;
+}
+
+static QByteArray modifyInput(QTextDocument *doc, int endOfExpression) {
+ int comma = endOfExpression;
+ while (comma > 0) {
+ const QChar ch = doc->characterAt(comma);
+ if (ch == QLatin1Char(','))
+ break;
+ if (ch == QLatin1Char(';') || ch == QLatin1Char('{') || ch == QLatin1Char('}')) {
+ // Safety net: we don't seem to have "connect(pointer, SIGNAL(" as
+ // input, so stop searching.
+ comma = -1;
+ break;
+ }
+ --comma;
+ }
+ if (comma < 0)
+ return QByteArray();
+ const int openBrace = findOpenParen(doc, comma);
+ if (openBrace < 0)
+ return QByteArray();
+
+ QByteArray modifiedInput = doc->toPlainText().toUtf8();
+ const int len = endOfExpression - comma;
+ QByteArray replacement(len - 4, ' ');
+ replacement.append(")->");
+ modifiedInput.replace(comma, len, replacement);
+ modifiedInput.insert(openBrace, '(');
+ return modifiedInput;
+}
+
+int ClangCompletionAssistProcessor::startCompletionInternal(const QString fileName,
+ unsigned line,
+ unsigned column,
+ int endOfExpression)
+{
+ bool signalCompletion = false;
+ bool slotCompletion = false;
+ QByteArray modifiedInput;
+
+ if (m_model->m_completionOperator == T_SIGNAL) {
+ signalCompletion = true;
+ modifiedInput = modifyInput(m_interface->textDocument(), endOfExpression);
+ } else if (m_model->m_completionOperator == T_SLOT) {
+ slotCompletion = true;
+ modifiedInput = modifyInput(m_interface->textDocument(), endOfExpression);
+ } else if (m_model->m_completionOperator == T_LPAREN) {
+ // Find the expression that precedes the current name
+ int index = endOfExpression;
+ while (m_interface->characterAt(index - 1).isSpace())
+ --index;
+
+ QTextCursor tc(m_interface->textDocument());
+ tc.setPosition(index);
+ ExpressionUnderCursor euc;
+ index = euc.startOfFunctionCall(tc);
+ int nameStart = findStartOfName(index);
+ QTextCursor tc2(m_interface->textDocument());
+ tc2.setPosition(nameStart);
+ tc2.setPosition(index, QTextCursor::KeepAnchor);
+ const QString functionName = tc2.selectedText().trimmed();
+ int l = line, c = column;
+ Convenience::convertPosition(m_interface->textDocument(), nameStart, &l, &c);
+
+ if (DebugTiming)
+ qDebug()<<"complete constructor or function @" << line<<":"<<column << "->"<<l<<":"<<c;
+
+ const QList<CodeCompletionResult> completions = unfilteredCompletion(
+ m_interface.data(), fileName, l, c, QByteArray(), signalCompletion || slotCompletion);
+ QList<CodeCompletionResult> functionCompletions;
+ foreach (const CodeCompletionResult &ccr, completions) {
+ if (ccr.completionKind() == CodeCompletionResult::FunctionCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::ConstructorCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::DestructorCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::SignalCompletionKind
+ || ccr.completionKind() == CodeCompletionResult::SlotCompletionKind)
+ if (ccr.text() == functionName)
+ functionCompletions.append(ccr);
+ }
+
+ if (!functionCompletions.isEmpty()) {
+ IFunctionHintProposalModel *model = new ClangFunctionHintModel(functionCompletions);
+ m_hintProposal = new FunctionHintProposal(m_startPosition, model);
+ return m_startPosition;
+ }
+ }
+
+ const QIcon snippetIcon = QIcon(QLatin1String(SNIPPET_ICON_PATH));
+ QList<CodeCompletionResult> completions = unfilteredCompletion(
+ m_interface.data(), fileName, line, column, modifiedInput, signalCompletion || slotCompletion);
+ QHash<QString, ClangAssistProposalItem *> items;
+ foreach (const CodeCompletionResult &ccr, completions) {
+ if (!ccr.isValid())
+ continue;
+ if (signalCompletion && ccr.completionKind() != CodeCompletionResult::SignalCompletionKind)
+ continue;
+ if (slotCompletion && ccr.completionKind() != CodeCompletionResult::SlotCompletionKind)
+ continue;
+
+ const QString txt(ccr.text());
+ ClangAssistProposalItem *item = items.value(txt, 0);
+ if (item) {
+ item->addOverload(ccr);
+ } else {
+ item = new ClangAssistProposalItem;
+ items.insert(txt, item);
+ item->setText(txt);
+ item->setDetail(ccr.hint());
+ item->setOrder(ccr.priority());
+
+ const QString snippet = ccr.snippet();
+ if (!snippet.isEmpty())
+ item->setData(snippet);
+ else
+ item->setData(qVariantFromValue(ccr));
+ }
+
+ // FIXME: show the effective accessebility instead of availability
+ switch (ccr.completionKind()) {
+ case CodeCompletionResult::ClassCompletionKind: item->setIcon(m_icons.iconForType(Icons::ClassIconType)); break;
+ case CodeCompletionResult::EnumCompletionKind: item->setIcon(m_icons.iconForType(Icons::EnumIconType)); break;
+ case CodeCompletionResult::EnumeratorCompletionKind: item->setIcon(m_icons.iconForType(Icons::EnumeratorIconType)); break;
+
+ case CodeCompletionResult::ConstructorCompletionKind: // fall through
+ case CodeCompletionResult::DestructorCompletionKind: // fall through
+ case CodeCompletionResult::FunctionCompletionKind:
+ case CodeCompletionResult::ObjCMessageCompletionKind:
+ switch (ccr.availability()) {
+ case CodeCompletionResult::Available:
+ case CodeCompletionResult::Deprecated:
+ item->setIcon(m_icons.iconForType(Icons::FuncPublicIconType));
+ break;
+ default:
+ item->setIcon(m_icons.iconForType(Icons::FuncPrivateIconType));
+ break;
+ }
+ break;
+
+ case CodeCompletionResult::SignalCompletionKind:
+ item->setIcon(m_icons.iconForType(Icons::SignalIconType));
+ break;
+
+ case CodeCompletionResult::SlotCompletionKind:
+ switch (ccr.availability()) {
+ case CodeCompletionResult::Available:
+ case CodeCompletionResult::Deprecated:
+ item->setIcon(m_icons.iconForType(Icons::SlotPublicIconType));
+ break;
+ case CodeCompletionResult::NotAccessible:
+ case CodeCompletionResult::NotAvailable:
+ item->setIcon(m_icons.iconForType(Icons::SlotPrivateIconType));
+ break;
+ }
+ break;
+
+ case CodeCompletionResult::NamespaceCompletionKind: item->setIcon(m_icons.iconForType(Icons::NamespaceIconType)); break;
+ case CodeCompletionResult::PreProcessorCompletionKind: item->setIcon(m_icons.iconForType(Icons::MacroIconType)); break;
+ case CodeCompletionResult::VariableCompletionKind:
+ switch (ccr.availability()) {
+ case CodeCompletionResult::Available:
+ case CodeCompletionResult::Deprecated:
+ item->setIcon(m_icons.iconForType(Icons::VarPublicIconType));
+ break;
+ default:
+ item->setIcon(m_icons.iconForType(Icons::VarPrivateIconType));
+ break;
+ }
+ break;
+
+ case CodeCompletionResult::KeywordCompletionKind:
+ item->setIcon(m_icons.iconForType(Icons::KeywordIconType));
+ break;
+
+ case CodeCompletionResult::ClangSnippetKind:
+ item->setIcon(snippetIcon);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ foreach (ClangAssistProposalItem *item, items.values())
+ m_completions.append(item);
+
+ return m_startPosition;
+}
+
+/**
+ * @brief Creates completion proposals for #include and given cursor
+ * @param cursor - cursor placed after opening bracked or quote
+ * @return false if completions list is empty
+ */
+bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
+{
+ QString directoryPrefix;
+ if (m_model->m_completionOperator == T_SLASH) {
+ QTextCursor c = cursor;
+ c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
+ QString sel = c.selectedText();
+ int startCharPos = sel.indexOf(QLatin1Char('"'));
+ if (startCharPos == -1) {
+ startCharPos = sel.indexOf(QLatin1Char('<'));
+ m_model->m_completionOperator = T_ANGLE_STRING_LITERAL;
+ } else {
+ m_model->m_completionOperator = T_STRING_LITERAL;
+ }
+ if (startCharPos != -1)
+ directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
+ }
+
+ // Make completion for all relevant includes
+ QStringList includePaths = m_interface->includePaths();
+ const QString &currentFilePath = QFileInfo(m_interface->fileName()).path();
+ if (!includePaths.contains(currentFilePath))
+ includePaths.append(currentFilePath);
+
+ const Core::MimeType mimeType = Core::MimeDatabase::findByType(QLatin1String("text/x-c++hdr"));
+ const QStringList suffixes = mimeType.suffixes();
+
+ foreach (const QString &includePath, includePaths) {
+ QString realPath = includePath;
+ if (!directoryPrefix.isEmpty()) {
+ realPath += QLatin1Char('/');
+ realPath += directoryPrefix;
+ }
+ completeIncludePath(realPath, suffixes);
+ }
+
+ foreach (const QString &frameworkPath, m_interface->frameworkPaths()) {
+ QString realPath = frameworkPath;
+ if (!directoryPrefix.isEmpty()) {
+ realPath += QLatin1Char('/');
+ realPath += directoryPrefix;
+ realPath += QLatin1String(".framework/Headers");
+ }
+ completeIncludePath(realPath, suffixes);
+ }
+
+ return !m_completions.isEmpty();
+}
+
+/**
+ * @brief Adds #include completion proposals using given include path
+ * @param realPath - one of directories where compiler searches includes
+ * @param suffixes - file suffixes for C/C++ header files
+ */
+void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath,
+ const QStringList &suffixes)
+{
+ QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ const QString hint =
+ QObject::tr("Location: ", "Parent folder for proposed #include completion")
+ + QDir::cleanPath(realPath);
+ while (i.hasNext()) {
+ const QString fileName = i.next();
+ const QFileInfo fileInfo = i.fileInfo();
+ const QString suffix = fileInfo.suffix();
+ if (suffix.isEmpty() || suffixes.contains(suffix)) {
+ QString text = fileName.mid(realPath.length() + 1);
+ if (fileInfo.isDir())
+ text += QLatin1Char('/');
+
+ ClangAssistProposalItem *item = new ClangAssistProposalItem;
+ item->setText(text);
+ item->setDetail(hint);
+ item->setIcon(m_icons.keywordIcon());
+ item->keepCompletionOperator(m_model->m_completionOperator);
+ m_completions.append(item);
+ }
+ }
+}
+
+void ClangCompletionAssistProcessor::completePreprocessor()
+{
+ foreach (const QString &preprocessorCompletion, m_preprocessorCompletions)
+ addCompletionItem(preprocessorCompletion,
+ m_icons.iconForType(Icons::MacroIconType));
+
+ if (m_interface->objcEnabled())
+ addCompletionItem(QLatin1String("import"),
+ m_icons.iconForType(Icons::MacroIconType));
+}
+
+void ClangCompletionAssistProcessor::addCompletionItem(const QString &text,
+ const QIcon &icon,
+ int order,
+ const QVariant &data)
+{
+ ClangAssistProposalItem *item = new ClangAssistProposalItem;
+ item->setText(text);
+ item->setIcon(icon);
+ item->setOrder(order);
+ item->setData(data);
+ item->keepCompletionOperator(m_model->m_completionOperator);
+ m_completions.append(item);
+}
diff --git a/src/plugins/clangcodemodel/clangcompletion.h b/src/plugins/clangcodemodel/clangcompletion.h
new file mode 100644
index 0000000000..7e9f027fb7
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangcompletion.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPEDITOR_INTERNAL_CLANGCOMPLETION_H
+#define CPPEDITOR_INTERNAL_CLANGCOMPLETION_H
+
+#include "clangcompleter.h"
+
+#include <cplusplus/Icons.h>
+
+#include <cpptools/cppcompletionassistprovider.h>
+
+#include <texteditor/codeassist/basicproposalitem.h>
+#include <texteditor/codeassist/completionassistprovider.h>
+#include <texteditor/codeassist/defaultassistinterface.h>
+#include <texteditor/codeassist/iassistprocessor.h>
+
+#include <QStringList>
+#include <QTextCursor>
+
+namespace ClangCodeModel {
+
+namespace Internal {
+class ClangAssistProposalModel;
+
+class ClangCompletionAssistProvider : public CppTools::CppCompletionAssistProvider
+{
+public:
+ ClangCompletionAssistProvider();
+
+ virtual TextEditor::IAssistProcessor *createProcessor() const;
+ virtual TextEditor::IAssistInterface *createAssistInterface(
+ ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor,
+ QTextDocument *document, int position, TextEditor::AssistReason reason) const;
+
+private:
+ ClangCodeModel::ClangCompleter::Ptr m_clangCompletionWrapper;
+};
+
+} // namespace Internal
+
+class CLANG_EXPORT ClangCompletionAssistInterface: public TextEditor::DefaultAssistInterface
+{
+public:
+ ClangCompletionAssistInterface(ClangCodeModel::ClangCompleter::Ptr clangWrapper,
+ QTextDocument *document,
+ int position,
+ const QString &fileName,
+ TextEditor::AssistReason reason,
+ const QStringList &options,
+ const QStringList &includePaths,
+ const QStringList &frameworkPaths,
+ const Internal::PchInfo::Ptr &pchInfo);
+
+ ClangCodeModel::ClangCompleter::Ptr clangWrapper() const
+ { return m_clangWrapper; }
+
+ const ClangCodeModel::Internal::UnsavedFiles &unsavedFiles() const
+ { return m_unsavedFiles; }
+
+ bool objcEnabled() const;
+
+ const QStringList &options() const
+ { return m_options; }
+
+ const QStringList &includePaths() const
+ { return m_includePaths; }
+
+ const QStringList &frameworkPaths() const
+ { return m_frameworkPaths; }
+
+private:
+ ClangCodeModel::ClangCompleter::Ptr m_clangWrapper;
+ ClangCodeModel::Internal::UnsavedFiles m_unsavedFiles;
+ QStringList m_options, m_includePaths, m_frameworkPaths;
+ Internal::PchInfo::Ptr m_savedPchPointer;
+};
+
+class CLANG_EXPORT ClangCompletionAssistProcessor : public TextEditor::IAssistProcessor
+{
+public:
+ ClangCompletionAssistProcessor();
+ virtual ~ClangCompletionAssistProcessor();
+
+ virtual TextEditor::IAssistProposal *perform(const TextEditor::IAssistInterface *interface);
+
+private:
+ int startCompletionHelper();
+ int startOfOperator(int pos, unsigned *kind, bool wantFunctionCall) const;
+ int findStartOfName(int pos = -1) const;
+ bool accepts() const;
+ TextEditor::IAssistProposal *createContentProposal();
+
+ int startCompletionInternal(const QString fileName,
+ unsigned line, unsigned column,
+ int endOfExpression);
+
+ bool completeInclude(const QTextCursor &cursor);
+ void completeIncludePath(const QString &realPath, const QStringList &suffixes);
+ void completePreprocessor();
+ void addCompletionItem(const QString &text,
+ const QIcon &icon = QIcon(),
+ int order = 0,
+ const QVariant &data = QVariant());
+
+private:
+ int m_startPosition;
+ QScopedPointer<const ClangCompletionAssistInterface> m_interface;
+ QList<TextEditor::BasicProposalItem *> m_completions;
+ CPlusPlus::Icons m_icons;
+ QStringList m_preprocessorCompletions;
+ QScopedPointer<Internal::ClangAssistProposalModel> m_model;
+ TextEditor::IAssistProposal *m_hintProposal;
+};
+
+} // namespace Clang
+
+#endif // CPPEDITOR_INTERNAL_CLANGCOMPLETION_H
diff --git a/src/plugins/clangcodemodel/clanghighlightingsupport.cpp b/src/plugins/clangcodemodel/clanghighlightingsupport.cpp
new file mode 100644
index 0000000000..57ca1435c5
--- /dev/null
+++ b/src/plugins/clangcodemodel/clanghighlightingsupport.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clanghighlightingsupport.h"
+
+#include <coreplugin/idocument.h>
+#include <texteditor/basetexteditor.h>
+#include <texteditor/itexteditor.h>
+
+#include <QTextBlock>
+#include <QTextEdit>
+#include "pchmanager.h"
+
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace CppTools;
+
+ClangHighlightingSupport::ClangHighlightingSupport(TextEditor::ITextEditor *textEditor, FastIndexer *fastIndexer)
+ : CppHighlightingSupport(textEditor)
+ , m_fastIndexer(fastIndexer)
+ , m_semanticMarker(new ClangCodeModel::SemanticMarker)
+{
+}
+
+ClangHighlightingSupport::~ClangHighlightingSupport()
+{
+}
+
+bool ClangHighlightingSupport::hightlighterHandlesIfdefedOutBlocks() const
+{
+#if CINDEX_VERSION_MINOR >= 21
+ return true;
+#else
+ return false;
+#endif
+}
+
+QFuture<TextEditor::HighlightingResult> ClangHighlightingSupport::highlightingFuture(
+ const CPlusPlus::Document::Ptr &doc,
+ const CPlusPlus::Snapshot &snapshot) const
+{
+ Q_UNUSED(doc);
+ Q_UNUSED(snapshot);
+
+ TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(editor()->widget());
+ int firstLine = 1;
+ int lastLine = ed->document()->blockCount();
+
+ const QString fileName = editor()->document()->filePath();
+ CppModelManagerInterface *modelManager = CppModelManagerInterface::instance();
+ QList<ProjectPart::Ptr> parts = modelManager->projectPart(fileName);
+ if (parts.isEmpty())
+ parts += modelManager->fallbackProjectPart();
+ QStringList options;
+ PchInfo::Ptr pchInfo;
+ foreach (const ProjectPart::Ptr &part, parts) {
+ if (part.isNull())
+ continue;
+ options = Utils::createClangOptions(part, fileName);
+ pchInfo = PCHManager::instance()->pchInfo(part);
+ if (!pchInfo.isNull())
+ options.append(ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName()));
+ if (!options.isEmpty())
+ break;
+ }
+
+ CreateMarkers *createMarkers = CreateMarkers::create(m_semanticMarker,
+ fileName, options,
+ firstLine, lastLine,
+ m_fastIndexer, pchInfo);
+ return createMarkers->start();
+}
diff --git a/src/plugins/clangcodemodel/clanghighlightingsupport.h b/src/plugins/clangcodemodel/clanghighlightingsupport.h
new file mode 100644
index 0000000000..7c795c6962
--- /dev/null
+++ b/src/plugins/clangcodemodel/clanghighlightingsupport.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_CLANGHIGHLIGHTINGSUPPORT_H
+#define CLANG_CLANGHIGHLIGHTINGSUPPORT_H
+
+#include "clangutils.h"
+#include "cppcreatemarkers.h"
+#include "fastindexer.h"
+
+#include <cpptools/cpphighlightingsupport.h>
+
+#include <QObject>
+#include <QScopedPointer>
+
+namespace ClangCodeModel {
+
+class ClangHighlightingSupport: public CppTools::CppHighlightingSupport
+{
+public:
+ ClangHighlightingSupport(TextEditor::ITextEditor *textEditor,
+ Internal::FastIndexer *fastIndexer);
+ ~ClangHighlightingSupport();
+
+ virtual bool requiresSemanticInfo() const
+ { return false; }
+
+ virtual bool hightlighterHandlesDiagnostics() const
+ { return true; }
+
+ virtual bool hightlighterHandlesIfdefedOutBlocks() const;
+
+ virtual QFuture<TextEditor::HighlightingResult> highlightingFuture(
+ const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) const;
+
+private:
+ Internal::FastIndexer *m_fastIndexer;
+ ClangCodeModel::SemanticMarker::Ptr m_semanticMarker;
+};
+
+} // namespace ClangCodeModel
+
+#endif // CLANG_CLANGHIGHLIGHTINGSUPPORT_H
diff --git a/src/plugins/clangcodemodel/clangindexer.cpp b/src/plugins/clangcodemodel/clangindexer.cpp
new file mode 100644
index 0000000000..9c9e6ca4cf
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangindexer.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangindexer.h"
+#include "clangsymbolsearcher.h"
+#include "clangutils.h"
+#include "indexer.h"
+#include "liveunitsmanager.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <cpptools/cppmodelmanagerinterface.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+
+#include <QDir>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+ClangIndexingSupport::ClangIndexingSupport(ClangIndexer *indexer)
+ : m_indexer(indexer)
+{
+}
+
+ClangIndexingSupport::~ClangIndexingSupport()
+{
+}
+
+QFuture<void> ClangIndexingSupport::refreshSourceFiles(const QStringList &sourceFiles)
+{
+ return m_indexer->refreshSourceFiles(sourceFiles);
+}
+
+CppTools::SymbolSearcher *ClangIndexingSupport::createSymbolSearcher(CppTools::SymbolSearcher::Parameters parameters, QSet<QString> fileNames)
+{
+ return new ClangSymbolSearcher(m_indexer, parameters, fileNames);
+}
+
+ClangIndexer::ClangIndexer()
+ : QObject(0)
+ , m_indexingSupport(new ClangIndexingSupport(this))
+ , m_isLoadingSession(false)
+ , m_clangIndexer(new Indexer(this))
+{
+ connect(m_clangIndexer, SIGNAL(indexingStarted(QFuture<void>)),
+ this, SLOT(onIndexingStarted(QFuture<void>)));
+
+ ProjectExplorer::ProjectExplorerPlugin *pe =
+ ProjectExplorer::ProjectExplorerPlugin::instance();
+
+ ProjectExplorer::SessionManager *session = pe->session();
+ connect(session, SIGNAL(aboutToLoadSession(QString)),
+ this, SLOT(onAboutToLoadSession(QString)));
+ connect(session, SIGNAL(sessionLoaded(QString)),
+ this, SLOT(onSessionLoaded(QString)));
+ connect(session, SIGNAL(aboutToSaveSession()),
+ this, SLOT(onAboutToSaveSession()));
+}
+
+ClangIndexer::~ClangIndexer()
+{
+ m_clangIndexer->cancel(true);
+}
+
+CppTools::CppIndexingSupport *ClangIndexer::indexingSupport()
+{
+ return m_indexingSupport.data();
+}
+
+QFuture<void> ClangIndexer::refreshSourceFiles(const QStringList &sourceFiles)
+{
+ typedef CppTools::ProjectPart ProjectPart;
+ CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance();
+ LiveUnitsManager *lum = LiveUnitsManager::instance();
+
+ if (m_clangIndexer->isBusy())
+ m_clangIndexer->cancel(true);
+
+ foreach (const QString &file, sourceFiles) {
+ if (lum->isTracking(file))
+ continue; // we get notified separately about open files.
+ const QList<ProjectPart::Ptr> &parts = mmi->projectPart(file);
+ if (!parts.isEmpty())
+ m_clangIndexer->addFile(file, parts.at(0));
+ else
+ m_clangIndexer->addFile(file, ProjectPart::Ptr());
+ }
+
+ if (!m_isLoadingSession)
+ m_clangIndexer->regenerate();
+
+ return QFuture<void>();
+}
+
+void ClangIndexer::match(ClangSymbolSearcher *searcher) const
+{
+ m_clangIndexer->match(searcher);
+}
+
+void ClangIndexer::onAboutToLoadSession(const QString &sessionName)
+{
+ m_isLoadingSession = true;
+
+ if (sessionName == QLatin1String("default"))
+ return;
+
+ QString path = Core::ICore::instance()->userResourcePath() + QLatin1String("/codemodel/");
+ if (QFile::exists(path) || QDir().mkpath(path))
+ m_clangIndexer->initialize(path + sessionName + QLatin1String(".qci"));
+}
+
+void ClangIndexer::onSessionLoaded(QString)
+{
+ m_isLoadingSession = false;
+ m_clangIndexer->regenerate();
+}
+
+void ClangIndexer::onAboutToSaveSession()
+{
+ m_clangIndexer->finalize();
+}
+
+void ClangIndexer::indexNow(const ClangCodeModel::Internal::Unit &unit)
+{
+ typedef CppTools::ProjectPart ProjectPart;
+
+ QString file = unit.fileName();
+ CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance();
+ const QList<ProjectPart::Ptr> &parts = mmi->projectPart(file);
+ ProjectPart::Ptr part;
+ if (!parts.isEmpty())
+ part = parts.at(0);
+ if (!m_isLoadingSession)
+ m_clangIndexer->runQuickIndexing(unit, part);
+}
+
+void ClangIndexer::onIndexingStarted(QFuture<void> indexingFuture)
+{
+ Core::ICore::instance()->progressManager()->addTask(indexingFuture,
+ tr("C++ Indexing"),
+ QLatin1String("Key.Temp.Indexing"));
+}
diff --git a/src/plugins/clangcodemodel/clangindexer.h b/src/plugins/clangcodemodel/clangindexer.h
new file mode 100644
index 0000000000..e366c5ccda
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangindexer.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGINDEXER_H
+#define CLANGINDEXER_H
+
+#include "fastindexer.h"
+
+#include <cpptools/cppindexingsupport.h>
+
+#include <QObject>
+
+namespace ClangCodeModel {
+
+class Indexer;
+
+namespace Internal {
+
+class ClangIndexer;
+class ClangSymbolSearcher;
+
+class ClangIndexingSupport: public CppTools::CppIndexingSupport
+{
+public:
+ ClangIndexingSupport(ClangIndexer *indexer);
+ virtual ~ClangIndexingSupport();
+
+ virtual QFuture<void> refreshSourceFiles(const QStringList &sourceFiles);
+ virtual CppTools::SymbolSearcher *createSymbolSearcher(CppTools::SymbolSearcher::Parameters parameters, QSet<QString> fileNames);
+
+private:
+ ClangIndexer *m_indexer;
+};
+
+class ClangIndexer: public QObject, public FastIndexer
+{
+ Q_OBJECT
+
+public:
+ ClangIndexer();
+ ~ClangIndexer();
+
+ CppTools::CppIndexingSupport *indexingSupport();
+
+ QFuture<void> refreshSourceFiles(const QStringList &sourceFiles);
+
+ void match(ClangSymbolSearcher *searcher) const;
+
+ void indexNow(const Unit &unit);
+
+public slots:
+ void onAboutToLoadSession(const QString &sessionName);
+ void onSessionLoaded(QString);
+ void onAboutToSaveSession();
+
+private slots:
+ void onIndexingStarted(QFuture<void> indexingFuture);
+
+private:
+ QScopedPointer<ClangIndexingSupport> m_indexingSupport;
+ bool m_isLoadingSession;
+ Indexer *m_clangIndexer;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // CLANGINDEXER_H
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
new file mode 100644
index 0000000000..db90ebad8f
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangcompletion.h"
+#include "clanghighlightingsupport.h"
+#include "clangmodelmanagersupport.h"
+
+#include <QCoreApplication>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+ModelManagerSupport::ModelManagerSupport(FastIndexer *fastIndexer)
+ : m_completionAssistProvider(new ClangCompletionAssistProvider)
+ , m_fastIndexer(fastIndexer)
+{
+}
+
+ModelManagerSupport::~ModelManagerSupport()
+{
+}
+
+QString ModelManagerSupport::id() const
+{
+ return QLatin1String("ClangCodeMode.ClangCodeMode");
+}
+
+QString ModelManagerSupport::displayName() const
+{
+ return QCoreApplication::translate("ModelManagerSupport::displayName",
+ "Clang");
+}
+
+CppTools::CppCompletionAssistProvider *ModelManagerSupport::completionAssistProvider()
+{
+ return m_completionAssistProvider.data();
+}
+
+CppTools::CppHighlightingSupport *ModelManagerSupport::highlightingSupport(
+ TextEditor::ITextEditor *editor)
+{
+ return new ClangHighlightingSupport(editor, m_fastIndexer);
+}
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
new file mode 100644
index 0000000000..833c5f6672
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H
+#define CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H
+
+#include <cpptools/cppmodelmanagersupport.h>
+
+#include <QScopedPointer>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class FastIndexer;
+
+class ModelManagerSupport: public CppTools::ModelManagerSupport
+{
+ Q_DISABLE_COPY(ModelManagerSupport)
+
+public:
+ ModelManagerSupport(FastIndexer *fastIndexer);
+ virtual ~ModelManagerSupport();
+
+ virtual QString id() const;
+ virtual QString displayName() const;
+
+ virtual CppTools::CppCompletionAssistProvider *completionAssistProvider();
+ virtual CppTools::CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor);
+
+private:
+ QScopedPointer<CppTools::CppCompletionAssistProvider> m_completionAssistProvider;
+ FastIndexer *m_fastIndexer;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H
diff --git a/src/plugins/clangcodemodel/clangprojectsettings.cpp b/src/plugins/clangcodemodel/clangprojectsettings.cpp
new file mode 100644
index 0000000000..a0712324e1
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangprojectsettings.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangprojectsettings.h"
+
+using namespace ClangCodeModel;
+
+ClangProjectSettings::ClangProjectSettings(ProjectExplorer::Project *project)
+ : m_project(project)
+ , m_pchUsage(PchUse_None)
+{
+ Q_ASSERT(project);
+
+ connect(project, SIGNAL(settingsLoaded()),
+ this, SLOT(pullSettings()));
+ connect(project, SIGNAL(aboutToSaveSettings()),
+ this, SLOT(pushSettings()));
+}
+
+ClangProjectSettings::~ClangProjectSettings()
+{
+}
+
+ProjectExplorer::Project *ClangProjectSettings::project() const
+{
+ return m_project;
+}
+
+ClangProjectSettings::PchUsage ClangProjectSettings::pchUsage() const
+{
+ return m_pchUsage;
+}
+
+void ClangProjectSettings::setPchUsage(ClangProjectSettings::PchUsage pchUsage)
+{
+ if (pchUsage < PchUse_None || pchUsage > PchUse_Custom)
+ return;
+
+ if (m_pchUsage != pchUsage) {
+ m_pchUsage = pchUsage;
+ emit pchSettingsChanged();
+ }
+}
+
+QString ClangProjectSettings::customPchFile() const
+{
+ return m_customPchFile;
+}
+
+void ClangProjectSettings::setCustomPchFile(const QString &customPchFile)
+{
+ if (m_customPchFile != customPchFile) {
+ m_customPchFile = customPchFile;
+ emit pchSettingsChanged();
+ }
+}
+
+static QLatin1String PchUsageKey("PchUsage");
+static QLatin1String CustomPchFileKey("CustomPchFile");
+static QLatin1String SettingsNameKey("ClangProjectSettings");
+
+void ClangProjectSettings::pushSettings()
+{
+ QVariantMap settings;
+ settings[PchUsageKey] = m_pchUsage;
+ settings[CustomPchFileKey] = m_customPchFile;
+
+ QVariant s(settings);
+ m_project->setNamedSettings(SettingsNameKey, s);
+}
+
+void ClangProjectSettings::pullSettings()
+{
+ QVariant s = m_project->namedSettings(SettingsNameKey);
+ QVariantMap settings = s.toMap();
+
+ const PchUsage storedPchUsage = static_cast<PchUsage>(
+ settings.value(PchUsageKey, PchUse_Unknown).toInt());
+ if (storedPchUsage != PchUse_Unknown)
+ setPchUsage(storedPchUsage);
+ setCustomPchFile(settings.value(CustomPchFileKey).toString());
+}
diff --git a/src/plugins/clangcodemodel/clangprojectsettings.h b/src/plugins/clangcodemodel/clangprojectsettings.h
new file mode 100644
index 0000000000..aa87553aa8
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangprojectsettings.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGPROJECTSETTINGS_H
+#define CLANGPROJECTSETTINGS_H
+
+#include "clang_global.h"
+
+#include <projectexplorer/project.h>
+
+#include <QObject>
+#include <QString>
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT ClangProjectSettings: public QObject
+{
+ Q_OBJECT
+
+public:
+ enum PchUsage {
+ PchUse_Unknown = 0,
+ PchUse_None = 1,
+ PchUse_BuildSystem_Exact = 2,
+ PchUse_BuildSystem_Fuzzy = 3,
+ PchUse_Custom = 4
+ };
+
+public:
+ ClangProjectSettings(ProjectExplorer::Project *project);
+ virtual ~ClangProjectSettings();
+
+ ProjectExplorer::Project *project() const;
+
+ PchUsage pchUsage() const;
+ void setPchUsage(PchUsage pchUsage);
+
+ QString customPchFile() const;
+ void setCustomPchFile(const QString &customPchFile);
+
+signals:
+ void pchSettingsChanged();
+
+public slots:
+ void pullSettings();
+ void pushSettings();
+
+private:
+ ProjectExplorer::Project *m_project;
+ PchUsage m_pchUsage;
+ QString m_customPchFile;
+};
+
+} // ClangCodeModel namespace
+
+#endif // CLANGPROJECTSETTINGS_H
diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp
new file mode 100644
index 0000000000..f8f021c4b5
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangprojectsettings.h"
+#include "clangprojectsettingspropertiespage.h"
+#include "pchmanager.h"
+
+#include <QButtonGroup>
+#include <QCoreApplication>
+#include <QFileDialog>
+
+using namespace ProjectExplorer;
+using namespace ClangCodeModel::Internal;
+
+static const char CLANGPROJECTSETTINGS_PANEL_ID[] = "ClangCodeModel.ProjectPanel";
+
+
+QString ClangProjectSettingsPanelFactory::id() const
+{
+ return QLatin1String(CLANGPROJECTSETTINGS_PANEL_ID);
+}
+
+QString ClangProjectSettingsPanelFactory::displayName() const
+{
+ return QCoreApplication::translate("ClangProjectSettingsPropertiesPage",
+ "Clang Settings");
+}
+
+int ClangProjectSettingsPanelFactory::priority() const
+{
+ return 60;
+}
+
+bool ClangProjectSettingsPanelFactory::supports(Project *project)
+{
+ Q_UNUSED(project);
+
+ return true;
+}
+
+PropertiesPanel *ClangProjectSettingsPanelFactory::createPanel(Project *project)
+{
+ PropertiesPanel *panel = new PropertiesPanel;
+ panel->setDisplayName(QCoreApplication::translate(
+ "ClangProjectSettingsPropertiesPage",
+ "Clang Settings"));
+ panel->setWidget(new ClangProjectSettingsWidget(project));
+ return panel;
+}
+
+ClangProjectSettingsWidget::ClangProjectSettingsWidget(Project *project)
+ : m_project(project)
+{
+ m_ui.setupUi(this);
+
+ ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(project);
+ Q_ASSERT(cps);
+
+ QButtonGroup *pchGroup = new QButtonGroup(this);
+ pchGroup->addButton(m_ui.noneButton, ClangProjectSettings::PchUse_None);
+ pchGroup->addButton(m_ui.exactButton, ClangProjectSettings::PchUse_BuildSystem_Exact);
+ pchGroup->addButton(m_ui.fuzzyButton, ClangProjectSettings::PchUse_BuildSystem_Fuzzy);
+ pchGroup->addButton(m_ui.customButton, ClangProjectSettings::PchUse_Custom);
+ switch (cps->pchUsage()) {
+ case ClangProjectSettings::PchUse_None:
+ case ClangProjectSettings::PchUse_BuildSystem_Exact:
+ case ClangProjectSettings::PchUse_BuildSystem_Fuzzy:
+ case ClangProjectSettings::PchUse_Custom:
+ pchGroup->button(cps->pchUsage())->setChecked(true);
+ break;
+ default: break;
+ }
+ pchUsageChanged(cps->pchUsage());
+ connect(pchGroup, SIGNAL(buttonClicked(int)),
+ this, SLOT(pchUsageChanged(int)));
+
+ m_ui.customField->setText(cps->customPchFile());
+ connect(m_ui.customField, SIGNAL(editingFinished()),
+ this, SLOT(customPchFileChanged()));
+ connect(m_ui.customButton, SIGNAL(clicked()),
+ this, SLOT(customPchButtonClicked()));
+}
+
+void ClangProjectSettingsWidget::pchUsageChanged(int id)
+{
+ ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project);
+ Q_ASSERT(cps);
+ cps->setPchUsage(static_cast<ClangProjectSettings::PchUsage>(id));
+
+ switch (id) {
+ case ClangProjectSettings::PchUse_None:
+ case ClangProjectSettings::PchUse_BuildSystem_Fuzzy:
+ case ClangProjectSettings::PchUse_BuildSystem_Exact:
+ m_ui.customField->setEnabled(false);
+ m_ui.chooseButton->setEnabled(false);
+ break;
+
+ case ClangProjectSettings::PchUse_Custom:
+ m_ui.customField->setEnabled(true);
+ m_ui.chooseButton->setEnabled(true);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ClangProjectSettingsWidget::customPchFileChanged()
+{
+ ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project);
+ Q_ASSERT(cps);
+ if (cps->pchUsage() != ClangProjectSettings::PchUse_Custom)
+ return;
+ QString fileName = m_ui.customField->text();
+ if (!QFile(fileName).exists())
+ return;
+
+ cps->setCustomPchFile(fileName);
+}
+
+void ClangProjectSettingsWidget::customPchButtonClicked()
+{
+ ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project);
+ Q_ASSERT(cps);
+
+ QFileDialog d(this);
+ d.setNameFilters(QStringList() << tr("Header Files (*.h)")
+ << tr("All Files (*)"));
+ d.setFileMode(QFileDialog::ExistingFile);
+ d.setDirectory(m_project->projectDirectory());
+ if (!d.exec())
+ return;
+ const QStringList fileNames = d.selectedFiles();
+ if (fileNames.isEmpty() || fileNames.first().isEmpty())
+ return;
+
+ m_ui.customField->setText(fileNames.first());
+ cps->setCustomPchFile(fileNames.first());
+}
diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h
new file mode 100644
index 0000000000..19f8136833
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGPROJECTSETTINGSPROPERTIESPAGE_H
+#define CLANGPROJECTSETTINGSPROPERTIESPAGE_H
+
+#include "ui_clangprojectsettingspropertiespage.h"
+
+#include <projectexplorer/iprojectproperties.h>
+
+#include <QString>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class ClangProjectSettingsPanelFactory: public ProjectExplorer::IProjectPanelFactory
+{
+public:
+ QString id() const;
+ QString displayName() const;
+ int priority() const;
+ bool supports(ProjectExplorer::Project *project);
+ ProjectExplorer::PropertiesPanel *createPanel(ProjectExplorer::Project *project);
+};
+
+class ClangProjectSettingsWidget: public QWidget
+{
+ Q_OBJECT
+
+public:
+ ClangProjectSettingsWidget(ProjectExplorer::Project *project);
+
+protected slots:
+ void pchUsageChanged(int id);
+ void customPchFileChanged();
+ void customPchButtonClicked();
+
+private:
+ Ui::ClangProjectSettingsPropertiesPage m_ui;
+ ProjectExplorer::Project *m_project;
+};
+
+} // ClangCodeModel namespace
+} // Internal namespace
+
+#endif // CLANGPROJECTSETTINGSPROPERTIESPAGE_H
diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui
new file mode 100644
index 0000000000..66dcf45c5b
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClangCodeModel::Internal::ClangProjectSettingsPropertiesPage</class>
+ <widget class="QWidget" name="ClangCodeModel::Internal::ClangProjectSettingsPropertiesPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>814</width>
+ <height>330</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Pre-compiled headers:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QRadioButton" name="noneButton">
+ <property name="text">
+ <string>None</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QRadioButton" name="exactButton">
+ <property name="text">
+ <string>Build system (exact)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QRadioButton" name="fuzzyButton">
+ <property name="text">
+ <string>Build system (fuzzy)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QRadioButton" name="customButton">
+ <property name="text">
+ <string>Custom</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLineEdit" name="customField">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QPushButton" name="chooseButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Choose...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>12</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/clangcodemodel/clangsymbolsearcher.cpp b/src/plugins/clangcodemodel/clangsymbolsearcher.cpp
new file mode 100644
index 0000000000..f1ab12db48
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangsymbolsearcher.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangsymbolsearcher.h"
+#include "symbol.h"
+
+#include <cpptools/searchsymbols.h>
+
+#include <cassert>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+ClangSymbolSearcher::ClangSymbolSearcher(ClangIndexer *indexer, const Parameters &parameters, QSet<QString> fileNames, QObject *parent)
+ : CppTools::SymbolSearcher(parent)
+ , m_indexer(indexer)
+ , m_parameters(parameters)
+ , m_fileNames(fileNames)
+ , m_future(0)
+{
+ assert(indexer);
+}
+
+ClangSymbolSearcher::~ClangSymbolSearcher()
+{
+}
+
+void ClangSymbolSearcher::runSearch(QFutureInterface<SearchResultItem> &future)
+{
+ m_future = &future;
+ m_indexer->match(this);
+ m_future = 0;
+}
+
+void ClangSymbolSearcher::search(const QLinkedList<Symbol> &allSymbols)
+{
+ QString findString = (m_parameters.flags & Find::FindRegularExpression
+ ? m_parameters.text : QRegExp::escape(m_parameters.text));
+ if (m_parameters.flags & Find::FindWholeWords)
+ findString = QString::fromLatin1("\\b%1\\b").arg(findString);
+ QRegExp matcher(findString, (m_parameters.flags & Find::FindCaseSensitively
+ ? Qt::CaseSensitive : Qt::CaseInsensitive));
+
+ const int chunkSize = 10;
+ QVector<Find::SearchResultItem> resultItems;
+ resultItems.reserve(100);
+
+ m_future->setProgressRange(0, allSymbols.size() % chunkSize);
+ m_future->setProgressValue(0);
+
+ int symbolNr = 0;
+ foreach (const Symbol &s, allSymbols) {
+ if (symbolNr % chunkSize == 0) {
+ m_future->setProgressValue(symbolNr / chunkSize);
+ m_future->reportResults(resultItems);
+ resultItems.clear();
+
+ if (m_future->isPaused())
+ m_future->waitForResume();
+ if (m_future->isCanceled())
+ return;
+ }
+ ++symbolNr;
+
+ CppTools::ModelItemInfo info;
+
+ switch (s.m_kind) {
+ case Symbol::Enum:
+ if (m_parameters.types & SymbolSearcher::Enums) {
+ info.type = CppTools::ModelItemInfo::Enum;
+ info.symbolType = QLatin1String("enum");
+ break;
+ } else {
+ continue;
+ }
+ case Symbol::Class:
+ if (m_parameters.types & SymbolSearcher::Classes) {
+ info.type = CppTools::ModelItemInfo::Class;
+ info.symbolType = QLatin1String("class");
+ break;
+ } else {
+ continue;
+ }
+ case Symbol::Method:
+ case Symbol::Function:
+ case Symbol::Constructor:
+ case Symbol::Destructor:
+ if (m_parameters.types & SymbolSearcher::Functions) {
+ info.type = CppTools::ModelItemInfo::Method;
+ break;
+ } else {
+ continue;
+ }
+ case Symbol::Declaration:
+ if (m_parameters.types & SymbolSearcher::Declarations) {
+ info.type = CppTools::ModelItemInfo::Declaration;
+ break;
+ } else {
+ continue;
+ }
+
+ default: continue;
+ }
+
+ if (matcher.indexIn(s.m_name) == -1)
+ continue;
+
+ info.symbolName = s.m_name;
+ info.fullyQualifiedName = s.m_qualification.split(QLatin1String("::")) << s.m_name;
+ info.fileName = s.m_location.fileName();
+ info.icon = s.iconForSymbol();
+ info.line = s.m_location.line();
+ info.column = s.m_location.column() - 1;
+
+ Find::SearchResultItem item;
+ item.path << s.m_qualification;
+ item.text = s.m_name;
+ item.icon = info.icon;
+ item.textMarkPos = -1;
+ item.textMarkLength = 0;
+ item.lineNumber = -1;
+ item.userData = qVariantFromValue(info);
+
+ resultItems << item;
+ }
+
+ if (!resultItems.isEmpty())
+ m_future->reportResults(resultItems);
+}
diff --git a/src/plugins/clangcodemodel/clangsymbolsearcher.h b/src/plugins/clangcodemodel/clangsymbolsearcher.h
new file mode 100644
index 0000000000..70615308de
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangsymbolsearcher.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGSYMBOLSEARCHER_H
+#define CLANGSYMBOLSEARCHER_H
+
+#include "clangindexer.h"
+
+#include <cpptools/cppindexingsupport.h>
+
+#include <QLinkedList>
+
+namespace ClangCodeModel {
+
+class Symbol;
+
+namespace Internal {
+
+class ClangSymbolSearcher: public CppTools::SymbolSearcher
+{
+ Q_OBJECT
+
+ typedef CppTools::SymbolSearcher::Parameters Parameters;
+ typedef Find::SearchResultItem SearchResultItem;
+
+public:
+ ClangSymbolSearcher(ClangIndexer *indexer, const Parameters &parameters, QSet<QString> fileNames, QObject *parent = 0);
+ virtual ~ClangSymbolSearcher();
+ virtual void runSearch(QFutureInterface<SearchResultItem> &future);
+
+ void search(const QLinkedList<Symbol> &allSymbols);
+
+private:
+ ClangIndexer *m_indexer;
+ const Parameters m_parameters;
+ const QSet<QString> m_fileNames;
+ QFutureInterface<SearchResultItem> *m_future;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // CLANGSYMBOLSEARCHER_H
diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp
new file mode 100644
index 0000000000..24e7d459d5
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangutils.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangutils.h"
+
+#include <clang-c/Index.h>
+
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/idocument.h>
+
+#include <QFile>
+#include <QSet>
+#include <QString>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace Core;
+using namespace CppTools;
+
+namespace ClangCodeModel {
+namespace Utils {
+
+namespace {
+bool isBlacklisted(const QString &path)
+{
+ static QStringList blacklistedPaths = QStringList()
+ << QLatin1String("lib/gcc/i686-apple-darwin");
+
+ foreach (const QString &blacklisted, blacklistedPaths)
+ if (path.contains(blacklisted))
+ return true;
+
+ return false;
+}
+} // anonymous namespace
+
+UnsavedFiles createUnsavedFiles(CppModelManagerInterface::WorkingCopy workingCopy)
+{
+ // TODO: change the modelmanager to hold one working copy, and amend it every time we ask for one.
+ // TODO: Reason: the UnsavedFile needs a QByteArray.
+
+ QSet<QString> modifiedFiles;
+ foreach (IDocument *doc, Core::DocumentManager::modifiedDocuments())
+ modifiedFiles.insert(doc->filePath());
+
+ UnsavedFiles result;
+ QHashIterator<QString, QPair<QByteArray, unsigned> > wcIter = workingCopy.iterator();
+ while (wcIter.hasNext()) {
+ wcIter.next();
+ const QString &fileName = wcIter.key();
+ if (modifiedFiles.contains(fileName) && QFile(fileName).exists())
+ result.insert(fileName, wcIter.value().first);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Creates list of command-line arguments required for correct parsing
+ * @param pPart Null if file isn't part of any project
+ * @param fileName Path to file, non-empty
+ */
+QStringList createClangOptions(const ProjectPart::Ptr &pPart, const QString &fileName)
+{
+ ProjectFile::Kind fileKind = ProjectFile::Unclassified;
+ if (!pPart.isNull())
+ foreach (const ProjectFile &file, pPart->files)
+ if (file.path == fileName) {
+ fileKind = file.kind;
+ break;
+ }
+ if (fileKind == ProjectFile::Unclassified)
+ fileKind = ProjectFile::classify(fileName);
+
+ return createClangOptions(pPart, fileKind);
+}
+
+static QStringList buildDefines(const QByteArray &defines, bool toolchainDefines)
+{
+ QStringList result;
+
+ foreach (QByteArray def, defines.split('\n')) {
+ if (def.isEmpty())
+ continue;
+
+ if (toolchainDefines) {
+ //### FIXME: the next 3 check shouldn't be needed: we probably don't want to get the compiler-defined defines in.
+ if (!def.startsWith("#define "))
+ continue;
+ if (def.startsWith("#define _"))
+ continue;
+ if (def.startsWith("#define OBJC_NEW_PROPERTIES"))
+ continue;
+ }
+
+ QByteArray str = def.mid(8);
+ int spaceIdx = str.indexOf(' ');
+ QString arg;
+ if (spaceIdx != -1) {
+ arg = QLatin1String("-D" + str.left(spaceIdx) + "=" + str.mid(spaceIdx + 1));
+ } else {
+ arg = QLatin1String("-D" + str);
+ }
+ arg = arg.replace(QLatin1String("\\\""), QLatin1String("\""));
+ arg = arg.replace(QLatin1String("\""), QLatin1String(""));
+ if (!result.contains(arg))
+ result.append(arg);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Creates list of command-line arguments required for correct parsing
+ * @param pPart Null if file isn't part of any project
+ * @param fileKind Determines language and source/header state
+ */
+QStringList createClangOptions(const ProjectPart::Ptr &pPart, ProjectFile::Kind fileKind)
+{
+ QStringList result = clangLanguageOption(fileKind);
+ switch (fileKind) {
+ default:
+ case CppTools::ProjectFile::CXXHeader:
+ case CppTools::ProjectFile::CXXSource:
+ case CppTools::ProjectFile::ObjCXXHeader:
+ case CppTools::ProjectFile::ObjCXXSource:
+ case CppTools::ProjectFile::CudaSource:
+ case CppTools::ProjectFile::OpenCLSource:
+ result << clangOptionsForCxx(pPart->qtVersion,
+ pPart->cxxVersion,
+ pPart->cxxExtensions);
+ break;
+ case CppTools::ProjectFile::CHeader:
+ case CppTools::ProjectFile::CSource:
+ case CppTools::ProjectFile::ObjCHeader:
+ case CppTools::ProjectFile::ObjCSource:
+ result << clangOptionsForC(pPart->cVersion,
+ pPart->cxxExtensions);
+ break;
+ }
+
+ if (pPart.isNull())
+ return result;
+
+ result << QLatin1String("-nostdinc");
+
+ result << buildDefines(pPart->toolchainDefines, true);
+ result << buildDefines(pPart->projectDefines, false);
+
+ foreach (const QString &frameworkPath, pPart->frameworkPaths)
+ result.append(QLatin1String("-F") + frameworkPath);
+ foreach (const QString &inc, pPart->includePaths)
+ if (!inc.isEmpty() && !isBlacklisted(inc))
+ result << (QLatin1String("-I") + inc);
+
+#if 0
+ qDebug() << "--- m_args:";
+ foreach (const QString &arg, result)
+ qDebug() << "\t" << qPrintable(arg);
+ qDebug() << "---";
+#endif
+
+ return result;
+}
+
+/// @return Option to speed up parsing with precompiled header
+QStringList createPCHInclusionOptions(const QStringList &pchFiles)
+{
+ QStringList opts;
+
+ foreach (const QString &pchFile, pchFiles) {
+ if (QFile(pchFile).exists()) {
+ opts += QLatin1String("-include-pch");
+ opts += pchFile;
+ }
+ }
+
+ return opts;
+}
+
+/// @return Option to speed up parsing with precompiled header
+QStringList createPCHInclusionOptions(const QString &pchFile)
+{
+ return createPCHInclusionOptions(QStringList() << pchFile);
+}
+
+/// @return "-std" flag to select standard, flags for C extensions
+QStringList clangOptionsForC(ProjectPart::CVersion cVersion, ProjectPart::CXXExtensions cxxExtensions)
+{
+ QStringList opts;
+ bool gnuExpensions = cxxExtensions & ProjectPart::GnuExtensions;
+ switch (cVersion) {
+ case ProjectPart::C89:
+ opts << (gnuExpensions ? QLatin1String("-std=gnu89") : QLatin1String("-std=c89"));
+ break;
+ case ProjectPart::C99:
+ opts << (gnuExpensions ? QLatin1String("-std=gnu99") : QLatin1String("-std=c99"));
+ break;
+ case ProjectPart::C11:
+ opts << (gnuExpensions ? QLatin1String("-std=gnu11") : QLatin1String("-std=c11"));
+ break;
+ }
+
+ if (cxxExtensions & ProjectPart::MicrosoftExtensions) {
+ opts << QLatin1String("-fms-extensions");
+ }
+
+#if defined(CINDEX_VERSION) // clang 3.2 or higher
+ if (cxxExtensions & ProjectPart::BorlandExtensions)
+ opts << QLatin1String("-fborland-extensions");
+#endif
+
+ return opts;
+}
+
+/// @return "-std" flag to select standard, flags for C++ extensions, Qt injections
+QStringList clangOptionsForCxx(ProjectPart::QtVersion qtVersion,
+ ProjectPart::CXXVersion cxxVersion,
+ ProjectPart::CXXExtensions cxxExtensions)
+{
+ QStringList opts;
+ bool gnuExpensions = cxxExtensions & ProjectPart::GnuExtensions;
+ switch (cxxVersion) {
+ case ProjectPart::CXX11:
+ opts << (gnuExpensions ? QLatin1String("-std=gnu++11") : QLatin1String("-std=c++11"));
+ break;
+ case ProjectPart::CXX98:
+ opts << (gnuExpensions ? QLatin1String("-std=gnu++98") : QLatin1String("-std=c++98"));
+ break;
+ }
+
+ if (cxxExtensions & ProjectPart::MicrosoftExtensions) {
+ opts << QLatin1String("-fms-extensions")
+ << QLatin1String("-fdelayed-template-parsing");
+ }
+
+#if defined(CINDEX_VERSION) // clang 3.2 or higher
+ if (cxxExtensions & ProjectPart::BorlandExtensions)
+ opts << QLatin1String("-fborland-extensions");
+#endif
+
+ static const QString injectedHeader(Core::ICore::instance()->resourcePath() + QLatin1String("/cplusplus/qt%1-qobjectdefs-injected.h"));
+// if (qtVersion == ProjectPart::Qt4)
+// opts << QLatin1String("-include") << injectedHeader.arg(QLatin1Char('4'));
+ if (qtVersion == ProjectPart::Qt5)
+ opts << QLatin1String("-include") << injectedHeader.arg(QLatin1Char('5'));
+
+ return opts;
+}
+
+/// @return "-x language-code"
+QStringList clangLanguageOption(ProjectFile::Kind fileKind)
+{
+ QStringList opts;
+ opts += QLatin1String("-x");
+
+ switch (fileKind) {
+ case ProjectFile::CHeader:
+// opts += QLatin1String("c-header");
+// break;
+ case ProjectFile::CXXHeader:
+ default:
+ opts += QLatin1String("c++-header");
+ break;
+ case ProjectFile::CXXSource:
+ opts += QLatin1String("c++");
+ break;
+ case ProjectFile::CSource:
+ opts += QLatin1String("c");
+ break;
+ case ProjectFile::ObjCHeader:
+// opts += QLatin1String("objective-c-header");
+// break;
+ case ProjectFile::ObjCXXHeader:
+ opts += QLatin1String("objective-c++-header");
+ break;
+ case ProjectFile::ObjCSource:
+ opts += QLatin1String("objective-c");
+ break;
+ case ProjectFile::ObjCXXSource:
+ opts += QLatin1String("objective-c++");
+ break;
+ case ProjectFile::OpenCLSource:
+ opts += QLatin1String("cl");
+ break;
+ case ProjectFile::CudaSource:
+ opts += QLatin1String("cuda");
+ break;
+ }
+
+ return opts;
+}
+
+} // namespace Utils
+} // namespace Clang
diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h
new file mode 100644
index 0000000000..bca42657f9
--- /dev/null
+++ b/src/plugins/clangcodemodel/clangutils.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPTOOLS_CLANGUTILS_H
+#define CPPTOOLS_CLANGUTILS_H
+
+#include "utils.h"
+
+#include <cpptools/cppmodelmanagerinterface.h>
+
+namespace ClangCodeModel {
+namespace Utils {
+
+ClangCodeModel::Internal::UnsavedFiles createUnsavedFiles(CppTools::CppModelManagerInterface::WorkingCopy workingCopy);
+
+QStringList createClangOptions(const CppTools::ProjectPart::Ptr &pPart, CppTools::ProjectFile::Kind fileKind);
+QStringList createClangOptions(const CppTools::ProjectPart::Ptr &pPart, const QString &fileName = QString());
+QStringList clangNonProjectFileOptions(CppTools::ProjectFile::Kind kind);
+QStringList createPCHInclusionOptions(const QStringList &pchFiles);
+QStringList createPCHInclusionOptions(const QString &pchFile);
+
+QStringList clangLanguageOption(CppTools::ProjectFile::Kind fileKind);
+QStringList clangOptionsForC(CppTools::ProjectPart::CVersion cVersion,
+ CppTools::ProjectPart::CXXExtensions cxxExtensions);
+QStringList clangOptionsForCxx(CppTools::ProjectPart::QtVersion qtVersion,
+ CppTools::ProjectPart::CXXVersion cxxVersion,
+ CppTools::ProjectPart::CXXExtensions cxxExtensions);
+
+} // namespace Utils
+} // namespace Clang
+
+#endif // CPPTOOLS_CLANGUTILS_H
diff --git a/src/plugins/clangcodemodel/completionproposalsbuilder.cpp b/src/plugins/clangcodemodel/completionproposalsbuilder.cpp
new file mode 100644
index 0000000000..235346e117
--- /dev/null
+++ b/src/plugins/clangcodemodel/completionproposalsbuilder.cpp
@@ -0,0 +1,748 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "completionproposalsbuilder.h"
+#include "utils_p.h"
+
+#include <QTextDocument>
+#include <QCoreApplication>
+
+enum PriorityFixes {
+ PriorityFix_ExplicitDestructorCall = 10
+};
+
+namespace ClangCodeModel {
+
+namespace {
+struct ObjCMessagePart {
+ QString text;
+ int signatureLen; // length of "setScale:" in "setScale: 13"
+
+ ObjCMessagePart() : signatureLen(0) {}
+
+ ObjCMessagePart(const QString &signature, int &indentBonus)
+ : text(signature)
+ , signatureLen(signature.length() + indentBonus)
+ {
+ indentBonus = 0;
+ }
+
+ void addToSignature(const QString &signature)
+ {
+ text += signature;
+ signatureLen += signature.length();
+ }
+};
+} // anonymous namespace
+
+/**
+ * @class ClangCodeModel::CompletionProposalsBuilder
+ * @brief Captures completion lists and than processes sequence of completion chunks
+ *
+ * Can produce several completion proposals for one CXCompletionResult, if and
+ * only if result contains chunks with kind 'Optional'
+ * Different proposals can have the same text, it's normal behavior.
+ *
+ * @note Unit tests are in \a clangcompletion_test.cpp
+ *
+ * @note Unresolved problems:
+ * Function hint not appear after space: "foo(1, ";
+ * Slot can have optional arguments, that produces 2 slots.
+ *
+ */
+
+CompletionProposalsBuilder::CompletionProposalsBuilder(QList<CodeCompletionResult> &results, quint64 contexts, bool isSignalSlotCompletion)
+ : m_results(results)
+ , m_contexts(contexts)
+ , m_isSignalSlotCompletion(isSignalSlotCompletion)
+{
+}
+
+void CompletionProposalsBuilder::operator ()(const CXCompletionResult &cxResult)
+{
+ resetWithResult(cxResult);
+
+#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5)
+ const QString brief = Internal::getQString(clang_getCompletionBriefComment(cxResult.CompletionString));
+ if (!brief.isEmpty())
+ m_comment += QLatin1String("<b>Brief:</b> ") + Qt::escape(brief);
+#endif
+
+ if (m_resultAvailability == CodeCompletionResult::Deprecated) {
+ m_comment += QLatin1String("<b>@note</b> ");
+ m_comment += QCoreApplication::translate("deprecated C++ symbol", "Is deprecated");
+ }
+
+ m_hint = QLatin1String("<p>");
+ switch (m_resultKind) {
+ case CodeCompletionResult::ObjCMessageCompletionKind:
+ concatChunksForObjectiveCMessage(cxResult);
+ break;
+ case CodeCompletionResult::ClassCompletionKind:
+ case CodeCompletionResult::NamespaceCompletionKind:
+ case CodeCompletionResult::EnumeratorCompletionKind:
+ concatChunksForNestedName(cxResult.CompletionString);
+ break;
+ case CodeCompletionResult::ClangSnippetKind:
+ concatChunksAsSnippet(cxResult.CompletionString);
+ break;
+ case CodeCompletionResult::SlotCompletionKind:
+ case CodeCompletionResult::SignalCompletionKind:
+ if (m_isSignalSlotCompletion)
+ concatSlotSignalSignature(cxResult.CompletionString);
+ else
+ concatChunksOnlyTypedText(cxResult.CompletionString);
+ break;
+ default:
+ concatChunksOnlyTypedText(cxResult.CompletionString);
+ break;
+ }
+ m_hint += QLatin1String("</p>");
+ m_hint += m_comment;
+
+ finalize();
+ foreach (const OptionalChunk &chunk, m_optionalChunks) {
+ m_hint.insert(chunk.positionInHint, chunk.hint);
+ finalize();
+ }
+}
+
+/**
+ * @return Internal ClangCodeModel's completion kind, that affects further postprocessing
+ */
+CodeCompletionResult::Kind CompletionProposalsBuilder::getKind(const CXCompletionResult &cxResult)
+{
+ CXCompletionString complStr = cxResult.CompletionString;
+ CXCursorKind cursorKind = cxResult.CursorKind;
+
+ switch (cursorKind) {
+ case CXCursor_Constructor:
+ return CodeCompletionResult::ConstructorCompletionKind;
+
+ case CXCursor_Destructor:
+ return CodeCompletionResult::DestructorCompletionKind;
+
+ case CXCursor_CXXMethod: {
+ const unsigned numAnnotations = clang_getCompletionNumAnnotations(complStr);
+ bool isSignal = false, isSlot = false;
+ for (unsigned i = 0; i < numAnnotations && !isSignal && !isSlot; ++i) {
+ CXString cxAnn = clang_getCompletionAnnotation(complStr, i);
+ QString ann = Internal::getQString(cxAnn);
+ isSignal = ann == QLatin1String("qt_signal");
+ isSlot = ann == QLatin1String("qt_slot");
+ }
+ if (isSignal)
+ return CodeCompletionResult::SignalCompletionKind;
+ if (isSlot)
+ return CodeCompletionResult::SlotCompletionKind;
+ } // intentional fall-through!
+ case CXCursor_ConversionFunction:
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_MemberRef:
+ case CXCursor_MemberRefExpr:
+ return CodeCompletionResult::FunctionCompletionKind;
+ break;
+
+ case CXCursor_FieldDecl:
+ case CXCursor_VarDecl:
+ case CXCursor_ParmDecl:
+ case CXCursor_ObjCIvarDecl:
+ case CXCursor_ObjCPropertyDecl:
+ case CXCursor_ObjCSynthesizeDecl:
+ case CXCursor_NonTypeTemplateParameter:
+ return CodeCompletionResult::VariableCompletionKind;
+
+ case CXCursor_Namespace:
+ case CXCursor_NamespaceAlias:
+ case CXCursor_NamespaceRef:
+ return CodeCompletionResult::NamespaceCompletionKind;
+
+ case CXCursor_StructDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassDecl:
+ case CXCursor_TypeRef:
+ case CXCursor_TemplateRef:
+ case CXCursor_TypedefDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ case CXCursor_ObjCClassRef:
+ case CXCursor_ObjCInterfaceDecl:
+ case CXCursor_ObjCImplementationDecl:
+ case CXCursor_ObjCCategoryDecl:
+ case CXCursor_ObjCCategoryImplDecl:
+ case CXCursor_ObjCProtocolDecl:
+ case CXCursor_ObjCProtocolRef:
+ case CXCursor_TemplateTypeParameter:
+ case CXCursor_TemplateTemplateParameter:
+ return CodeCompletionResult::ClassCompletionKind;
+
+ case CXCursor_EnumConstantDecl:
+ return CodeCompletionResult::EnumeratorCompletionKind;
+
+ case CXCursor_EnumDecl:
+ return CodeCompletionResult::EnumCompletionKind;
+
+ case CXCursor_MacroDefinition: {
+ const unsigned numChunks = clang_getNumCompletionChunks(complStr);
+ for (unsigned i = 0; i < numChunks; ++i) {
+ CXCompletionChunkKind kind = clang_getCompletionChunkKind(complStr, i);
+ if (kind == CXCompletionChunk_Placeholder) {
+ return CodeCompletionResult::FunctionCompletionKind;
+ }
+ }
+ return CodeCompletionResult::PreProcessorCompletionKind;
+ }
+
+ case CXCursor_PreprocessingDirective:
+ case CXCursor_MacroExpansion:
+ case CXCursor_InclusionDirective:
+ return CodeCompletionResult::PreProcessorCompletionKind;
+
+ case CXCursor_ObjCClassMethodDecl:
+ case CXCursor_ObjCInstanceMethodDecl:
+ return CodeCompletionResult::ObjCMessageCompletionKind;
+
+ case CXCursor_NotImplemented: {
+ const unsigned numChunks = clang_getNumCompletionChunks(complStr);
+ for (unsigned i = 0; i < numChunks; ++i) {
+ CXCompletionChunkKind kind = clang_getCompletionChunkKind(complStr, i);
+ if (kind == CXCompletionChunk_Placeholder) {
+ return CodeCompletionResult::ClangSnippetKind;
+ }
+ }
+ return CodeCompletionResult::KeywordCompletionKind;
+ }
+
+ default:
+ break;
+ }
+
+ return CodeCompletionResult::Other;
+}
+
+/**
+ * @return Symbol availability, which is almost unused
+ */
+CodeCompletionResult::Availability CompletionProposalsBuilder::getAvailability(const CXCompletionResult &cxResult)
+{
+ CXCompletionString complStr = cxResult.CompletionString;
+ switch (clang_getCompletionAvailability(complStr)) {
+ case CXAvailability_Deprecated:
+ return CodeCompletionResult::Deprecated;
+
+ case CXAvailability_NotAvailable:
+ return CodeCompletionResult::NotAvailable;
+
+ case CXAvailability_NotAccessible:
+ return CodeCompletionResult::NotAccessible;
+
+ default:
+ return CodeCompletionResult::Available;
+ }
+}
+
+/**
+ * @return Start index of name, which is unused in Qt signal/slot signature
+ * @param text Text of Placeholder completion string chunk
+ */
+int CompletionProposalsBuilder::findNameInPlaceholder(const QString &text)
+{
+ bool firstIdPassed = false;
+ bool isInIdentifier = false;
+ int bracesCounter = 0;
+ int idStart = 0;
+
+ for (int i = 0, n = text.size(); i < n; ++i) {
+ const QChar ch = text[i];
+
+ if (ch == QLatin1Char(':')) {
+ firstIdPassed = false;
+ isInIdentifier = false;
+ }
+
+ if (ch == QLatin1Char('<') || ch == QLatin1Char('(')) {
+ if (isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const"))
+ firstIdPassed = false;
+ ++bracesCounter;
+ isInIdentifier = false;
+ } else if (ch == QLatin1Char('>') || ch == QLatin1Char(')')) {
+ if (isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const"))
+ firstIdPassed = false;
+ --bracesCounter;
+ isInIdentifier = false;
+ } else if (bracesCounter == 0) {
+ if (isInIdentifier) {
+ isInIdentifier = ch.isLetterOrNumber() || (ch == QLatin1Char('_'));
+ if (!isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const"))
+ firstIdPassed = false;
+ } else if (ch.isLetter() || (ch == QLatin1Char('_'))) {
+ if (firstIdPassed)
+ return i;
+ isInIdentifier = true;
+ idStart = i;
+ firstIdPassed = true;
+ }
+ }
+ }
+ return text.size();
+}
+
+void CompletionProposalsBuilder::resetWithResult(const CXCompletionResult &cxResult)
+{
+ m_priority = clang_getCompletionPriority(cxResult.CompletionString);
+ m_resultKind = getKind(cxResult);
+ m_resultAvailability = getAvailability(cxResult);
+ m_hasParameters = false;
+ m_hint.clear();
+ m_text.clear();
+ m_snippet.clear();
+ m_comment.clear();
+ m_optionalChunks.clear();
+}
+
+/**
+ * @brief Appends completion proposal initialized with collected data
+ */
+void CompletionProposalsBuilder::finalize()
+{
+ // Fixes code completion: operator and destructor cases
+ if ((m_contexts & CXCompletionContext_DotMemberAccess)
+ || (m_contexts & CXCompletionContext_ArrowMemberAccess)
+ || (m_contexts & CXCompletionContext_AnyValue)) {
+ if (m_resultKind == CodeCompletionResult::DestructorCompletionKind)
+ m_priority *= PriorityFix_ExplicitDestructorCall;
+ else if (m_resultKind == CodeCompletionResult::FunctionCompletionKind
+ && m_text.startsWith(QLatin1String("operator")))
+ return;
+ }
+
+ CodeCompletionResult ccr(m_priority);
+ ccr.setCompletionKind(m_resultKind);
+ ccr.setAvailability(m_resultAvailability);
+ ccr.setHasParameters(m_hasParameters);
+ ccr.setHint(m_hint);
+ ccr.setText(m_text);
+ ccr.setSnippet(m_snippet);
+ m_results.append(ccr);
+}
+
+/**
+ * @brief Creates text, hint and snippet
+ *
+ * Text is just signature, e.g. 'length' for [NSString length] or 'respondsToSelector:'
+ * for [id respondsToSelector:(SEL)sel].
+ * Snippet is actual text, where any message parameter becames snippet part:
+ * 'respondsToSelector:$(SEL)sel$'.
+ * Hint consists of snippet preview and doxygen 'return' entry with returned type.
+ */
+void CompletionProposalsBuilder::concatChunksForObjectiveCMessage(const CXCompletionResult &cxResult)
+{
+ CXCompletionString cxString = cxResult.CompletionString;
+ const unsigned count = clang_getNumCompletionChunks(cxString);
+ unsigned index = 0;
+ QString hintPrefix;
+ if (cxResult.CursorKind == CXCursor_ObjCClassMethodDecl)
+ hintPrefix += QLatin1Char('+');
+ else
+ hintPrefix += QLatin1Char('-');
+ int indentBonus = 1;
+
+ bool addSpaceAtPrefixEnd = true;
+ for (; index < count; ++index) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, index);
+ if (chunkKind == CXCompletionChunk_TypedText || chunkKind == CXCompletionChunk_Informative) {
+ break;
+ }
+ const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, index), false);
+ if (chunkKind == CXCompletionChunk_ResultType) {
+ hintPrefix += QLatin1String("(");
+ hintPrefix += Qt::escape(text);
+ hintPrefix += QLatin1String(") ");
+ indentBonus += 3 + text.length();
+ addSpaceAtPrefixEnd = false;
+ } else {
+ hintPrefix += Qt::escape(text);
+ indentBonus += text.length();
+ m_snippet += text;
+ }
+ }
+ if (addSpaceAtPrefixEnd) {
+ m_snippet += QLatin1Char(' ');
+ hintPrefix += QLatin1Char(' ');
+ indentBonus += 1;
+ }
+
+ m_hint += hintPrefix;
+
+ QList<ObjCMessagePart> parts;
+ bool previousWasTypedText = false;
+ for (; index < count; ++index) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, index);
+ const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, index), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_TypedText:
+ if (previousWasTypedText)
+ parts.back().addToSignature(text);
+ else
+ parts.append(ObjCMessagePart(text, indentBonus));
+ m_snippet += text;
+ m_text += text;
+ break;
+ case CXCompletionChunk_Informative:
+ parts.append(ObjCMessagePart(text, indentBonus));
+ break;
+ case CXCompletionChunk_Text:
+ case CXCompletionChunk_LeftParen:
+ case CXCompletionChunk_RightParen:
+ case CXCompletionChunk_Comma:
+ case CXCompletionChunk_HorizontalSpace:
+ m_snippet += text;
+ parts.back().text += Qt::escape(text);
+ break;
+ case CXCompletionChunk_Placeholder:
+ appendSnippet(text);
+ parts.back().text += QLatin1String("<b>");
+ parts.back().text += Qt::escape(text);
+ parts.back().text += QLatin1String("</b>");
+ break;
+ case CXCompletionChunk_LeftAngle:
+ m_snippet += text;
+ parts.back().text += QLatin1String("&lt;");
+ break;
+ case CXCompletionChunk_RightAngle:
+ m_snippet += text;
+ parts.back().text += QLatin1String("&gt;");
+ break;
+ default:
+ break;
+ }
+
+ previousWasTypedText = (chunkKind == CXCompletionChunk_TypedText);
+ }
+
+ int indent = 0;
+ foreach (const ObjCMessagePart &part, parts)
+ indent = qMax(indent, part.signatureLen);
+ bool isFirstPart = true;
+ foreach (const ObjCMessagePart &part, parts) {
+ if (!isFirstPart)
+ m_hint += QLatin1String("<br/>");
+ isFirstPart = false;
+ for (int i = 0; i < indent - part.signatureLen; ++i)
+ m_hint += QLatin1String("&nbsp;");
+ m_hint += part.text;
+ }
+}
+
+/**
+ * @brief Creates entries like 'MyClass', 'MyNamespace::', 'MyEnumClass::Value1'
+ */
+void CompletionProposalsBuilder::concatChunksForNestedName(const CXCompletionString &cxString)
+{
+ bool hasPlaceholder = false;
+ unsigned count = clang_getNumCompletionChunks(cxString);
+ for (unsigned i = 0; i < count; ++i) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i);
+ QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_TypedText:
+ case CXCompletionChunk_Text:
+ m_text += text;
+ m_snippet += text;
+ m_hint += text;
+ break;
+
+ case CXCompletionChunk_LeftAngle:
+ case CXCompletionChunk_RightAngle:
+ case CXCompletionChunk_Comma:
+ case CXCompletionChunk_HorizontalSpace:
+ m_snippet += text;
+ m_hint += Qt::escape(text);
+ break;
+
+ case CXCompletionChunk_Placeholder:
+ hasPlaceholder = true;
+ appendSnippet(text);
+ appendHintBold(text);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (!hasPlaceholder)
+ m_snippet.clear();
+}
+
+/**
+ * @brief Creates text, snippet and hint for snippet preview
+ *
+ * Text is copy of snippet without '$' marks.
+ * Hint also have 'return' doxygen entry if applicable (e.g. 'typeid...')
+ */
+void CompletionProposalsBuilder::concatChunksAsSnippet(const CXCompletionString &cxString)
+{
+ unsigned count = clang_getNumCompletionChunks(cxString);
+ for (unsigned i = 0; i < count; ++i) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i);
+ const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_ResultType:
+ attachResultTypeToComment(text);
+ break;
+
+ case CXCompletionChunk_Placeholder:
+ m_text += text;
+ appendSnippet(text);
+ appendHintBold(text);
+ break;
+ case CXCompletionChunk_LeftAngle:
+ m_snippet += text;
+ m_text += text;
+ m_hint += QLatin1String("&lt;");
+ break;
+ case CXCompletionChunk_RightAngle:
+ m_snippet += text;
+ m_text += text;
+ m_hint += QLatin1String("&gt;");
+ break;
+
+ case CXCompletionChunk_VerticalSpace:
+ m_snippet += QLatin1Char('\n');
+ m_text += QLatin1Char(' ');
+ m_hint += QLatin1String("<br/>");
+ break;
+
+ default:
+ m_snippet += text;
+ m_text += text;
+ m_hint += text;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Creates short text and hint with details
+ *
+ * Text is just function or variable name. Hint also contains function signature
+ * or variable type.
+ */
+void CompletionProposalsBuilder::concatChunksOnlyTypedText(const CXCompletionString &cxString)
+{
+ bool previousChunkWasLParen = false;
+ bool isInsideTemplateSpec = false;
+
+ unsigned count = clang_getNumCompletionChunks(cxString);
+ for (unsigned i = 0; i < count; ++i) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i);
+ QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_LeftParen:
+ case CXCompletionChunk_RightParen:
+ case CXCompletionChunk_Text:
+ case CXCompletionChunk_LeftAngle:
+ case CXCompletionChunk_RightAngle:
+ m_hint += Qt::escape(text);
+ break;
+
+ case CXCompletionChunk_HorizontalSpace:
+ case CXCompletionChunk_Comma:
+ if (isInsideTemplateSpec) {
+ m_snippet += text;
+ }
+ m_hint += Qt::escape(text);
+ break;
+
+ case CXCompletionChunk_Placeholder:
+ if (isInsideTemplateSpec) {
+ appendSnippet(text);
+ }
+ m_hint += Qt::escape(text);
+ break;
+
+ case CXCompletionChunk_TypedText:
+ m_text = text;
+ appendHintBold(text);
+ break;
+
+ case CXCompletionChunk_ResultType: {
+ m_hint += Qt::escape(text);
+ QChar last = text[text.size() - 1];
+ if (last != QLatin1Char('*') && last != QLatin1Char('&'))
+ m_hint += QLatin1Char(' ');
+ }
+ break;
+
+ case CXCompletionChunk_Informative:
+ if (text == QLatin1String(" const"))
+ m_hint += text;
+ break;
+
+ case CXCompletionChunk_Optional:
+ appendOptionalChunks(clang_getCompletionChunkCompletionString(cxString, i),
+ m_hint.size());
+ break;
+
+ default:
+ break;
+ }
+
+ if (chunkKind == CXCompletionChunk_RightParen && previousChunkWasLParen)
+ m_hasParameters = false;
+
+ if (chunkKind == CXCompletionChunk_LeftParen) {
+ previousChunkWasLParen = true;
+ m_hasParameters = true;
+ } else {
+ previousChunkWasLParen = false;
+ }
+
+ if (chunkKind == CXCompletionChunk_LeftAngle) {
+ m_snippet = m_text;
+ m_snippet += text;
+ isInsideTemplateSpec = true;
+ } else if (chunkKind == CXCompletionChunk_RightAngle) {
+ isInsideTemplateSpec = false;
+ m_snippet += text;
+ }
+ }
+}
+
+/**
+ * @brief Produces signal/slot signatures for 'connect' methods family
+ */
+void CompletionProposalsBuilder::concatSlotSignalSignature(const CXCompletionString &cxString)
+{
+ QString resultType;
+
+ unsigned count = clang_getNumCompletionChunks(cxString);
+ for (unsigned i = 0; i < count; ++i) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i);
+ QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_Placeholder:
+ text.truncate(findNameInPlaceholder(text));
+ // fall-through
+ case CXCompletionChunk_TypedText:
+ case CXCompletionChunk_LeftParen:
+ case CXCompletionChunk_RightParen:
+ case CXCompletionChunk_Comma:
+ case CXCompletionChunk_Text:
+ m_text += text;
+ break;
+ case CXCompletionChunk_ResultType:
+ resultType += text;
+ resultType += QLatin1Char(' ');
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ const QString parent = Internal::getQString(clang_getCompletionParent(cxString, NULL));
+ if (m_resultKind == CodeCompletionResult::SlotCompletionKind)
+ m_hint += QObject::tr("Slot of %1, returns %2").arg(parent).arg(resultType);
+ else
+ m_hint += QObject::tr("Signal of %1, returns %2").arg(parent).arg(resultType);
+}
+
+/**
+ * @brief Stores optional part for further postprocessing in \a finalize()
+ * @param insertionIndex Index where to insert optional chunk into hint
+ */
+void CompletionProposalsBuilder::appendOptionalChunks(const CXCompletionString &cxString,
+ int insertionIndex)
+{
+ OptionalChunk chunk;
+ chunk.positionInHint = insertionIndex;
+
+ unsigned count = clang_getNumCompletionChunks(cxString);
+ for (unsigned i = 0; i < count; ++i) {
+ CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i);
+ QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false);
+
+ switch (chunkKind) {
+ case CXCompletionChunk_Placeholder:
+ chunk.hint += Qt::escape(text);
+ break;
+
+ case CXCompletionChunk_Comma:
+ chunk.hint += text;
+ chunk.hint += QLatin1Char(' ');
+ break;
+
+ case CXCompletionChunk_Optional:
+ m_optionalChunks.append(chunk);
+ appendOptionalChunks(clang_getCompletionChunkCompletionString(cxString, i),
+ insertionIndex + chunk.hint.size());
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ m_optionalChunks.append(chunk);
+}
+
+void CompletionProposalsBuilder::attachResultTypeToComment(const QString &resultType)
+{
+ if (resultType.isEmpty())
+ return;
+
+ if (!m_comment.isEmpty())
+ m_comment += QLatin1String("<br/>");
+
+ m_comment += QLatin1String("<b>@return</b> ");
+ m_comment += resultType;
+}
+
+void CompletionProposalsBuilder::appendSnippet(const QString &text)
+{
+ m_snippet += QLatin1Char('$');
+ m_snippet += text;
+ m_snippet += QLatin1Char('$');
+}
+
+void CompletionProposalsBuilder::appendHintBold(const QString &text)
+{
+ m_hint += QLatin1String("<b>");
+ m_hint += Qt::escape(text);
+ m_hint += QLatin1String("</b>");
+}
+
+} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/completionproposalsbuilder.h b/src/plugins/clangcodemodel/completionproposalsbuilder.h
new file mode 100644
index 0000000000..919fbd9c89
--- /dev/null
+++ b/src/plugins/clangcodemodel/completionproposalsbuilder.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H
+#define CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H
+
+#include "clangcompleter.h"
+#include "clang_global.h"
+#include <clang-c/Index.h>
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT CompletionProposalsBuilder
+{
+public:
+ CompletionProposalsBuilder(QList<CodeCompletionResult> &results, quint64 contexts, bool isSignalSlotCompletion);
+ void operator ()(const CXCompletionResult &cxResult);
+
+private:
+ struct OptionalChunk {
+ int positionInHint;
+ QString hint;
+
+ OptionalChunk() : positionInHint(0) {}
+ };
+
+ static CodeCompletionResult::Kind getKind(const CXCompletionResult &cxResult);
+ static CodeCompletionResult::Availability getAvailability(const CXCompletionResult &cxResult);
+ static int findNameInPlaceholder(const QString &text);
+ void resetWithResult(const CXCompletionResult &cxResult);
+ void finalize();
+
+ void concatChunksForObjectiveCMessage(const CXCompletionResult &cxResult);
+ void concatChunksForNestedName(const CXCompletionString &cxString);
+ void concatChunksAsSnippet(const CXCompletionString &cxString);
+ void concatChunksOnlyTypedText(const CXCompletionString &cxString);
+ void concatSlotSignalSignature(const CXCompletionString &cxString);
+ void appendOptionalChunks(const CXCompletionString &cxString,
+ int insertionIndex);
+ void attachResultTypeToComment(const QString &text);
+
+ void appendSnippet(const QString &text);
+ void appendHintBold(const QString &text);
+
+ QList<CodeCompletionResult> &m_results;
+ const quint64 m_contexts;
+ const bool m_isSignalSlotCompletion;
+
+ unsigned m_priority;
+ CodeCompletionResult::Kind m_resultKind;
+ CodeCompletionResult::Availability m_resultAvailability;
+ bool m_hasParameters;
+ QString m_hint;
+ QString m_text;
+ QString m_snippet;
+ QString m_comment;
+ QList<OptionalChunk> m_optionalChunks;
+};
+
+} // namespace ClangCodeModel
+
+#endif // CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H
diff --git a/src/plugins/clangcodemodel/constants.h b/src/plugins/clangcodemodel/constants.h
new file mode 100644
index 0000000000..2699cbeca8
--- /dev/null
+++ b/src/plugins/clangcodemodel/constants.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include <QtCore/QLatin1Char>
+#include <QtCore/QLatin1Char>
+
+namespace ClangCodeModel {
+namespace Constants {
+
+static const QLatin1Char kLParen('(');
+static const QLatin1Char kRParen(')');
+static const QLatin1Char kLBrace('{');
+static const QLatin1Char kRBrace('}');
+static const QLatin1Char kLBracket('[');
+static const QLatin1Char kRBracket(']');
+static const QLatin1Char kLABracket('<');
+static const QLatin1Char kRABracket('>');
+static const QLatin1Char kSemiColon(';');
+static const QLatin1Char kPound('#');
+static const QLatin1Char kColon(':');
+static const QLatin1Char kExclamation('!');
+static const QLatin1Char kSpace(' ');
+static const QLatin1Char kSlash('/');
+static const QLatin1Char kStar('*');
+static const QLatin1Char kDoubleQuote('"');
+static const QLatin1Char kNewLine('\n');
+static const QLatin1Char kHorizontalTab('\t');
+
+}
+}
+
+#endif // CONSTANTS_H
diff --git a/src/plugins/clangcodemodel/cppcreatemarkers.cpp b/src/plugins/clangcodemodel/cppcreatemarkers.cpp
new file mode 100644
index 0000000000..ebd162e730
--- /dev/null
+++ b/src/plugins/clangcodemodel/cppcreatemarkers.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangutils.h"
+#include "cppcreatemarkers.h"
+
+#include <cplusplus/CppDocument.h>
+#include <cpptools/cppmodelmanagerinterface.h>
+#include <utils/runextensions.h>
+
+#include <QCoreApplication>
+#include <QMutexLocker>
+#include <QThreadPool>
+
+#include <QDebug>
+
+static const bool DebugTiming = !qgetenv("QTC_CLANG_VERBOSE").isEmpty();
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace CppTools;
+
+CreateMarkers *CreateMarkers::create(SemanticMarker::Ptr semanticMarker,
+ const QString &fileName,
+ const QStringList &options,
+ unsigned firstLine, unsigned lastLine,
+ FastIndexer *fastIndexer,
+ const Internal::PchInfo::Ptr &pchInfo)
+{
+ if (semanticMarker.isNull())
+ return 0;
+ else
+ return new CreateMarkers(semanticMarker, fileName, options, firstLine, lastLine, fastIndexer, pchInfo);
+}
+
+CreateMarkers::CreateMarkers(SemanticMarker::Ptr semanticMarker,
+ const QString &fileName,
+ const QStringList &options,
+ unsigned firstLine, unsigned lastLine,
+ FastIndexer *fastIndexer,
+ const Internal::PchInfo::Ptr &pchInfo)
+ : m_marker(semanticMarker)
+ , m_pchInfo(pchInfo)
+ , m_fileName(fileName)
+ , m_options(options)
+ , m_firstLine(firstLine)
+ , m_lastLine(lastLine)
+ , m_fastIndexer(fastIndexer)
+{
+ Q_ASSERT(!semanticMarker.isNull());
+
+ m_flushRequested = false;
+ m_flushLine = 0;
+
+ m_unsavedFiles = Utils::createUnsavedFiles(CppModelManagerInterface::instance()->workingCopy());
+}
+
+CreateMarkers::~CreateMarkers()
+{ }
+
+void CreateMarkers::run()
+{
+ QMutexLocker lock(m_marker->mutex());
+ if (isCanceled())
+ return;
+
+ m_options += QLatin1String("-fspell-checking");
+
+ QTime t;
+ if (DebugTiming) {
+ qDebug() << "*** Highlighting from" << m_firstLine << "to" << m_lastLine << "of" << m_fileName;
+ qDebug() << "***** Options: " << m_options.join(QLatin1String(" "));
+ t.start();
+ }
+
+ m_usages.clear();
+ m_marker->setFileName(m_fileName);
+ m_marker->setCompilationOptions(m_options);
+
+ m_marker->reparse(m_unsavedFiles);
+
+ if (DebugTiming)
+ qDebug() << "*** Reparse for highlighting took" << t.elapsed() << "ms.";
+
+ m_pchInfo.clear();
+
+ typedef CPlusPlus::Document::DiagnosticMessage OtherDiagnostic;
+ QList<OtherDiagnostic> msgs;
+ foreach (const ClangCodeModel::Diagnostic &d, m_marker->diagnostics()) {
+ if (DebugTiming)
+ qDebug() << d.severityAsString() << d.location() << d.spelling();
+
+ if (d.location().fileName() != m_marker->fileName())
+ continue;
+
+ // TODO: retrieve fix-its for this diagnostic
+
+ int level;
+ switch (d.severity()) {
+ case Diagnostic::Fatal: level = OtherDiagnostic::Fatal; break;
+ case Diagnostic::Error: level = OtherDiagnostic::Error; break;
+ case Diagnostic::Warning: level = OtherDiagnostic::Warning; break;
+ default: continue;
+ }
+ msgs.append(OtherDiagnostic(level, d.location().fileName(), d.location().line(),
+ d.location().column(), d.spelling(), d.length()));
+ }
+ if (isCanceled()) {
+ reportFinished();
+ return;
+ }
+
+ CppModelManagerInterface *mmi = CppModelManagerInterface::instance();
+ static const QString key = QLatin1String("ClangCodeModel.Diagnostics");
+ mmi->setExtraDiagnostics(m_marker->fileName(), key, msgs);
+#if CINDEX_VERSION_MINOR >= 21
+ mmi->setIfdefedOutBlocks(m_marker->fileName(), m_marker->ifdefedOutBlocks());
+#endif
+
+ if (isCanceled()) {
+ reportFinished();
+ return;
+ }
+
+ 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();
+ return;
+ }
+
+ flush();
+ reportFinished();
+
+ if (DebugTiming) {
+ qDebug() << "*** Highlighting took" << t.elapsed() << "ms in total.";
+ t.restart();
+ }
+
+ if (m_fastIndexer)
+ m_fastIndexer->indexNow(m_marker->unit());
+
+ if (DebugTiming)
+ qDebug() << "*** Fast re-indexing took" << t.elapsed() << "ms in total.";
+}
+
+void CreateMarkers::addUse(const SourceMarker &marker)
+{
+// if (! enclosingFunctionDefinition()) {
+ if (m_usages.size() >= 100) {
+ if (m_flushRequested && marker.line != m_flushLine)
+ flush();
+ else if (! m_flushRequested) {
+ m_flushRequested = true;
+ m_flushLine = marker.line;
+ }
+ }
+// }
+
+ m_usages.append(marker);
+}
+
+void CreateMarkers::flush()
+{
+ m_flushRequested = false;
+ m_flushLine = 0;
+
+ if (m_usages.isEmpty())
+ return;
+
+ reportResults(m_usages);
+ m_usages.clear();
+}
diff --git a/src/plugins/clangcodemodel/cppcreatemarkers.h b/src/plugins/clangcodemodel/cppcreatemarkers.h
new file mode 100644
index 0000000000..9f42f47c32
--- /dev/null
+++ b/src/plugins/clangcodemodel/cppcreatemarkers.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPCREATEMARKERS_H
+#define CPPCREATEMARKERS_H
+
+#include "fastindexer.h"
+#include "sourcemarker.h"
+#include "semanticmarker.h"
+#include "pchinfo.h"
+
+#include <texteditor/semantichighlighter.h>
+
+#include <QSet>
+#include <QFuture>
+#include <QtConcurrentRun>
+
+namespace ClangCodeModel {
+
+class CreateMarkers:
+ public QObject,
+ public QRunnable,
+ public QFutureInterface<TextEditor::HighlightingResult>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(CreateMarkers)
+
+public:
+ virtual ~CreateMarkers();
+
+ virtual void run();
+
+ typedef TextEditor::HighlightingResult SourceMarker;
+
+ typedef QFuture<SourceMarker> Future;
+
+ Future start()
+ {
+ this->setRunnable(this);
+ this->reportStarted();
+ Future future = this->future();
+ QThreadPool::globalInstance()->start(this, QThread::LowestPriority);
+ return future;
+ }
+
+ static CreateMarkers *create(ClangCodeModel::SemanticMarker::Ptr semanticMarker,
+ const QString &fileName,
+ const QStringList &options,
+ unsigned firstLine, unsigned lastLine,
+ Internal::FastIndexer *fastIndexer,
+ const Internal::PchInfo::Ptr &pchInfo);
+
+ void addUse(const SourceMarker &marker);
+ void flush();
+
+protected:
+ CreateMarkers(ClangCodeModel::SemanticMarker::Ptr semanticMarker,
+ const QString &fileName, const QStringList &options,
+ unsigned firstLine, unsigned lastLine,
+ Internal::FastIndexer *fastIndexer,
+ const Internal::PchInfo::Ptr &pchInfo);
+
+private:
+ ClangCodeModel::SemanticMarker::Ptr m_marker;
+ Internal::PchInfo::Ptr m_pchInfo;
+ QString m_fileName;
+ QStringList m_options;
+ unsigned m_firstLine;
+ unsigned m_lastLine;
+ Internal::FastIndexer *m_fastIndexer;
+ QVector<SourceMarker> m_usages;
+ bool m_flushRequested;
+ unsigned m_flushLine;
+
+ ClangCodeModel::Internal::UnsavedFiles m_unsavedFiles;
+};
+
+} // namespace ClangCodeModel
+
+#endif // CPPCREATEMARKERS_H
diff --git a/src/plugins/clangcodemodel/cxprettyprinter.cpp b/src/plugins/clangcodemodel/cxprettyprinter.cpp
new file mode 100644
index 0000000000..1b954462cd
--- /dev/null
+++ b/src/plugins/clangcodemodel/cxprettyprinter.cpp
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "cxprettyprinter.h"
+#include "utils_p.h"
+#include "cxraii.h"
+#include <QStringList>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+CXPrettyPrinter::CXPrettyPrinter()
+ : m_indent(0)
+{
+}
+
+QString CXPrettyPrinter::toString(CXCompletionChunkKind kind) const
+{
+ switch (kind) {
+ case CXCompletionChunk_Optional:
+ return QLatin1String("Optional");
+ case CXCompletionChunk_TypedText:
+ return QLatin1String("TypedText");
+ case CXCompletionChunk_Text:
+ return QLatin1String("Text");
+ case CXCompletionChunk_Placeholder:
+ return QLatin1String("Placeholder");
+ case CXCompletionChunk_Informative:
+ return QLatin1String("Informative");
+ case CXCompletionChunk_CurrentParameter:
+ return QLatin1String("CurrentParameter");
+ case CXCompletionChunk_LeftParen:
+ return QLatin1String("LeftParen");
+ case CXCompletionChunk_RightParen:
+ return QLatin1String("RightParen");
+ case CXCompletionChunk_LeftBracket:
+ return QLatin1String("LeftBracket");
+ case CXCompletionChunk_RightBracket:
+ return QLatin1String("RightBracket");
+ case CXCompletionChunk_LeftBrace:
+ return QLatin1String("LeftBrace");
+ case CXCompletionChunk_RightBrace:
+ return QLatin1String("RightBrace");
+ case CXCompletionChunk_LeftAngle:
+ return QLatin1String("LeftAngle");
+ case CXCompletionChunk_RightAngle:
+ return QLatin1String("RightAngle");
+ case CXCompletionChunk_Comma:
+ return QLatin1String("Comma");
+ case CXCompletionChunk_ResultType:
+ return QLatin1String("ResultType");
+ case CXCompletionChunk_Colon:
+ return QLatin1String("Colon");
+ case CXCompletionChunk_SemiColon:
+ return QLatin1String("SemiColon");
+ case CXCompletionChunk_Equal:
+ return QLatin1String("Equal");
+ case CXCompletionChunk_HorizontalSpace:
+ return QLatin1String("HorizontalSpace");
+ case CXCompletionChunk_VerticalSpace:
+ return QLatin1String("VerticalSpace");
+ default:
+ return QLatin1String("<UNKNOWN>");
+ }
+}
+
+QString CXPrettyPrinter::toString(CXAvailabilityKind kind) const
+{
+ switch (kind) {
+ case CXAvailability_Available:
+ return QLatin1String("Available");
+ case CXAvailability_Deprecated:
+ return QLatin1String("Deprecated");
+ case CXAvailability_NotAccessible:
+ return QLatin1String("NotAccessible");
+ case CXAvailability_NotAvailable:
+ return QLatin1String("NotAvailable");
+ default:
+ return QLatin1String("<UNKNOWN>");
+ }
+}
+
+QString CXPrettyPrinter::toString(CXCursorKind kind) const
+{
+ return getQString(clang_getCursorKindSpelling(kind));
+}
+
+QString CXPrettyPrinter::toString(CXDiagnosticSeverity severity) const
+{
+ switch (severity)
+ {
+ case CXDiagnostic_Ignored:
+ return QLatin1String("Ignored");
+ case CXDiagnostic_Note:
+ return QLatin1String("Note");
+ case CXDiagnostic_Warning:
+ return QLatin1String("Warning");
+ case CXDiagnostic_Error:
+ return QLatin1String("Error");
+ case CXDiagnostic_Fatal:
+ return QLatin1String("Fatal");
+ default:
+ return QLatin1String("<UNKNOWN>");
+ }
+}
+
+QString CXPrettyPrinter::jsonForCompletionMeta(CXCodeCompleteResults *results)
+{
+ QString json;
+ m_printed.swap(json);
+ m_indent = 0;
+
+ m_printed += QLatin1String("CXCodeCompleteResults {");
+ m_indent += 4;
+
+ CXCursorKind containerKind = clang_codeCompleteGetContainerKind(results, NULL);
+ writeLineEnd();
+ m_printed += QLatin1String("'container CursorKind': ");
+ m_printed += toString(containerKind);
+ m_printed += QLatin1Char(',');
+
+ QString containerUSR(Internal::getQString(clang_codeCompleteGetContainerUSR(results)));
+ if (!containerUSR.isEmpty()) {
+ writeLineEnd();
+ m_printed += QLatin1String("'container USR': ");
+ m_printed += containerUSR;
+ m_printed += QLatin1Char(',');
+ }
+
+ QString objCSelector(Internal::getQString(clang_codeCompleteGetObjCSelector(results)));
+ if (!objCSelector.isEmpty()) {
+ writeLineEnd();
+ m_printed += QLatin1String("'Objective-C selector': ");
+ m_printed += objCSelector;
+ m_printed += QLatin1Char(',');
+ }
+
+ writeLineEnd();
+ m_printed += QLatin1String("'contexts': [");
+ m_indent += 4;
+ writeCompletionContexts(results);
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1Char(']');
+
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1Char('}');
+
+ m_printed.swap(json);
+ return json;
+}
+
+QString CXPrettyPrinter::jsonForCompletionString(const CXCompletionString &string)
+{
+ QString json;
+ m_printed.swap(json);
+ m_indent = 0;
+
+ m_printed += QLatin1String("CXCompletionString: ");
+ writeCompletionStringJson(string);
+
+ m_printed.swap(json);
+ return json;
+}
+
+QString CXPrettyPrinter::jsonForCompletion(const CXCompletionResult &result)
+{
+ QString json;
+ m_printed.swap(json);
+ m_indent = 4;
+
+ m_printed += QLatin1String("CXCompletionResult: {\n"
+ " CompletionString: ");
+ writeCompletionStringJson(result.CompletionString);
+ m_printed += QLatin1Char('\n');
+
+ m_printed += QLatin1String(" CursorKind: ");
+ m_printed += toString(result.CursorKind);
+ m_printed += QLatin1String(";\n}");
+
+ m_printed.swap(json);
+ return json;
+}
+
+/**
+ * @brief CXPrettyPrinter::jsonForDiagnsotic
+ * @param diagnostic
+ * @return
+ *
+ * List of used clang-c API calls:
+ * CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D);
+ * CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic);
+ * CXString clang_getDiagnosticOption(CXDiagnostic Diag,
+ * CXString *Disable);
+ * unsigned clang_getDiagnosticCategory(CXDiagnostic);
+ * CXString clang_getDiagnosticCategoryText(CXDiagnostic);
+ * unsigned clang_getDiagnosticNumRanges(CXDiagnostic);
+ * CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diagnostic,
+ * unsigned Range);
+ * unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diagnostic);
+ * CXString clang_getDiagnosticFixIt(CXDiagnostic Diagnostic,
+ * unsigned FixIt,
+ * CXSourceRange *ReplacementRange);
+ */
+QString CXPrettyPrinter::jsonForDiagnsotic(const CXDiagnostic &diagnostic)
+{
+ QString json;
+ m_printed.swap(json);
+ m_indent = 0;
+
+ m_printed += QLatin1String("CXDiagnostic: ");
+ writeDiagnosticJson(diagnostic);
+
+ m_printed.swap(json);
+ return json;
+}
+
+void CXPrettyPrinter::writeCompletionContexts(CXCodeCompleteResults *results)
+{
+ quint64 contexts = clang_codeCompleteGetContexts(results);
+ QStringList lines;
+
+ if (contexts & CXCompletionContext_AnyType)
+ lines << QLatin1String("'any type'");
+ if (contexts & CXCompletionContext_AnyValue)
+ lines << QLatin1String("'any value'");
+ if (contexts & CXCompletionContext_ObjCObjectValue)
+ lines << QLatin1String("'Objective-C object'");
+ if (contexts & CXCompletionContext_ObjCSelectorValue)
+ lines << QLatin1String("'Objective-C selector'");
+ if (contexts & CXCompletionContext_CXXClassTypeValue)
+ lines << QLatin1String("'C++ class'");
+ if (contexts & CXCompletionContext_DotMemberAccess)
+ lines << QLatin1String("'. member access'");
+ if (contexts & CXCompletionContext_ArrowMemberAccess)
+ lines << QLatin1String("'-> member access'");
+ if (contexts & CXCompletionContext_ObjCPropertyAccess)
+ lines << QLatin1String("'. Objective-C property access'");
+ if (contexts & CXCompletionContext_EnumTag)
+ lines << QLatin1String("'enum tag'");
+ if (contexts & CXCompletionContext_UnionTag)
+ lines << QLatin1String("'union tag'");
+ if (contexts & CXCompletionContext_StructTag)
+ lines << QLatin1String("'struct tag'");
+ if (contexts & CXCompletionContext_ClassTag)
+ lines << QLatin1String("'C++ class tag'");
+ if (contexts & CXCompletionContext_Namespace)
+ lines << QLatin1String("'namespace tag'");
+ if (contexts & CXCompletionContext_NestedNameSpecifier)
+ lines << QLatin1String("'C++ nested name specifier'");
+ if (contexts & CXCompletionContext_ObjCInterface)
+ lines << QLatin1String("'Objective-C interface'");
+ if (contexts & CXCompletionContext_ObjCProtocol)
+ lines << QLatin1String("'Objective-C protocol'");
+ if (contexts & CXCompletionContext_ObjCCategory)
+ lines << QLatin1String("'Objective-C category'");
+ if (contexts & CXCompletionContext_ObjCInstanceMessage)
+ lines << QLatin1String("'Objective-C instance message'");
+ if (contexts & CXCompletionContext_ObjCClassMessage)
+ lines << QLatin1String("'Objective-C class message'");
+ if (contexts & CXCompletionContext_ObjCSelectorName)
+ lines << QLatin1String("'Objective-C selector name'");
+ if (contexts & CXCompletionContext_MacroName)
+ lines << QLatin1String("'macro name'");
+ if (contexts & CXCompletionContext_NaturalLanguage)
+ lines << QLatin1String("'natural language'");
+
+ foreach (const QString &line, lines) {
+ writeLineEnd();
+ m_printed += line + QLatin1String(",");
+ }
+}
+
+void CXPrettyPrinter::writeCompletionStringJson(const CXCompletionString &string)
+{
+ m_printed += QLatin1Char('{');
+ writeLineEnd();
+
+ // availability
+ m_printed += QLatin1String("availability: ");
+ m_printed += toString(clang_getCompletionAvailability(string));
+ m_printed += QLatin1Char(';');
+ writeLineEnd();
+
+ // priority
+ m_printed += QLatin1String("priority: ");
+ m_printed += QString::number(clang_getCompletionPriority(string));
+ m_printed += QLatin1Char(';');
+ writeLineEnd();
+
+ // parent
+ m_printed += QLatin1String("parent: \'");
+ m_printed += getQString(clang_getCompletionParent(string, NULL));
+ m_printed += QLatin1String("\';");
+ writeLineEnd();
+
+ // chunks
+ m_printed += QLatin1String("chunks: [");
+ m_indent += 4;
+ unsigned numChunks = clang_getNumCompletionChunks(string);
+ for (unsigned i = 0; i < numChunks; ++i) {
+ writeLineEnd();
+ writeCompletionChunkJson(string, i);
+ }
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1Char(']');
+ writeLineEnd();
+
+ // annotation
+ m_printed += QLatin1String("annotations: [");
+ m_indent += 4;
+ unsigned numAnns = clang_getCompletionNumAnnotations(string);
+ for (unsigned i = 0; i < numAnns; ++i) {
+ writeLineEnd();
+ writeCompletionAnnotationJson(string, i);
+ }
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1Char(']');
+ writeLineEnd();
+
+ m_printed += QLatin1Char('}');
+}
+
+void CXPrettyPrinter::writeCompletionChunkJson(const CXCompletionString &string, unsigned i)
+{
+ QString text = getQString(clang_getCompletionChunkText(string, i));
+ QString kind = toString(clang_getCompletionChunkKind(string, i));
+ CXCompletionString optional = clang_getCompletionChunkCompletionString(string, i);
+
+ m_printed += kind;
+ m_printed += QLatin1String(": ");
+ if (!text.isEmpty()) {
+ m_printed += QLatin1Char('\'');
+ m_printed += text;
+ m_printed += QLatin1Char('\'');
+ }
+ if (optional != NULL) {
+ if (!text.isEmpty())
+ m_printed += QLatin1String(", ");
+ m_indent += 4;
+ writeCompletionStringJson(optional);
+ m_indent -= 4;
+ }
+}
+
+void CXPrettyPrinter::writeCompletionAnnotationJson(const CXCompletionString &string, unsigned i)
+{
+ m_printed += QLatin1Char('\'');
+ m_printed += getQString(clang_getCompletionAnnotation(string, i));
+ m_printed += QLatin1Char('\'');
+}
+
+void CXPrettyPrinter::writeDiagnosticJson(const CXDiagnostic &diag)
+{
+ m_printed += QLatin1String("{");
+ m_indent += 4;
+ writeLineEnd();
+
+ // message
+ m_printed += QLatin1Char('\'');
+ m_printed += getQString(clang_formatDiagnostic(diag, /*options*/ 0));
+ m_printed += QLatin1Char('\'');
+ writeLineEnd();
+
+ // severity
+ m_printed += QLatin1String("severity: ");
+ m_printed += toString(clang_getDiagnosticSeverity(diag));
+ writeLineEnd();
+
+ // location
+ m_printed += QLatin1String("location: ");
+ writeLocationJson(clang_getDiagnosticLocation(diag));
+ writeLineEnd();
+
+ // fix-its
+ unsigned numFixIts = clang_getDiagnosticNumFixIts(diag);
+ if (numFixIts > 0) {
+ m_printed += QLatin1String("FixIts: [");
+ writeLineEnd();
+ for (unsigned i = 0; i < numFixIts; ++i) {
+ writeFixItJson(diag, i);
+ writeLineEnd();
+ }
+ m_printed += QLatin1String("]");
+ writeLineEnd();
+ }
+
+ // clang CLI options
+ CXString cxDisabler;
+ QString enabler = getQString(clang_getDiagnosticOption(diag, &cxDisabler));
+ QString disabler = getQString(cxDisabler);
+ if (!enabler.isEmpty()) {
+ m_printed += QLatin1String("enabledBy: \'");
+ m_printed += enabler;
+ m_printed += QLatin1String("';");
+ writeLineEnd();
+ }
+ if (!disabler.isEmpty()) {
+ m_printed += QLatin1String("disabledBy: \'");
+ m_printed += disabler;
+ m_printed += QLatin1String("';");
+ writeLineEnd();
+ }
+
+ // diagnostic category
+ m_printed += QLatin1String("category: \'");
+ m_printed += getQString(clang_getDiagnosticCategoryText(diag));
+ m_printed += QLatin1String("';");
+
+ // ranges
+ unsigned numRanges = clang_getDiagnosticNumRanges(diag);
+ if (numRanges > 0) {
+ writeLineEnd();
+ m_printed += QLatin1String("ranges: [");
+ m_indent += 4;
+ for (unsigned i = 0; i < numRanges; ++i) {
+ writeLineEnd();
+ writeRangeJson(clang_getDiagnosticRange(diag, i));
+ }
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1String("]");
+ }
+
+ // children
+ CXDiagnosticSet set(clang_getChildDiagnostics(diag));
+ unsigned numChildren = clang_getNumDiagnosticsInSet(set);
+ if (numChildren > 0) {
+ writeLineEnd();
+ m_printed += QLatin1String("children: [");
+ m_indent += 4;
+ for (unsigned i = 0; i < numChildren; ++i) {
+ writeLineEnd();
+ ScopedCXDiagnostic child(clang_getDiagnosticInSet(set, i));
+ writeDiagnosticJson(child);
+ }
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1String("]");
+ }
+
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1String("}");
+}
+
+void CXPrettyPrinter::writeFixItJson(const CXDiagnostic &diag, unsigned i)
+{
+ CXSourceRange range; // half-open range [a, b)
+ QString text = getQString(clang_getDiagnosticFixIt(diag, i, &range));
+
+ m_printed += QLatin1String("{ newText: ");
+ m_printed += QLatin1String("\'");
+ m_printed += text;
+ m_printed += QLatin1String("\', range: ");
+ writeRangeJson(range);
+ m_printed += QLatin1String("}");
+}
+
+void CXPrettyPrinter::writeRangeJson(const CXSourceRange &range)
+{
+ SourceLocation start = getSpellingLocation(clang_getRangeStart(range));
+ SourceLocation end = getSpellingLocation(clang_getRangeEnd(range));
+
+ m_printed += QLatin1Char('{');
+ m_indent += 4;
+ writeLineEnd();
+
+ m_printed += QLatin1String("file: \'");
+ m_printed += start.fileName();
+ m_printed += QLatin1String("\',");
+ writeLineEnd();
+
+ m_printed += QLatin1String("from: {");
+ m_printed += QString::number(start.line());
+ m_printed += QLatin1String(", ");
+ m_printed += QString::number(start.column());
+ m_printed += QLatin1String("},");
+
+ m_printed += QLatin1String("to: {");
+ m_printed += QString::number(end.line());
+ m_printed += QLatin1String(", ");
+ m_printed += QString::number(end.column());
+ m_printed += QLatin1Char('}');
+
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1Char('}');
+}
+
+void CXPrettyPrinter::writeLocationJson(const CXSourceLocation &location)
+{
+ SourceLocation loc = getSpellingLocation(location);
+ m_printed += QLatin1Char('{');
+ m_indent += 4;
+ writeLineEnd();
+
+ m_printed += QLatin1String("file: \'");
+ m_printed += loc.fileName();
+ m_printed += QLatin1String("\',");
+ writeLineEnd();
+
+ m_printed += QLatin1String("line: ");
+ m_printed += QString::number(loc.line());
+ m_printed += QLatin1String(",");
+ writeLineEnd();
+
+ m_printed += QLatin1String("column: ");
+ m_printed += QString::number(loc.column());
+
+ m_indent -= 4;
+ writeLineEnd();
+ m_printed += QLatin1String("}");
+}
+
+void CXPrettyPrinter::writeLineEnd()
+{
+ m_printed += QLatin1Char('\n');
+ for (int i = 0; i < m_indent; ++i)
+ m_printed += QLatin1Char(' ');
+}
diff --git a/src/plugins/clangcodemodel/cxprettyprinter.h b/src/plugins/clangcodemodel/cxprettyprinter.h
new file mode 100644
index 0000000000..2143181dac
--- /dev/null
+++ b/src/plugins/clangcodemodel/cxprettyprinter.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CXPRETTYPRINTER_H
+#define CXPRETTYPRINTER_H
+
+#include <clang-c/Index.h>
+#include <QString>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class CXPrettyPrinter
+{
+public:
+ CXPrettyPrinter();
+
+ QString toString(CXCompletionChunkKind kind) const;
+ QString toString(CXAvailabilityKind kind) const;
+ QString toString(CXCursorKind kind) const;
+ QString toString(CXDiagnosticSeverity severity) const;
+
+ QString jsonForCompletionMeta(CXCodeCompleteResults *results);
+ QString jsonForCompletionString(const CXCompletionString &string);
+ QString jsonForCompletion(const CXCompletionResult &result);
+ QString jsonForDiagnsotic(const CXDiagnostic &diagnostic);
+
+private:
+ int m_indent;
+ QString m_printed;
+
+ void writeCompletionContexts(CXCodeCompleteResults *results);
+ void writeCompletionStringJson(const CXCompletionString &string);
+ void writeCompletionChunkJson(const CXCompletionString &string, unsigned i);
+ void writeCompletionAnnotationJson(const CXCompletionString &string, unsigned i);
+
+ void writeDiagnosticJson(const CXDiagnostic &diag);
+ void writeFixItJson(const CXDiagnostic &diag, unsigned i);
+
+ void writeRangeJson(const CXSourceRange &range);
+ void writeLocationJson(const CXSourceLocation &location);
+
+ void writeLineEnd();
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // CXPRETTYPRINTER_H
diff --git a/src/plugins/clangcodemodel/cxraii.h b/src/plugins/clangcodemodel/cxraii.h
new file mode 100644
index 0000000000..9461bb8dc1
--- /dev/null
+++ b/src/plugins/clangcodemodel/cxraii.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CXRAII_H
+#define CXRAII_H
+
+#include <clang-c/Index.h>
+
+// Simple RAII types for their CX correspondings
+
+namespace ClangCodeModel {
+namespace Internal {
+
+template <class CXType_T>
+struct ScopedCXType
+{
+protected:
+ typedef void (*DisposeFunction)(CXType_T);
+
+ ScopedCXType(DisposeFunction f)
+ : m_cx(0), m_disposeFunction(f)
+ {}
+ ScopedCXType(const CXType_T &cx, DisposeFunction f)
+ : m_cx(cx) , m_disposeFunction(f)
+ {}
+
+public:
+ ~ScopedCXType()
+ {
+ dispose();
+ }
+
+ operator CXType_T() const { return m_cx; }
+ bool operator!() const { return !m_cx; }
+ bool isNull() const { return !m_cx; }
+ void reset(const CXType_T &cx)
+ {
+ dispose();
+ m_cx = cx;
+ }
+
+private:
+ ScopedCXType(const ScopedCXType &);
+ const ScopedCXType &operator=(const ScopedCXType &);
+
+ void dispose()
+ {
+ if (m_cx)
+ m_disposeFunction(m_cx);
+ }
+
+ CXType_T m_cx;
+ DisposeFunction m_disposeFunction;
+};
+
+struct ScopedCXIndex : ScopedCXType<CXIndex>
+{
+ ScopedCXIndex()
+ : ScopedCXType<CXIndex>(&clang_disposeIndex)
+ {}
+ ScopedCXIndex(const CXIndex &index)
+ : ScopedCXType<CXIndex>(index, &clang_disposeIndex)
+ {}
+};
+
+struct ScopedCXTranslationUnit : ScopedCXType<CXTranslationUnit>
+{
+ ScopedCXTranslationUnit()
+ : ScopedCXType<CXTranslationUnit>(&clang_disposeTranslationUnit)
+ {}
+ ScopedCXTranslationUnit(const CXTranslationUnit &unit)
+ : ScopedCXType<CXTranslationUnit>(unit, &clang_disposeTranslationUnit)
+ {}
+};
+
+struct ScopedCXDiagnostic : ScopedCXType<CXDiagnostic>
+{
+ ScopedCXDiagnostic()
+ : ScopedCXType<CXDiagnostic>(&clang_disposeDiagnostic)
+ {}
+ ScopedCXDiagnostic(const CXDiagnostic &diagnostic)
+ : ScopedCXType<CXDiagnostic>(diagnostic, &clang_disposeDiagnostic)
+ {}
+};
+
+struct ScopedCXDiagnosticSet : ScopedCXType<CXDiagnostic>
+{
+ ScopedCXDiagnosticSet()
+ : ScopedCXType<CXDiagnosticSet>(&clang_disposeDiagnosticSet)
+ {}
+ ScopedCXDiagnosticSet(const CXDiagnostic &diagnostic)
+ : ScopedCXType<CXDiagnosticSet>(diagnostic, &clang_disposeDiagnosticSet)
+ {}
+};
+
+struct ScopedCXCodeCompleteResults : ScopedCXType<CXCodeCompleteResults*>
+{
+ ScopedCXCodeCompleteResults()
+ : ScopedCXType<CXCodeCompleteResults*>(&clang_disposeCodeCompleteResults)
+ {}
+ ScopedCXCodeCompleteResults(CXCodeCompleteResults *results)
+ : ScopedCXType<CXCodeCompleteResults*>(results, &clang_disposeCodeCompleteResults)
+ {}
+
+ unsigned size() const
+ {
+ return static_cast<CXCodeCompleteResults *>(*this)->NumResults;
+ }
+
+ const CXCompletionResult &completionAt(unsigned i)
+ {
+ return static_cast<CXCodeCompleteResults *>(*this)->Results[i];
+ }
+};
+
+} // Internal
+} // ClangCodeModel
+
+#endif // CXRAII_H
diff --git a/src/plugins/clangcodemodel/dependencygraph.cpp b/src/plugins/clangcodemodel/dependencygraph.cpp
new file mode 100644
index 0000000000..f0aadd6692
--- /dev/null
+++ b/src/plugins/clangcodemodel/dependencygraph.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "dependencygraph.h"
+
+#include <QtCore/QtConcurrentRun>
+
+using namespace ClangCodeModel;
+using namespace Internal;
+
+DependencyGraph::DependencyGraph()
+{
+ m_includeTracker.setResolutionMode(IncludeTracker::EveryMatchResolution);
+}
+
+DependencyGraph::~DependencyGraph()
+{
+ discard();
+}
+
+void DependencyGraph::cancel()
+{
+ if (m_computeWatcher.isRunning()) {
+ m_computeWatcher.cancel();
+ m_computeWatcher.waitForFinished();
+ }
+}
+
+void DependencyGraph::addFile(const QString &fileName, const QStringList &compilationOptions)
+{
+ cancel();
+
+ m_files.append(qMakePair(fileName, compilationOptions));
+}
+
+QFuture<void> DependencyGraph::compute()
+{
+ QFuture<void> future = QtConcurrent::run(this, &DependencyGraph::computeCore);
+ m_computeWatcher.setFuture(future);
+ return future;
+}
+
+void DependencyGraph::computeCore()
+{
+ for (int i = 0; i < m_files.size(); ++i) {
+ if (m_computeWatcher.isCanceled())
+ break;
+
+ const QPair<QString, QStringList> &p = m_files.at(i);
+ const QString &currentFile = p.first;
+ const QStringList &options = p.second;
+ const QPair<bool, NodeRefSetIt> &v = findVertex(currentFile);
+ if (!v.first)
+ processIncludes(insertVertex(currentFile), options);
+ }
+
+ emit dependencyGraphAvailable();
+}
+
+void DependencyGraph::processIncludes(NodeRefSetIt currentIt,
+ const QStringList &compilationOptions)
+{
+ const QString &currentFile = currentIt.key();
+ const QStringList &includes = m_includeTracker.directIncludes(currentFile, compilationOptions);
+ foreach (const QString &include, includes) {
+ if (m_computeWatcher.isCanceled())
+ return;
+
+ QPair<bool, NodeRefSetIt> v = findVertex(include);
+ if (!v.first) {
+ v.second = insertVertex(include);
+ processIncludes(v.second, compilationOptions);
+ }
+ insertEdge(currentIt, v.second);
+ }
+}
+
+namespace {
+
+struct SimpleVisitor
+{
+ bool acceptFile(const QString &fileName)
+ {
+ m_allFiles.append(fileName);
+ return false;
+ }
+
+ QStringList m_allFiles;
+};
+
+}
+
+QStringList DependencyGraph::collectDependencies(const QString &referenceFile,
+ DependencyRole role) const
+{
+ SimpleVisitor visitor;
+ collectDependencies(referenceFile, role, &visitor);
+
+ return visitor.m_allFiles;
+}
+
+bool DependencyGraph::hasDependency(const QString &referenceFile, DependencyRole role) const
+{
+ QPair<bool, NodeRefSetIt> v = findVertex(referenceFile);
+ if (!v.first)
+ return false;
+
+ NodeListIt nodeIt = v.second.value();
+
+ if (role == FilesDirectlyIncludedBy || role == FilesIncludedBy)
+ return nodeIt->m_out != 0;
+
+ return nodeIt->m_in != 0;
+}
+
+void DependencyGraph::discard()
+{
+ cancel();
+
+ for (NodeListIt it = m_nodes.begin(); it != m_nodes.end(); ++it) {
+ deleteAdjacencies(it->m_out);
+ deleteAdjacencies(it->m_in);
+ }
+ m_nodes.clear();
+ m_nodesRefs.clear();
+ m_files.clear();
+}
+
+
+DependencyGraph::Node::Node(const QString &fileName)
+ : m_fileName(fileName)
+ , m_out(0)
+ , m_in(0)
+{}
+
+DependencyGraph::AdjacencyNode::AdjacencyNode(NodeListIt it)
+ : m_next(0)
+ , m_nodeIt(it)
+{}
+
+QPair<bool, DependencyGraph::NodeRefSetIt> DependencyGraph::findVertex(const QString &s) const
+{
+ bool found = false;
+ NodeRefSetIt it = const_cast<NodeRefSet &>(m_nodesRefs).find(s);
+ if (it != m_nodesRefs.end())
+ found = true;
+ return qMakePair(found, it);
+}
+
+DependencyGraph::NodeRefSetIt DependencyGraph::insertVertex(const QString &s)
+{
+ Q_ASSERT(m_nodesRefs.find(s) == m_nodesRefs.end());
+
+ m_nodes.append(Node(s));
+ return m_nodesRefs.insert(s, m_nodes.end() - 1);
+}
+
+void DependencyGraph::insertEdge(DependencyGraph::NodeRefSetIt fromIt,
+ DependencyGraph::NodeRefSetIt toIt)
+{
+ NodeListIt nodeFromIt = fromIt.value();
+ NodeListIt nodeToIt = toIt.value();
+
+ createAdjacency(&nodeFromIt->m_out, new AdjacencyNode(nodeToIt));
+ createAdjacency(&nodeToIt->m_in, new AdjacencyNode(nodeFromIt));
+}
+
+void DependencyGraph::deleteAdjacencies(AdjacencyNode *node)
+{
+ while (node) {
+ AdjacencyNode *next = node->m_next;
+ delete node;
+ node = next;
+ }
+}
+
+void DependencyGraph::createAdjacency(AdjacencyNode **node, AdjacencyNode *newNode)
+{
+ if (*node)
+ newNode->m_next = *node;
+ *node = newNode;
+}
diff --git a/src/plugins/clangcodemodel/dependencygraph.h b/src/plugins/clangcodemodel/dependencygraph.h
new file mode 100644
index 0000000000..9dccc37e04
--- /dev/null
+++ b/src/plugins/clangcodemodel/dependencygraph.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef DEPENDENCYGRAPH_H
+#define DEPENDENCYGRAPH_H
+
+#include "includetracker.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QLinkedList>
+#include <QtCore/QHash>
+#include <QtCore/QPair>
+#include <QtCore/QQueue>
+#include <QtCore/QFuture>
+#include <QtCore/QFutureWatcher>
+#include <QtCore/QDebug>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class DependencyGraph : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(DependencyGraph)
+
+public:
+ DependencyGraph();
+ ~DependencyGraph();
+
+ void addFile(const QString &fileName, const QStringList &compilationOptions);
+
+ QFuture<void> compute();
+
+ enum DependencyRole
+ {
+ FilesDirectlyIncludedBy, // Only direct inclusions
+ FilesIncludedBy, // Both direct and indirect inclusions
+ FilesWhichDirectlyInclude, // This one is directly included from...
+ FilesWhichInclude // This one is directly or indirectly included from...
+ };
+
+ /*
+ * You should use this version if you simply want all the dependencies, no matter what.
+ */
+ QStringList collectDependencies(const QString &referenceFile, DependencyRole role) const;
+
+ /*
+ * You should use this version if you might be interested on a particular dependency
+ * and don't want to continue the search once you have found it. In this case you need
+ * supply a visitor. Currently the visitor concept simply requires that a type Visitor_T
+ * models a function that will receive a file string s and indicate whether or not to
+ * continue:
+ *
+ * Visitor_T().acceptFile(s) must be a valid expression.
+ *
+ */
+ template <class Visitor_T>
+ void collectDependencies(const QString &referenceFile,
+ DependencyRole role,
+ Visitor_T *visitor) const;
+
+ bool hasDependency(const QString &referenceFile, DependencyRole role) const;
+
+ void discard();
+
+signals:
+ void dependencyGraphAvailable();
+
+private:
+ QList<QPair<QString, QStringList> > m_files;
+ IncludeTracker m_includeTracker;
+ QFutureWatcher<void> m_computeWatcher;
+
+ void cancel();
+ void computeCore();
+
+ // The dependency graph is represent as an adjacency list. The vertices contains
+ // a list of *out* edges and a list of *in* edges. Each vertex corresponds to a file.
+ // Its out edges correspond to the files which get directly included by this one, while
+ // its in edges correspond to files that directly include this one.
+ //
+ // For better space efficiency, the adjacency nodes doen't explicitly store the file
+ // names themselves, but rather an iterator to the corresponding vertex. In addition,
+ // for speed efficiency we keep track of a hash table that contains iterators to the
+ // actual vertex storage container, which actually contains the strings for the file
+ // names. The vertex container itself is a linked list, it has the semantics we need,
+ // in particular regarding iterator invalidation.
+
+ struct AdjacencyNode;
+ struct Node
+ {
+ Node(const QString &fileName);
+
+ QString m_fileName;
+ AdjacencyNode *m_out;
+ AdjacencyNode *m_in;
+ };
+
+ typedef QLinkedList<Node> NodeList;
+ typedef NodeList::iterator NodeListIt;
+ typedef QHash<QString, NodeListIt> NodeRefSet;
+ typedef NodeRefSet::iterator NodeRefSetIt;
+
+ struct AdjacencyNode
+ {
+ AdjacencyNode(NodeListIt it);
+
+ AdjacencyNode *m_next;
+ NodeListIt m_nodeIt;
+ };
+
+
+ void processIncludes(NodeRefSetIt currentFileIt,
+ const QStringList &compilationOptions);
+
+ template <class Visitor_T>
+ void collectFilesBFS(NodeListIt nodeIt, DependencyRole role, Visitor_T *visitor) const;
+
+
+ // Core graph operations and data
+
+ QPair<bool, NodeRefSetIt> findVertex(const QString &s) const;
+ NodeRefSetIt insertVertex(const QString &s);
+ void insertEdge(NodeRefSetIt fromIt, NodeRefSetIt toIt);
+
+ void deleteAdjacencies(AdjacencyNode *node);
+ void createAdjacency(AdjacencyNode **node, AdjacencyNode *newNode);
+
+ NodeList m_nodes;
+ NodeRefSet m_nodesRefs;
+};
+
+template <class Visitor_T>
+void DependencyGraph::collectDependencies(const QString &referenceFile,
+ DependencyRole role,
+ Visitor_T *visitor) const
+{
+ if (m_computeWatcher.isRunning())
+ return;
+
+ QPair<bool, NodeRefSetIt> v = findVertex(referenceFile);
+ if (!v.first)
+ return;
+
+ NodeListIt nodeIt = v.second.value();
+
+ if (role == FilesDirectlyIncludedBy || role == FilesWhichDirectlyInclude) {
+ AdjacencyNode *adj;
+ if (role == FilesDirectlyIncludedBy)
+ adj = nodeIt->m_out;
+ else
+ adj = nodeIt->m_in;
+
+ for (; adj; adj = adj->m_next) {
+ NodeListIt dependentIt = adj->m_nodeIt;
+ if (visitor->acceptFile(dependentIt->m_fileName))
+ return;
+ }
+ } else {
+ collectFilesBFS(nodeIt, role, visitor);
+ }
+}
+
+template <class Visitor_T>
+void DependencyGraph::collectFilesBFS(NodeListIt nodeIt,
+ DependencyRole role,
+ Visitor_T *visitor) const
+{
+ Q_ASSERT(role == FilesIncludedBy || role == FilesWhichInclude);
+
+ if (m_computeWatcher.isRunning())
+ return;
+
+ QQueue<NodeListIt> q;
+ q.enqueue(nodeIt);
+
+ QSet<QString> visited;
+ visited.insert(nodeIt->m_fileName);
+
+ while (!q.isEmpty()) {
+ NodeListIt currentIt = q.dequeue();
+ AdjacencyNode *adj;
+ if (role == FilesIncludedBy)
+ adj = currentIt->m_out;
+ else
+ adj = currentIt->m_in;
+ while (adj) {
+ NodeListIt adjNodeIt = adj->m_nodeIt;
+ adj = adj->m_next;
+
+ const QString &adjFileName = adjNodeIt->m_fileName;
+ if (visited.contains(adjFileName))
+ continue;
+
+ if (visitor->acceptFile(adjFileName))
+ return;
+
+ visited.insert(adjFileName);
+ q.enqueue(adjNodeIt);
+ }
+ }
+}
+
+
+} // Internal
+} // ClangCodeModel
+
+#endif // DEPENDENCYGRAPH_H
diff --git a/src/plugins/clangcodemodel/diagnostic.cpp b/src/plugins/clangcodemodel/diagnostic.cpp
new file mode 100644
index 0000000000..f0aaef70fc
--- /dev/null
+++ b/src/plugins/clangcodemodel/diagnostic.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "diagnostic.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+
+using namespace ClangCodeModel;
+
+Diagnostic::Diagnostic()
+ : m_severity(Unknown)
+ , m_length(0)
+{}
+
+Diagnostic::Diagnostic(Severity severity, const SourceLocation &location, unsigned length, const QString &spelling)
+ : m_severity(severity)
+ , m_loc(location)
+ , m_length(length)
+ , m_spelling(spelling)
+{}
+
+const QString Diagnostic::severityAsString() const
+{
+ if (m_severity == Unknown)
+ return QString();
+
+ static QStringList strs = QStringList()
+ << QCoreApplication::translate("Diagnostic", "ignored")
+ << QCoreApplication::translate("Diagnostic", "note")
+ << QCoreApplication::translate("Diagnostic", "warning")
+ << QCoreApplication::translate("Diagnostic", "error")
+ << QCoreApplication::translate("Diagnostic", "fatal")
+ ;
+
+ return strs.at(m_severity);
+}
diff --git a/src/plugins/clangcodemodel/diagnostic.h b/src/plugins/clangcodemodel/diagnostic.h
new file mode 100644
index 0000000000..8ecbcb09e7
--- /dev/null
+++ b/src/plugins/clangcodemodel/diagnostic.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_DIAGNOSTIC_H
+#define CLANG_DIAGNOSTIC_H
+
+#include "clang_global.h"
+#include "sourcelocation.h"
+
+#include <QMetaType>
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT Diagnostic
+{
+public:
+ enum Severity {
+ Unknown = -1,
+ Ignored = 0,
+ Note = 1,
+ Warning = 2,
+ Error = 3,
+ Fatal = 4
+ };
+
+public:
+ Diagnostic();
+ Diagnostic(Severity severity, const SourceLocation &location, unsigned length, const QString &spelling);
+
+ Severity severity() const
+ { return m_severity; }
+
+ const QString severityAsString() const;
+
+ const SourceLocation &location() const
+ { return m_loc; }
+
+ unsigned length() const
+ { return m_length; }
+
+ const QString &spelling() const
+ { return m_spelling; }
+
+private:
+ Severity m_severity;
+ SourceLocation m_loc;
+ unsigned m_length;
+ QString m_spelling;
+};
+
+} // namespace ClangCodeModel
+
+Q_DECLARE_METATYPE(ClangCodeModel::Diagnostic)
+Q_DECLARE_METATYPE(QList<ClangCodeModel::Diagnostic>)
+
+#endif // CLANG_DIAGNOSTIC_H
diff --git a/src/plugins/clangcodemodel/fastindexer.cpp b/src/plugins/clangcodemodel/fastindexer.cpp
new file mode 100644
index 0000000000..bb122f3abc
--- /dev/null
+++ b/src/plugins/clangcodemodel/fastindexer.cpp
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "fastindexer.h"
+
+using namespace ClangCodeModel::Internal;
+
+FastIndexer::~FastIndexer()
+{
+}
diff --git a/src/plugins/clangcodemodel/fastindexer.h b/src/plugins/clangcodemodel/fastindexer.h
new file mode 100644
index 0000000000..024d3d7b9e
--- /dev/null
+++ b/src/plugins/clangcodemodel/fastindexer.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef FASTINDEXER_H
+#define FASTINDEXER_H
+
+#include "unit.h"
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class FastIndexer
+{
+public:
+ virtual ~FastIndexer() = 0;
+
+ virtual void indexNow(const Unit &unit) = 0;
+};
+
+} // Internal namespace
+} // ClangCodeModel namespace
+
+#endif // FASTINDEXER_H
diff --git a/src/plugins/clangcodemodel/index.cpp b/src/plugins/clangcodemodel/index.cpp
new file mode 100644
index 0000000000..529ad48066
--- /dev/null
+++ b/src/plugins/clangcodemodel/index.cpp
@@ -0,0 +1,491 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangsymbolsearcher.h"
+#include "index.h"
+
+#include <QStringList>
+#include <QLinkedList>
+#include <QHash>
+#include <QDataStream>
+#include <QPair>
+#include <QFileInfo>
+#include <QMutex>
+#include <QMutexLocker>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class ClangSymbolSearcher;
+
+class IndexPrivate
+{
+public:
+ IndexPrivate();
+
+ void insertSymbol(const Symbol &symbol, const QDateTime &timeStamp);
+ QList<Symbol> symbols(const QString &fileName) const;
+ QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind) const;
+ QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const;
+ QList<Symbol> symbols(const QString &fileName, const QString &uqName) const;
+ QList<Symbol> symbols(Symbol::Kind kind) const;
+
+ void match(ClangSymbolSearcher *searcher) const;
+
+ void insertFile(const QString &fileName, const QDateTime &timeStamp);
+ void removeFile(const QString &fileName);
+ void removeFiles(const QStringList &fileNames);
+ bool containsFile(const QString &fileName) const;
+ QStringList files() const;
+
+ void clear();
+
+ bool isEmpty() const;
+
+ void trackTimeStamp(const Symbol &symbol, const QDateTime &timeStamp);
+ void trackTimeStamp(const QString &fileName, const QDateTime &timeStamp);
+
+ bool validate(const QString &fileName) const;
+
+ QByteArray serialize() const;
+ void deserialize(const QByteArray &data);
+
+private:
+ typedef QLinkedList<Symbol> SymbolCont;
+ typedef SymbolCont::iterator SymbolIt;
+
+ typedef QHash<QString, QList<SymbolIt> > NameIndex;
+ typedef QHash<Symbol::Kind, NameIndex> KindIndex;
+ typedef QHash<QString, KindIndex> FileIndex;
+
+ typedef QList<SymbolIt>::iterator SymbolIndexIt;
+ typedef NameIndex::iterator NameIndexIt;
+ typedef KindIndex::iterator KindIndexIt;
+ typedef FileIndex::iterator FileIndexIt;
+ typedef FileIndex::const_iterator FileIndexCIt;
+
+ void insertSymbol(const Symbol &symbol);
+ void removeSymbol(SymbolIndexIt it);
+
+ QPair<bool, SymbolIndexIt> findEquivalentSymbol(const Symbol &symbol);
+ void updateEquivalentSymbol(SymbolIndexIt it, const Symbol &symbol);
+
+ void createIndexes(SymbolIt it);
+ QList<SymbolIt> removeIndexes(const QString &fileName);
+
+ static QList<Symbol> symbolsFromIterators(const QList<SymbolIt> &symbolList);
+
+ // @TODO: Sharing of compilation options...
+
+ mutable QMutex m_mutex;
+ SymbolCont m_container;
+ FileIndex m_files;
+ QHash<QString, QDateTime> m_timeStamps;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+using namespace ClangCodeModel;
+using namespace Internal;
+
+IndexPrivate::IndexPrivate()
+ : m_mutex(QMutex::Recursive)
+{
+}
+
+void IndexPrivate::createIndexes(SymbolIt it)
+{
+ m_files[it->m_location.fileName()][it->m_kind][it->m_name].append(it);
+}
+
+QList<QLinkedList<Symbol>::iterator> IndexPrivate::removeIndexes(const QString &fileName)
+{
+ QList<SymbolIt> iterators;
+ KindIndex kindIndex = m_files.take(fileName);
+ KindIndexIt it = kindIndex.begin();
+ KindIndexIt eit = kindIndex.end();
+ for (; it != eit; ++it) {
+ NameIndex nameIndex = *it;
+ NameIndexIt nit = nameIndex.begin();
+ NameIndexIt neit = nameIndex.end();
+ for (; nit != neit; ++nit)
+ iterators.append(*nit);
+ }
+ return iterators;
+}
+
+void IndexPrivate::insertSymbol(const Symbol &symbol)
+{
+ QMutexLocker locker(&m_mutex);
+
+ SymbolIt it = m_container.insert(m_container.begin(), symbol);
+ createIndexes(it);
+}
+
+void IndexPrivate::insertSymbol(const Symbol &symbol, const QDateTime &timeStamp)
+{
+ const QPair<bool, SymbolIndexIt> &find = findEquivalentSymbol(symbol);
+ if (find.first)
+ updateEquivalentSymbol(find.second, symbol);
+ else
+ insertSymbol(symbol);
+
+ trackTimeStamp(symbol, timeStamp);
+}
+
+QPair<bool, IndexPrivate::SymbolIndexIt> IndexPrivate::findEquivalentSymbol(const Symbol &symbol)
+{
+ // Despite the loop below finding a symbol should be efficient, since we already filter
+ // the file name, the kind, and the qualified name through the indexing mechanism. In many
+ // cases it will iterate only once.
+ QList<SymbolIt> &byName = m_files[symbol.m_location.fileName()][symbol.m_kind][symbol.m_name];
+ for (SymbolIndexIt it = byName.begin(); it != byName.end(); ++it) {
+ const Symbol &candidateSymbol = *(*it);
+ // @TODO: Overloads, template specializations
+ if (candidateSymbol.m_qualification == symbol.m_qualification)
+ return qMakePair(true, it);
+ }
+
+ return qMakePair(false, QList<SymbolIt>::iterator());
+}
+
+void IndexPrivate::updateEquivalentSymbol(SymbolIndexIt it, const Symbol &symbol)
+{
+ SymbolIt symbolIt = *it;
+
+ Q_ASSERT(symbolIt->m_kind == symbol.m_kind);
+ Q_ASSERT(symbolIt->m_qualification == symbol.m_qualification);
+ Q_ASSERT(symbolIt->m_name == symbol.m_name);
+ Q_ASSERT(symbolIt->m_location.fileName() == symbol.m_location.fileName());
+
+ symbolIt->m_location = symbol.m_location;
+}
+
+void IndexPrivate::removeSymbol(SymbolIndexIt it)
+{
+ SymbolIt symbolIt = *it;
+
+ m_container.erase(symbolIt);
+
+ KindIndex &kindIndex = m_files[symbolIt->m_location.fileName()];
+ NameIndex &nameIndex = kindIndex[symbolIt->m_kind];
+ QList<SymbolIt> &byName = nameIndex[symbolIt->m_name];
+ byName.erase(it);
+ if (byName.isEmpty()) {
+ nameIndex.remove(symbolIt->m_name);
+ if (nameIndex.isEmpty()) {
+ kindIndex.remove(symbolIt->m_kind);
+ if (kindIndex.isEmpty())
+ m_files.remove(symbolIt->m_location.fileName());
+ }
+ }
+}
+
+QList<Symbol> IndexPrivate::symbols(const QString &fileName) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ QList<Symbol> all;
+ const QList<NameIndex> &byKind = m_files.value(fileName).values();
+ foreach (const NameIndex &nameIndex, byKind) {
+ const QList<QList<SymbolIt> > &byName = nameIndex.values();
+ foreach (const QList<SymbolIt> &symbols, byName)
+ all.append(symbolsFromIterators(symbols));
+ }
+ return all;
+}
+
+QList<Symbol> IndexPrivate::symbols(const QString &fileName, Symbol::Kind kind) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ QList<Symbol> all;
+ const QList<QList<SymbolIt> > &byName = m_files.value(fileName).value(kind).values();
+ foreach (const QList<SymbolIt> &symbols, byName)
+ all.append(symbolsFromIterators(symbols));
+ return all;
+}
+
+QList<Symbol> IndexPrivate::symbols(const QString &fileName,
+ Symbol::Kind kind,
+ const QString &uqName) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return symbolsFromIterators(m_files.value(fileName).value(kind).value(uqName));
+}
+
+QList<Symbol> IndexPrivate::symbols(Symbol::Kind kind) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ QList<Symbol> all;
+ FileIndexCIt it = m_files.begin();
+ FileIndexCIt eit = m_files.end();
+ for (; it != eit; ++it)
+ all.append(symbols(it.key(), kind));
+ return all;
+}
+
+void IndexPrivate::match(ClangSymbolSearcher *searcher) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ searcher->search(m_container);
+}
+
+QList<Symbol> IndexPrivate::symbolsFromIterators(const QList<SymbolIt> &symbolList)
+{
+ QList<Symbol> all;
+ foreach (SymbolIt symbolIt, symbolList)
+ all.append(*symbolIt);
+ return all;
+}
+
+void IndexPrivate::trackTimeStamp(const Symbol &symbol, const QDateTime &timeStamp)
+{
+ QMutexLocker locker(&m_mutex);
+
+ trackTimeStamp(symbol.m_location.fileName(), timeStamp);
+}
+
+void IndexPrivate::trackTimeStamp(const QString &fileName, const QDateTime &timeStamp)
+{
+ QMutexLocker locker(&m_mutex);
+
+ // We keep track of time stamps on a per file basis (most recent one).
+ m_timeStamps[fileName] = timeStamp;
+}
+
+bool IndexPrivate::validate(const QString &fileName) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ const QDateTime &timeStamp = m_timeStamps.value(fileName);
+ if (!timeStamp.isValid())
+ return false;
+
+ QFileInfo fileInfo(fileName);
+ if (fileInfo.lastModified() > timeStamp)
+ return false;
+
+ return true;
+}
+
+void IndexPrivate::insertFile(const QString &fileName, const QDateTime &timeStamp)
+{
+ QMutexLocker locker(&m_mutex);
+
+ trackTimeStamp(fileName, timeStamp);
+}
+
+QStringList IndexPrivate::files() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_timeStamps.keys();
+}
+
+bool IndexPrivate::containsFile(const QString &fileName) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_timeStamps.contains(fileName);
+}
+
+void IndexPrivate::removeFile(const QString &fileName)
+{
+ QMutexLocker locker(&m_mutex);
+
+ const QList<SymbolIt> &iterators = removeIndexes(fileName);
+ foreach (SymbolIt it, iterators)
+ m_container.erase(it);
+
+ m_timeStamps.remove(fileName);
+}
+
+void IndexPrivate::removeFiles(const QStringList &fileNames)
+{
+ QMutexLocker locker(&m_mutex);
+
+ foreach (const QString &fileName, fileNames)
+ removeFile(fileName);
+}
+
+void IndexPrivate::clear()
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_container.clear();
+ m_files.clear();
+ m_timeStamps.clear();
+}
+
+bool IndexPrivate::isEmpty() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_timeStamps.isEmpty();
+}
+
+QByteArray IndexPrivate::serialize() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << (quint32)0x0A0BFFEE;
+ stream << (quint16)1;
+ stream.setVersion(QDataStream::Qt_4_7);
+ stream << m_container;
+ stream << m_timeStamps;
+
+ return data;
+}
+
+void IndexPrivate::deserialize(const QByteArray &data)
+{
+ QMutexLocker locker(&m_mutex);
+
+ clear();
+
+ // @TODO: Version compatibility handling.
+
+ QDataStream stream(data);
+
+ quint32 header;
+ stream >> header;
+ if (header != 0x0A0BFFEE)
+ return;
+
+ quint16 indexVersion;
+ stream >> indexVersion;
+ if (indexVersion != 1)
+ return;
+
+ stream.setVersion(QDataStream::Qt_4_7);
+
+ SymbolCont symbols;
+ stream >> symbols;
+ stream >> m_timeStamps;
+
+ // @TODO: Overload the related functions with batch versions.
+ foreach (const Symbol &symbol, symbols)
+ insertSymbol(symbol);
+}
+
+
+Index::Index()
+ : d(new IndexPrivate)
+{}
+
+Index::~Index()
+{}
+
+void Index::insertSymbol(const Symbol &symbol, const QDateTime &timeStamp)
+{
+ d->insertSymbol(symbol, timeStamp);
+}
+
+QList<Symbol> Index::symbols(const QString &fileName) const
+{
+ return d->symbols(fileName);
+}
+
+QList<Symbol> Index::symbols(const QString &fileName, Symbol::Kind kind) const
+{
+ return d->symbols(fileName, kind);
+}
+
+QList<Symbol> Index::symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const
+{
+ return d->symbols(fileName, kind, uqName);
+}
+
+QList<Symbol> Index::symbols(Symbol::Kind kind) const
+{
+ return d->symbols(kind);
+}
+
+void Index::match(ClangSymbolSearcher *searcher) const
+{
+ d->match(searcher);
+}
+
+void Index::insertFile(const QString &fileName, const QDateTime &timeStamp)
+{
+ d->insertFile(fileName, timeStamp);
+}
+
+QStringList Index::files() const
+{
+ return d->files();
+}
+
+bool Index::containsFile(const QString &fileName) const
+{
+ return d->containsFile(fileName);
+}
+
+void Index::removeFile(const QString &fileName)
+{
+ d->removeFile(fileName);
+}
+
+void Index::removeFiles(const QStringList &fileNames)
+{
+ d->removeFiles(fileNames);
+}
+
+void Index::clear()
+{
+ d->clear();
+}
+
+bool Index::isEmpty() const
+{
+ return d->isEmpty();
+}
+
+bool Index::validate(const QString &fileName) const
+{
+ return d->validate(fileName);
+}
+
+QByteArray Index::serialize() const
+{
+ return d->serialize();
+}
+
+void Index::deserialize(const QByteArray &data)
+{
+ d->deserialize(data);
+}
diff --git a/src/plugins/clangcodemodel/index.h b/src/plugins/clangcodemodel/index.h
new file mode 100644
index 0000000000..20d4f5fe00
--- /dev/null
+++ b/src/plugins/clangcodemodel/index.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef INDEX_H
+#define INDEX_H
+
+#include "symbol.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QDateTime>
+#include <QStringList>
+
+namespace ClangCodeModel {
+
+class Symbol;
+
+namespace Internal {
+
+class ClangSymbolSearcher;
+class IndexPrivate;
+
+class Index
+{
+public:
+ Index();
+ ~Index();
+
+ void insertSymbol(const Symbol &symbol, const QDateTime &timeStamp);
+ QList<Symbol> symbols(const QString &fileName) const;
+ QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind) const;
+ QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const;
+ QList<Symbol> symbols(Symbol::Kind kind) const;
+
+ void match(ClangSymbolSearcher *searcher) const;
+
+ void insertFile(const QString &fileName, const QDateTime &timeStamp);
+ void removeFile(const QString &fileName);
+ void removeFiles(const QStringList &fileNames);
+ bool containsFile(const QString &fileName) const;
+ QStringList files() const;
+
+ bool validate(const QString &fileName) const;
+
+ void clear();
+
+ bool isEmpty() const;
+
+ QByteArray serialize() const;
+ void deserialize(const QByteArray &data);
+
+private:
+ QScopedPointer<IndexPrivate> d;
+};
+
+} // Internal
+} // ClangCodeModel
+
+#endif // INDEX_H
diff --git a/src/plugins/clangcodemodel/indexer.cpp b/src/plugins/clangcodemodel/indexer.cpp
new file mode 100644
index 0000000000..6769ec2729
--- /dev/null
+++ b/src/plugins/clangcodemodel/indexer.cpp
@@ -0,0 +1,1286 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "clangutils.h"
+#include "indexer.h"
+#include "index.h"
+#include "cxraii.h"
+#include "sourcelocation.h"
+#include "liveunitsmanager.h"
+#include "utils_p.h"
+#include "clangsymbolsearcher.h"
+#include "pchmanager.h"
+#include "raii/scopedclangoptions.h"
+
+#include <clang-c/Index.h>
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/fileutils.h>
+#include <utils/QtConcurrentTools>
+
+#include <QDebug>
+#include <QVector>
+#include <QHash>
+#include <QSet>
+#include <QFile>
+#include <QFileInfo>
+#include <QFutureWatcher>
+#include <QDir>
+#include <QFuture>
+#include <QTime>
+#include <QRunnable>
+#include <QThreadPool>
+#include <QDateTime>
+#include <QStringBuilder>
+
+#include <cassert>
+
+//#define DEBUG
+//#define DEBUG_DIAGNOSTICS
+
+#ifdef DEBUG
+ #define BEGIN_PROFILE_SCOPE(ID) { ScopepTimer t(ID);
+ #define END_PROFILE_SCOPE }
+#else
+ #define BEGIN_PROFILE_SCOPE(ID)
+ #define END_PROFILE_SCOPE
+#endif
+
+using namespace Utils;
+using namespace ClangCodeModel;
+using namespace Internal;
+
+namespace ClangCodeModel {
+
+// The indexing result, containing the symbols found, reported by the indexer processor.
+struct IndexingResult
+{
+ typedef CppTools::ProjectPart ProjectPart;
+
+ IndexingResult()
+ {}
+
+ IndexingResult(const QVector<Symbol> &symbol,
+ const QSet<QString> &processedFiles,
+ const Unit &unit,
+ const ProjectPart::Ptr &projectPart)
+ : m_symbolsInfo(symbol)
+ , m_processedFiles(processedFiles)
+ , m_unit(unit)
+ , m_projectPart(projectPart)
+ {}
+
+ QVector<Symbol> m_symbolsInfo;
+ QSet<QString> m_processedFiles;
+ Unit m_unit;
+ ProjectPart::Ptr m_projectPart;
+};
+
+class LibClangIndexer;
+
+class IndexerPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef CppTools::ProjectPart ProjectPart;
+
+public:
+ IndexerPrivate(Indexer *indexer);
+ ~IndexerPrivate()
+ { cancel(true); }
+
+ // This enumeration is used to index a vector. So be careful when changing.
+ enum FileType {
+ ImplementationFile = 0,
+ HeaderFile,
+ TotalFileTypes
+ };
+
+ struct FileData
+ {
+ FileData() : m_upToDate(false) {}
+ FileData(const QString &fileName,
+ const ProjectPart::Ptr &projectPart,
+ bool upToDate = false)
+ : m_fileName(fileName)
+ , m_projectPart(projectPart)
+ , m_upToDate(upToDate)
+ , m_managementOptions(CXTranslationUnit_DetailedPreprocessingRecord
+ | CXTranslationUnit_Incomplete)
+ {}
+
+ QString m_fileName;
+ ProjectPart::Ptr m_projectPart;
+ bool m_upToDate;
+ unsigned m_managementOptions;
+ };
+
+ void synchronize(const QVector<IndexingResult> &results);
+ void finished(LibClangIndexer *indexer);
+ bool noIndexersRunning() const;
+
+private:
+ mutable QMutex m_mutex;
+
+ void indexingFinished();
+ void cancelIndexing();
+ int runningIndexerCount() const;
+
+public slots:
+ void dependencyGraphComputed();
+ void restoredSymbolsAnalysed();
+
+public:
+ enum IndexingMode {
+ RelaxedIndexing, // Index symbols from any file.
+ ConstrainedIndexing // Index symbols only from the requested files.
+ };
+
+ void startLoading();
+ void concludeLoading();
+
+ void computeDependencyGraph();
+ void analyzeRestoredSymbols();
+
+ void runQuickIndexing(const Unit &unit, const ProjectPart::Ptr &part);
+ void run();
+ void run(const QStringList &fileNames);
+ void runCore(const QHash<QString, FileData> &headers,
+ const QHash<QString, FileData> &impls,
+ IndexingMode mode);
+ void watchIndexingThreads(QFutureInterface<void> &future);
+ bool isBusy() const;
+ void cancel(bool wait);
+ void reset();
+
+ bool addFile(const QString &fileName,
+ ProjectPart::Ptr projectPart);
+ void addOrUpdateFileData(const QString &fileName,
+ ProjectPart::Ptr projectPart,
+ bool upToDate);
+ QStringList allFiles() const;
+ bool isTrackingFile(const QString &fileName, FileType type) const;
+ static FileType identifyFileType(const QString &fileName);
+ static void populateFileNames(QStringList *all, const QList<FileData> &data);
+ QStringList compilationOptions(const QString &fileName) const;
+
+ bool deserealizeSymbols();
+ void serializeSymbols() const;
+
+ QList<Symbol> symbols(Symbol::Kind kind) const;
+ QList<Symbol> symbols(const QString &fileName, const Symbol::Kind kind) const;
+ void match(ClangSymbolSearcher *searcher) const;
+
+ Indexer *m_q;
+ QVector<QHash<QString, FileData> > m_files;
+ Index m_index;
+ bool m_hasQueuedFullRun;
+ QSet<QString> m_queuedFilesRun;
+ QString m_storagePath;
+ bool m_isLoaded;
+// DependencyGraph m_dependencyGraph;
+ QScopedPointer<QFutureWatcher<void> >m_loadingWatcher;
+ QScopedPointer<QFutureWatcher<void> >m_indexingWatcher;
+ QThreadPool m_indexingPool;
+ QSet<LibClangIndexer *> m_runningIndexers;
+};
+
+} // ClangCodeModel
+
+Q_DECLARE_METATYPE(IndexingResult)
+
+namespace {
+
+struct ScopepTimer
+{
+ ScopepTimer(int id = 0) : m_id(id) { m_t.start(); }
+ ~ScopepTimer() { qDebug() << "\t#Timer" << m_id << ":" << m_t.elapsed() << "ms"; }
+ int m_id;
+ QTime m_t;
+};
+
+} // Anonymous
+
+namespace ClangCodeModel {
+
+class LibClangIndexer: public QRunnable
+{
+protected:
+ typedef CppTools::ProjectPart ProjectPart;
+
+public:
+ LibClangIndexer(IndexerPrivate *indexer)
+ : m_indexer(indexer)
+ , m_isCanceled(false)
+ {}
+
+ virtual ~LibClangIndexer()
+ {}
+
+ void cancel()
+ { m_isCanceled = true; }
+
+protected:
+ void propagateResults(const ProjectPart::Ptr &projectPart)
+ {
+ if (!isCanceled()) {
+ QVector<IndexingResult> indexingResults;
+ indexingResults.reserve(m_allFiles.size());
+
+ foreach (const QString &fn, m_allFiles.keys()) {
+ QVector<ClangCodeModel::Symbol> symbols; unfoldSymbols(symbols, fn);
+ QSet<QString> processedFiles = QSet<QString>::fromList(m_allFiles.keys());
+ Unit unit(fn);
+ IndexingResult indexingResult(symbols, processedFiles, unit, projectPart);
+ indexingResults.append(indexingResult);
+
+ // TODO: includes need to be propagated to the dependency table.
+ }
+ m_indexer->synchronize(indexingResults);
+ }
+
+ qDeleteAll(m_allFiles.values());
+ m_allFiles.clear();
+ qDeleteAll(m_allSymbols);
+ m_allSymbols.clear();
+ }
+
+protected:
+ static inline LibClangIndexer *indexer(CXClientData d)
+ { return static_cast<LibClangIndexer *>(d); }
+
+ static int abortQuery(CXClientData client_data, void *reserved) {
+ Q_UNUSED(reserved);
+
+ return indexer(client_data)->isCanceled();
+ }
+
+ static void diagnostic(CXClientData client_data, CXDiagnosticSet diagSet, void *reserved) {
+ Q_UNUSED(client_data);
+ Q_UNUSED(diagSet);
+ Q_UNUSED(reserved);
+ }
+
+ static CXIdxClientFile enteredMainFile(CXClientData client_data, CXFile file, void *reserved) {
+ Q_UNUSED(client_data);
+ Q_UNUSED(reserved);
+
+ const QString fileName = getQString(clang_getFileName(file));
+// qDebug() << "enteredMainFile:" << fileName;
+ LibClangIndexer *lci = indexer(client_data);
+ File *f = lci->file(fileName);
+ f->setMainFile();
+
+ return f;
+ }
+
+ static CXIdxClientFile includedFile(CXClientData client_data, const CXIdxIncludedFileInfo *info) {
+ Q_UNUSED(client_data);
+
+ File *includingFile = 0;
+ clang_indexLoc_getFileLocation(info->hashLoc, reinterpret_cast<CXIdxClientFile*>(&includingFile), 0, 0, 0, 0);
+
+ const QString fileName = getQString(clang_getFileName(info->file));
+ File *f = indexer(client_data)->file(fileName);
+
+ if (includingFile)
+ includingFile->addInclude(f);
+
+ return f;
+ }
+
+ static CXIdxClientFile importedASTFile(CXClientData client_data, const CXIdxImportedASTFileInfo *info) {
+ const QString fileName = getQString(clang_getFileName(info->file));
+
+// qDebug() << "importedASTFile:" << fileName;
+
+ indexer(client_data)->m_importedASTs.insert(fileName, false);
+
+ return info->file;
+ }
+
+ static CXIdxClientContainer startedTranslationUnit(CXClientData client_data, void *reserved) {
+ Q_UNUSED(client_data);
+ Q_UNUSED(reserved);
+
+// qDebug() << "startedTranslationUnit";
+ return 0;
+ }
+
+ static void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo *info) {
+ LibClangIndexer *lci = indexer(client_data);
+
+ File *includingFile = 0;
+ unsigned line = 0, column = 0, offset = 0;
+ clang_indexLoc_getFileLocation(info->loc, reinterpret_cast<CXIdxClientFile*>(&includingFile), 0, &line, &column, &offset);
+
+ QString kind = getQString(clang_getCursorKindSpelling(info->cursor.kind));
+ QString displayName = getQString(clang_getCursorDisplayName(info->cursor));
+ QString spellingName = getQString(clang_getCursorSpelling(info->cursor));
+// qDebug() << (includingFile ? includingFile->name() : QLatin1String("<UNKNOWN FILE>")) << ":"<<line<<":"<<column<<": display name ="<<displayName<<"spelling name ="<<spellingName<<"of kind"<<kind;
+
+ Symbol *sym = lci->newSymbol(info->cursor.kind, displayName, spellingName, includingFile, line, column, offset);
+
+ // TODO: add to decl container...
+ if (includingFile) // TODO: check why includingFile can be null...
+ includingFile->addSymbol(sym);
+
+ if (const CXIdxContainerInfo *semanticContainer = info->semanticContainer) {
+ if (Symbol *container = static_cast<Symbol *>(clang_index_getClientContainer(semanticContainer))) {
+ sym->semanticContainer = container;
+ container->addSymbol(sym);
+ }
+ }
+
+ // TODO: ObjC containers
+ // TODO: index forward decls too?
+
+ if (info->declAsContainer)
+ clang_index_setClientContainer(info->declAsContainer, sym);
+ }
+
+ static void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo *info) {
+ Q_UNUSED(client_data);
+ Q_UNUSED(info);
+
+ // TODO: well, we do get the info, so why not (optionally?) remember all references?
+ }
+
+protected:
+ struct File;
+ struct Symbol;
+
+ typedef QHash<QString, File *> FilesByName;
+ struct File
+ {
+ File(const QString &fileName)
+ : m_fileName(fileName)
+ {}
+
+ void addInclude(File *f)
+ {
+ assert(f);
+ m_includes.insert(f->name(), f);
+ }
+
+ QList<File *> includes() const
+ { return m_includes.values(); }
+
+ QString name() const
+ { return m_fileName; }
+
+ void setMainFile(bool isMainFile = true)
+ { m_isMainFile = isMainFile; }
+
+ bool isMainFile() const
+ { return m_isMainFile; }
+
+ void addSymbol(Symbol *symbol)
+ {
+ assert(symbol);
+ m_symbols.append(symbol);
+ }
+
+ QVector<Symbol *> symbols() const
+ { return m_symbols; }
+
+ private:
+ QString m_fileName;
+ FilesByName m_includes;
+ bool m_isMainFile;
+ QVector<Symbol *> m_symbols;
+ };
+
+ struct Symbol
+ {
+ Symbol(enum CXCursorKind kind, const QString &displayName, const QString &spellingName, File *file, unsigned line, unsigned column, unsigned offset)
+ : kind(kind)
+ , displayName(displayName)
+ , spellingName(spellingName)
+ , file(file)
+ , line(line)
+ , column(column)
+ , offset(offset)
+ , semanticContainer(0)
+ {}
+
+ QString spellKind() const
+ { return getQString(clang_getCursorKindSpelling(kind)); }
+
+ void addSymbol(Symbol *symbol)
+ { symbols.append(symbol); }
+
+ enum CXCursorKind kind;
+ QString displayName, spellingName;
+ File *file;
+ unsigned line, column, offset;
+ Symbol *semanticContainer;
+ QVector<Symbol *> symbols;
+ };
+
+protected:
+ bool isCanceled() const
+ { return m_isCanceled; }
+
+ void finish()
+ { m_indexer->finished(this); }
+
+ File *file(const QString &fileName)
+ {
+ File *f = m_allFiles[fileName];
+ if (!f) {
+ f = new File(fileName);
+ m_allFiles.insert(fileName, f);
+ }
+ return f;
+ }
+
+ Symbol *newSymbol(enum CXCursorKind kind, const QString &displayName, const QString &spellingName, File *file, unsigned line, unsigned column, unsigned offset)
+ {
+ Symbol *s = new Symbol(kind, displayName, spellingName, file, line, column, offset);
+ m_allSymbols.append(s);
+ return s;
+ }
+
+ void dumpInfo()
+ {
+ qDebug() << "=== indexing info dump ===";
+ qDebug() << "indexed" << m_allFiles.size() << "files. Main files:";
+ foreach (const File *f, m_allFiles) {
+ if (!f->isMainFile())
+ continue;
+ qDebug() << f->name() << ":";
+ foreach (const File *inc, f->includes())
+ qDebug() << " includes" << inc->name();
+ dumpSymbols(f->symbols(), QByteArray(" "));
+ }
+
+ qDebug() << "=== end of dump ===";
+ }
+
+ void dumpSymbols(const QVector<Symbol *> &symbols, const QByteArray &indent)
+ {
+ if (symbols.isEmpty())
+ return;
+
+ qDebug("%scontained symbols:", indent.constData());
+ QByteArray newIndent = indent + " ";
+ foreach (const Symbol *s, symbols) {
+ qDebug("%s%s (%s)", newIndent.constData(), s->spellingName.toUtf8().constData(), s->spellKind().toUtf8().constData());
+ dumpSymbols(s->symbols, newIndent);
+ }
+ }
+
+ void unfoldSymbols(QVector<ClangCodeModel::Symbol> &result, const QString &fileName) {
+ const QVector<Symbol *> symbolsForFile = file(fileName)->symbols();
+ foreach (const Symbol *s, symbolsForFile) {
+ unfoldSymbols(s, result);
+ }
+ }
+
+ void unfoldSymbols(const Symbol *s, QVector<ClangCodeModel::Symbol> &result) {
+ if (!s->file)
+ return;
+
+ ClangCodeModel::Symbol sym;
+ sym.m_name = s->spellingName;
+ sym.m_qualification = s->spellingName;
+
+ static QLatin1String sep("::");
+ for (Symbol *parent = s->semanticContainer; parent; parent = parent->semanticContainer)
+ sym.m_qualification = parent->spellingName + sep + sym.m_qualification;
+
+ sym.m_location = SourceLocation(s->file->name(), s->line, s->column, s->offset);
+
+ switch (s->kind) {
+ case CXCursor_EnumDecl: sym.m_kind = ClangCodeModel::Symbol::Enum; break;
+ case CXCursor_StructDecl:
+ case CXCursor_ClassDecl: sym.m_kind = ClangCodeModel::Symbol::Class; break;
+ case CXCursor_CXXMethod: sym.m_kind = ClangCodeModel::Symbol::Method; break;
+ case CXCursor_FunctionTemplate:
+ case CXCursor_FunctionDecl: sym.m_kind = ClangCodeModel::Symbol::Function; break;
+ case CXCursor_DeclStmt: sym.m_kind = ClangCodeModel::Symbol::Declaration; break;
+ case CXCursor_Constructor: sym.m_kind = ClangCodeModel::Symbol::Constructor; break;
+ case CXCursor_Destructor: sym.m_kind = ClangCodeModel::Symbol::Destructor; break;
+ default: sym.m_kind = ClangCodeModel::Symbol::Unknown; break;
+ }
+
+ result.append(sym);
+ }
+
+protected:
+ static IndexerCallbacks IndexCB;
+
+protected:
+ IndexerPrivate *m_indexer;
+ bool m_isCanceled;
+ QHash<QString, bool> m_importedASTs;
+ FilesByName m_allFiles;
+ QVector<Symbol *> m_allSymbols;
+};
+
+IndexerCallbacks LibClangIndexer::IndexCB = {
+ abortQuery,
+ diagnostic,
+ enteredMainFile,
+ includedFile,
+ importedASTFile,
+ startedTranslationUnit,
+ indexDeclaration,
+ indexEntityReference
+};
+
+class ProjectPartIndexer: public LibClangIndexer
+{
+public:
+ ProjectPartIndexer(IndexerPrivate *indexer, const QList<IndexerPrivate::FileData> &todo)
+ : LibClangIndexer(indexer)
+ , m_todo(todo)
+ {}
+
+ void run()
+ {
+ if (isCanceled() || m_todo.isEmpty()) {
+ finish();
+ return;
+ }
+
+ const ProjectPart::Ptr &pPart = m_todo[0].m_projectPart;
+
+restart:
+ CXIndex idx;
+ if (!(idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
+ /* displayDiagnosics=*/1))) {
+ qDebug() << "Could not create Index";
+ return;
+ }
+
+ CXIndexAction idxAction = clang_IndexAction_create(idx);
+ const unsigned index_opts = CXIndexOpt_SuppressWarnings;
+
+ PCHManager *pchManager = PCHManager::instance();
+ PchInfo::Ptr pchInfo = pchManager->pchInfo(pPart);
+
+ for (int i = 0, ei = m_todo.size(); i < ei; ++i) {
+ const IndexerPrivate::FileData &fd = m_todo.at(i);
+ if (fd.m_upToDate)
+ continue;
+
+ if (pchManager->pchInfo(pPart) != pchInfo) {
+ clang_IndexAction_dispose(idxAction);
+ clang_disposeIndex(idx);
+ goto restart;
+ }
+
+ QStringList opts = ClangCodeModel::Utils::createClangOptions(pPart, fd.m_fileName);
+ if (!pchInfo.isNull())
+ opts.append(Utils::createPCHInclusionOptions(pchInfo->fileName()));
+
+ ScopedClangOptions scopedOpts(opts);
+ QByteArray fileName = fd.m_fileName.toUtf8();
+
+// qDebug() << "Indexing file" << fd.m_fileName << "with options" << opts;
+ unsigned parsingOptions = fd.m_managementOptions;
+ parsingOptions |= CXTranslationUnit_SkipFunctionBodies;
+
+ /*int result =*/ clang_indexSourceFile(idxAction, this,
+ &IndexCB, sizeof(IndexCB),
+ index_opts, fileName.constData(),
+ scopedOpts.data(), scopedOpts.size(), 0, 0, 0,
+ parsingOptions);
+
+ // index imported ASTs:
+ foreach (const QString &astFile, m_importedASTs.keys()) {
+ if (m_importedASTs.value(astFile))
+ continue;
+
+ if (CXTranslationUnit TU = clang_createTranslationUnit(
+ idx, astFile.toUtf8().constData())) {
+ /*result =*/ clang_indexTranslationUnit(idxAction, this,
+ &IndexCB,
+ sizeof(IndexCB),
+ index_opts, TU);
+ clang_disposeTranslationUnit(TU);
+ }
+
+ m_importedASTs[astFile] = true;
+ }
+
+ propagateResults(fd.m_projectPart);
+ if (isCanceled())
+ break;
+ }
+
+// dumpInfo();
+
+ clang_IndexAction_dispose(idxAction);
+ clang_disposeIndex(idx);
+
+ finish();
+ }
+
+private:
+ QList<IndexerPrivate::FileData> m_todo;
+};
+
+class QuickIndexer: public LibClangIndexer
+{
+public:
+ QuickIndexer(IndexerPrivate *indexer, const Unit &unit, const ProjectPart::Ptr &projectPart)
+ : LibClangIndexer(indexer)
+ , m_unit(unit)
+ , m_projectPart(projectPart)
+ {}
+
+ void run()
+ {
+ if (isCanceled() || !m_unit.isLoaded()) {
+ finish();
+ return;
+ }
+
+ CXIndexAction idxAction = clang_IndexAction_create(m_unit.clangIndex());
+ const unsigned index_opts = CXIndexOpt_SuppressWarnings;
+
+// qDebug() << "Indexing TU" << m_unit.fileName() << "...";
+ /*int result =*/ clang_indexTranslationUnit(idxAction, this,
+ &IndexCB, sizeof(IndexCB),
+ index_opts,
+ m_unit.clangTranslationUnit());
+
+ propagateResults(m_projectPart);
+
+ clang_IndexAction_dispose(idxAction);
+ finish();
+ }
+
+private:
+ Unit m_unit;
+ ProjectPart::Ptr m_projectPart;
+};
+
+} // ClangCodeModel
+
+IndexerPrivate::IndexerPrivate(Indexer *indexer)
+ : m_mutex(QMutex::Recursive)
+ , m_q(indexer)
+ , m_files(TotalFileTypes)
+ , m_hasQueuedFullRun(false)
+ , m_isLoaded(false)
+ , m_loadingWatcher(new QFutureWatcher<void>)
+ , m_indexingWatcher(new QFutureWatcher<void>)
+{
+// const int magicThreadCount = QThread::idealThreadCount() * 4 / 3;
+ const int magicThreadCount = QThread::idealThreadCount() - 1;
+ m_indexingPool.setMaxThreadCount(std::max(magicThreadCount, 1));
+ m_indexingPool.setExpiryTimeout(1000);
+}
+
+void IndexerPrivate::runCore(const QHash<QString, FileData> & /*headers*/,
+ const QHash<QString, FileData> &impls,
+ IndexingMode /*mode*/)
+{
+ QMutexLocker locker(&m_mutex);
+
+ typedef QHash<QString, FileData>::const_iterator FileContIt;
+ QHash<ProjectPart::Ptr, QList<IndexerPrivate::FileData> > parts;
+ typedef QHash<ProjectPart::Ptr, QList<IndexerPrivate::FileData> >::Iterator PartIter;
+ LiveUnitsManager *lum = LiveUnitsManager::instance();
+
+ for (FileContIt tit = impls.begin(), eit = impls.end(); tit != eit; ++tit) {
+ if (!tit->m_upToDate && !lum->isTracking(tit.key())) {
+ const IndexerPrivate::FileData &fd = tit.value();
+ parts[fd.m_projectPart].append(fd);
+ }
+ }
+
+ if (parts.isEmpty())
+ return;
+
+ for (PartIter i = parts.begin(), ei = parts.end(); i != ei; ++i) {
+ ProjectPartIndexer *ppi = new ProjectPartIndexer(this, i.value());
+ m_runningIndexers.insert(ppi);
+ m_indexingPool.start(ppi);
+ }
+
+ QFuture<void> task = QtConcurrent::run(&IndexerPrivate::watchIndexingThreads, this);
+ m_indexingWatcher->setFuture(task);
+ emit m_q->indexingStarted(task);
+}
+
+void IndexerPrivate::watchIndexingThreads(QFutureInterface<void> &future)
+{
+ int maxTodo = runningIndexerCount();
+ future.setProgressRange(0, maxTodo);
+
+ int todo = -1;
+ while (todo) {
+ int newTodo = runningIndexerCount();
+ if (todo != newTodo)
+ future.setProgressValue(maxTodo - newTodo);
+ todo = newTodo;
+ if (future.isCanceled()) {
+ cancelIndexing();
+ return;
+ }
+ m_indexingPool.waitForDone(500);
+ }
+}
+
+void IndexerPrivate::run()
+{
+ Q_ASSERT(m_isLoaded);
+
+ QMutexLocker locker(&m_mutex);
+
+ if (m_runningIndexers.isEmpty()) {
+ runCore(m_files.value(HeaderFile),
+ m_files.value(ImplementationFile),
+ RelaxedIndexing);
+ } else {
+ m_hasQueuedFullRun = true;
+ cancelIndexing();
+ }
+}
+
+void IndexerPrivate::run(const QStringList &fileNames)
+{
+ Q_ASSERT(m_isLoaded);
+ QMutexLocker locker(&m_mutex);
+
+ if (noIndexersRunning()) {
+ QVector<QHash<QString, FileData> > files(TotalFileTypes);
+ foreach (const QString &fileName, fileNames) {
+ FileType type = identifyFileType(fileName);
+ if (!isTrackingFile(fileName, type)) {
+ // @TODO
+ continue;
+ }
+
+ FileData *data = &m_files[type][fileName];
+ data->m_upToDate = false;
+ files[type].insert(fileName, *data);
+ m_index.removeFile(fileName);
+ }
+ runCore(files.value(HeaderFile),
+ files.value(ImplementationFile),
+ ConstrainedIndexing);
+ } else {
+ m_queuedFilesRun.unite(fileNames.toSet());
+ }
+}
+
+bool IndexerPrivate::isBusy() const
+{
+ return !noIndexersRunning() || m_loadingWatcher->isRunning();
+}
+
+void IndexerPrivate::cancel(bool wait)
+{
+// m_dependencyGraph.discard();
+
+ m_loadingWatcher->cancel();
+ cancelIndexing();
+ if (wait) {
+ m_loadingWatcher->waitForFinished();
+ m_indexingWatcher->waitForFinished();
+ while (!noIndexersRunning())
+ m_indexingPool.waitForDone(100);
+ }
+}
+
+void IndexerPrivate::reset()
+{
+ cancel(true);
+ serializeSymbols();
+
+ for (int i = 0; i < TotalFileTypes; ++i)
+ m_files[i].clear();
+ m_hasQueuedFullRun = false;
+ m_queuedFilesRun.clear();
+ m_storagePath.clear();
+ m_index.clear();
+ m_isLoaded = false;
+}
+
+void IndexerPrivate::synchronize(const QVector<IndexingResult> &results)
+{
+ foreach (IndexingResult result, results) {
+ QMutexLocker locker(&m_mutex);
+
+ result.m_unit.makeUnique();
+
+ foreach (const Symbol &symbol, result.m_symbolsInfo) {
+ addOrUpdateFileData(symbol.m_location.fileName(),
+ result.m_projectPart,
+ true);
+
+ // Make the symbol available in the database.
+ m_index.insertSymbol(symbol, result.m_unit.timeStamp());
+ }
+
+ // There might be files which were processed but did not "generate" any indexable symbol,
+ // but we still need to make the index aware of them.
+ result.m_processedFiles.insert(result.m_unit.fileName());
+ foreach (const QString &fileName, result.m_processedFiles) {
+ if (!m_index.containsFile(fileName))
+ m_index.insertFile(fileName, result.m_unit.timeStamp());
+ }
+
+ // If this unit is being kept alive, update in the manager.
+ if (LiveUnitsManager::instance()->isTracking(result.m_unit.fileName()))
+ LiveUnitsManager::instance()->updateUnit(result.m_unit.fileName(), result.m_unit);
+ }
+}
+
+void IndexerPrivate::finished(LibClangIndexer *indexer)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_runningIndexers.remove(indexer);
+ if (noIndexersRunning())
+ indexingFinished();
+}
+
+bool IndexerPrivate::noIndexersRunning() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_runningIndexers.isEmpty();
+}
+
+void IndexerPrivate::indexingFinished()
+{
+ if (m_hasQueuedFullRun) {
+ m_hasQueuedFullRun = false;
+ run();
+ } else if (!m_queuedFilesRun.isEmpty()) {
+ const QStringList &files = m_queuedFilesRun.toList();
+ m_queuedFilesRun.clear();
+ run(files);
+ }
+
+ emit m_q->indexingFinished();
+}
+
+void IndexerPrivate::cancelIndexing()
+{
+ QMutexLocker locker(&m_mutex);
+
+ foreach (LibClangIndexer* partIndexer, m_runningIndexers) {
+ partIndexer->cancel();
+ }
+}
+
+int IndexerPrivate::runningIndexerCount() const
+{
+ QMutexLocker locker(&m_mutex);
+ return m_runningIndexers.size();
+}
+
+void IndexerPrivate::addOrUpdateFileData(const QString &fileName,
+ ProjectPart::Ptr projectPart,
+ bool upToDate)
+{
+ Q_ASSERT(QDir::isAbsolutePath(fileName));
+
+ QString cleanFileName(normalizeFileName(fileName));
+
+ FileType fileType = identifyFileType(cleanFileName);
+ if (isTrackingFile(cleanFileName, fileType)) {
+ m_files[fileType][cleanFileName].m_projectPart = projectPart;
+ m_files[fileType][cleanFileName].m_upToDate = upToDate;
+ } else {
+ m_files[fileType].insert(cleanFileName,
+ FileData(cleanFileName, projectPart, upToDate));
+ }
+
+ if (!upToDate)
+ m_index.removeFile(cleanFileName);
+}
+
+bool IndexerPrivate::addFile(const QString &fileName,
+ ProjectPart::Ptr projectPart)
+{
+ if (isBusy()
+ || fileName.trimmed().isEmpty()
+ || !QFileInfo(fileName).isFile())
+ return false;
+
+ addOrUpdateFileData(fileName, projectPart, false);
+
+ return true;
+}
+
+QStringList IndexerPrivate::allFiles() const
+{
+ QStringList all;
+ populateFileNames(&all, m_files.at(ImplementationFile).values());
+ populateFileNames(&all, m_files.at(HeaderFile).values());
+ return all;
+}
+
+bool IndexerPrivate::isTrackingFile(const QString &fileName, FileType type) const
+{
+ return m_files.value(type).contains(normalizeFileName(fileName));
+}
+
+QStringList IndexerPrivate::compilationOptions(const QString &fileName) const
+{
+ FileType type = identifyFileType(fileName);
+ return Utils::createClangOptions(m_files.value(type).value(normalizeFileName(fileName)).m_projectPart);
+}
+
+IndexerPrivate::FileType IndexerPrivate::identifyFileType(const QString &fileName)
+{
+ const QString fn = fileName.toLower();
+ if (fn.endsWith(QLatin1String(".cpp"))
+ || fn.endsWith(QLatin1String(".cxx"))
+ || fn.endsWith(QLatin1String(".cc"))
+ || fn.endsWith(QLatin1String(".c"))
+ || fn.endsWith(QLatin1String(".m"))
+ || fn.endsWith(QLatin1String(".mm"))) {
+ return ImplementationFile;
+ }
+
+ // Everything that is not an implementation file is treated as a header. This makes things
+ // easier when handling standard library files and any other file that does not use
+ // conventional suffixes.
+ return HeaderFile;
+}
+
+void IndexerPrivate::populateFileNames(QStringList *all, const QList<FileData> &data)
+{
+ foreach (const FileData &fileData, data)
+ all->append(fileData.m_fileName);
+}
+
+
+namespace {
+
+struct DepedencyVisitor
+{
+ DepedencyVisitor(IndexerPrivate *indexer) : m_indexer(indexer) {}
+
+ bool acceptFile(const QString &includer)
+ {
+ IndexerPrivate::FileType fileType = IndexerPrivate::identifyFileType(includer);
+ if (m_indexer->isTrackingFile(includer, fileType)) {
+ m_match = m_indexer->m_files.at(fileType).value(includer);
+ return true;
+ }
+ return false;
+ }
+
+ IndexerPrivate *m_indexer;
+ IndexerPrivate::FileData m_match;
+};
+
+} // Anonymous
+
+void IndexerPrivate::startLoading()
+{
+ // In the case of existent persisted symbols, we restore them and make them visible
+ // to the indexer. However, we need a dependency graph in order to identify the proper
+ // options.
+
+ if (deserealizeSymbols() && !m_index.isEmpty())
+ computeDependencyGraph();
+ else
+ concludeLoading();
+}
+
+void IndexerPrivate::concludeLoading()
+{
+ m_isLoaded = true;
+ run();
+}
+
+void IndexerPrivate::computeDependencyGraph()
+{
+ // FIXME
+// for (int fileType = ImplementationFile; fileType < TotalFileTypes; ++fileType) {
+// QHash<QString, FileData>::iterator it = m_files[fileType].begin();
+// for (; it != m_files[fileType].end(); ++it)
+// m_dependencyGraph.addFile(it.value().m_fileName, it.value().m_compilationOptions);
+// }
+
+ m_loadingWatcher.reset(new QFutureWatcher<void>);
+ connect(m_loadingWatcher.data(), SIGNAL(finished()), this, SLOT(dependencyGraphComputed()));
+// m_loadingWatcher->setFuture(m_dependencyGraph.compute());
+}
+
+void IndexerPrivate::dependencyGraphComputed()
+{
+ if (m_loadingWatcher->isCanceled())
+ return;
+
+ m_loadingWatcher.reset(new QFutureWatcher<void>);
+ connect(m_loadingWatcher.data(), SIGNAL(finished()), this, SLOT(restoredSymbolsAnalysed()));
+ m_loadingWatcher->setFuture(QtConcurrent::run(this, &IndexerPrivate::analyzeRestoredSymbols));
+}
+
+void IndexerPrivate::analyzeRestoredSymbols()
+{
+ // @TODO: We only check for time stamps, so we still need to handle somehow the case in
+ // which the project options (for example a .pro file) changed while "outside" a Creator
+ // session.
+
+ foreach (const QString &fileName, m_index.files()) {
+ bool upToDate = m_index.validate(fileName);
+
+ FileType fileType = identifyFileType(fileName);
+ if (isTrackingFile(fileName, fileType)) {
+ // When the file is already being tracked we simply need to update its state.
+ if (upToDate)
+ m_files[fileType][fileName].m_upToDate = true;
+ } else {
+ // If it's not being tracked we need to find at least one tracked dependency
+ // so we can use its options.
+ DepedencyVisitor visitor(this);
+// m_dependencyGraph.collectDependencies(fileName,
+// DependencyGraph::FilesWhichInclude,
+// &visitor);
+ if (!visitor.m_match.m_fileName.isEmpty()) {
+ addOrUpdateFileData(fileName,
+ visitor.m_match.m_projectPart,
+ upToDate);
+ } else {
+ m_index.removeFile(fileName);
+ }
+ }
+
+ if (!upToDate && m_index.containsFile(fileName))
+ m_index.removeFile(fileName);
+ }
+}
+
+void IndexerPrivate::runQuickIndexing(const Unit &unit, const CppTools::ProjectPart::Ptr &part)
+{
+ QMutexLocker locker(&m_mutex);
+
+ addOrUpdateFileData(unit.fileName(), part, false);
+
+ QuickIndexer indexer(this, unit, part);
+ indexer.run();
+}
+
+void IndexerPrivate::restoredSymbolsAnalysed()
+{
+ if (m_loadingWatcher->isCanceled())
+ return;
+
+ concludeLoading();
+}
+
+bool IndexerPrivate::deserealizeSymbols()
+{
+ if (m_storagePath.isEmpty())
+ return false;
+
+ ::Utils::FileReader reader;
+ if (reader.fetch(m_storagePath)) {
+ m_index.deserialize(reader.data());
+ return true;
+ }
+
+ return false;
+}
+
+void IndexerPrivate::serializeSymbols() const
+{
+ if (m_storagePath.isEmpty())
+ return;
+
+ ::Utils::FileSaver saver(m_storagePath);
+ saver.write(m_index.serialize());
+ if (!saver.finalize())
+ qWarning("Failed to serialize index");
+}
+
+QList<Symbol> IndexerPrivate::symbols(Symbol::Kind kind) const
+{
+ if (m_loadingWatcher->isRunning())
+ return QList<Symbol>();
+
+ return m_index.symbols(kind);
+}
+
+QList<Symbol> IndexerPrivate::symbols(const QString &fileName, const Symbol::Kind kind) const
+{
+ if (m_loadingWatcher->isRunning())
+ return QList<Symbol>();
+
+ if (kind == Symbol::Unknown)
+ return m_index.symbols(fileName);
+
+ return m_index.symbols(fileName, kind);
+}
+
+void IndexerPrivate::match(ClangSymbolSearcher *searcher) const
+{
+ if (m_loadingWatcher->isRunning())
+ return;
+
+ m_index.match(searcher);
+}
+
+Indexer::Indexer(QObject *parent)
+ : QObject(parent)
+ , m_d(new IndexerPrivate(this))
+{}
+
+Indexer::~Indexer()
+{}
+
+void Indexer::regenerate()
+{
+ if (!m_d->m_isLoaded) {
+ if (m_d->m_loadingWatcher->isRunning())
+ return;
+ m_d->startLoading();
+ } else {
+ m_d->run();
+ }
+}
+
+void Indexer::initialize(const QString &storagePath)
+{
+ Q_ASSERT(!m_d->m_isLoaded);
+
+ m_d->m_storagePath = storagePath;
+}
+
+void Indexer::evaluateFile(const QString &fileName)
+{
+ if (!m_d->m_isLoaded)
+ return;
+
+ m_d->run(QStringList(normalizeFileName(fileName)));
+}
+
+bool Indexer::isBusy() const
+{
+ return m_d->isBusy();
+}
+
+void Indexer::cancel(bool waitForFinished)
+{
+ return m_d->cancel(waitForFinished);
+}
+
+void Indexer::finalize()
+{
+ m_d->reset();
+}
+
+bool Indexer::addFile(const QString &fileName, ProjectPart::Ptr projectPart)
+{
+ return m_d->addFile(fileName, projectPart);
+}
+
+QStringList Indexer::allFiles() const
+{
+ return m_d->allFiles();
+}
+
+QStringList Indexer::compilationOptions(const QString &fileName) const
+{
+ return m_d->compilationOptions(fileName);
+}
+
+QList<Symbol> Indexer::allFunctions() const
+{
+ return m_d->symbols(Symbol::Function);
+}
+
+QList<Symbol> Indexer::allClasses() const
+{
+ return m_d->symbols(Symbol::Class);
+}
+
+QList<Symbol> Indexer::allMethods() const
+{
+ return m_d->symbols(Symbol::Method);
+}
+
+QList<Symbol> Indexer::allConstructors() const
+{
+ return m_d->symbols(Symbol::Constructor);
+}
+
+QList<Symbol> Indexer::allDestructors() const
+{
+ return m_d->symbols(Symbol::Destructor);
+}
+
+QList<Symbol> Indexer::functionsFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Function);
+}
+
+QList<Symbol> Indexer::classesFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Class);
+}
+
+QList<Symbol> Indexer::methodsFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Method);
+}
+
+QList<Symbol> Indexer::constructorsFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Constructor);
+}
+
+QList<Symbol> Indexer::destructorsFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Destructor);
+}
+
+QList<Symbol> Indexer::allFromFile(const QString &fileName) const
+{
+ return m_d->symbols(fileName, Symbol::Unknown);
+}
+
+void Indexer::match(ClangSymbolSearcher *searcher) const
+{
+ m_d->match(searcher);
+}
+
+void Indexer::runQuickIndexing(const Unit &unit, const CppTools::ProjectPart::Ptr &part)
+{
+ m_d->runQuickIndexing(unit, part);
+}
+
+#include "indexer.moc"
diff --git a/src/plugins/clangcodemodel/indexer.h b/src/plugins/clangcodemodel/indexer.h
new file mode 100644
index 0000000000..fc5079ebfd
--- /dev/null
+++ b/src/plugins/clangcodemodel/indexer.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef INDEXER_H
+#define INDEXER_H
+
+#include "clang_global.h"
+#include "symbol.h"
+#include "unit.h"
+
+#include <cpptools/cppmodelmanagerinterface.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QFuture>
+
+namespace ClangCodeModel {
+
+namespace Internal {
+class ClangSymbolSearcher;
+} // namespace Internal
+
+class IndexerPrivate;
+
+class CLANG_EXPORT Indexer : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef CppTools::ProjectPart ProjectPart;
+
+public:
+ Indexer(QObject *parent = 0);
+ ~Indexer();
+
+ void initialize(const QString &storagePath);
+ void finalize();
+
+ void regenerate();
+ void evaluateFile(const QString &fileName);
+ bool isBusy() const;
+ void cancel(bool waitForFinished);
+
+ bool addFile(const QString &fileName, ProjectPart::Ptr projectPart);
+ QStringList allFiles() const;
+ QStringList compilationOptions(const QString &fileName) const;
+
+ QList<Symbol> allFunctions() const;
+ QList<Symbol> allClasses() const;
+ QList<Symbol> allMethods() const;
+ QList<Symbol> allConstructors() const;
+ QList<Symbol> allDestructors() const;
+ QList<Symbol> functionsFromFile(const QString &fileName) const;
+ QList<Symbol> classesFromFile(const QString &fileName) const;
+ QList<Symbol> methodsFromFile(const QString &fileName) const;
+ QList<Symbol> constructorsFromFile(const QString &fileName) const;
+ QList<Symbol> destructorsFromFile(const QString &fileName) const;
+ QList<Symbol> allFromFile(const QString &fileName) const;
+
+ void match(Internal::ClangSymbolSearcher *searcher) const;
+
+ void runQuickIndexing(const Internal::Unit &unit, const ProjectPart::Ptr &part);
+
+signals:
+ void indexingStarted(QFuture<void> future);
+ void indexingFinished();
+
+private:
+ friend class IndexerPrivate;
+ QScopedPointer<IndexerPrivate> m_d;
+};
+
+} // ClangCodeModel
+
+#endif // INDEXER_H
diff --git a/src/plugins/clangcodemodel/liveunitsmanager.cpp b/src/plugins/clangcodemodel/liveunitsmanager.cpp
new file mode 100644
index 0000000000..5cf653fbb8
--- /dev/null
+++ b/src/plugins/clangcodemodel/liveunitsmanager.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "liveunitsmanager.h"
+
+#include <coreplugin/idocument.h>
+
+using namespace ClangCodeModel;
+using namespace Internal;
+
+LiveUnitsManager *LiveUnitsManager::m_instance = 0;
+
+LiveUnitsManager::LiveUnitsManager()
+{
+ Q_ASSERT(!m_instance);
+ m_instance = this;
+
+ qRegisterMetaType<ClangCodeModel::Internal::Unit>();
+}
+
+LiveUnitsManager::~LiveUnitsManager()
+{
+ m_instance = 0;
+}
+
+void LiveUnitsManager::requestTracking(const QString &fileName)
+{
+ if (!fileName.isEmpty() && !isTracking(fileName))
+ m_units.insert(fileName, Unit(fileName));
+}
+
+void LiveUnitsManager::cancelTrackingRequest(const QString &fileName)
+{
+ if (!isTracking(fileName))
+ return;
+
+ // If no one else is tracking this particular unit, we remove it.
+ if (m_units[fileName].isUnique())
+ m_units.remove(fileName);
+}
+
+void LiveUnitsManager::updateUnit(const QString &fileName, const Unit &unit)
+{
+ if (!isTracking(fileName))
+ return;
+
+ m_units[fileName] = unit;
+
+ emit unitAvailable(unit);
+}
+
+Unit LiveUnitsManager::unit(const QString &fileName)
+{
+ return m_units.value(fileName);
+}
+
+void LiveUnitsManager::editorOpened(Core::IEditor *editor)
+{
+ requestTracking(editor->document()->filePath());
+}
+
+void LiveUnitsManager::editorAboutToClose(Core::IEditor *editor)
+{
+ cancelTrackingRequest(editor->document()->filePath());
+}
diff --git a/src/plugins/clangcodemodel/liveunitsmanager.h b/src/plugins/clangcodemodel/liveunitsmanager.h
new file mode 100644
index 0000000000..ea74bf8044
--- /dev/null
+++ b/src/plugins/clangcodemodel/liveunitsmanager.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LIVEUNITSMANAGER_H
+#define LIVEUNITSMANAGER_H
+
+#include "unit.h"
+
+#include <coreplugin/editormanager/ieditor.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QHash>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class LiveUnitsManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ LiveUnitsManager();
+ ~LiveUnitsManager();
+ static LiveUnitsManager *instance()
+ { return m_instance; }
+
+ void requestTracking(const QString &fileName);
+ bool isTracking(const QString &fileName) const
+ { return m_units.contains(fileName); }
+
+ void cancelTrackingRequest(const QString &fileName);
+
+ void updateUnit(const QString &fileName, const Unit &unit);
+ Unit unit(const QString &fileName);
+
+public slots:
+ void editorOpened(Core::IEditor *editor);
+ void editorAboutToClose(Core::IEditor *editor);
+
+signals:
+ void unitAvailable(const ClangCodeModel::Internal::Unit &unit);
+
+private:
+ static LiveUnitsManager *m_instance;
+ QHash<QString, Unit> m_units;
+};
+
+} // Internal
+} // ClangCodeModel
+
+#endif // LIVEUNITSMANAGER_H
diff --git a/src/plugins/clangcodemodel/pchinfo.cpp b/src/plugins/clangcodemodel/pchinfo.cpp
new file mode 100644
index 0000000000..e4a482a475
--- /dev/null
+++ b/src/plugins/clangcodemodel/pchinfo.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "pchinfo.h"
+
+#include <QDir>
+
+using namespace ClangCodeModel::Internal;
+
+PchInfo::PchInfo()
+{
+}
+
+PchInfo::~PchInfo()
+{
+}
+
+PchInfo::Ptr PchInfo::createEmpty()
+{
+ return Ptr(new PchInfo);
+}
+
+PchInfo::Ptr PchInfo::createWithFileName(const QString &inputFileName,
+ const QStringList &options,
+ bool objcEnabled)
+{
+ Ptr result(new PchInfo);
+ result->m_inputFileName = inputFileName;
+ result->m_options = options;
+ result->m_objcEnabled = objcEnabled;
+
+ // The next 2 lines are just here to generate the file name....
+ result->m_file.open();
+ result->m_file.close();
+ return result;
+}
diff --git a/src/plugins/clangcodemodel/pchinfo.h b/src/plugins/clangcodemodel/pchinfo.h
new file mode 100644
index 0000000000..44a4353720
--- /dev/null
+++ b/src/plugins/clangcodemodel/pchinfo.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef PCHINFO_H
+#define PCHINFO_H
+
+#include <QString>
+#include <QStringList>
+#include <QSharedPointer>
+#include <QTemporaryFile>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class PchInfo
+{
+ PchInfo();
+
+public:
+ typedef QSharedPointer<PchInfo> Ptr;
+
+public:
+ ~PchInfo();
+
+ static Ptr createEmpty();
+ static Ptr createWithFileName(const QString &inputFileName,
+ const QStringList &options, bool objcEnabled);
+
+ /// \return the (temporary) file name for the PCH file.
+ QString fileName() const
+ { return m_file.fileName(); }
+
+ /// \return the input file for the PCH compilation.
+ QString inputFileName() const
+ { return m_inputFileName; }
+
+ /// \return the options used to generate this PCH file.
+ QStringList options() const
+ { return m_options; }
+
+ bool objcWasEnabled() const
+ { return m_objcEnabled; }
+
+private:
+ QString m_inputFileName;
+ QStringList m_options;
+ bool m_objcEnabled;
+ QTemporaryFile m_file;
+};
+
+} // Internal namespace
+} // ClangCodeModel namespace
+
+#endif // PCHINFO_H
diff --git a/src/plugins/clangcodemodel/pchmanager.cpp b/src/plugins/clangcodemodel/pchmanager.cpp
new file mode 100644
index 0000000000..2a551f5c66
--- /dev/null
+++ b/src/plugins/clangcodemodel/pchmanager.cpp
@@ -0,0 +1,433 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "pchmanager.h"
+#include "utils.h"
+#include "clangutils.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
+#include <utils/runextensions.h>
+
+#include <QFile>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace CPlusPlus;
+
+PCHManager *PCHManager::m_instance = 0;
+
+PCHManager::PCHManager(QObject *parent)
+ : QObject(parent)
+{
+ Q_ASSERT(!m_instance);
+ m_instance = this;
+
+ QObject *msgMgr = Core::MessageManager::instance();
+ connect(this, SIGNAL(pchMessage(QString, Core::MessageManager::PrintToOutputPaneFlags)),
+ msgMgr, SLOT(write(QString, Core::MessageManager::PrintToOutputPaneFlags)));
+
+ connect(&m_pchGenerationWatcher, SIGNAL(finished()),
+ this, SLOT(updateActivePCHFiles()));
+}
+
+PCHManager::~PCHManager()
+{
+ Q_ASSERT(m_instance);
+ m_instance = 0;
+ qDeleteAll(m_projectSettings.values());
+ m_projectSettings.clear();
+}
+
+PCHManager *PCHManager::instance()
+{
+ return m_instance;
+}
+
+PchInfo::Ptr PCHManager::pchInfo(const ProjectPart::Ptr &projectPart) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_activePCHFiles[projectPart];
+}
+
+ClangProjectSettings *PCHManager::settingsForProject(ProjectExplorer::Project *project)
+{
+ QMutexLocker locker(&m_mutex);
+
+ ClangProjectSettings *cps = m_projectSettings.value(project);
+ if (!cps) {
+ cps = new ClangProjectSettings(project);
+ m_projectSettings.insert(project, cps);
+ cps->pullSettings();
+ connect(cps, SIGNAL(pchSettingsChanged()),
+ this, SLOT(clangProjectSettingsChanged()));
+ }
+ return cps;
+}
+
+void PCHManager::setPCHInfo(const QList<ProjectPart::Ptr> &projectParts,
+ const PchInfo::Ptr &pchInfo,
+ const QPair<bool, QStringList> &msgs)
+{
+ QMutexLocker locker(&m_mutex);
+
+ foreach (ProjectPart::Ptr pPart, projectParts)
+ m_activePCHFiles[pPart] = pchInfo;
+
+ if (pchInfo) {
+ if (msgs.first) {
+ if (!pchInfo->fileName().isEmpty())
+ emit pchMessage(tr("Successfully generated PCH file \"%1\".").arg(
+ pchInfo->fileName()), Core::MessageManager::Silent);
+ } else {
+ emit pchMessage(tr("Failed to generate PCH file \"%1\".").arg(
+ pchInfo->fileName()), Core::MessageManager::Silent);
+ }
+ if (!msgs.second.isEmpty())
+ emit pchMessage(msgs.second.join(QLatin1String("\n")), Core::MessageManager::Flash);
+ }
+}
+
+void PCHManager::clangProjectSettingsChanged()
+{
+ ClangProjectSettings *cps = qobject_cast<ClangProjectSettings *>(sender());
+ if (!cps)
+ return;
+
+ onProjectPartsUpdated(cps->project());
+}
+
+void PCHManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
+{
+ Q_UNUSED(project);
+
+ // we cannot ask the ModelManager for the parts, because, depending on
+ // the order of signal delivery, it might already have wiped any information
+ // about the project.
+
+ updateActivePCHFiles();
+}
+
+void PCHManager::onProjectPartsUpdated(ProjectExplorer::Project *project)
+{
+ ClangProjectSettings *cps = settingsForProject(project);
+ Q_ASSERT(cps);
+
+ CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance();
+ const QList<ProjectPart::Ptr> projectParts = mmi->projectInfo(
+ cps->project()).projectParts();
+ updatePchInfo(cps, projectParts);
+
+ emit pchInfoUpdated();
+}
+
+void PCHManager::updatePchInfo(ClangProjectSettings *cps,
+ const QList<ProjectPart::Ptr> &projectParts)
+{
+ if (m_pchGenerationWatcher.isRunning()) {
+// m_pchGenerationWatcher.cancel();
+ m_pchGenerationWatcher.waitForFinished();
+ }
+
+ QFuture<void> future = QtConcurrent::run(&PCHManager::doPchInfoUpdate,
+ cps->pchUsage(),
+ cps->customPchFile(),
+ projectParts);
+ m_pchGenerationWatcher.setFuture(future);
+ Core::ProgressManager::addTask(future, tr("Precompiling..."), "Key.Tmp.Precompiling");
+}
+
+namespace {
+
+bool hasObjCFiles(const CppTools::ProjectPart::Ptr &projectPart)
+{
+ foreach (const CppTools::ProjectFile &file, projectPart->files) {
+ switch (file.kind) {
+ case CppTools::ProjectFile::ObjCHeader:
+ case CppTools::ProjectFile::ObjCSource:
+ case CppTools::ProjectFile::ObjCXXHeader:
+ case CppTools::ProjectFile::ObjCXXSource:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool hasCppFiles(const CppTools::ProjectPart::Ptr &projectPart)
+{
+ foreach (const CppTools::ProjectFile &file, projectPart->files) {
+ switch (file.kind) {
+ case CppTools::ProjectFile::CudaSource:
+ case CppTools::ProjectFile::CXXHeader:
+ case CppTools::ProjectFile::CXXSource:
+ case CppTools::ProjectFile::OpenCLSource:
+ case CppTools::ProjectFile::ObjCXXHeader:
+ case CppTools::ProjectFile::ObjCXXSource:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+CppTools::ProjectFile::Kind getPrefixFileKind(bool hasObjectiveC, bool hasCPlusPlus)
+{
+ if (hasObjectiveC && hasCPlusPlus)
+ return CppTools::ProjectFile::ObjCXXHeader;
+ else if (hasObjectiveC)
+ return CppTools::ProjectFile::ObjCHeader;
+ else if (hasCPlusPlus)
+ return CppTools::ProjectFile::CXXHeader;
+ return CppTools::ProjectFile::CHeader;
+}
+
+}
+
+void PCHManager::doPchInfoUpdate(QFutureInterface<void> &future,
+ ClangProjectSettings::PchUsage pchUsage,
+ const QString customPchFile,
+ const QList<ProjectPart::Ptr> projectParts)
+{
+ PCHManager *pchManager = PCHManager::instance();
+
+// qDebug() << "switching to" << pchUsage;
+
+ if (pchUsage == ClangProjectSettings::PchUse_None
+ || (pchUsage == ClangProjectSettings::PchUse_Custom && customPchFile.isEmpty())) {
+ future.setProgressRange(0, 2);
+ Core::MessageManager::write(QLatin1String("updatePchInfo: switching to none"),
+ Core::MessageManager::Silent);
+ PchInfo::Ptr emptyPch = PchInfo::createEmpty();
+ pchManager->setPCHInfo(projectParts, emptyPch, qMakePair(true, QStringList()));
+ future.setProgressValue(1);
+ } else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Fuzzy) {
+ Core::MessageManager::write(
+ QLatin1String("updatePchInfo: switching to build system (fuzzy)"),
+ Core::MessageManager::Silent);
+ QHash<QString, QSet<QString> > includes, frameworks;
+ QHash<QString, QSet<QByteArray> > definesPerPCH;
+ QHash<QString, bool> objc;
+ QHash<QString, bool> cplusplus;
+ QHash<QString, ProjectPart::QtVersion> qtVersions;
+ QHash<QString, ProjectPart::CVersion> cVersions;
+ QHash<QString, ProjectPart::CXXVersion> cxxVersions;
+ QHash<QString, ProjectPart::CXXExtensions> cxxExtensionsMap;
+ QHash<QString, QList<ProjectPart::Ptr> > inputToParts;
+ foreach (const ProjectPart::Ptr &projectPart, projectParts) {
+ if (projectPart->precompiledHeaders.isEmpty())
+ continue;
+ const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
+ if (!QFile(pch).exists())
+ continue;
+ inputToParts[pch].append(projectPart);
+
+ includes[pch].unite(QSet<QString>::fromList(projectPart->includePaths));
+ frameworks[pch].unite(QSet<QString>::fromList(projectPart->frameworkPaths));
+ cVersions[pch] = std::max(cVersions.value(pch, ProjectPart::C89), projectPart->cVersion);
+ cxxVersions[pch] = std::max(cxxVersions.value(pch, ProjectPart::CXX98), projectPart->cxxVersion);
+ cxxExtensionsMap[pch] = cxxExtensionsMap[pch] | projectPart->cxxExtensions;
+
+ if (hasObjCFiles(projectPart))
+ objc[pch] = true;
+ if (hasCppFiles(projectPart))
+ cplusplus[pch] = true;
+
+ QSet<QByteArray> projectDefines = QSet<QByteArray>::fromList(projectPart->toolchainDefines.split('\n'));
+ QMutableSetIterator<QByteArray> iter(projectDefines);
+ while (iter.hasNext()){
+ QByteArray v = iter.next();
+ if (v.startsWith("#define _") || v.isEmpty()) // TODO: see ProjectPart::createClangOptions
+ iter.remove();
+ }
+ projectDefines.unite(QSet<QByteArray>::fromList(projectPart->projectDefines.split('\n')));
+
+ if (definesPerPCH.contains(pch)) {
+ definesPerPCH[pch].intersect(projectDefines);
+ } else {
+ definesPerPCH[pch] = projectDefines;
+ }
+
+ qtVersions[pch] = projectPart->qtVersion;
+ }
+
+ future.setProgressRange(0, definesPerPCH.size() + 1);
+ future.setProgressValue(0);
+
+ foreach (const QString &pch, inputToParts.keys()) {
+ if (future.isCanceled())
+ return;
+ ProjectPart::Ptr projectPart(new ProjectPart);
+ projectPart->qtVersion = qtVersions[pch];
+ projectPart->cVersion = cVersions[pch];
+ projectPart->cxxVersion = cxxVersions[pch];
+ projectPart->cxxExtensions = cxxExtensionsMap[pch];
+ projectPart->includePaths = includes[pch].toList();
+ projectPart->frameworkPaths = frameworks[pch].toList();
+
+ QList<QByteArray> defines = definesPerPCH[pch].toList();
+ if (!defines.isEmpty()) {
+ projectPart->projectDefines = defines[0];
+ for (int i = 1; i < defines.size(); ++i) {
+ projectPart->projectDefines += '\n';
+ projectPart->projectDefines += defines[i];
+ }
+ }
+
+ CppTools::ProjectFile::Kind prefixFileKind =
+ getPrefixFileKind(objc.value(pch, false), cplusplus.value(pch, false));
+
+ QStringList options = Utils::createClangOptions(projectPart, prefixFileKind);
+ projectPart.reset();
+
+ PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, true);
+ QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
+ if (pchInfo.isNull()) {
+
+ pchInfo = PchInfo::createWithFileName(pch, options, objc[pch]);
+ msgs = precompile(pchInfo);
+ }
+ pchManager->setPCHInfo(inputToParts[pch], pchInfo, msgs);
+ future.setProgressValue(future.progressValue() + 1);
+ }
+ } else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Exact) {
+ future.setProgressRange(0, projectParts.size() + 1);
+ future.setProgressValue(0);
+ Core::MessageManager::write(
+ QLatin1String("updatePchInfo: switching to build system (exact)"),
+ Core::MessageManager::Silent);
+ foreach (const ProjectPart::Ptr &projectPart, projectParts) {
+ if (future.isCanceled())
+ return;
+ if (projectPart->precompiledHeaders.isEmpty())
+ continue;
+ const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
+ if (!QFile(pch).exists())
+ continue;
+
+ const bool hasObjC = hasObjCFiles(projectPart);
+ QStringList options = Utils::createClangOptions(
+ projectPart, getPrefixFileKind(hasObjC, hasCppFiles(projectPart)));
+
+ PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, false);
+ QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
+ if (pchInfo.isNull()) {
+ pchInfo = PchInfo::createWithFileName(pch, options, hasObjC);
+ msgs = precompile(pchInfo);
+ }
+ pchManager->setPCHInfo(QList<ProjectPart::Ptr>() << projectPart,
+ pchInfo, msgs);
+ future.setProgressValue(future.progressValue() + 1);
+ }
+ } else if (pchUsage == ClangProjectSettings::PchUse_Custom) {
+ future.setProgressRange(0, 2);
+ future.setProgressValue(0);
+ Core::MessageManager::write(
+ QLatin1String("updatePchInfo: switching to custom") + customPchFile,
+ Core::MessageManager::Silent);
+
+ QSet<QString> includes, frameworks;
+ bool objc = false;
+ bool cplusplus = false;
+ ProjectPart::Ptr united(new ProjectPart());
+ united->cxxVersion = ProjectPart::CXX98;
+ foreach (const ProjectPart::Ptr &projectPart, projectParts) {
+ includes.unite(QSet<QString>::fromList(projectPart->includePaths));
+ frameworks.unite(QSet<QString>::fromList(projectPart->frameworkPaths));
+ united->cVersion = std::max(united->cVersion, projectPart->cVersion);
+ united->cxxVersion = std::max(united->cxxVersion, projectPart->cxxVersion);
+ united->qtVersion = std::max(united->qtVersion, projectPart->qtVersion);
+ objc |= hasObjCFiles(projectPart);
+ cplusplus |= hasCppFiles(projectPart);
+ }
+ united->frameworkPaths = frameworks.toList();
+ united->includePaths = includes.toList();
+ QStringList opts = Utils::createClangOptions(
+ united, getPrefixFileKind(objc, cplusplus));
+ united.clear();
+
+ PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(customPchFile, opts, true);
+ QPair<bool, QStringList> msgs = qMakePair(true, QStringList());;
+ if (future.isCanceled())
+ return;
+ if (pchInfo.isNull()) {
+ pchInfo = PchInfo::createWithFileName(customPchFile, opts, objc);
+ msgs = precompile(pchInfo);
+ }
+ pchManager->setPCHInfo(projectParts, pchInfo, msgs);
+ future.setProgressValue(1);
+ }
+
+ future.setProgressValue(future.progressValue() + 1);
+}
+
+PchInfo::Ptr PCHManager::findMatchingPCH(const QString &inputFileName,
+ const QStringList &options,
+ bool fuzzyMatching) const
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (fuzzyMatching) {
+ QStringList opts = options;
+ opts.sort();
+ foreach (PchInfo::Ptr pchInfo, m_activePCHFiles.values()) {
+ if (pchInfo->inputFileName() != inputFileName)
+ continue;
+ QStringList pchOpts = pchInfo->options();
+ pchOpts.sort();
+ if (pchOpts == opts)
+ return pchInfo;
+ }
+ } else {
+ foreach (PchInfo::Ptr pchInfo, m_activePCHFiles.values())
+ if (pchInfo->inputFileName() == inputFileName
+ && pchInfo->options() == options)
+ return pchInfo;
+ }
+
+ return PchInfo::Ptr();
+}
+
+void PCHManager::updateActivePCHFiles()
+{
+ QMutexLocker locker(&m_mutex);
+
+ QSet<ProjectPart::Ptr> activeParts;
+ CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance();
+ foreach (const CppTools::CppModelManagerInterface::ProjectInfo &pi, mmi->projectInfos())
+ activeParts.unite(QSet<ProjectPart::Ptr>::fromList(pi.projectParts()));
+ QList<ProjectPart::Ptr> partsWithPCHFiles = m_activePCHFiles.keys();
+ foreach (ProjectPart::Ptr pPart, partsWithPCHFiles)
+ if (!activeParts.contains(pPart))
+ m_activePCHFiles.remove(pPart);
+}
diff --git a/src/plugins/clangcodemodel/pchmanager.h b/src/plugins/clangcodemodel/pchmanager.h
new file mode 100644
index 0000000000..638d7147ca
--- /dev/null
+++ b/src/plugins/clangcodemodel/pchmanager.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef PCHMANAGER_H
+#define PCHMANAGER_H
+
+#include "clangprojectsettings.h"
+#include "pchinfo.h"
+
+#include <cpptools/cppmodelmanagerinterface.h>
+#include <projectexplorer/project.h>
+#include <coreplugin/messagemanager.h>
+
+#include <QFutureWatcher>
+#include <QHash>
+#include <QMutex>
+#include <QObject>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class PCHManager: public QObject
+{
+ Q_OBJECT
+
+ typedef CppTools::ProjectPart ProjectPart;
+
+ static PCHManager *m_instance;
+
+public:
+ PCHManager(QObject *parent = 0);
+ virtual ~PCHManager();
+
+ static PCHManager *instance();
+
+ PchInfo::Ptr pchInfo(const ProjectPart::Ptr &projectPart) const;
+ ClangProjectSettings *settingsForProject(ProjectExplorer::Project *project);
+
+signals:
+ void pchInfoUpdated(); // TODO: check if this is used
+ void pchMessage(const QString &message, Core::MessageManager::PrintToOutputPaneFlags flags);
+
+public slots:
+ void clangProjectSettingsChanged();
+ void onAboutToRemoveProject(ProjectExplorer::Project *project);
+ void onProjectPartsUpdated(ProjectExplorer::Project *project);
+
+private slots:
+ void updateActivePCHFiles();
+
+private:
+ void updatePchInfo(ClangProjectSettings *cps,
+ const QList<ProjectPart::Ptr> &projectParts);
+ static void doPchInfoUpdate(QFutureInterface<void> &future,
+ ClangProjectSettings::PchUsage pchUsage,
+ const QString customPchFile,
+ const QList<ProjectPart::Ptr> projectParts);
+ void setPCHInfo(const QList<ProjectPart::Ptr> &projectParts,
+ const PchInfo::Ptr &pchInfo,
+ const QPair<bool, QStringList> &msgs);
+ PchInfo::Ptr findMatchingPCH(const QString &inputFileName, const QStringList &options,
+ bool fuzzyMatching) const;
+
+private:
+ mutable QMutex m_mutex;
+ QHash<ProjectPart::Ptr, PchInfo::Ptr> m_activePCHFiles;
+ QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings;
+ QFutureWatcher<void> m_pchGenerationWatcher;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // PCHMANAGER_H
diff --git a/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp b/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp
new file mode 100644
index 0000000000..24f8b7cefb
--- /dev/null
+++ b/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "scopedclangoptions.h"
+
+namespace ClangCodeModel {
+
+/**
+ * @class ClangCodeModel::ScopedClangOptions
+ * @brief Converts QStringList to raw options, acceptable by clang-c parsing and indexing API
+ */
+
+ScopedClangOptions::ScopedClangOptions(const QStringList &options)
+ : m_size(options.size())
+ , m_rawOptions(new const char*[options.size()])
+{
+ for (int i = 0 ; i < m_size; ++i)
+ m_rawOptions[i] = qstrdup(options[i].toUtf8());
+}
+
+ScopedClangOptions::~ScopedClangOptions()
+{
+ for (int i = 0; i < m_size; ++i)
+ delete[] m_rawOptions[i];
+ delete[] m_rawOptions;
+}
+
+const char **ScopedClangOptions::data() const
+{
+ return m_rawOptions;
+}
+
+int ScopedClangOptions::size() const
+{
+ return m_size;
+}
+
+/**
+ * @class ClangCodeModel::SharedClangOptions
+ * @brief Shared wrapper around \a {ClangCodeModel::ScopedClangOptions} ScopedClangOptions
+ */
+
+SharedClangOptions::SharedClangOptions()
+ : d(0)
+{
+}
+
+SharedClangOptions::SharedClangOptions(const QStringList &options)
+ : d(new ScopedClangOptions(options))
+{
+}
+
+/**
+ * @return Replaces options with new options list
+ */
+void SharedClangOptions::reloadOptions(const QStringList &options)
+{
+ d = QSharedPointer<ScopedClangOptions>(new ScopedClangOptions(options));
+}
+
+/**
+ * @return Pointer to clang raw options or NULL if uninitialized
+ */
+const char **SharedClangOptions::data() const
+{
+ return d ? d->data() : 0;
+}
+
+/**
+ * @return Options count or 0 if uninitialized
+ */
+int SharedClangOptions::size() const
+{
+ return d ? d->size() : 0;
+}
+
+} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/raii/scopedclangoptions.h b/src/plugins/clangcodemodel/raii/scopedclangoptions.h
new file mode 100644
index 0000000000..9a0e2596fb
--- /dev/null
+++ b/src/plugins/clangcodemodel/raii/scopedclangoptions.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H
+#define CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H
+
+#include "../clang_global.h"
+#include <QStringList>
+#include <QSharedPointer>
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT ScopedClangOptions
+{
+public:
+ ScopedClangOptions(const QStringList &options);
+ ~ScopedClangOptions();
+
+ const char **data() const;
+ int size() const;
+
+private:
+ void release();
+
+ int m_size;
+ const char **m_rawOptions;
+};
+
+class CLANG_EXPORT SharedClangOptions
+{
+public:
+ SharedClangOptions();
+ SharedClangOptions(const QStringList &options);
+
+ void reloadOptions(const QStringList &options);
+
+ const char **data() const;
+ int size() const;
+
+private:
+ QSharedPointer<ScopedClangOptions> d;
+};
+
+} // namespace ClangCodeModel
+
+#endif // CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H
diff --git a/src/plugins/clangcodemodel/semanticmarker.cpp b/src/plugins/clangcodemodel/semanticmarker.cpp
new file mode 100644
index 0000000000..34c7acf934
--- /dev/null
+++ b/src/plugins/clangcodemodel/semanticmarker.cpp
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "semanticmarker.h"
+#include "unit.h"
+#include "utils_p.h"
+#include "cxraii.h"
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+static const unsigned ATTACHED_NOTES_LIMIT = 10;
+
+SemanticMarker::SemanticMarker()
+{
+}
+
+SemanticMarker::~SemanticMarker()
+{
+}
+
+QString SemanticMarker::fileName() const
+{
+ if (!m_unit)
+ return QString();
+
+ return m_unit->fileName();
+}
+
+void SemanticMarker::setFileName(const QString &fileName)
+{
+ if (this->fileName() == fileName)
+ return;
+
+ QStringList oldOptions;
+ if (m_unit)
+ oldOptions = m_unit->compilationOptions();
+ m_unit.reset(new Unit(fileName));
+ if (!oldOptions.isEmpty())
+ m_unit->setCompilationOptions(oldOptions);
+
+ unsigned clangOpts = clang_defaultEditingTranslationUnitOptions();
+ clangOpts |= CXTranslationUnit_Incomplete;
+ clangOpts |= CXTranslationUnit_DetailedPreprocessingRecord;
+ clangOpts &= ~CXTranslationUnit_CacheCompletionResults;
+ m_unit->setManagementOptions(clangOpts);
+}
+
+void SemanticMarker::setCompilationOptions(const QStringList &options)
+{
+ Q_ASSERT(m_unit);
+
+ if (m_unit->compilationOptions() == options)
+ return;
+
+ m_unit->setCompilationOptions(options);
+}
+
+void SemanticMarker::reparse(const UnsavedFiles &unsavedFiles)
+{
+ Q_ASSERT(m_unit);
+
+ m_unit->setUnsavedFiles(unsavedFiles);
+ if (m_unit->isLoaded())
+ m_unit->reparse();
+ else
+ m_unit->parse();
+}
+
+/**
+ * \brief Calculate one or several ranges and append diagnostic for each range
+ * Extracted from SemanticMarker::diagnostics() to reuse code
+ */
+static void appendDiagnostic(const CXDiagnostic &diag,
+ const CXSourceLocation &cxLocation,
+ Diagnostic::Severity severity,
+ const QString &spelling,
+ QList<Diagnostic> &diagnostics)
+{
+ const unsigned rangeCount = clang_getDiagnosticNumRanges(diag);
+ bool expandLocation = true;
+
+ for (unsigned i = 0; i < rangeCount; ++i) {
+ CXSourceRange r = clang_getDiagnosticRange(diag, i);
+ const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r));
+ const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r));
+ unsigned length = spellEnd.offset() - spellBegin.offset();
+
+ // File name can be empty due clang bug
+ if (!spellBegin.fileName().isEmpty()) {
+ Diagnostic d(severity, spellBegin, length, spelling);
+ diagnostics.append(d);
+ expandLocation = false;
+ }
+ }
+
+ if (expandLocation) {
+ const SourceLocation &location = Internal::getExpansionLocation(cxLocation);
+ Diagnostic d(severity, location, 0, spelling);
+ diagnostics.append(d);
+ }
+}
+
+QList<Diagnostic> SemanticMarker::diagnostics() const
+{
+ QList<Diagnostic> diagnostics;
+ if (!m_unit || !m_unit->isLoaded())
+ return diagnostics;
+
+ const unsigned diagCount = m_unit->getNumDiagnostics();
+ for (unsigned i = 0; i < diagCount; ++i) {
+ ScopedCXDiagnostic diag(m_unit->getDiagnostic(i));
+
+ Diagnostic::Severity severity = static_cast<Diagnostic::Severity>(clang_getDiagnosticSeverity(diag));
+ if (severity == Diagnostic::Ignored || severity == Diagnostic::Note)
+ continue;
+
+ CXSourceLocation cxLocation = clang_getDiagnosticLocation(diag);
+ QString spelling = Internal::getQString(clang_getDiagnosticSpelling(diag));
+
+ // Attach messages with Diagnostic::Note severity
+ ScopedCXDiagnosticSet cxChildren(clang_getChildDiagnostics(diag));
+ const unsigned numChildren = clang_getNumDiagnosticsInSet(cxChildren);
+ const unsigned size = qMin(ATTACHED_NOTES_LIMIT, numChildren);
+ for (unsigned di = 0; di < size; ++di) {
+ ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di));
+ spelling.append(QLatin1String("\n "));
+ spelling.append(Internal::getQString(clang_getDiagnosticSpelling(child)));
+ }
+
+ // Fatal error may occur in another file, but it breaks whole parsing
+ // Typical fatal error is unresolved #include
+ if (severity == Diagnostic::Fatal) {
+ for (unsigned di = 0; di < numChildren; ++di) {
+ ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di));
+ appendDiagnostic(child, clang_getDiagnosticLocation(child), Diagnostic::Warning, spelling, diagnostics);
+ }
+ }
+
+ appendDiagnostic(diag, cxLocation, severity, spelling, diagnostics);
+ }
+
+ return diagnostics;
+}
+
+QList<TextEditor::BlockRange> SemanticMarker::ifdefedOutBlocks() const
+{
+ QList<TextEditor::BlockRange> blocks;
+
+ if (!m_unit || !m_unit->isLoaded())
+ return blocks;
+
+#if CINDEX_VERSION_MINOR >= 21
+ CXSourceRangeList *skippedRanges = clang_getSkippedRanges(m_unit->clangTranslationUnit(),
+ m_unit->getFile());
+ blocks.reserve(skippedRanges->count);
+ for (unsigned i = 0; i < skippedRanges->count; ++i) {
+ const CXSourceRange &r = skippedRanges->ranges[i];
+ const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r));
+ if (spellBegin.fileName() != fileName())
+ continue;
+ const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r));
+ const int begin = spellBegin.offset() + 1;
+ const int end = spellEnd.offset() - spellEnd.column();
+ blocks.append(TextEditor::BlockRange(begin, end));
+ }
+ clang_disposeSourceRangeList(skippedRanges);
+#endif
+
+ return blocks;
+}
+
+namespace {
+static void add(QList<SourceMarker> &markers,
+ const CXSourceRange &extent,
+ SourceMarker::Kind kind)
+{
+ CXSourceLocation start = clang_getRangeStart(extent);
+ CXSourceLocation end = clang_getRangeEnd(extent);
+ const SourceLocation &location = Internal::getExpansionLocation(start);
+ const SourceLocation &locationEnd = Internal::getExpansionLocation(end);
+
+ if (location.offset() < locationEnd.offset()) {
+ const unsigned length = locationEnd.offset() - location.offset();
+ markers.append(SourceMarker(location, length, kind));
+ }
+}
+
+/**
+ * @brief Selects correct highlighting for cursor that is reference
+ * @return SourceMarker::Unknown if cannot select highlighting
+ */
+static SourceMarker::Kind getKindByReferencedCursor(const CXCursor &cursor)
+{
+ const CXCursor referenced = clang_getCursorReferenced(cursor);
+ switch (clang_getCursorKind(referenced)) {
+ case CXCursor_EnumConstantDecl:
+ return SourceMarker::Enumeration;
+
+ case CXCursor_FieldDecl:
+ case CXCursor_ObjCIvarDecl:
+ case CXCursor_ObjCPropertyDecl:
+ return SourceMarker::Field;
+
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_Constructor:
+ return SourceMarker::Function;
+
+ case CXCursor_VarDecl:
+ case CXCursor_ParmDecl:
+ case CXCursor_NonTypeTemplateParameter:
+ return SourceMarker::Local;
+
+ case CXCursor_CXXMethod:
+ if (clang_CXXMethod_isVirtual(referenced))
+ return SourceMarker::VirtualMethod;
+ else
+ return SourceMarker::Function;
+
+ case CXCursor_ObjCClassMethodDecl:
+ case CXCursor_ObjCInstanceMethodDecl:
+ // calling method as property, e.h. "layer.shouldRasterize = YES"
+ return SourceMarker::Field;
+
+ case CXCursor_UnexposedDecl:
+ // NSObject "self" method which is a pseudo keyword
+ if (clang_getCursorLanguage(referenced) == CXLanguage_ObjC)
+ return SourceMarker::PseudoKeyword;
+ break;
+
+ default:
+ break;
+ }
+ return SourceMarker::Unknown;
+}
+
+static const QSet<QString> ObjcPseudoKeywords = QSet<QString>()
+ << QLatin1String("end")
+ << QLatin1String("try")
+ << QLatin1String("defs")
+ << QLatin1String("throw")
+ << QLatin1String("class")
+ << QLatin1String("catch")
+ << QLatin1String("encode")
+ << QLatin1String("public")
+ << QLatin1String("dynamic")
+ << QLatin1String("finally")
+ << QLatin1String("package")
+ << QLatin1String("private")
+ << QLatin1String("optional")
+ << QLatin1String("property")
+ << QLatin1String("protocol")
+ << QLatin1String("required")
+ << QLatin1String("selector")
+ << QLatin1String("interface")
+ << QLatin1String("protected")
+ << QLatin1String("synthesize")
+ << QLatin1String("not_keyword")
+ << QLatin1String("synchronized")
+ << QLatin1String("implementation")
+ << QLatin1String("compatibility_alias")
+ ;
+
+} // Anonymous namespace
+
+/**
+ * @brief SemanticMarker::sourceMarkersInRange
+ * @param firstLine - first line where to generate highlighting markers
+ * @param lastLine - last line where to generate highlighting markers
+ *
+ * There still two kinds of problems:
+ * - clang_annotateTokens() can return wrong cursor, and it's normal behavior
+ * - some cases no handled
+ *
+ * Problems caused by wrong cursors:
+ * - range-based for from C++ 2011
+ * - identifiers in some compound statements have type DeclStmt
+ * or CompoundStmt which refers to top-level construction.
+ * - CXCursor_ObjCIvarDecl mapped to field, but instance variable have
+ * incorrect cursor kind if it declared in private interface
+ * @interface MyApplication() {
+ * NSArray* _items;
+ * }
+ *
+ * Missed cases:
+ * - global variables highlighted as locals
+ * - appropriate marker had not been selected for listed cursors:
+ * CXCursor_ObjCProtocolExpr, CXCursor_ObjCEncodeExpr,
+ * CXCursor_ObjCDynamicDecl, CXCursor_ObjCBridgedCastExpr,
+ * CXCursor_ObjCSuperClassRef
+ * - template members of template classes&functions always highlighted
+ * as members, even if they are functions - no way to differ found.
+ * - @1, @{}, @[]
+ */
+QList<SourceMarker> SemanticMarker::sourceMarkersInRange(unsigned firstLine,
+ unsigned lastLine)
+{
+ Q_ASSERT(m_unit);
+
+ QList<SourceMarker> result;
+
+ if (!m_unit->isLoaded())
+ return result;
+
+ // Highlighting called asynchronously, and a few lines at the end can be deleted for this time.
+ CXSourceRange unitRange = clang_getCursorExtent(m_unit->getTranslationUnitCursor());
+ SourceLocation unitEnd = getExpansionLocation(clang_getRangeEnd(unitRange));
+ if (lastLine > unitEnd.line())
+ lastLine = unitEnd.line();
+
+ if (firstLine > lastLine)
+ return result;
+
+ IdentifierTokens idTokens(*m_unit, firstLine, lastLine);
+
+ const CXSourceRange *atTokenExtent = 0;
+ for (unsigned i = 0; i < idTokens.count(); ++i) {
+ const CXToken &tok = idTokens.token(i);
+ CXTokenKind kind = clang_getTokenKind(tok);
+ if (atTokenExtent) {
+ if (CXToken_Literal == kind) {
+ if (m_unit->getTokenSpelling(tok).startsWith(QLatin1Char('"')))
+ add(result, *atTokenExtent, SourceMarker::ObjCString);
+ atTokenExtent = 0;
+ continue;
+ } else {
+ add(result, *atTokenExtent, SourceMarker::PseudoKeyword);
+ atTokenExtent = 0;
+ }
+ }
+
+ const CXSourceRange &tokenExtent = idTokens.extent(i);
+
+ if (CXToken_Keyword == kind) {
+ QString spell = m_unit->getTokenSpelling(tok);
+ if (ObjcPseudoKeywords.contains(spell))
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ continue;
+ }
+
+ if (CXToken_Punctuation == kind) {
+ static const QLatin1String at("@");
+ if (m_unit->getTokenSpelling(tok) == at)
+ atTokenExtent = &tokenExtent;
+ continue;
+ }
+
+ if (CXToken_Identifier != kind)
+ continue;
+
+ const CXCursor &cursor = idTokens.cursor(i);
+ const CXCursorKind cursorKind = clang_getCursorKind(cursor);
+ if (clang_isInvalid(cursorKind))
+ continue;
+
+ switch (cursorKind) {
+ case CXCursor_EnumConstantDecl:
+ add(result, tokenExtent, SourceMarker::Enumeration);
+ break;
+
+ case CXCursor_ClassDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ case CXCursor_EnumDecl:
+ case CXCursor_Namespace:
+ case CXCursor_NamespaceRef:
+ case CXCursor_NamespaceAlias:
+ case CXCursor_StructDecl:
+ case CXCursor_TemplateRef:
+ case CXCursor_TypeRef:
+ case CXCursor_TypedefDecl:
+ case CXCursor_Constructor:
+ case CXCursor_TemplateTypeParameter:
+ case CXCursor_TemplateTemplateParameter:
+ case CXCursor_UnexposedDecl: /* friend class MyClass; */
+ add(result, tokenExtent, SourceMarker::Type);
+ break;
+
+ case CXCursor_ParmDecl:
+ case CXCursor_VariableRef:
+ case CXCursor_VarDecl:
+ case CXCursor_NonTypeTemplateParameter:
+ add(result, tokenExtent, SourceMarker::Local);
+ break;
+
+ case CXCursor_MemberRefExpr:
+ case CXCursor_MemberRef:
+ case CXCursor_DeclRefExpr:
+ case CXCursor_CallExpr: {
+ SourceMarker::Kind kind = getKindByReferencedCursor(cursor);
+ if (kind == SourceMarker::Unknown && cursorKind == CXCursor_MemberRefExpr) {
+ /* template class member in template function */
+ kind = SourceMarker::Field;
+ }
+ if (kind != SourceMarker::Unknown)
+ add(result, tokenExtent, kind);
+ } break;
+
+ case CXCursor_FieldDecl:
+ add(result, tokenExtent, SourceMarker::Field);
+ break;
+
+ case CXCursor_Destructor:
+ case CXCursor_CXXMethod: {
+ if (clang_CXXMethod_isVirtual(cursor))
+ add(result, tokenExtent, SourceMarker::VirtualMethod);
+ else
+ add(result, tokenExtent, SourceMarker::Function);
+ } break;
+
+ case CXCursor_CXXOverrideAttr:
+ case CXCursor_CXXFinalAttr:
+ case CXCursor_AnnotateAttr: // 'annotate' in '__attribute__((annotate("AnyComment")))'
+ case CXCursor_UnexposedAttr: // 'align' in '__declspec(align(8))'
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ break;
+
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_OverloadedDeclRef:
+ add(result, tokenExtent, SourceMarker::Function);
+ break;
+
+ case CXCursor_ObjCInstanceMethodDecl:
+ case CXCursor_ObjCClassMethodDecl:
+ case CXCursor_ObjCSelectorExpr:
+ add(result, tokenExtent, SourceMarker::ObjectiveCMessage);
+ break;
+
+ case CXCursor_ObjCMessageExpr: {
+ static const QLatin1String super("super");
+ if (m_unit->getTokenSpelling(tok) == super)
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ else
+ add(result, tokenExtent, SourceMarker::ObjectiveCMessage);
+ } break;
+
+ case CXCursor_ObjCCategoryDecl:
+ case CXCursor_ObjCCategoryImplDecl:
+ case CXCursor_ObjCImplementationDecl:
+ case CXCursor_ObjCInterfaceDecl:
+ case CXCursor_ObjCProtocolDecl:
+ case CXCursor_ObjCProtocolRef:
+ case CXCursor_ObjCClassRef:
+ case CXCursor_ObjCSuperClassRef:
+ case CXCursor_TypeAliasDecl: // C++11 type alias: 'using value_t = T'
+ add(result, tokenExtent, SourceMarker::Type);
+ break;
+
+ case CXCursor_ObjCSynthesizeDecl:
+ case CXCursor_ObjCDynamicDecl:
+ case CXCursor_ObjCPropertyDecl:
+ case CXCursor_ObjCIvarDecl:
+ add(result, tokenExtent, SourceMarker::Field);
+ break;
+
+ case CXCursor_MacroDefinition:
+ case CXCursor_MacroExpansion:
+ add(result, tokenExtent, SourceMarker::Macro);
+ break;
+
+ case CXCursor_LabelRef:
+ case CXCursor_LabelStmt:
+ add(result, tokenExtent, SourceMarker::Label);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+
+Unit SemanticMarker::unit() const
+{
+ return *m_unit;
+}
diff --git a/src/plugins/clangcodemodel/semanticmarker.h b/src/plugins/clangcodemodel/semanticmarker.h
new file mode 100644
index 0000000000..4874bb5b22
--- /dev/null
+++ b/src/plugins/clangcodemodel/semanticmarker.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_SEMANTICMARKER_H
+#define CLANG_SEMANTICMARKER_H
+
+#include "clang_global.h"
+#include "diagnostic.h"
+#include "fastindexer.h"
+#include "sourcemarker.h"
+#include "utils.h"
+
+#include <texteditor/itexteditor.h>
+
+#include <QMutex>
+#include <QScopedPointer>
+#include <QSharedPointer>
+#include <QString>
+#include <QStringList>
+
+namespace ClangCodeModel {
+
+namespace Internal {
+class Unit;
+}
+
+class CLANG_EXPORT SemanticMarker
+{
+ Q_DISABLE_COPY(SemanticMarker)
+
+public:
+ typedef QSharedPointer<SemanticMarker> Ptr;
+
+public:
+ SemanticMarker();
+ ~SemanticMarker();
+
+ QMutex *mutex() const
+ { return &m_mutex; }
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ void setCompilationOptions(const QStringList &options);
+
+ void reparse(const Internal::UnsavedFiles &unsavedFiles);
+
+ QList<Diagnostic> diagnostics() const;
+
+ QList<TextEditor::BlockRange> ifdefedOutBlocks() const;
+
+ QList<SourceMarker> sourceMarkersInRange(unsigned firstLine,
+ unsigned lastLine);
+
+ Internal::Unit unit() const;
+
+private:
+ mutable QMutex m_mutex;
+ QScopedPointer<Internal::Unit> m_unit;
+};
+
+} // namespace ClangCodeModel
+
+#endif // CLANG_SEMANTICMARKER_H
diff --git a/src/plugins/clangcodemodel/sourcelocation.cpp b/src/plugins/clangcodemodel/sourcelocation.cpp
new file mode 100644
index 0000000000..be5ace24bf
--- /dev/null
+++ b/src/plugins/clangcodemodel/sourcelocation.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "sourcelocation.h"
+
+using namespace ClangCodeModel;
+
+SourceLocation::SourceLocation()
+ : m_line(0)
+ , m_column(0)
+ , m_offset(0)
+{}
+
+SourceLocation::SourceLocation(const QString &fileName,
+ unsigned line,
+ unsigned column,
+ unsigned offset)
+ : m_fileName(fileName)
+ , m_line(line)
+ , m_column(column)
+ , m_offset(offset)
+{}
+
+namespace ClangCodeModel {
+
+bool operator==(const SourceLocation &a, const SourceLocation &b)
+{
+ return a.line() == b.line()
+ && a.column() == b.column()
+ && a.offset() == b.offset()
+ && a.fileName() == b.fileName()
+ ;
+}
+
+bool operator!=(const SourceLocation &a, const SourceLocation &b)
+{
+ return !(a == b);
+}
+
+QDebug operator<<(QDebug dbg, const SourceLocation &location)
+{
+ dbg.nospace() << location.fileName()
+ << " ["
+ << location.line()
+ << ":"
+ << location.column()
+ << "("
+ << location.offset()
+ << ")]";
+ return dbg.space();
+}
+
+} // ClangCodeModel
diff --git a/src/plugins/clangcodemodel/sourcelocation.h b/src/plugins/clangcodemodel/sourcelocation.h
new file mode 100644
index 0000000000..757e9e7c2d
--- /dev/null
+++ b/src/plugins/clangcodemodel/sourcelocation.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SOURCELOCATION_H
+#define SOURCELOCATION_H
+
+#include "clang_global.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT SourceLocation
+{
+public:
+ SourceLocation();
+ SourceLocation(const QString &fileName,
+ unsigned line = 0,
+ unsigned column = 0,
+ unsigned offset = 0);
+
+ bool isNull() const { return m_fileName.isEmpty(); }
+ const QString &fileName() const { return m_fileName; }
+ unsigned line() const { return m_line; }
+ unsigned column() const { return m_column; }
+ unsigned offset() const { return m_offset; }
+
+private:
+ QString m_fileName;
+ unsigned m_line;
+ unsigned m_column;
+ unsigned m_offset;
+};
+
+bool operator==(const SourceLocation &a, const SourceLocation &b);
+bool operator!=(const SourceLocation &a, const SourceLocation &b);
+
+QDebug operator<<(QDebug dbg, const SourceLocation &location);
+
+} // ClangCodeModel
+
+#endif // SOURCELOCATION_H
diff --git a/src/plugins/clangcodemodel/sourcemarker.cpp b/src/plugins/clangcodemodel/sourcemarker.cpp
new file mode 100644
index 0000000000..ff412e259d
--- /dev/null
+++ b/src/plugins/clangcodemodel/sourcemarker.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "sourcemarker.h"
+
+using namespace ClangCodeModel;
+
+SourceMarker::SourceMarker()
+ : m_length(0), m_kind(Unknown)
+{}
+
+SourceMarker::SourceMarker(const SourceLocation &location, unsigned length, Kind kind)
+ : m_loc(location), m_length(length), m_kind(kind)
+{
+}
diff --git a/src/plugins/clangcodemodel/sourcemarker.h b/src/plugins/clangcodemodel/sourcemarker.h
new file mode 100644
index 0000000000..ce1e10ecd6
--- /dev/null
+++ b/src/plugins/clangcodemodel/sourcemarker.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_SOURCEMARKER_H
+#define CLANG_SOURCEMARKER_H
+
+#include "clang_global.h"
+#include "sourcelocation.h"
+
+namespace ClangCodeModel {
+
+class CLANG_EXPORT SourceMarker
+{
+public: // TODO: remove this, it's about the same as the TextEditor::SemanticHighlighter::Result
+ enum Kind {
+ Unknown = 0,
+ Type = 1,
+ Local,
+ Field,
+ Enumeration,
+ VirtualMethod,
+ Label,
+ Macro,
+ Function,
+ PseudoKeyword,
+ ObjCString,
+
+ ObjectiveCMessage = VirtualMethod
+ };
+
+ SourceMarker();
+ SourceMarker(const SourceLocation &location,
+ unsigned length,
+ Kind kind);
+
+ bool isValid() const
+ { return m_loc.line() != 0; }
+
+ bool isInvalid() const
+ { return m_loc.line() == 0; }
+
+ const SourceLocation &location() const
+ { return m_loc; }
+
+ unsigned length() const
+ { return m_length; }
+
+ Kind kind() const
+ { return m_kind; }
+
+ bool lessThan(const SourceMarker &other) const
+ {
+ if (m_loc.line() != other.m_loc.line())
+ return m_loc.line() < other.m_loc.line();
+ if (m_loc.column() != other.m_loc.column())
+ return m_loc.column() < other.m_loc.column();
+ return m_length < other.m_length;
+ }
+
+private:
+ SourceLocation m_loc;
+ unsigned m_length;
+ Kind m_kind;
+};
+
+CLANG_EXPORT inline bool operator<(const SourceMarker &one, const SourceMarker &two)
+{ return one.lessThan(two); }
+
+} // namespace Clang
+
+#endif // CLANG_SOURCEMARKER_H
diff --git a/src/plugins/clangcodemodel/symbol.cpp b/src/plugins/clangcodemodel/symbol.cpp
new file mode 100644
index 0000000000..235a49e5ec
--- /dev/null
+++ b/src/plugins/clangcodemodel/symbol.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "symbol.h"
+
+#include <cplusplus/Icons.h>
+
+using namespace ClangCodeModel;
+
+Symbol::Symbol()
+ : m_kind(Unknown)
+{}
+
+Symbol::Symbol(const QString &name,
+ const QString &qualification,
+ Kind type,
+ const SourceLocation &location)
+ : m_name(name)
+ , m_qualification(qualification)
+ , m_location(location)
+ , m_kind(type)
+{}
+
+QIcon Symbol::iconForSymbol() const
+{
+ CPlusPlus::Icons icons;
+ switch (m_kind) {
+ case Enum:
+ return icons.iconForType(CPlusPlus::Icons::EnumIconType);
+ case Class:
+ return icons.iconForType(CPlusPlus::Icons::ClassIconType);
+ case Method:
+ case Function:
+ case Declaration:
+ case Constructor:
+ case Destructor:
+ return icons.iconForType(CPlusPlus::Icons::FuncPublicIconType);
+ default:
+ return icons.iconForType(CPlusPlus::Icons::UnknownIconType);
+ }
+}
+
+namespace ClangCodeModel {
+
+QDataStream &operator<<(QDataStream &stream, const Symbol &symbol)
+{
+ stream << symbol.m_name
+ << symbol.m_qualification
+ << symbol.m_location.fileName()
+ << (quint32)symbol.m_location.line()
+ << (quint16)symbol.m_location.column()
+ << (quint32)symbol.m_location.offset()
+ << (qint8)symbol.m_kind;
+
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, Symbol &symbol)
+{
+ QString fileName;
+ quint32 line;
+ quint16 column;
+ quint32 offset;
+ quint8 kind;
+ stream >> symbol.m_name
+ >> symbol.m_qualification
+ >> fileName
+ >> line
+ >> column
+ >> offset
+ >> kind;
+ symbol.m_location = SourceLocation(fileName, line, column, offset);
+ symbol.m_kind = Symbol::Kind(kind);
+
+ return stream;
+}
+
+bool operator==(const Symbol &a, const Symbol &b)
+{
+ return a.m_name == b.m_name
+ && a.m_qualification == b.m_qualification
+ && a.m_location == b.m_location
+ && a.m_kind == b.m_kind;
+}
+
+bool operator!=(const Symbol &a, const Symbol &b)
+{
+ return !(a == b);
+}
+
+} // ClangCodeModel
diff --git a/src/plugins/clangcodemodel/symbol.h b/src/plugins/clangcodemodel/symbol.h
new file mode 100644
index 0000000000..b25ee4d598
--- /dev/null
+++ b/src/plugins/clangcodemodel/symbol.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef INDEXEDSYMBOLINFO_H
+#define INDEXEDSYMBOLINFO_H
+
+#include "sourcelocation.h"
+
+#include <QString>
+#include <QDataStream>
+#include <QIcon>
+
+namespace ClangCodeModel {
+
+class Symbol
+{
+public:
+ enum Kind {
+ Enum,
+ Class,
+ Method, // A member-function.
+ Function, // A free-function (global or within a namespace).
+ Declaration,
+ Constructor,
+ Destructor,
+ Unknown
+ };
+
+ Symbol();
+ Symbol(const QString &name,
+ const QString &qualification,
+ Kind type,
+ const SourceLocation &location);
+
+ QString m_name;
+ QString m_qualification;
+ SourceLocation m_location;
+ Kind m_kind;
+
+ QIcon iconForSymbol() const;
+};
+
+QDataStream &operator<<(QDataStream &stream, const Symbol &symbol);
+QDataStream &operator>>(QDataStream &stream, Symbol &symbol);
+
+bool operator==(const Symbol &a, const Symbol &b);
+bool operator!=(const Symbol &a, const Symbol &b);
+
+} // Clang
+
+#endif // INDEXEDSYMBOLINFO_H
diff --git a/src/plugins/clangcodemodel/test/clang_tests_database.qrc b/src/plugins/clangcodemodel/test/clang_tests_database.qrc
new file mode 100644
index 0000000000..0014d36087
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/clang_tests_database.qrc
@@ -0,0 +1,20 @@
+<RCC>
+ <qresource prefix="/unittests/ClangCodeModel">
+ <file>cxx_regression_1.cpp</file>
+ <file>cxx_regression_2.cpp</file>
+ <file>cxx_regression_3.cpp</file>
+ <file>cxx_regression_4.cpp</file>
+ <file>cxx_regression_5.cpp</file>
+ <file>cxx_regression_6.cpp</file>
+ <file>cxx_regression_7.cpp</file>
+ <file>cxx_regression_8.cpp</file>
+ <file>cxx_regression_9.cpp</file>
+ <file>cxx_snippets_1.cpp</file>
+ <file>cxx_snippets_2.cpp</file>
+ <file>cxx_snippets_3.cpp</file>
+ <file>cxx_snippets_4.cpp</file>
+ <file>objc_messages_1.mm</file>
+ <file>objc_messages_2.mm</file>
+ <file>objc_messages_3.mm</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/clangcodemodel/test/clangcompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcompletion_test.cpp
new file mode 100644
index 0000000000..0214051705
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/clangcompletion_test.cpp
@@ -0,0 +1,392 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/**
+ * @file clangcompletion_test.cpp
+ * @brief Performs test for C/C++ code completion
+ *
+ * All test cases given as strings with @ character that points to completion
+ * location.
+ */
+
+#ifdef WITH_TESTS
+
+// Disabled because there still no tool to detect system Objective-C headers
+#define ENABLE_OBJC_TESTS 0
+
+#include <QtTest>
+#include <QDebug>
+#undef interface // Canceling "#DEFINE interface struct" on Windows
+
+#include "completiontesthelper.h"
+#include "../clangcodemodelplugin.h"
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+////////////////////////////////////////////////////////////////////////////////
+// Test cases
+
+/**
+ * \defgroup Regression tests
+ *
+ * This group tests possible regressions in non-standard completion chunks
+ * handling: for example, macro arguments and clang's code snippets.
+ *
+ * @{
+ */
+
+void ClangCodeModelPlugin::test_CXX_regressions()
+{
+ QFETCH(QString, file);
+ QFETCH(QStringList, unexpected);
+ QFETCH(QStringList, mustHave);
+
+ CompletionTestHelper helper;
+ helper << file;
+
+ QStringList proposals = helper.codeCompleteTexts();
+
+ foreach (const QString &p, unexpected)
+ QTEST_ASSERT(false == proposals.contains(p));
+
+ foreach (const QString &p, mustHave)
+ QTEST_ASSERT(true == proposals.contains(p));
+}
+
+void ClangCodeModelPlugin::test_CXX_regressions_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QStringList>("unexpected");
+ QTest::addColumn<QStringList>("mustHave");
+
+ QString file;
+ QStringList unexpected;
+ QStringList mustHave;
+
+ file = QLatin1String("cxx_regression_1.cpp");
+ mustHave << QLatin1String("sqr");
+ mustHave << QLatin1String("~Math");
+ unexpected << QLatin1String("operator=");
+ QTest::newRow("case 1: method call completion") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_2.cpp");
+ unexpected << QLatin1String("i_second");
+ unexpected << QLatin1String("c_second");
+ unexpected << QLatin1String("f_second");
+ mustHave << QLatin1String("i_first");
+ mustHave << QLatin1String("c_first");
+ QTest::newRow("case 2: multiple anonymous structs") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_3.cpp");
+ mustHave << QLatin1String("i8");
+ mustHave << QLatin1String("i64");
+ mustHave << QLatin1String("~Priv");
+ unexpected << QLatin1String("operator=");
+ QTest::newRow("case 3: nested class resolution") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_4.cpp");
+ mustHave << QLatin1String("action");
+ QTest::newRow("case 4: local (in function) class resolution") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_5.cpp");
+ mustHave << QLatin1String("doB");
+ unexpected << QLatin1String("doA");
+ QTest::newRow("case 5: nested template class resolution") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_6.cpp");
+ mustHave << QLatin1String("OwningPtr");
+ QTest::newRow("case 6: using particular symbol from namespace") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_7.cpp");
+ mustHave << QLatin1String("dataMember");
+ mustHave << QLatin1String("anotherMember");
+ QTest::newRow("case 7: template class inherited from template parameter") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_8.cpp");
+ mustHave << QLatin1String("utils::");
+ unexpected << QLatin1String("utils");
+ QTest::newRow("case 8: namespace completion in function body") << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+
+ file = QLatin1String("cxx_regression_9.cpp");
+ mustHave << QLatin1String("EnumScoped::Value1");
+ mustHave << QLatin1String("EnumScoped::Value2");
+ mustHave << QLatin1String("EnumScoped::Value3");
+ unexpected << QLatin1String("Value1");
+ unexpected << QLatin1String("EnumScoped");
+ QTest::newRow("case 9: c++11 enum class, value used in switch and 'case' completed")
+ << file << unexpected << mustHave;
+ mustHave.clear();
+ unexpected.clear();
+}
+
+void ClangCodeModelPlugin::test_CXX_snippets()
+{
+ QFETCH(QString, file);
+ QFETCH(QStringList, texts);
+ QFETCH(QStringList, snippets);
+ Q_ASSERT(texts.size() == snippets.size());
+
+ CompletionTestHelper helper;
+ helper << file;
+
+ QList<CodeCompletionResult> proposals = helper.codeComplete();
+
+ for (int i = 0, n = texts.size(); i < n; ++i) {
+ const QString &text = texts[i];
+ const QString &snippet = snippets[i];
+ const QString snippetError =
+ QLatin1String("Text and snippet mismatch: text '") + text
+ + QLatin1String("', snippet '") + snippet
+ + QLatin1String("', got snippet '%1'");
+
+ bool hasText = false;
+ foreach (const CodeCompletionResult &ccr, proposals) {
+ if (ccr.text() != text)
+ continue;
+ hasText = true;
+ QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii());
+ }
+ const QString textError(QLatin1String("Text not found:") + text);
+ QVERIFY2(hasText, textError.toAscii());
+ }
+}
+
+void ClangCodeModelPlugin::test_CXX_snippets_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QStringList>("texts");
+ QTest::addColumn<QStringList>("snippets");
+
+ QString file;
+ QStringList texts;
+ QStringList snippets;
+
+ file = QLatin1String("cxx_snippets_1.cpp");
+ texts << QLatin1String("reinterpret_cast<type>(expression)");
+ snippets << QLatin1String("reinterpret_cast<$type$>($expression$)");
+
+ texts << QLatin1String("static_cast<type>(expression)");
+ snippets << QLatin1String("static_cast<$type$>($expression$)");
+
+ texts << QLatin1String("new type(expressions)");
+ snippets << QLatin1String("new $type$($expressions$)");
+
+ QTest::newRow("case: snippets for var declaration") << file << texts << snippets;
+ texts.clear();
+ snippets.clear();
+
+ file = QLatin1String("cxx_snippets_2.cpp");
+ texts << QLatin1String("private");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("protected");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("public");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("friend");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("virtual");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("typedef type name");
+ snippets << QLatin1String("typedef $type$ $name$");
+
+ QTest::newRow("case: snippets inside class declaration") << file << texts << snippets;
+ texts.clear();
+ snippets.clear();
+
+ file = QLatin1String("cxx_snippets_3.cpp");
+ texts << QLatin1String("List");
+ snippets << QLatin1String("List<$class Item$>");
+
+ texts << QLatin1String("Tuple");
+ snippets << QLatin1String("Tuple<$class First$, $class Second$, $typename Third$>");
+
+ QTest::newRow("case: template class insertion as snippet") << file << texts << snippets;
+ texts.clear();
+ snippets.clear();
+
+ file = QLatin1String("cxx_snippets_4.cpp");
+ texts << QLatin1String("clamp");
+ snippets << QLatin1String("");
+
+ texts << QLatin1String("perform");
+ snippets << QLatin1String("perform<$class T$>");
+
+ QTest::newRow("case: template function insertion as snippet") << file << texts << snippets;
+ texts.clear();
+ snippets.clear();
+}
+
+void ClangCodeModelPlugin::test_ObjC_hints()
+{
+ QFETCH(QString, file);
+ QFETCH(QStringList, texts);
+ QFETCH(QStringList, snippets);
+ QFETCH(QStringList, hints);
+ Q_ASSERT(texts.size() == snippets.size());
+ Q_ASSERT(texts.size() == hints.size());
+
+ CompletionTestHelper helper;
+ helper << file;
+
+ QList<CodeCompletionResult> proposals = helper.codeComplete();
+
+ for (int i = 0, n = texts.size(); i < n; ++i) {
+ const QString &text = texts[i];
+ const QString &snippet = snippets[i];
+ const QString &hint = hints[i];
+ const QString snippetError =
+ QLatin1String("Text and snippet mismatch: text '") + text
+ + QLatin1String("', snippet '") + snippet
+ + QLatin1String("', got snippet '%1'");
+ const QString hintError =
+ QLatin1String("Text and hint mismatch: text '") + text
+ + QLatin1String("', hint\n'") + hint
+ + QLatin1String(", got hint\n'%1'");
+
+ bool hasText = false;
+ QStringList texts;
+ foreach (const CodeCompletionResult &ccr, proposals) {
+ texts << ccr.text();
+ if (ccr.text() != text)
+ continue;
+ hasText = true;
+ QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii());
+ QVERIFY2(hint == ccr.hint(), hintError.arg(ccr.hint()).toAscii());
+ }
+ const QString textError(QString::fromLatin1("Text '%1' not found in set %2")
+ .arg(text).arg(texts.join(QLatin1Char(','))));
+ QVERIFY2(hasText, textError.toAscii());
+ }
+}
+
+static QString makeObjCHint(const char *cHintPattern)
+{
+ QString hintPattern(QString::fromUtf8(cHintPattern));
+ QStringList lines = hintPattern.split(QLatin1Char('\n'));
+ QString hint = QLatin1String("<p>");
+ bool prependNewline = false;
+ foreach (const QString &line, lines) {
+ if (prependNewline)
+ hint += QLatin1String("<br/>");
+ prependNewline = true;
+ int i = 0;
+ while (i < line.size() && line[i] == QLatin1Char(' ')) {
+ ++i;
+ hint += QLatin1String("&nbsp;");
+ }
+ hint += line.mid(i);
+ }
+ hint += QLatin1String("</p>");
+ return hint;
+}
+
+void ClangCodeModelPlugin::test_ObjC_hints_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QStringList>("texts");
+ QTest::addColumn<QStringList>("snippets");
+ QTest::addColumn<QStringList>("hints");
+
+ QString file;
+ QStringList texts;
+ QStringList snippets;
+ QStringList hints;
+
+ file = QLatin1String("objc_messages_1.mm");
+ texts << QLatin1String("spectacleQuality:");
+ snippets << QLatin1String("spectacleQuality:$(bool)$");
+ hints << makeObjCHint("-(int) spectacleQuality:<b>(bool)</b>");
+ texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:");
+ snippets << QLatin1String("desiredAmountForDramaDose:$(int)$ andPersonsCount:$(int)$");
+ hints << makeObjCHint("-(int) desiredAmountForDramaDose:<b>(int)</b> \n"
+ " andPersonsCount:<b>(int)</b>");
+
+ QTest::newRow("case: objective-c instance messages call") << file << texts << snippets << hints;
+ texts.clear();
+ snippets.clear();
+ hints.clear();
+
+ file = QLatin1String("objc_messages_2.mm");
+ texts << QLatin1String("eatenAmount");
+ snippets << QLatin1String("(int) eatenAmount");
+ hints << makeObjCHint("+(int) eatenAmount");
+ texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:");
+ snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count");
+ hints << makeObjCHint("+(int) desiredAmountForDramaDose:(int)dose \n"
+ " andPersonsCount:(int)count");
+
+ QTest::newRow("case: objective-c class messages in @implementation") << file << texts << snippets << hints;
+ texts.clear();
+ snippets.clear();
+ hints.clear();
+
+ file = QLatin1String("objc_messages_3.mm");
+ texts << QLatin1String("eatenAmount");
+ snippets << QLatin1String("(int) eatenAmount");
+ hints << makeObjCHint("-(int) eatenAmount");
+ texts << QLatin1String("spectacleQuality");
+ snippets << QLatin1String("(int) spectacleQuality");
+ hints << makeObjCHint("-(int) spectacleQuality");
+ texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:");
+ snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count");
+ hints << makeObjCHint("-(int) desiredAmountForDramaDose:(int)dose \n"
+ " andPersonsCount:(int)count");
+ texts << QLatin1String("initWithOldTracker:");
+ snippets << QLatin1String("(id) initWithOldTracker:(Bbbb<Aaaa> *)aabb");
+ hints << makeObjCHint("-(id) initWithOldTracker:(Bbbb&lt;Aaaa&gt; *)aabb");
+
+ QTest::newRow("case: objective-c class messages from base class") << file << texts << snippets << hints;
+ texts.clear();
+ snippets.clear();
+ hints.clear();
+}
+
+#endif
diff --git a/src/plugins/clangcodemodel/test/completiontesthelper.cpp b/src/plugins/clangcodemodel/test/completiontesthelper.cpp
new file mode 100644
index 0000000000..f3b4c5c871
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/completiontesthelper.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifdef WITH_TESTS
+
+#include "completiontesthelper.h"
+#include "../clangcompletion.h"
+#include "../clangcompleter.h"
+#include "../clangcodemodelplugin.h"
+
+#include <cpptools/cppcompletionassist.h>
+
+#include <texteditor/basetextdocument.h>
+#include <texteditor/plaintexteditor.h>
+#include <texteditor/codeassist/iassistproposal.h>
+#include <texteditor/codeassist/iassistproposalmodel.h>
+#include <texteditor/codeassist/basicproposalitemlistmodel.h>
+
+#include <utils/fileutils.h>
+#include <utils/changeset.h>
+
+#include <QDir>
+#include <QtTest>
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+using namespace TextEditor;
+using namespace CPlusPlus;
+using namespace CppTools::Internal;
+
+namespace ClangCodeModel {
+namespace Internal {
+
+CompletionTestHelper::CompletionTestHelper(QObject *parent) :
+ QObject(parent),
+ m_completer(new ClangCompleter()),
+ m_position(m_line),
+ m_line(0),
+ m_column(0)
+{
+ m_clangOptions << QLatin1String("-std=c++0x")
+ << QLatin1String("-ObjC++");
+}
+
+CompletionTestHelper::~CompletionTestHelper()
+{
+}
+
+void CompletionTestHelper::operator <<(const QString &fileName)
+{
+ QResource res(QLatin1String(":/unittests/ClangCodeModel/") + fileName);
+ m_sourceCode = QByteArray(reinterpret_cast<const char*>(res.data()), res.size());
+ findCompletionPos();
+
+ QString path = QDir::tempPath() + QLatin1String("/file.h");
+ ::Utils::FileSaver srcSaver(path);
+ srcSaver.write(m_sourceCode);
+ srcSaver.finalize();
+
+ m_completer->setFileName(path);
+ m_completer->setOptions(m_clangOptions);
+}
+
+QStringList CompletionTestHelper::codeCompleteTexts()
+{
+ QList<CodeCompletionResult> results =
+ m_completer->codeCompleteAt(m_line, m_column, m_unsavedFiles);
+
+ QStringList completions;
+ foreach (const CodeCompletionResult& ccr, results)
+ completions << ccr.text();
+ return completions;
+}
+
+QList<CodeCompletionResult> CompletionTestHelper::codeComplete()
+{
+ return m_completer->codeCompleteAt(m_line, m_column, m_unsavedFiles);
+}
+
+int CompletionTestHelper::position() const
+{
+ return m_position;
+}
+
+const QByteArray &CompletionTestHelper::source() const
+{
+ return m_sourceCode;
+}
+
+void CompletionTestHelper::addOption(const QString &option)
+{
+ m_clangOptions << option;
+}
+
+void CompletionTestHelper::findCompletionPos()
+{
+ m_position = m_sourceCode.indexOf("<<<<");
+ QVERIFY(m_position != -1);
+ m_sourceCode[m_position] = ' ';
+ m_sourceCode[m_position + 1] = ' ';
+ m_sourceCode[m_position + 2] = ' ';
+ m_sourceCode[m_position + 3] = ' ';
+
+ // substring from 0 to '@' position
+ QByteArray substr(m_sourceCode.data(), m_position);
+
+ m_line = 1;
+ m_column = 1;
+ for (int i = 0; i < substr.size(); ++i) {
+ if (substr[i] == '\n') {
+ ++m_line;
+ m_column = 1;
+ } else {
+ ++m_column;
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif
diff --git a/src/plugins/clangcodemodel/test/completiontesthelper.h b/src/plugins/clangcodemodel/test/completiontesthelper.h
new file mode 100644
index 0000000000..95418b2c95
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/completiontesthelper.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H
+#define CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H
+
+#ifdef WITH_TESTS
+
+#include <QObject>
+#include <QTextDocument>
+#include <texteditor/basetexteditor.h>
+#include <cplusplus/CppDocument.h>
+#include <clangcompleter.h>
+
+namespace TextEditor { class IAssistProposal; }
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class CompletionTestHelper : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CompletionTestHelper(QObject *parent = 0);
+ ~CompletionTestHelper();
+
+ void operator <<(const QString &fileName);
+ QStringList codeCompleteTexts();
+ QList<CodeCompletionResult> codeComplete();
+
+ int position() const;
+ const QByteArray &source() const;
+
+ void addOption(const QString &option);
+
+private:
+ void findCompletionPos();
+
+ UnsavedFiles m_unsavedFiles;
+ ClangCompleter::Ptr m_completer;
+ QStringList m_clangOptions;
+
+ QByteArray m_sourceCode;
+ int m_position;
+ int m_line;
+ int m_column;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif
+
+#endif // CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_1.cpp b/src/plugins/clangcodemodel/test/cxx_regression_1.cpp
new file mode 100644
index 0000000000..15ae3540e9
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_1.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'sqr'
+ * Not expected: '~Math', 'operator='s
+ */
+
+class Math
+{
+ int sqr(int a);
+};
+
+void foo()
+{
+ Math math;
+ int sqr = math.<<<<;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_2.cpp b/src/plugins/clangcodemodel/test/cxx_regression_2.cpp
new file mode 100644
index 0000000000..e342bde00a
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_2.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'i_first' 'c_first'
+ * Not expected: 'i_second' 'c_second' 'f_second'
+ */
+
+typedef struct {
+ int i_first;
+ char c_first;
+} S1;
+
+typedef struct {
+ int i_second;
+ char c_second;
+ float f_second;
+} S2;
+
+void foo()
+{
+ S1 s;
+ s.<<<<;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_3.cpp b/src/plugins/clangcodemodel/test/cxx_regression_3.cpp
new file mode 100644
index 0000000000..a1fd800998
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_3.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'i8' 'i64'
+ * Unexpected: 'Priv' 'operator='
+ */
+
+class Example
+{
+public:
+ Example();
+ ~Example();
+
+private:
+ class Priv;
+ Priv *d;
+};
+
+class Example::Priv
+{
+public:
+ int i8;
+ int i64;
+
+ Priv() : i8(8), i64(64) {}
+};
+
+Example::Example()
+ : d(new Example::Priv())
+{
+ d-><<<<;
+}
+
+Example::~Example()
+{
+}
+
+void f()
+{
+ Example w;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_4.cpp b/src/plugins/clangcodemodel/test/cxx_regression_4.cpp
new file mode 100644
index 0000000000..ac18460891
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_4.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'action'
+ */
+
+void func()
+{
+ struct impl
+ {
+ static void action() {}
+ };
+ impl::<<<<;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_5.cpp b/src/plugins/clangcodemodel/test/cxx_regression_5.cpp
new file mode 100644
index 0000000000..b5ab3e362f
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_5.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'doB'
+ * Not expected: 'doA'
+ */
+
+struct A {
+ struct Inside {
+ void doA() {}
+ };
+};
+
+struct B {
+ struct Inside {
+ void doB() {}
+ };
+};
+
+template<class T> class C {
+public:
+ typename T::Inside inner;
+};
+
+int main()
+{
+ C<A> ca;
+ C<B> cb;
+ ca.inner.doA();
+ cb.inner.<<<<;
+
+ return 0;
+}
+
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_6.cpp b/src/plugins/clangcodemodel/test/cxx_regression_6.cpp
new file mode 100644
index 0000000000..81f5ce78a5
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_6.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'OwningPtr'
+ */
+
+namespace llvm {
+class OwningPtr;
+}
+
+namespace clang {
+using llvm::OwningPtr;
+}
+
+class llvm::OwningPtr
+{
+};
+
+void foo()
+{
+ clang::<<<< ptr;
+ (void)ptr;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_7.cpp b/src/plugins/clangcodemodel/test/cxx_regression_7.cpp
new file mode 100644
index 0000000000..5e9b9095d5
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_7.cpp
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'dataMember', 'anotherMember'
+ */
+
+class Data {
+ int dataMember;
+};
+
+template <class T> class Other : public T
+{
+ int anotherMember;
+};
+
+void func()
+{
+ Other<Data> c;
+ c.<<<<;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_8.cpp b/src/plugins/clangcodemodel/test/cxx_regression_8.cpp
new file mode 100644
index 0000000000..6305c5c76c
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_8.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'utils::'
+ * Not expected: 'utils'
+ */
+
+namespace utils
+{
+int sqr(int a)
+{
+ return a * a;
+}
+}
+
+void foo()
+{
+ <<<<
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_regression_9.cpp b/src/plugins/clangcodemodel/test/cxx_regression_9.cpp
new file mode 100644
index 0000000000..59a77646ec
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_regression_9.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected: 'EnumScoped::Value1', 'EnumScoped::Value2', 'EnumScoped::Value3'
+ * Unexpected: 'Value1'
+ */
+
+enum class EnumScoped
+{
+ Value1,
+ Value2,
+ Value3
+};
+
+class ClassOwnsEnum
+{
+};
+
+int main()
+{
+ EnumScoped scoped = ;
+ switch (scoped) {
+ default:
+ break;
+ case <<<<
+ }
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp
new file mode 100644
index 0000000000..2d0bfc390b
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ Expected:
+ text 'reinterpret_cast<type>(expression)'
+ snippet 'reinterpret_cast<$type$>($expression$)'
+
+ text 'static_cast<type>(expression)'
+ snippet 'static_cast<$type$>($expression$)'
+
+ text 'new type(expressions)'
+ snippet 'new $type$($expressions$)'
+ */
+
+void foo()
+{
+ int data[] = {
+ 1, 2, 3
+ };
+ char *cdata = <<<<;
+}
diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp
new file mode 100644
index 0000000000..1d4853df94
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ Expected:
+ text 'private',
+ text 'protected',
+ text 'public',
+ text 'friend',
+ text 'virtual'
+
+ text 'typedef type name', snippet 'typedef $type$ $name$'
+ */
+
+class A
+{
+ <<<<
+};
diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp
new file mode 100644
index 0000000000..6258c49c65
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+// Expected:
+// (List, List<$class Item$>),
+// (Tuple, Tuple<$class First$, $class Second$, $typename Third$>)
+
+template <class Item>
+class List
+{
+ Item *data;
+};
+
+template <class First, class Second, typename Third>
+class Tuple
+{
+ First *data;
+ Second *data2;
+ Third *data3;
+};
+
+void check()
+{
+ <<<<
+}
+
diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp
new file mode 100644
index 0000000000..e4bd99fe74
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+// Expected:
+// (clamp, ),
+// (perform, perform<$class T$>)
+// (perform3, perform3<$class T$, $int E$, $class D$>)
+
+// note: clang understands if parameter is redundant
+
+template<class T>
+T clamp(T value, T a = 0.0, T b = 1.0)
+{
+ if (value < a)
+ return a;
+ if (value > b)
+ return b;
+ return value;
+}
+
+template<class T>
+void perform()
+{
+}
+
+template<class T, int E, class D>
+void perform3()
+{
+}
+
+void check()
+{
+ <<<<
+}
diff --git a/src/plugins/clangcodemodel/test/objc_messages_1.mm b/src/plugins/clangcodemodel/test/objc_messages_1.mm
new file mode 100644
index 0000000000..f64f4e985f
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/objc_messages_1.mm
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+/*
+ * Expected texts:
+ * eatenAmount
+ * spectacleQuality:
+ * desiredAmountForDramaDose:andPersonsCount:
+ *
+ * Expected hints:
+ * -(int) eatenAmount
+ *
+ * -(int) spectacleQuality:(bool)unused
+ */
+
+@interface PopCornTracker {
+ int _quality;
+ int _eatenAmount;
+ int _remainedAmount;
+}
++ (int) eatenAmount;
+- (int) spectacleQuality : (bool)unused;
+- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count;
+@end
+
+@implementation PopCornTracker
+- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count
+{
+ [self <<<<];
+}
+@end
diff --git a/src/plugins/clangcodemodel/test/objc_messages_2.mm b/src/plugins/clangcodemodel/test/objc_messages_2.mm
new file mode 100644
index 0000000000..f534e0aa88
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/objc_messages_2.mm
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+@interface PopCornTracker {
+ int _quality;
+ int _eatenAmount;
+ int _remainedAmount;
+}
++ (int) eatenAmount;
+- (int) spectacleQuality;
++ (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count;
+@end
+
+@implementation PopCornTracker
++ <<<<
+@end
diff --git a/src/plugins/clangcodemodel/test/objc_messages_3.mm b/src/plugins/clangcodemodel/test/objc_messages_3.mm
new file mode 100644
index 0000000000..8231b2fe8b
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/objc_messages_3.mm
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+@protocol Aaaa
+@end
+
+@interface Bbbb
+@end
+
+@interface PopCornTracker {
+ int _quality;
+ int _eatenAmount;
+ int _remainedAmount;
+}
+- (int) eatenAmount;
+- (int) spectacleQuality;
+- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count;
++ (id) createNewTracker;
++ (id) createOldTracker:(Bbbb<Aaaa> *) aabb;
+- (id) initWithOldTracker:(Bbbb<Aaaa> *) aabb;
+@end
+
+@interface AdvancedPopCornTracker : PopCornTracker {
+}
+
+- <<<<
+
+@end
diff --git a/src/plugins/clangcodemodel/unit.cpp b/src/plugins/clangcodemodel/unit.cpp
new file mode 100644
index 0000000000..553d1f45ea
--- /dev/null
+++ b/src/plugins/clangcodemodel/unit.cpp
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "unit.h"
+#include "unsavedfiledata.h"
+#include "utils_p.h"
+#include "raii/scopedclangoptions.h"
+
+#include <clang-c/Index.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QVector>
+#include <QtCore/QSharedData>
+#include <QtCore/QDateTime>
+#include <QtAlgorithms>
+
+#ifdef DEBUG_UNIT_COUNT
+# include <QAtomicInt>
+# include <QDebug>
+static QBasicAtomicInt unitDataCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+#endif // DEBUG_UNIT_COUNT
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class UnitData : public QSharedData
+{
+public:
+ UnitData();
+ UnitData(const QString &fileName);
+ ~UnitData();
+
+ void swap(UnitData *unitData);
+
+ void unload();
+ bool isLoaded() const;
+
+ void updateTimeStamp();
+
+ CXIndex m_index;
+ CXTranslationUnit m_tu;
+ QByteArray m_fileName;
+ QStringList m_compOptions;
+ SharedClangOptions m_sharedCompOptions;
+ unsigned m_managementOptions;
+ UnsavedFiles m_unsaved;
+ QDateTime m_timeStamp;
+};
+
+} // Internal
+} // Clang
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+UnitData::UnitData()
+ : m_index(0)
+ , m_tu(0)
+ , m_managementOptions(0)
+{
+}
+
+static const int DisplayDiagnostics = qgetenv("QTC_CLANG_VERBOSE").isEmpty() ? 0 : 1;
+
+UnitData::UnitData(const QString &fileName)
+ : m_index(clang_createIndex(/*excludeDeclsFromPCH*/ 1, DisplayDiagnostics))
+ , m_tu(0)
+ , m_fileName(fileName.toUtf8())
+ , m_managementOptions(0)
+{
+}
+
+UnitData::~UnitData()
+{
+ unload();
+ clang_disposeIndex(m_index);
+ m_index = 0;
+}
+
+void UnitData::swap(UnitData *other)
+{
+ qSwap(m_index, other->m_index);
+ qSwap(m_tu, other->m_tu);
+ qSwap(m_fileName, other->m_fileName);
+ qSwap(m_compOptions, other->m_compOptions);
+ qSwap(m_sharedCompOptions, other->m_sharedCompOptions);
+ qSwap(m_managementOptions, other->m_managementOptions);
+ qSwap(m_unsaved, other->m_unsaved);
+ qSwap(m_timeStamp, other->m_timeStamp);
+}
+
+void UnitData::unload()
+{
+ if (m_tu) {
+ clang_disposeTranslationUnit(m_tu);
+ m_tu = 0;
+
+#ifdef DEBUG_UNIT_COUNT
+ qDebug() << "# translation units:" << (unitDataCount.fetchAndAddOrdered(-1) - 1);
+#endif // DEBUG_UNIT_COUNT
+ }
+}
+
+bool UnitData::isLoaded() const
+{
+ return m_tu && m_index;
+}
+
+void UnitData::updateTimeStamp()
+{
+ m_timeStamp = QDateTime::currentDateTime();
+}
+
+Unit::Unit()
+ : m_data(new UnitData)
+{}
+
+Unit::Unit(const QString &fileName)
+ : m_data(new UnitData(fileName))
+{}
+
+Unit::Unit(const Unit &unit)
+ : m_data(unit.m_data)
+{}
+
+Unit &Unit::operator =(const Unit &unit)
+{
+ if (this != &unit)
+ m_data = unit.m_data;
+ return *this;
+}
+
+Unit::~Unit()
+{}
+
+const QString Unit::fileName() const
+{
+ const QByteArray &name = m_data->m_fileName;
+ return QString::fromUtf8(name.data(), name.size());
+}
+
+bool Unit::isLoaded() const
+{
+ return m_data->isLoaded();
+}
+
+const QDateTime &Unit::timeStamp() const
+{
+ return m_data->m_timeStamp;
+}
+
+QStringList Unit::compilationOptions() const
+{
+ return m_data->m_compOptions;
+}
+
+void Unit::setCompilationOptions(const QStringList &compOptions)
+{
+ m_data->m_compOptions = compOptions;
+ m_data->m_sharedCompOptions.reloadOptions(compOptions);
+}
+
+UnsavedFiles Unit::unsavedFiles() const
+{
+ return m_data->m_unsaved;
+}
+
+void Unit::setUnsavedFiles(const UnsavedFiles &unsavedFiles)
+{
+ m_data->m_unsaved = unsavedFiles;
+}
+
+unsigned Unit::managementOptions() const
+{
+ return m_data->m_managementOptions;
+}
+
+void Unit::setManagementOptions(unsigned managementOptions)
+{
+ m_data->m_managementOptions = managementOptions;
+}
+
+bool Unit::isUnique() const
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+ return m_data->ref.load() == 1;
+#else
+ return m_data->ref == 1;
+#endif
+}
+
+void Unit::makeUnique()
+{
+ UnitData *uniqueData = new UnitData;
+ m_data->swap(uniqueData); // Notice we swap the data itself and not the shared pointer.
+ m_data = QExplicitlySharedDataPointer<UnitData>(uniqueData);
+}
+
+void Unit::parse()
+{
+ m_data->unload();
+
+ m_data->updateTimeStamp();
+
+ UnsavedFileData unsaved(m_data->m_unsaved);
+ m_data->m_tu = clang_parseTranslationUnit(m_data->m_index,
+ m_data->m_fileName.constData(),
+ m_data->m_sharedCompOptions.data(),
+ m_data->m_sharedCompOptions.size(),
+ unsaved.files(),
+ unsaved.count(),
+ m_data->m_managementOptions);
+}
+
+void Unit::reparse()
+{
+ Q_ASSERT(isLoaded());
+
+ UnsavedFileData unsaved(m_data->m_unsaved);
+ const unsigned opts = clang_defaultReparseOptions(m_data->m_tu);
+ if (clang_reparseTranslationUnit(m_data->m_tu, unsaved.count(), unsaved.files(), opts) != 0)
+ m_data->unload();
+}
+
+void Unit::create()
+{
+ // @TODO
+}
+
+void Unit::createFromSourceFile()
+{
+ // @TODO
+}
+
+int Unit::save(const QString &unitFileName)
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_saveTranslationUnit(m_data->m_tu,
+ unitFileName.toUtf8().constData(),
+ clang_defaultSaveOptions(m_data->m_tu));
+}
+
+void Unit::unload()
+{
+ m_data->unload();
+}
+
+CXFile Unit::getFile() const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getFile(m_data->m_tu, m_data->m_fileName.constData());
+}
+
+CXCursor Unit::getCursor(const CXSourceLocation &location) const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getCursor(m_data->m_tu, location);
+}
+
+CXSourceLocation Unit::getLocation(const CXFile &file, unsigned line, unsigned column) const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getLocation(m_data->m_tu, file, line, column);
+}
+
+void Unit::codeCompleteAt(unsigned line, unsigned column, ScopedCXCodeCompleteResults &results)
+{
+ unsigned flags = clang_defaultCodeCompleteOptions();
+#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5)
+ flags |= CXCodeComplete_IncludeBriefComments;
+#endif
+
+ UnsavedFileData unsaved(m_data->m_unsaved);
+ results.reset(clang_codeCompleteAt(m_data->m_tu, m_data->m_fileName.constData(),
+ line, column,
+ unsaved.files(), unsaved.count(), flags));
+}
+
+void Unit::tokenize(CXSourceRange range, CXToken **tokens, unsigned *tokenCount) const
+{
+ Q_ASSERT(isLoaded());
+ Q_ASSERT(tokens);
+ Q_ASSERT(tokenCount);
+ Q_ASSERT(!clang_Range_isNull(range));
+
+ clang_tokenize(m_data->m_tu, range, tokens, tokenCount);
+}
+
+void Unit::disposeTokens(CXToken *tokens, unsigned tokenCount) const
+{
+ Q_ASSERT(isLoaded());
+
+ clang_disposeTokens(m_data->m_tu, tokens, tokenCount);
+}
+
+CXSourceRange Unit::getTokenExtent(const CXToken &token) const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getTokenExtent(m_data->m_tu, token);
+}
+
+void Unit::annotateTokens(CXToken *tokens, unsigned tokenCount, CXCursor *cursors) const
+{
+ Q_ASSERT(isLoaded());
+ Q_ASSERT(tokens);
+ Q_ASSERT(cursors);
+
+ clang_annotateTokens(m_data->m_tu, tokens, tokenCount, cursors);
+}
+
+CXTranslationUnit Unit::clangTranslationUnit() const
+{
+ Q_ASSERT(isLoaded());
+
+ return m_data->m_tu;
+}
+
+CXIndex Unit::clangIndex() const
+{
+ Q_ASSERT(isLoaded());
+
+ return m_data->m_index;
+}
+
+QString Unit::getTokenSpelling(const CXToken &tok) const
+{
+ Q_ASSERT(isLoaded());
+
+ return getQString(clang_getTokenSpelling(m_data->m_tu, tok));
+}
+
+CXCursor Unit::getTranslationUnitCursor() const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getTranslationUnitCursor(m_data->m_tu);
+}
+
+CXString Unit::getTranslationUnitSpelling() const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getTranslationUnitSpelling(m_data->m_tu);
+}
+
+void Unit::getInclusions(CXInclusionVisitor visitor, CXClientData clientData) const
+{
+ Q_ASSERT(isLoaded());
+
+ clang_getInclusions(m_data->m_tu, visitor, clientData);
+}
+
+unsigned Unit::getNumDiagnostics() const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getNumDiagnostics(m_data->m_tu);
+}
+
+CXDiagnostic Unit::getDiagnostic(unsigned index) const
+{
+ Q_ASSERT(isLoaded());
+
+ return clang_getDiagnostic(m_data->m_tu, index);
+}
+
+IdentifierTokens::IdentifierTokens(const Unit &unit, unsigned firstLine, unsigned lastLine)
+ : m_unit(unit)
+ , m_tokenCount(0)
+ , m_tokens(0)
+ , m_cursors(0)
+ , m_extents(0)
+{
+ Q_ASSERT(unit.isLoaded());
+
+ // Calculate the range:
+ CXFile file = unit.getFile();
+ CXSourceLocation startLocation = unit.getLocation(file, firstLine, 1);
+ CXSourceLocation endLocation = unit.getLocation(file, lastLine, 1);
+ CXSourceRange range = clang_getRange(startLocation, endLocation);
+
+ // Retrieve all identifier tokens:
+ unit.tokenize(range, &m_tokens, &m_tokenCount);
+ if (m_tokenCount == 0)
+ return;
+
+ // Get the cursors for the tokens:
+ m_cursors = new CXCursor[m_tokenCount];
+ unit.annotateTokens(m_tokens,
+ m_tokenCount,
+ m_cursors);
+
+ m_extents = new CXSourceRange[m_tokenCount];
+ // Create the markers using the cursor to check the types:
+ for (unsigned i = 0; i < m_tokenCount; ++i)
+ m_extents[i] = unit.getTokenExtent(m_tokens[i]);
+}
+
+IdentifierTokens::~IdentifierTokens()
+{
+ dispose();
+}
+
+void IdentifierTokens::dispose()
+{
+ if (!m_unit.isLoaded())
+ return;
+
+ if (m_tokenCount && m_tokens) {
+ m_unit.disposeTokens(m_tokens, m_tokenCount);
+ m_tokens = 0;
+ m_tokenCount = 0;
+ }
+
+ if (m_cursors) {
+ delete[] m_cursors;
+ m_cursors = 0;
+ }
+
+ if (m_extents) {
+ delete[] m_extents;
+ m_extents = 0;
+ }
+}
diff --git a/src/plugins/clangcodemodel/unit.h b/src/plugins/clangcodemodel/unit.h
new file mode 100644
index 0000000000..98145d06d0
--- /dev/null
+++ b/src/plugins/clangcodemodel/unit.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef UNIT_H
+#define UNIT_H
+
+#include "utils.h"
+
+#include "cxraii.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QMetaType>
+#include <QString>
+#include <QStringList>
+#include <QVarLengthArray>
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+QT_END_NAMESPACE
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class UnitData;
+
+/*
+ * This is a minimal wrapper around clang's translation unit functionality.
+ * It should should contain only the very basic primitives which allow other
+ * components such as code completion, code navigation, and others to access
+ * data which directly depends on the translation unit.
+ *
+ * In other words, what's wrapped here is only the functions that receive a
+ * CXTranslationUnit as a parameter. And this class itself is then the corresponding
+ * abstraction of the CXTranslationUnit.
+ *
+ * Notes:
+ * - This class is not thread-safe.
+ * - It's responsibility of the client to make sure that the wrapped translation
+ * unit is consistent with the other data such as cursor and locations being used.
+ * - The data of the TU is shared.
+ *
+ *
+ * @TODO: This is similar but not exactly the same as the current ClangWrapper class.
+ * That class is now tuned to specific features, so it's not generic enough to be used
+ * an underlying component and aslo don't provide the data in a fine granularity as
+ * needed here. At some point we should split ClangWrapper into its different logical
+ * components and use this is the underlying structure.
+ */
+class Unit
+{
+public:
+ Unit();
+ explicit Unit(const QString &fileName);
+ Unit(const Unit &unit);
+ Unit &operator=(const Unit &unit);
+ ~Unit();
+
+ bool isLoaded() const;
+
+ const QString fileName() const;
+
+ const QDateTime &timeStamp() const;
+
+ QStringList compilationOptions() const;
+ void setCompilationOptions(const QStringList &compOptions);
+
+ UnsavedFiles unsavedFiles() const;
+ void setUnsavedFiles(const UnsavedFiles &unsavedFiles);
+
+ unsigned managementOptions() const;
+ void setManagementOptions(unsigned managementOptions);
+
+ // Sharing control facilities
+ // Method isUnique is just like an "isDetached", however makeUnique is mostly some kind
+ // of "detach" but which actually moves the data to this particular instance, invalidating
+ // then all the other shares.
+ bool isUnique() const;
+ void makeUnique();
+
+ // Methods for generating the TU. Name mappings are direct, for example:
+ // - parse corresponds to clang_parseTranslationUnit
+ // - createFromSourceFile corresponds to clang_createTranslationUnitFromSourceFile
+ void parse();
+ void reparse();
+ void create();
+ void createFromSourceFile();
+ int save(const QString &unitFileName);
+ void unload();
+
+ // Simple forwarding methods, separated by clang categories for convenience.
+ // As above, the names are directly mapped. Separated by categories as clang for convenience.
+ // Note that only methods that take the TU as a parameter should be wrapped.
+
+ // - Diagnostic reporting
+ unsigned getNumDiagnostics() const;
+ CXDiagnostic getDiagnostic(unsigned index) const;
+
+ // - Translation unit manipulation
+ CXString getTranslationUnitSpelling() const;
+
+ // - File manipulation routines
+ CXFile getFile() const;
+
+ // - Mapping between cursors and source code
+ CXCursor getCursor(const CXSourceLocation &location) const;
+
+ // - Miscellaneous utility functions
+ void getInclusions(CXInclusionVisitor visitor, CXClientData clientData) const;
+
+ // - Cursor manipulations
+ CXCursor getTranslationUnitCursor() const;
+
+ // - Physical source locations
+ CXSourceLocation getLocation(const CXFile &file, unsigned line, unsigned column) const;
+
+ void codeCompleteAt(unsigned line, unsigned column, ScopedCXCodeCompleteResults &results);
+
+ void tokenize(CXSourceRange range, CXToken **tokens, unsigned *tokenCount) const;
+ void disposeTokens(CXToken *tokens, unsigned tokenCount) const;
+ CXSourceRange getTokenExtent(const CXToken &token) const;
+ void annotateTokens(CXToken *tokens, unsigned tokenCount, CXCursor *cursors) const;
+
+ CXTranslationUnit clangTranslationUnit() const;
+ CXIndex clangIndex() const;
+
+ QString getTokenSpelling(const CXToken &tok) const;
+
+private:
+ QExplicitlySharedDataPointer<UnitData> m_data;
+};
+
+class IdentifierTokens
+{
+ Q_DISABLE_COPY(IdentifierTokens)
+
+public:
+ IdentifierTokens(const Unit &m_unit, unsigned firstLine, unsigned lastLine);
+ ~IdentifierTokens();
+
+ unsigned count() const
+ { return m_tokenCount; }
+
+ const CXToken &token(unsigned nr) const
+ { Q_ASSERT(nr < count()); return m_tokens[nr]; }
+
+ const CXCursor &cursor(unsigned nr) const
+ { Q_ASSERT(nr < count()); return m_cursors[nr]; }
+
+ const CXSourceRange &extent(unsigned nr) const
+ { Q_ASSERT(nr < count()); return m_extents[nr]; }
+
+private:
+ void dispose();
+
+private:
+ const Unit &m_unit;
+ unsigned m_tokenCount;
+ CXToken *m_tokens;
+
+ CXCursor *m_cursors;
+ CXSourceRange *m_extents;
+};
+
+} // Internal
+} // Clang
+
+Q_DECLARE_METATYPE(ClangCodeModel::Internal::Unit)
+
+#endif // UNIT_H
diff --git a/src/plugins/clangcodemodel/unsavedfiledata.cpp b/src/plugins/clangcodemodel/unsavedfiledata.cpp
new file mode 100644
index 0000000000..73c93c9de8
--- /dev/null
+++ b/src/plugins/clangcodemodel/unsavedfiledata.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "unsavedfiledata.h"
+
+using namespace ClangCodeModel::Internal;
+
+UnsavedFileData::UnsavedFileData(const UnsavedFiles &unsavedFiles)
+ : m_count(unsavedFiles.count())
+ , m_files(0)
+{
+ if (m_count) {
+ m_files = new CXUnsavedFile[m_count];
+ unsigned idx = 0;
+ for (UnsavedFiles::const_iterator it = unsavedFiles.begin(); it != unsavedFiles.end(); ++it, ++idx) {
+ QByteArray contents = it.value();
+ const char *contentChars = qstrdup(contents.constData());
+ m_files[idx].Contents = contentChars;
+ m_files[idx].Length = contents.size();
+
+ const char *fileName = qstrdup(it.key().toUtf8().constData());
+ m_files[idx].Filename = fileName;
+ }
+ }
+}
+
+UnsavedFileData::~UnsavedFileData()
+{
+ for (unsigned i = 0; i < m_count; ++i) {
+ delete[] m_files[i].Contents;
+ delete[] m_files[i].Filename;
+ }
+
+ delete[] m_files;
+}
diff --git a/src/plugins/clangcodemodel/unsavedfiledata.h b/src/plugins/clangcodemodel/unsavedfiledata.h
new file mode 100644
index 0000000000..34ec1d1201
--- /dev/null
+++ b/src/plugins/clangcodemodel/unsavedfiledata.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_INTERNAL_UNSAVEDFILEDATA_H
+#define CLANG_INTERNAL_UNSAVEDFILEDATA_H
+
+#include "utils.h"
+
+#include <clang-c/Index.h>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class UnsavedFileData
+{
+ UnsavedFileData(const UnsavedFileData &);
+ UnsavedFileData &operator=(const UnsavedFileData &);
+
+ typedef ClangCodeModel::Internal::UnsavedFiles UnsavedFiles;
+
+public:
+ UnsavedFileData(const UnsavedFiles &unsavedFiles);
+ ~UnsavedFileData();
+
+ unsigned count() const
+ { return m_count; }
+
+ CXUnsavedFile *files() const
+ { return m_files; }
+
+private:
+ unsigned m_count;
+ CXUnsavedFile *m_files;
+};
+
+} // namespace Internal
+} // namespace ClangCodeModel
+
+#endif // CLANG_INTERNAL_UNSAVEDFILEDATA_H
diff --git a/src/plugins/clangcodemodel/utils.cpp b/src/plugins/clangcodemodel/utils.cpp
new file mode 100644
index 0000000000..a147594d53
--- /dev/null
+++ b/src/plugins/clangcodemodel/utils.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "diagnostic.h"
+#include "unit.h"
+#include "utils.h"
+#include "utils_p.h"
+
+#include <clang-c/Index.h>
+
+#include <QMutex>
+#include <QMutexLocker>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+QPair<bool, QStringList> precompile(const PchInfo::Ptr &pchInfo)
+{
+// qDebug() << "*** Precompiling" << pchInfo->inputFileName()
+// << "into" << pchInfo->fileName()
+// << "with options" << pchInfo->options();
+
+ bool ok = false;
+
+ Internal::Unit unit(pchInfo->inputFileName());
+ unit.setCompilationOptions(pchInfo->options());
+
+ unsigned parseOpts = CXTranslationUnit_ForSerialization
+ | CXTranslationUnit_Incomplete;
+ unit.setManagementOptions(parseOpts);
+
+ unit.parse();
+ if (unit.isLoaded())
+ ok = CXSaveError_None == unit.save(pchInfo->fileName());
+
+ return qMakePair(ok, Internal::formattedDiagnostics(unit));
+}
+
+namespace {
+static bool clangInitialised = false;
+static QMutex initialisationMutex;
+} // anonymous namespace
+
+void initializeClang()
+{
+ if (clangInitialised)
+ return;
+
+ QMutexLocker locker(&initialisationMutex);
+ if (clangInitialised)
+ return;
+
+ clang_toggleCrashRecovery(1);
+ clang_enableStackTraces();
+ clangInitialised = true;
+
+ qRegisterMetaType<ClangCodeModel::Diagnostic>();
+ qRegisterMetaType<QList<ClangCodeModel::Diagnostic> >();
+}
+
+} // Internal namespace
+} // ClangCodeModel namespace
+
diff --git a/src/plugins/clangcodemodel/utils.h b/src/plugins/clangcodemodel/utils.h
new file mode 100644
index 0000000000..385e397f88
--- /dev/null
+++ b/src/plugins/clangcodemodel/utils.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include "pchinfo.h"
+
+#include <QString>
+#include <QStringList>
+#include <QByteArray>
+#include <QMap>
+#include <QPair>
+
+/*
+ * A header for globally visible typedefs. This is particularly useful
+ * so we don't have to #include files simply because of a typedef. Still,
+ * not every typedef should go in here, only the minimal subset of the
+ * ones which are needed quite often.
+ */
+namespace ClangCodeModel {
+namespace Internal {
+
+typedef QMap<QString, QByteArray> UnsavedFiles;
+
+/**
+ * Utility method to create a PCH file from a header file.
+ *
+ * \returns a boolean indicating success (true) or failure (false), and a
+ * list of diagnostic messages.
+ */
+QPair<bool, QStringList> precompile(const PchInfo::Ptr &pchInfo);
+
+void initializeClang();
+
+} // Internal namespace
+} // ClangCodeModel namespace
+
+#endif // UTILS_H
diff --git a/src/plugins/clangcodemodel/utils_p.cpp b/src/plugins/clangcodemodel/utils_p.cpp
new file mode 100644
index 0000000000..dbc764d004
--- /dev/null
+++ b/src/plugins/clangcodemodel/utils_p.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "unit.h"
+#include "utils_p.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+
+namespace ClangCodeModel {
+namespace Internal {
+
+QString getQString(const CXString &cxString, bool disposeCXString)
+{
+ QString s = QString::fromUtf8(clang_getCString(cxString));
+ if (disposeCXString)
+ clang_disposeString(cxString);
+ return s;
+}
+
+namespace {
+
+SourceLocation getLocation(const CXSourceLocation &loc,
+ void (*clangFunction)(CXSourceLocation,
+ CXFile *,
+ unsigned *,
+ unsigned *,
+ unsigned *))
+{
+ CXFile file;
+ unsigned line, column, offset;
+ (*clangFunction)(loc, &file, &line, &column, &offset);
+ return SourceLocation(normalizeFileName(getQString(clang_getFileName(file))),
+ line,
+ column,
+ offset);
+}
+
+} // Anonymous
+
+SourceLocation getInstantiationLocation(const CXSourceLocation &loc)
+{
+ return getLocation(loc, &clang_getInstantiationLocation);
+}
+
+SourceLocation getSpellingLocation(const CXSourceLocation &loc)
+{
+ return getLocation(loc, &clang_getSpellingLocation);
+}
+
+SourceLocation getExpansionLocation(const CXSourceLocation &loc)
+{
+// return getLocation(loc, &clang_getExpansionLocation);
+ return getLocation(loc, &clang_getInstantiationLocation);
+}
+
+QString normalizeFileName(const QString &fileName)
+{
+ if (fileName.isEmpty())
+ return fileName;
+
+ return normalizeFileName(QFileInfo(fileName));
+}
+
+QString normalizeFileName(const QFileInfo &fileInfo)
+{
+ if (!fileInfo.isFile())
+ return QString();
+
+ return QDir::cleanPath(fileInfo.absoluteFilePath());
+}
+
+QStringList formattedDiagnostics(const Unit &unit)
+{
+ QStringList diags;
+ if (!unit.isLoaded())
+ return diags;
+
+ const unsigned count = unit.getNumDiagnostics();
+ for (unsigned i = 0; i < count; ++i) {
+ CXDiagnostic diag = unit.getDiagnostic(i);
+
+ unsigned opt = CXDiagnostic_DisplaySourceLocation
+ | CXDiagnostic_DisplayColumn
+ | CXDiagnostic_DisplaySourceRanges
+ | CXDiagnostic_DisplayOption
+ | CXDiagnostic_DisplayCategoryId
+ | CXDiagnostic_DisplayCategoryName
+ ;
+ diags << getQString(clang_formatDiagnostic(diag, opt));
+ clang_disposeDiagnostic(diag);
+ }
+
+ return diags;
+}
+
+} // Internal
+} // ClangCodeModel
diff --git a/src/plugins/clangcodemodel/utils_p.h b/src/plugins/clangcodemodel/utils_p.h
new file mode 100644
index 0000000000..f88c10641e
--- /dev/null
+++ b/src/plugins/clangcodemodel/utils_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CLANG_REUSE_H
+#define CLANG_REUSE_H
+
+#include "sourcelocation.h"
+
+#include <clang-c/Index.h>
+
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+class QFileInfo;
+QT_END_NAMESPACE
+
+namespace ClangCodeModel {
+namespace Internal {
+
+class Unit;
+
+QString getQString(const CXString &cxString, bool disposeCXString = true);
+
+SourceLocation getInstantiatonLocation(const CXSourceLocation &loc); // Deprecated
+SourceLocation getSpellingLocation(const CXSourceLocation &loc);
+SourceLocation getExpansionLocation(const CXSourceLocation &loc);
+
+// There are slight differences of behavior from apparently similar Qt file processing functions.
+// For instance, QFileInfo::absoluteFilePath will uppercase driver letters, while the corresponding
+// QDir function will not do so. Besides, we need to keep paths clean. So in order to avoid
+// inconsistencies the functions below should be used for any indexing related task.
+QString normalizeFileName(const QString &fileName);
+QString normalizeFileName(const QFileInfo &fileInfo);
+
+QStringList formattedDiagnostics(const Unit &unit);
+
+} // Internal
+} // ClangCodeModel
+
+#endif // CLANG_REUSE_H
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 376ec787b8..b5ff2a08bd 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -1565,6 +1565,8 @@ void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
fs.toTextCharFormat(TextEditor::C_FUNCTION);
m_semanticHighlightFormatMap[CppHighlightingSupport::PseudoKeywordUse] =
fs.toTextCharFormat(TextEditor::C_KEYWORD);
+ m_semanticHighlightFormatMap[CppHighlightingSupport::StringUse] =
+ fs.toTextCharFormat(TextEditor::C_STRING);
m_keywordFormat = fs.toTextCharFormat(TextEditor::C_KEYWORD);
// only set the background, we do not want to modify foreground properties
diff --git a/src/plugins/cpptools/cppchecksymbols.h b/src/plugins/cpptools/cppchecksymbols.h
index 870bc709f8..0c94c3a7b2 100644
--- a/src/plugins/cpptools/cppchecksymbols.h
+++ b/src/plugins/cpptools/cppchecksymbols.h
@@ -42,6 +42,17 @@
namespace CppTools {
+#ifdef _MSC_VER
+// Short explanation: QFutureInterface is currently used as a base for an exported class
+// (see CreateMarkers). Even though it's not explicitly marked to be exported VC++ will
+// implicitly export it (more details on the General Rules and Limitations of dllexport/
+// dllimport MSDN page). When seen here as a base of CheckSymbols the compiler tries to
+// instantiate this template (since the declaration of template doesn't have any explicit
+// export/import attribute) which will eventually collide with the already defined symbol.
+// The line below simply tells the compiler to import the template instead of instantiating.
+template class Q_DECL_IMPORT QFutureInterface<CppEditor::Internal::SemanticInfo::Use>;
+#endif
+
class CPPTOOLS_EXPORT CheckSymbols:
protected CPlusPlus::ASTVisitor,
public QRunnable,
diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp
index 7202ea2828..3a5b41224c 100644
--- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp
+++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp
@@ -44,7 +44,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(QWidget *parent)
{
m_ui->setupUi(this);
- m_ui->theGroupBox->setVisible(false);
+ m_ui->theGroupBox->setVisible(true);
}
CppCodeModelSettingsWidget::~CppCodeModelSettingsWidget()
diff --git a/src/plugins/cpptools/cpphighlightingsupport.h b/src/plugins/cpptools/cpphighlightingsupport.h
index ee085732b2..5a97f66c04 100644
--- a/src/plugins/cpptools/cpphighlightingsupport.h
+++ b/src/plugins/cpptools/cpphighlightingsupport.h
@@ -57,7 +57,8 @@ public:
LabelUse,
MacroUse,
FunctionUse,
- PseudoKeywordUse
+ PseudoKeywordUse,
+ StringUse
};
public:
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 45b49e8803..4e4018b8a2 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -50,6 +50,12 @@ SUBDIRS = \
baremetal \
ios
+# prefer qmake variable set on command line over env var
+isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR)
+!isEmpty(LLVM_INSTALL_DIR) {
+ SUBDIRS += clangcodemodel
+}
+
isEmpty(QBS_INSTALL_DIR): QBS_INSTALL_DIR = $$(QBS_INSTALL_DIR)
exists(../shared/qbs/qbs.pro)|!isEmpty(QBS_INSTALL_DIR): \
SUBDIRS += \