diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 866fbf89d59..fac54a782e4 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -1250,6 +1250,9 @@ void CMakeBuildSystem::setupCMakeSymbolsHash() { m_cmakeSymbolsHash.clear(); + m_projectKeywords.functions.clear(); + m_projectKeywords.variables.clear(); + for (const auto &cmakeFile : std::as_const(m_cmakeFiles)) { for (const auto &func : cmakeFile.cmakeListFile.Functions) { if (func.LowerCaseName() != "function" && func.LowerCaseName() != "macro" @@ -1265,8 +1268,17 @@ void CMakeBuildSystem::setupCMakeSymbolsHash() link.targetLine = arg.Line; link.targetColumn = arg.Column - 1; m_cmakeSymbolsHash.insert(QString::fromUtf8(arg.Value), link); + + if (func.LowerCaseName() == "option") + m_projectKeywords.variables << QString::fromUtf8(arg.Value); + else + m_projectKeywords.functions << QString::fromUtf8(arg.Value); } } + + // Code completion setup + if (CMakeTool *tool = CMakeKitAspect::cmakeTool(target()->kit())) + tool->keywords(); } void CMakeBuildSystem::ensureBuildDirectory(const BuildDirParameters ¶meters) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index ca67d5549e2..d802797ebd6 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -119,6 +119,7 @@ public: QString warning() const; const QHash &cmakeSymbolsHash() const { return m_cmakeSymbolsHash; } + CMakeKeywords projectKeywords() const { return m_projectKeywords; } signals: void configurationCleared(); @@ -223,6 +224,7 @@ private: QList m_buildTargets; QSet m_cmakeFiles; QHash m_cmakeSymbolsHash; + CMakeKeywords m_projectKeywords; QHash m_filesToBeRenamed; diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp index 18bc57a4905..cdad2183487 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp @@ -3,51 +3,318 @@ #include "cmakefilecompletionassist.h" +#include "cmakebuildsystem.h" +#include "cmakebuildtarget.h" #include "cmakekitaspect.h" +#include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmaketool.h" #include +#include +#include #include +#include #include #include +#include +#include + +#include +#include using namespace TextEditor; using namespace ProjectExplorer; +using namespace Utils; namespace CMakeProjectManager::Internal { -class CMakeFileCompletionAssist : public KeywordsCompletionAssistProcessor +class CMakeFileCompletionAssist : public AsyncProcessor { public: CMakeFileCompletionAssist(); IAssistProposal *performAsync() final; + + const QIcon m_variableIcon; + const QIcon m_projectVariableIcon; + const QIcon m_functionIcon; + const QIcon m_projectFunctionIcon; + const QIcon m_propertyIcon; + const QIcon m_argsIcon; + const QIcon m_genexIcon; + const QIcon m_moduleIcon; + const QIcon m_targetsIcon; + + TextEditor::SnippetAssistCollector m_snippetCollector; }; -CMakeFileCompletionAssist::CMakeFileCompletionAssist() : - KeywordsCompletionAssistProcessor(Keywords()) +CMakeFileCompletionAssist::CMakeFileCompletionAssist() + : m_variableIcon(CodeModelIcon::iconForType(CodeModelIcon::VarPublic)) + , m_projectVariableIcon(CodeModelIcon::iconForType(CodeModelIcon::VarPublicStatic)) + , m_functionIcon(CodeModelIcon::iconForType(CodeModelIcon::FuncPublic)) + , m_projectFunctionIcon(CodeModelIcon::iconForType(CodeModelIcon::FuncPublicStatic)) + , m_propertyIcon(CodeModelIcon::iconForType(CodeModelIcon::Property)) + , m_argsIcon(CodeModelIcon::iconForType(CodeModelIcon::Enum)) + , m_genexIcon(CodeModelIcon::iconForType(CodeModelIcon::Class)) + , m_moduleIcon( + ProjectExplorer::DirectoryIcon(ProjectExplorer::Constants::FILEOVERLAY_MODULES).icon()) + , m_targetsIcon(ProjectExplorer::Icons::BUILD.icon()) + , m_snippetCollector(Constants::CMAKE_SNIPPETS_GROUP_ID, + CodeModelIcon::iconForType(CodeModelIcon::Keyword)) + +{} + +static bool isInComment(const AssistInterface *interface) { - setSnippetGroup(Constants::CMAKE_SNIPPETS_GROUP_ID); - setDynamicCompletionFunction(&TextEditor::pathComplete); + QTextCursor tc(interface->textDocument()); + tc.setPosition(interface->position()); + tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + return tc.selectedText().contains('#'); +} + +static bool isValidIdentifierChar(const QChar &chr) +{ + return chr.isLetterOrNumber() || chr == '_' || chr == '-'; +} + +static int findWordStart(const AssistInterface *interface, int pos) +{ + // Find start position + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && isValidIdentifierChar(chr)); + + return ++pos; +} + +static int findFunctionStart(const AssistInterface *interface) +{ + int pos = interface->position(); + + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr != '('); + + if (pos > 0 && chr == '(') { + // allow space between function name and ( + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr.isSpace()); + ++pos; + } + + return pos; +} + +static int findFunctionEnd(const AssistInterface *interface) +{ + int pos = interface->position(); + + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr != ')'); + + return pos; +} + +static int findPathStart(const AssistInterface *interface) +{ + // For pragmatic reasons, we don't support spaces in file names here. + static const auto canOccurInFilePath = [](const QChar &c) { + return c.isLetterOrNumber() || c == '.' || c == '/' || c == '_' || c == '-'; + }; + + int pos = interface->position(); + QChar chr; + // Skip to the start of a name + do { + chr = interface->characterAt(--pos); + } while (canOccurInFilePath(chr)); + + return ++pos; +} + +QList generateList(const QStringList &words, const QIcon &icon) +{ + return transform(words, [&icon](const QString &word) -> AssistProposalItemInterface * { + AssistProposalItem *item = new AssistProposalItem(); + item->setText(word); + item->setIcon(icon); + return item; + }); +} + +static int addFilePathItems(const AssistInterface *interface, + QList &items, + int symbolStartPos) +{ + if (interface->filePath().isEmpty()) + return symbolStartPos; + + const int startPos = findPathStart(interface); + + if (interface->reason() == IdleEditor + && interface->position() - startPos + < TextEditorSettings::completionSettings().m_characterThreshold) + return symbolStartPos; + + const QString word = interface->textAt(startPos, interface->position() - startPos); + FilePath baseDir = interface->filePath().absoluteFilePath().parentDir(); + const int lastSlashPos = word.lastIndexOf(QLatin1Char('/')); + + QString prefix = word; + if (lastSlashPos != -1) { + prefix = word.mid(lastSlashPos + 1); + baseDir = baseDir.pathAppended(word.left(lastSlashPos)); + } + + const FilePaths filesPaths = baseDir.dirEntries( + FileFilter({QString("%1*").arg(prefix)}, QDir::AllEntries | QDir::NoDotAndDotDot)); + for (const auto &file : filesPaths) { + AssistProposalItem *item = new AssistProposalItem; + QString fileName = file.fileName(); + if (file.isDir()) + fileName.append("/"); + item->setText(fileName); + item->setIcon(FileIconProvider::icon(file)); + + items << item; + } + + return startPos; } IAssistProposal *CMakeFileCompletionAssist::performAsync() { - Keywords kw; - const Utils::FilePath &filePath = interface()->filePath(); + CMakeKeywords keywords; + CMakeKeywords projectKeywords; + Project *project = nullptr; + const FilePath &filePath = interface()->filePath(); if (!filePath.isEmpty() && filePath.isFile()) { - Project *p = ProjectManager::projectForFile(filePath); - if (p && p->activeTarget()) { - CMakeTool *cmake = CMakeKitAspect::cmakeTool(p->activeTarget()->kit()); + project = static_cast(ProjectManager::projectForFile(filePath)); + if (project && project->activeTarget()) { + CMakeTool *cmake = CMakeKitAspect::cmakeTool(project->activeTarget()->kit()); if (cmake && cmake->isValid()) - kw = cmake->keywords(); + keywords = cmake->keywords(); } } - setKeywords(kw); - return KeywordsCompletionAssistProcessor::performAsync(); + QStringList buildTargets; + if (project && project->activeTarget()) { + const auto bs = qobject_cast(project->activeTarget()->buildSystem()); + if (bs) { + for (const auto &target : std::as_const(bs->buildTargets())) + if (target.targetType != TargetType::UtilityType) + buildTargets << target.title; + projectKeywords = bs->projectKeywords(); + } + } + + if (isInComment(interface())) + return nullptr; + + const int startPos = findWordStart(interface(), interface()->position()); + + const int functionStart = findFunctionStart(interface()); + const int prevFunctionEnd = findFunctionEnd(interface()); + + QString functionName; + if (functionStart > prevFunctionEnd) { + int functionStartPos = findWordStart(interface(), functionStart); + functionName + = interface()->textAt(functionStartPos, functionStart - functionStartPos); + } + + if (interface()->reason() == IdleEditor) { + const QChar chr = interface()->characterAt(interface()->position()); + const int wordSize = interface()->position() - startPos; + if (isValidIdentifierChar(chr) + || wordSize < TextEditorSettings::completionSettings().m_characterThreshold) { + return nullptr; + } + } + + QList items; + + const QString varGenexToken = interface()->textAt(startPos - 2, 2); + if (varGenexToken == "${" || varGenexToken == "$<") { + if (varGenexToken == "${") { + items.append(generateList(keywords.variables, m_variableIcon)); + items.append(generateList(projectKeywords.variables, m_projectVariableIcon)); + } + if (varGenexToken == "$<") + items.append(generateList(keywords.generatorExpressions, m_genexIcon)); + + return new GenericProposal(startPos, items); + } + + int fileStartPos = startPos; + const auto onlyFileItems = [&] { return fileStartPos != startPos; }; + + if (functionName == "if" || functionName == "elseif" || functionName == "while" + || functionName == "set" || functionName == "list") { + items.append(generateList(keywords.variables, m_variableIcon)); + items.append(generateList(projectKeywords.variables, m_projectVariableIcon)); + } + + if (functionName.contains("path") || functionName.contains("file") + || functionName.contains("add_executable") || functionName.contains("add_library") + || functionName == "include" || functionName == "add_subdirectory" + || functionName == "install" || functionName == "target_sources" || functionName == "set" + || functionName == "list") { + fileStartPos = addFilePathItems(interface(), items, startPos); + } + + if (functionName == "set_property") + items.append(generateList(keywords.properties, m_propertyIcon)); + + if (functionName == "set_directory_properties") + items.append(generateList(keywords.directoryProperties, m_propertyIcon)); + if (functionName == "set_source_files_properties") + items.append(generateList(keywords.sourceProperties, m_propertyIcon)); + if (functionName == "set_target_properties") + items.append(generateList(keywords.targetProperties, m_propertyIcon)); + if (functionName == "set_tests_properties") + items.append(generateList(keywords.testProperties, m_propertyIcon)); + + if (functionName == "include" && !onlyFileItems()) + items.append(generateList(keywords.includeStandardModules, m_moduleIcon)); + if (functionName == "find_package") + items.append(generateList(keywords.findModules, m_moduleIcon)); + + if ((functionName.contains("target") || functionName == "install" + || functionName == "add_dependencies" || functionName == "set_property" + || functionName == "export") + && !onlyFileItems()) + items.append(generateList(buildTargets, m_targetsIcon)); + + if (keywords.functionArgs.contains(functionName) && !onlyFileItems()) { + QStringList functionSymbols = keywords.functionArgs.value(functionName); + items.append(generateList(functionSymbols, m_argsIcon)); + } else if (functionName.isEmpty()) { + // On a new line we just want functions + items.append(generateList(keywords.functions, m_functionIcon)); + items.append(generateList(projectKeywords.functions, m_projectFunctionIcon)); + } else { + // Inside an unknown function we could have variables or properties + fileStartPos = addFilePathItems(interface(), items, startPos); + if (!onlyFileItems()) { + items.append(generateList(keywords.variables, m_variableIcon)); + items.append(generateList(projectKeywords.variables, m_projectVariableIcon)); + items.append(generateList(keywords.properties, m_propertyIcon)); + items.append(generateList(buildTargets, m_targetsIcon)); + } + } + + if (!onlyFileItems()) + items.append(m_snippetCollector.collect()); + + return new GenericProposal(startPos, items); } IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor(const AssistInterface *) const @@ -55,4 +322,15 @@ IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor(const Assis return new CMakeFileCompletionAssist; } -} // CMakeProjectManager::Internal +int CMakeFileCompletionAssistProvider::activationCharSequenceLength() const +{ + return 2; +} + +bool CMakeFileCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const +{ + return sequence.endsWith("${") || sequence.endsWith("$<") || sequence.endsWith("/") + || sequence.endsWith("("); +} + +} // namespace CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h index 1b60a2e72f6..9b692c895c4 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h @@ -3,7 +3,7 @@ #pragma once -#include +#include namespace CMakeProjectManager::Internal { @@ -11,6 +11,8 @@ class CMakeFileCompletionAssistProvider : public TextEditor::CompletionAssistPro { public: TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const final; + int activationCharSequenceLength() const final; + bool isActivationCharSequence(const QString &sequence) const final; }; } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index 67067555653..46d88dc932e 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -6,6 +6,7 @@ #include "cmakeprojectmanagertr.h" #include "cmaketoolmanager.h" +#include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -90,6 +92,14 @@ public: QVector m_fileApis; QStringList m_variables; QStringList m_functions; + QStringList m_properties; + QStringList m_generatorExpressions; + QStringList m_directoryProperties; + QStringList m_sourceProperties; + QStringList m_targetProperties; + QStringList m_testProperties; + QStringList m_includeStandardModules; + QStringList m_findModules; CMakeTool::Version m_version; }; @@ -243,7 +253,7 @@ QList CMakeTool::supportedGenerators() const return isValid() ? m_introspection->m_generators : QList(); } -TextEditor::Keywords CMakeTool::keywords() +CMakeKeywords CMakeTool::keywords() { if (!isValid()) return {}; @@ -252,27 +262,37 @@ TextEditor::Keywords CMakeTool::keywords() Process proc; runCMake(proc, {"--help-command-list"}, 5); if (proc.result() == ProcessResult::FinishedWithSuccess) - m_introspection->m_functions = proc.cleanedStdOut().split('\n'); - - runCMake(proc, {"--help-commands"}, 5); - if (proc.result() == ProcessResult::FinishedWithSuccess) - parseFunctionDetailsOutput(proc.cleanedStdOut()); + m_introspection->m_functions = Utils::filtered(proc.cleanedStdOut().split('\n'), + std::not_fn(&QString::isEmpty)); runCMake(proc, {"--help-property-list"}, 5); if (proc.result() == ProcessResult::FinishedWithSuccess) - m_introspection->m_variables = parseVariableOutput(proc.cleanedStdOut()); + m_introspection->m_properties = parseVariableOutput(proc.cleanedStdOut()); runCMake(proc, {"--help-variable-list"}, 5); if (proc.result() == ProcessResult::FinishedWithSuccess) { - m_introspection->m_variables.append(parseVariableOutput(proc.cleanedStdOut())); - m_introspection->m_variables = Utils::filteredUnique(m_introspection->m_variables); + m_introspection->m_variables = Utils::filteredUnique( + parseVariableOutput(proc.cleanedStdOut())); Utils::sort(m_introspection->m_variables); } + + parseSyntaxHighlightingXml(); } - return TextEditor::Keywords(m_introspection->m_variables, - m_introspection->m_functions, - m_introspection->m_functionArgs); + CMakeKeywords keywords; + keywords.functions = m_introspection->m_functions; + keywords.variables = m_introspection->m_variables; + keywords.functionArgs = m_introspection->m_functionArgs; + keywords.properties = m_introspection->m_properties; + keywords.generatorExpressions = m_introspection->m_generatorExpressions; + keywords.directoryProperties = m_introspection->m_directoryProperties; + keywords.sourceProperties = m_introspection->m_sourceProperties; + keywords.targetProperties = m_introspection->m_targetProperties; + keywords.testProperties = m_introspection->m_testProperties; + keywords.includeStandardModules = m_introspection->m_includeStandardModules; + keywords.findModules = m_introspection->m_findModules; + + return keywords; } bool CMakeTool::hasFileApi(bool ignoreCache) const @@ -400,6 +420,7 @@ void CMakeTool::readInformation(bool ignoreCache) const fetchFromCapabilities(ignoreCache); } + static QStringList parseDefinition(const QString &definition) { QStringList result; @@ -455,7 +476,7 @@ void CMakeTool::parseFunctionDetailsOutput(const QString &output) const QString command = words.takeFirst(); if (functionSet.contains(command)) { const QStringList tmp = Utils::sorted( - words + m_introspection->m_functionArgs[command]); + words + m_introspection->m_functionArgs[command]); m_introspection->m_functionArgs[command] = Utils::filteredUnique(tmp); } } @@ -471,12 +492,19 @@ void CMakeTool::parseFunctionDetailsOutput(const QString &output) QStringList CMakeTool::parseVariableOutput(const QString &output) { - const QStringList variableList = output.split('\n'); + const QStringList variableList = Utils::filtered(output.split('\n'), + std::not_fn(&QString::isEmpty)); QStringList result; for (const QString &v : variableList) { if (v.startsWith("CMAKE_COMPILER_IS_GNU")) { // This key takes a compiler name :-/ result << "CMAKE_COMPILER_IS_GNUCC" << "CMAKE_COMPILER_IS_GNUCXX"; + } else if (v.contains("") && v.contains("")) { + const QString tmp = QString(v).replace("", "%1").replace("", "%2"); + result << tmp.arg("DEBUG").arg("C") << tmp.arg("DEBUG").arg("CXX") + << tmp.arg("RELEASE").arg("C") << tmp.arg("RELEASE").arg("CXX") + << tmp.arg("MINSIZEREL").arg("C") << tmp.arg("MINSIZEREL").arg("CXX") + << tmp.arg("RELWITHDEBINFO").arg("C") << tmp.arg("RELWITHDEBINFO").arg("CXX"); } else if (v.contains("")) { const QString tmp = QString(v).replace("", "%1"); result << tmp.arg("DEBUG") << tmp.arg("RELEASE") << tmp.arg("MINSIZEREL") @@ -491,6 +519,87 @@ QStringList CMakeTool::parseVariableOutput(const QString &output) return result; } +void CMakeTool::parseSyntaxHighlightingXml() +{ + QSet functionSet = Utils::toSet(m_introspection->m_functions); + + const FilePath cmakeXml = Core::ICore::resourcePath("generic-highlighter/syntax/cmake.xml"); + QXmlStreamReader reader(cmakeXml.fileContents().value_or(QByteArray())); + + auto readItemList = [](QXmlStreamReader &reader) -> QStringList { + QStringList arguments; + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() == u"item") + arguments.append(reader.readElementText()); + else + reader.skipCurrentElement(); + } + return arguments; + }; + + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() != u"highlighting") + continue; + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() == u"list") { + const auto name = reader.attributes().value("name").toString(); + if (name.endsWith(u"_sargs") || name.endsWith(u"_nargs")) { + const auto functionName = name.left(name.length() - 6); + QStringList arguments = readItemList(reader); + + if (m_introspection->m_functionArgs.contains(functionName)) + arguments.append(m_introspection->m_functionArgs.value(functionName)); + + m_introspection->m_functionArgs[functionName] = arguments; + + // Functions that are part of CMake modules like ExternalProject_Add + // which are not reported by cmake --help-list-commands + if (!functionSet.contains(functionName)) { + functionSet.insert(functionName); + m_introspection->m_functions.append(functionName); + } + } else if (name == u"generator-expressions") { + m_introspection->m_generatorExpressions = readItemList(reader); + } else if (name == u"directory-properties") { + m_introspection->m_directoryProperties = readItemList(reader); + } else if (name == u"source-properties") { + m_introspection->m_sourceProperties = readItemList(reader); + } else if (name == u"target-properties") { + m_introspection->m_targetProperties = readItemList(reader); + } else if (name == u"test-properties") { + m_introspection->m_testProperties = readItemList(reader); + } else if (name == u"standard-modules") { + m_introspection->m_includeStandardModules = readItemList(reader); + } else if (name == u"standard-finder-modules") { + m_introspection->m_findModules = readItemList(reader); + } else { + reader.skipCurrentElement(); + } + } else { + reader.skipCurrentElement(); + } + } + } + + // Some commands have the same arguments as other commands and the `cmake.xml` + // but their relationship is weirdly defined in the `cmake.xml` file. + using ListStringPair = QList>; + const ListStringPair functionPairs = {{"if", "elseif"}, + {"while", "elseif"}, + {"find_path", "find_file"}, + {"find_program", "find_library"}, + {"target_link_libraries", "target_compile_definitions"}, + {"target_link_options", "target_compile_definitions"}, + {"target_link_directories", "target_compile_options"}, + {"set_target_properties", "set_directory_properties"}, + {"set_tests_properties", "set_directory_properties"}}; + for (const auto &pair : std::as_const(functionPairs)) { + if (!m_introspection->m_functionArgs.contains(pair.first)) + m_introspection->m_functionArgs[pair.first] = m_introspection->m_functionArgs.value( + pair.second); + } +} + void CMakeTool::fetchFromCapabilities(bool ignoreCache) const { expected_str cache = PersistentCacheStore::byKey( diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h index 4ae2ab1f998..d090b6fd97f 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.h +++ b/src/plugins/cmakeprojectmanager/cmaketool.h @@ -19,6 +19,21 @@ namespace CMakeProjectManager { namespace Internal { class IntrospectionData; } +struct CMAKE_EXPORT CMakeKeywords +{ + QStringList variables; + QStringList functions; + QStringList properties; + QStringList generatorExpressions; + QStringList directoryProperties; + QStringList sourceProperties; + QStringList targetProperties; + QStringList testProperties; + QStringList includeStandardModules; + QStringList findModules; + QMap functionArgs; +}; + class CMAKE_EXPORT CMakeTool { public: @@ -73,7 +88,7 @@ public: bool isAutoRun() const; bool autoCreateBuildDirectory() const; QList supportedGenerators() const; - TextEditor::Keywords keywords(); + CMakeKeywords keywords(); bool hasFileApi(bool ignoreCache = false) const; Version version() const; QString versionDisplay() const; @@ -101,6 +116,7 @@ private: void runCMake(Utils::Process &proc, const QStringList &args, int timeoutS = 1) const; void parseFunctionDetailsOutput(const QString &output); QStringList parseVariableOutput(const QString &output); + void parseSyntaxHighlightingXml(); void fetchFromCapabilities(bool ignoreCache = false) const; void parseFromCapabilities(const QString &input) const;