forked from qt-creator/qt-creator
CMakePM: Support local functions and variables for code completion
... and also navigation via F2. Change-Id: I0f1242c6ff502973de180643b369c635636b0112 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
#include "cmakekitaspect.h"
|
#include "cmakekitaspect.h"
|
||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
|
|
||||||
|
#include "3rdparty/cmake/cmListFileCache.h"
|
||||||
|
|
||||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/coreplugintr.h>
|
#include <coreplugin/coreplugintr.h>
|
||||||
@@ -146,6 +148,35 @@ static QString unescape(const QString &s)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QHash<QString, Utils::Link> getLocalSymbolsHash(const QByteArray &content, const Utils::FilePath &filePath)
|
||||||
|
{
|
||||||
|
cmListFile cmakeListFile;
|
||||||
|
if (!content.isEmpty()) {
|
||||||
|
std::string errorString;
|
||||||
|
const std::string fileName = "buffer";
|
||||||
|
if (!cmakeListFile.ParseString(content.toStdString(), fileName, errorString))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, Utils::Link> hash;
|
||||||
|
for (const auto &func : cmakeListFile.Functions) {
|
||||||
|
if (func.LowerCaseName() != "function" && func.LowerCaseName() != "macro"
|
||||||
|
&& func.LowerCaseName() != "set" && func.LowerCaseName() != "option")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (func.Arguments().size() == 0)
|
||||||
|
continue;
|
||||||
|
auto arg = func.Arguments()[0];
|
||||||
|
|
||||||
|
Utils::Link link;
|
||||||
|
link.targetFilePath = filePath;
|
||||||
|
link.targetLine = arg.Line;
|
||||||
|
link.targetColumn = arg.Column - 1;
|
||||||
|
hash.insert(QString::fromUtf8(arg.Value), link);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
||||||
const Utils::LinkHandler &processLinkCallback,
|
const Utils::LinkHandler &processLinkCallback,
|
||||||
bool/* resolveTarget*/,
|
bool/* resolveTarget*/,
|
||||||
@@ -194,6 +225,12 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
|||||||
buffer.replace("${CMAKE_CURRENT_SOURCE_DIR}", dir.path());
|
buffer.replace("${CMAKE_CURRENT_SOURCE_DIR}", dir.path());
|
||||||
buffer.replace("${CMAKE_CURRENT_LIST_DIR}", dir.path());
|
buffer.replace("${CMAKE_CURRENT_LIST_DIR}", dir.path());
|
||||||
|
|
||||||
|
auto addTextStartEndToLink = [&](Utils::Link &link) {
|
||||||
|
link.linkTextStart = cursor.position() - column + beginPos + 1;
|
||||||
|
link.linkTextEnd = cursor.position() - column + endPos;
|
||||||
|
return link;
|
||||||
|
};
|
||||||
|
|
||||||
if (auto project = ProjectTree::currentProject()) {
|
if (auto project = ProjectTree::currentProject()) {
|
||||||
buffer.replace("${CMAKE_SOURCE_DIR}", project->projectDirectory().path());
|
buffer.replace("${CMAKE_SOURCE_DIR}", project->projectDirectory().path());
|
||||||
if (auto bs = ProjectTree::currentBuildSystem()) {
|
if (auto bs = ProjectTree::currentBuildSystem()) {
|
||||||
@@ -216,14 +253,35 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
|||||||
const CMakeBuildSystem *cbs = static_cast<const CMakeBuildSystem *>(bs);
|
const CMakeBuildSystem *cbs = static_cast<const CMakeBuildSystem *>(bs);
|
||||||
if (cbs->cmakeSymbolsHash().contains(buffer)) {
|
if (cbs->cmakeSymbolsHash().contains(buffer)) {
|
||||||
link = cbs->cmakeSymbolsHash().value(buffer);
|
link = cbs->cmakeSymbolsHash().value(buffer);
|
||||||
link.linkTextStart = cursor.position() - column + beginPos + 1;
|
addTextStartEndToLink(link);
|
||||||
link.linkTextEnd = cursor.position() - column + endPos;
|
|
||||||
return processLinkCallback(link);
|
return processLinkCallback(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Resolve more variables
|
// TODO: Resolve more variables
|
||||||
|
|
||||||
|
// Resolve local variables and functions
|
||||||
|
auto findFunctionEnd = [cursor, this]() -> int {
|
||||||
|
int pos = cursor.position();
|
||||||
|
QChar chr;
|
||||||
|
do {
|
||||||
|
chr = textDocument()->characterAt(--pos);
|
||||||
|
} while (pos > 0 && chr != ')');
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
auto hash = getLocalSymbolsHash(textDocument()->textAt(0, findFunctionEnd() + 1).toUtf8(),
|
||||||
|
textDocument()->filePath());
|
||||||
|
|
||||||
|
// Strip variable coating
|
||||||
|
if (buffer.startsWith("${") && buffer.endsWith("}"))
|
||||||
|
buffer = buffer.mid(2, buffer.size() - 3);
|
||||||
|
|
||||||
|
if (hash.contains(buffer)) {
|
||||||
|
link = hash.value(buffer);
|
||||||
|
addTextStartEndToLink(link);
|
||||||
|
return processLinkCallback(link);
|
||||||
|
}
|
||||||
|
|
||||||
Utils::FilePath fileName = dir.withNewPath(unescape(buffer));
|
Utils::FilePath fileName = dir.withNewPath(unescape(buffer));
|
||||||
if (fileName.isRelativePath())
|
if (fileName.isRelativePath())
|
||||||
fileName = dir.pathAppended(fileName.path());
|
fileName = dir.pathAppended(fileName.path());
|
||||||
@@ -236,9 +294,9 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
|||||||
return processLinkCallback(link);
|
return processLinkCallback(link);
|
||||||
}
|
}
|
||||||
link.targetFilePath = fileName;
|
link.targetFilePath = fileName;
|
||||||
link.linkTextStart = cursor.position() - column + beginPos + 1;
|
addTextStartEndToLink(link);
|
||||||
link.linkTextEnd = cursor.position() - column + endPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processLinkCallback(link);
|
processLinkCallback(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "cmaketool.h"
|
#include "cmaketool.h"
|
||||||
#include "cmaketoolmanager.h"
|
#include "cmaketoolmanager.h"
|
||||||
|
|
||||||
|
#include "3rdparty/cmake/cmListFileCache.h"
|
||||||
|
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
#include <projectexplorer/projectexplorericons.h>
|
#include <projectexplorer/projectexplorericons.h>
|
||||||
@@ -191,6 +193,30 @@ static int addFilePathItems(const AssistInterface *interface,
|
|||||||
return startPos;
|
return startPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
|
||||||
|
{
|
||||||
|
cmListFile cmakeListFile;
|
||||||
|
std::string errorString;
|
||||||
|
if (!content.isEmpty()) {
|
||||||
|
const std::string fileName = "buffer";
|
||||||
|
if (!cmakeListFile.ParseString(content.toStdString(), fileName, errorString))
|
||||||
|
return {{}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList variables;
|
||||||
|
QStringList functions;
|
||||||
|
for (const auto &func : cmakeListFile.Functions) {
|
||||||
|
if (func.Arguments().size() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (func.LowerCaseName() == "macro" || func.LowerCaseName() == "function")
|
||||||
|
functions << QString::fromUtf8(func.Arguments()[0].Value);
|
||||||
|
if (func.LowerCaseName() == "set" || func.LowerCaseName() == "option")
|
||||||
|
variables << QString::fromUtf8(func.Arguments()[0].Value);
|
||||||
|
}
|
||||||
|
return {functions, variables};
|
||||||
|
}
|
||||||
|
|
||||||
IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
||||||
{
|
{
|
||||||
CMakeKeywords keywords;
|
CMakeKeywords keywords;
|
||||||
@@ -245,6 +271,9 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto [localFunctions, localVariables] = getLocalFunctionsAndVariables(
|
||||||
|
interface()->textAt(0, prevFunctionEnd + 1).toUtf8());
|
||||||
|
|
||||||
QList<AssistProposalItemInterface *> items;
|
QList<AssistProposalItemInterface *> items;
|
||||||
|
|
||||||
const QString varGenexToken = interface()->textAt(startPos - 2, 2);
|
const QString varGenexToken = interface()->textAt(startPos - 2, 2);
|
||||||
@@ -267,6 +296,7 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
|| functionName == "cmake_print_variables") {
|
|| functionName == "cmake_print_variables") {
|
||||||
items.append(generateList(keywords.variables, m_variableIcon));
|
items.append(generateList(keywords.variables, m_variableIcon));
|
||||||
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
||||||
|
items.append(generateList(localVariables, m_variableIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionName == "if" || functionName == "elseif" || functionName == "cmake_policy")
|
if (functionName == "if" || functionName == "elseif" || functionName == "cmake_policy")
|
||||||
@@ -310,6 +340,7 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
// On a new line we just want functions
|
// On a new line we just want functions
|
||||||
items.append(generateList(keywords.functions, m_functionIcon));
|
items.append(generateList(keywords.functions, m_functionIcon));
|
||||||
items.append(generateList(projectKeywords.functions, m_projectFunctionIcon));
|
items.append(generateList(projectKeywords.functions, m_projectFunctionIcon));
|
||||||
|
items.append(generateList(localFunctions, m_functionIcon));
|
||||||
|
|
||||||
// Snippets would make more sense only for the top level suggestions
|
// Snippets would make more sense only for the top level suggestions
|
||||||
items.append(m_snippetCollector.collect());
|
items.append(m_snippetCollector.collect());
|
||||||
@@ -319,6 +350,8 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
if (!onlyFileItems()) {
|
if (!onlyFileItems()) {
|
||||||
items.append(generateList(keywords.variables, m_variableIcon));
|
items.append(generateList(keywords.variables, m_variableIcon));
|
||||||
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
||||||
|
items.append(generateList(localVariables, m_variableIcon));
|
||||||
|
|
||||||
items.append(generateList(keywords.properties, m_propertyIcon));
|
items.append(generateList(keywords.properties, m_propertyIcon));
|
||||||
items.append(generateList(buildTargets, m_targetsIcon));
|
items.append(generateList(buildTargets, m_targetsIcon));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user