forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/6.0'
Conflicts: cmake/QtCreatorIDEBranding.cmake qbs/modules/qtc/qtc.qbs qtcreator_ide_branding.pri Change-Id: I366a1a0e378811dfc9b4e6f42ec96426dbb15356
This commit is contained in:
@@ -338,7 +338,9 @@ IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper()
|
||||
return createProposal();
|
||||
break;
|
||||
case ClangCompletionContextAnalyzer::CompleteIncludePath:
|
||||
if (completeInclude(analyzer.positionEndOfExpression()))
|
||||
m_completions = completeInclude(analyzer.positionEndOfExpression(), m_completionOperator,
|
||||
m_interface.data(), m_interface->headerPaths());
|
||||
if (!m_completions.isEmpty())
|
||||
return createProposal();
|
||||
break;
|
||||
case ClangCompletionContextAnalyzer::CompletePreprocessorDirective:
|
||||
@@ -441,38 +443,46 @@ bool ClangCompletionAssistProcessor::accepts() const
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @param position - cursor placed after opening bracked or quote
|
||||
* @param completionOperator - the type of token
|
||||
* @param interface - relevant document data
|
||||
* @param headerPaths - the include paths
|
||||
* @return the list of completion items
|
||||
*/
|
||||
bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
|
||||
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::completeInclude(
|
||||
int position, unsigned completionOperator, const TextEditor::AssistInterface *interface,
|
||||
const ProjectExplorer::HeaderPaths &headerPaths)
|
||||
{
|
||||
QTextCursor cursor(interface->textDocument());
|
||||
cursor.setPosition(position);
|
||||
QString directoryPrefix;
|
||||
if (m_completionOperator == T_SLASH) {
|
||||
if (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_completionOperator = T_ANGLE_STRING_LITERAL;
|
||||
completionOperator = T_ANGLE_STRING_LITERAL;
|
||||
} else {
|
||||
m_completionOperator = T_STRING_LITERAL;
|
||||
completionOperator = T_STRING_LITERAL;
|
||||
}
|
||||
if (startCharPos != -1)
|
||||
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
|
||||
}
|
||||
|
||||
// Make completion for all relevant includes
|
||||
ProjectExplorer::HeaderPaths headerPaths = m_interface->headerPaths();
|
||||
ProjectExplorer::HeaderPaths allHeaderPaths = headerPaths;
|
||||
const auto currentFilePath = ProjectExplorer::HeaderPath::makeUser(
|
||||
m_interface->filePath().toFileInfo().path());
|
||||
if (!headerPaths.contains(currentFilePath))
|
||||
headerPaths.append(currentFilePath);
|
||||
interface->filePath().toFileInfo().path());
|
||||
if (!allHeaderPaths.contains(currentFilePath))
|
||||
allHeaderPaths.append(currentFilePath);
|
||||
|
||||
const ::Utils::MimeType mimeType = ::Utils::mimeTypeForName("text/x-c++hdr");
|
||||
const QStringList suffixes = mimeType.suffixes();
|
||||
|
||||
foreach (const ProjectExplorer::HeaderPath &headerPath, headerPaths) {
|
||||
QList<AssistProposalItemInterface *> completions;
|
||||
foreach (const ProjectExplorer::HeaderPath &headerPath, allHeaderPaths) {
|
||||
QString realPath = headerPath.path;
|
||||
if (!directoryPrefix.isEmpty()) {
|
||||
realPath += QLatin1Char('/');
|
||||
@@ -480,11 +490,11 @@ bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
|
||||
if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
|
||||
realPath += QLatin1String(".framework/Headers");
|
||||
}
|
||||
completeIncludePath(realPath, suffixes);
|
||||
completions << completeIncludePath(realPath, suffixes, completionOperator);
|
||||
}
|
||||
|
||||
QList<QPair<AssistProposalItemInterface *, QString>> completionsForSorting;
|
||||
for (AssistProposalItemInterface * const item : qAsConst(m_completions)) {
|
||||
for (AssistProposalItemInterface * const item : qAsConst(completions)) {
|
||||
QString s = item->text();
|
||||
s.replace('/', QChar(0)); // The dir separator should compare less than anything else.
|
||||
completionsForSorting << qMakePair(item, s);
|
||||
@@ -493,26 +503,21 @@ bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
|
||||
return left.second < right.second;
|
||||
});
|
||||
for (int i = 0; i < completionsForSorting.count(); ++i)
|
||||
m_completions[i] = completionsForSorting[i].first;
|
||||
completions[i] = completionsForSorting[i].first;
|
||||
|
||||
return !m_completions.isEmpty();
|
||||
}
|
||||
|
||||
bool ClangCompletionAssistProcessor::completeInclude(int position)
|
||||
{
|
||||
QTextCursor textCursor(m_interface->textDocument()); // TODO: Simplify, move into function
|
||||
textCursor.setPosition(position);
|
||||
return completeInclude(textCursor);
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds #include completion proposals using given include path
|
||||
* @brief Finds #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
|
||||
* @return a list of matching completion items
|
||||
*/
|
||||
void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath,
|
||||
const QStringList &suffixes)
|
||||
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::completeIncludePath(
|
||||
const QString &realPath, const QStringList &suffixes, unsigned completionOperator)
|
||||
{
|
||||
QList<AssistProposalItemInterface *> completions;
|
||||
QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
//: Parent folder for proposed #include completion
|
||||
const QString hint = tr("Location: %1").arg(QDir::toNativeSeparators(QDir::cleanPath(realPath)));
|
||||
@@ -529,10 +534,11 @@ void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath
|
||||
item->setText(text);
|
||||
item->setDetail(hint);
|
||||
item->setIcon(CPlusPlus::Icons::keywordIcon());
|
||||
item->setCompletionOperator(m_completionOperator);
|
||||
m_completions.append(item);
|
||||
item->setCompletionOperator(completionOperator);
|
||||
completions.append(item);
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
bool ClangCompletionAssistProcessor::completePreprocessorDirectives()
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QTextCursor>
|
||||
|
||||
namespace TextEditor {
|
||||
class AssistInterface;
|
||||
class AssistProposalItemInterface;
|
||||
}
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
@@ -48,6 +53,11 @@ public:
|
||||
ClangCompletionAssistProcessor();
|
||||
~ClangCompletionAssistProcessor() override;
|
||||
|
||||
static QList<TextEditor::AssistProposalItemInterface *> completeInclude(
|
||||
int position, unsigned completionOperator,
|
||||
const TextEditor::AssistInterface *interface,
|
||||
const ProjectExplorer::HeaderPaths &headerPaths);
|
||||
|
||||
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
|
||||
|
||||
void handleAvailableCompletions(const CodeCompletions &completions);
|
||||
@@ -65,12 +75,10 @@ private:
|
||||
TextEditor::IAssistProposal *createProposal();
|
||||
TextEditor::IAssistProposal *createFunctionHintProposal(
|
||||
const CodeCompletions &completions);
|
||||
|
||||
QList<TextEditor::AssistProposalItemInterface *> toAssistProposalItems(
|
||||
const CodeCompletions &completions) const;
|
||||
bool completeInclude(const QTextCursor &cursor);
|
||||
bool completeInclude(int position);
|
||||
void completeIncludePath(const QString &realPath, const QStringList &suffixes);
|
||||
static QList<TextEditor::AssistProposalItemInterface *> completeIncludePath(
|
||||
const QString &realPath, const QStringList &suffixes, unsigned completionOperator);
|
||||
bool completePreprocessorDirectives();
|
||||
bool completeDoxygenKeywords();
|
||||
void addCompletionItem(const QString &text,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "clangdclient.h"
|
||||
|
||||
#include "clangcompletionassistprocessor.h"
|
||||
#include "clangcompletioncontextanalyzer.h"
|
||||
#include "clangdiagnosticmanager.h"
|
||||
#include "clangmodelmanagersupport.h"
|
||||
@@ -66,6 +67,7 @@
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDateTime>
|
||||
@@ -810,14 +812,15 @@ public:
|
||||
};
|
||||
|
||||
|
||||
enum class CustomAssistMode { Doxygen, Preprocessor };
|
||||
enum class CustomAssistMode { Doxygen, Preprocessor, IncludePath };
|
||||
class CustomAssistProcessor : public IAssistProcessor
|
||||
{
|
||||
public:
|
||||
CustomAssistProcessor(ClangdClient *client, int position, unsigned completionOperator,
|
||||
CustomAssistMode mode)
|
||||
CustomAssistProcessor(ClangdClient *client, int position, int endPos,
|
||||
unsigned completionOperator, CustomAssistMode mode)
|
||||
: m_client(client)
|
||||
, m_position(position)
|
||||
, m_endPos(endPos)
|
||||
, m_completionOperator(completionOperator)
|
||||
, m_mode(mode)
|
||||
{}
|
||||
@@ -833,7 +836,7 @@ private:
|
||||
CPlusPlus::Icons::keywordIcon());
|
||||
}
|
||||
break;
|
||||
case CustomAssistMode::Preprocessor:
|
||||
case CustomAssistMode::Preprocessor: {
|
||||
static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro);
|
||||
for (const QString &completion
|
||||
: CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) {
|
||||
@@ -843,6 +846,17 @@ private:
|
||||
completions << createItem("import", macroIcon);
|
||||
break;
|
||||
}
|
||||
case ClangCodeModel::Internal::CustomAssistMode::IncludePath: {
|
||||
HeaderPaths headerPaths;
|
||||
const CppEditor::ProjectPart::ConstPtr projectPart
|
||||
= projectPartForFile(interface->filePath().toString());
|
||||
if (projectPart)
|
||||
headerPaths = projectPart->headerPaths;
|
||||
completions = ClangCompletionAssistProcessor::completeInclude(
|
||||
m_endPos, m_completionOperator, interface, headerPaths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GenericProposalModelPtr model(new GenericProposalModel);
|
||||
model->loadContent(completions);
|
||||
const auto proposal = new GenericProposal(m_position, model);
|
||||
@@ -864,6 +878,7 @@ private:
|
||||
|
||||
ClangdClient * const m_client;
|
||||
const int m_position;
|
||||
const int m_endPos;
|
||||
const unsigned m_completionOperator;
|
||||
const CustomAssistMode m_mode;
|
||||
};
|
||||
@@ -1110,6 +1125,12 @@ public:
|
||||
using LanguageClientCompletionItem::LanguageClientCompletionItem;
|
||||
void apply(TextDocumentManipulatorInterface &manipulator,
|
||||
int basePosition) const override;
|
||||
|
||||
enum class SpecialQtType { Signal, Slot, None };
|
||||
static SpecialQtType getQtType(const CompletionItem &item);
|
||||
|
||||
private:
|
||||
QIcon icon() const override;
|
||||
};
|
||||
|
||||
class ClangdClient::ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor
|
||||
@@ -1155,15 +1176,7 @@ ClangdClient::ClangdCompletionAssistProcessor::generateCompletionItems(
|
||||
// whether the cursor was on the second argument of a (dis)connect() call.
|
||||
// If so, we offer only signals, as nothing else makes sense in that context.
|
||||
static const auto criterion = [](const CompletionItem &ci) {
|
||||
const Utils::optional<MarkupOrString> doc = ci.documentation();
|
||||
if (!doc)
|
||||
return false;
|
||||
QString docText;
|
||||
if (Utils::holds_alternative<QString>(*doc))
|
||||
docText = Utils::get<QString>(*doc);
|
||||
else if (Utils::holds_alternative<MarkupContent>(*doc))
|
||||
docText = Utils::get<MarkupContent>(*doc).content();
|
||||
return docText.contains("Annotation: qt_signal");
|
||||
return ClangdCompletionItem::getQtType(ci) == ClangdCompletionItem::SpecialQtType::Signal;
|
||||
};
|
||||
const QTextDocument *doc = document();
|
||||
const int pos = basePos();
|
||||
@@ -1526,6 +1539,30 @@ QString ClangdClient::displayNameFromDocumentSymbol(SymbolKind kind, const QStri
|
||||
}
|
||||
}
|
||||
|
||||
// Force re-parse of all open files that include the changed ui header.
|
||||
// Otherwise, we potentially have stale diagnostics.
|
||||
void ClangdClient::handleUiHeaderChange(const QString &fileName)
|
||||
{
|
||||
const QRegularExpression includeRex("#include.*" + fileName + R"([>"])");
|
||||
const QVector<Client *> &allClients = LanguageClientManager::clients();
|
||||
for (Client * const client : allClients) {
|
||||
if (!client->reachable() || !qobject_cast<ClangdClient *>(client))
|
||||
continue;
|
||||
for (IDocument * const doc : DocumentModel::openedDocuments()) {
|
||||
const auto textDoc = qobject_cast<TextDocument *>(doc);
|
||||
if (!textDoc || !client->documentOpen(textDoc))
|
||||
continue;
|
||||
const QTextCursor includePos = textDoc->document()->find(includeRex);
|
||||
if (includePos.isNull())
|
||||
continue;
|
||||
qCDebug(clangdLog) << "updating" << textDoc->filePath() << "due to change in UI header"
|
||||
<< fileName;
|
||||
client->documentContentsChanged(textDoc, 0, 0, 0);
|
||||
break; // No sane project includes the same UI header twice.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Location> &locations)
|
||||
{
|
||||
const auto refData = runningFindUsages.find(key);
|
||||
@@ -2817,14 +2854,26 @@ IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor(
|
||||
qCDebug(clangdLogCompletion) << "creating doxygen processor";
|
||||
return new CustomAssistProcessor(m_client,
|
||||
contextAnalyzer.positionForProposal(),
|
||||
contextAnalyzer.positionEndOfExpression(),
|
||||
contextAnalyzer.completionOperator(),
|
||||
CustomAssistMode::Doxygen);
|
||||
case ClangCompletionContextAnalyzer::CompletePreprocessorDirective:
|
||||
qCDebug(clangdLogCompletion) << "creating macro processor";
|
||||
return new CustomAssistProcessor(m_client,
|
||||
contextAnalyzer.positionForProposal(),
|
||||
contextAnalyzer.positionEndOfExpression(),
|
||||
contextAnalyzer.completionOperator(),
|
||||
CustomAssistMode::Preprocessor);
|
||||
case ClangCompletionContextAnalyzer::CompleteIncludePath:
|
||||
if (m_client->versionNumber() < QVersionNumber(14)) { // https://reviews.llvm.org/D112996
|
||||
qCDebug(clangdLogCompletion) << "creating include processor";
|
||||
return new CustomAssistProcessor(m_client,
|
||||
contextAnalyzer.positionForProposal(),
|
||||
contextAnalyzer.positionEndOfExpression(),
|
||||
contextAnalyzer.completionOperator(),
|
||||
CustomAssistMode::IncludePath);
|
||||
}
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -3009,6 +3058,38 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
|
||||
}
|
||||
}
|
||||
|
||||
ClangdCompletionItem::SpecialQtType ClangdCompletionItem::getQtType(const CompletionItem &item)
|
||||
{
|
||||
const Utils::optional<MarkupOrString> doc = item.documentation();
|
||||
if (!doc)
|
||||
return SpecialQtType::None;
|
||||
QString docText;
|
||||
if (Utils::holds_alternative<QString>(*doc))
|
||||
docText = Utils::get<QString>(*doc);
|
||||
else if (Utils::holds_alternative<MarkupContent>(*doc))
|
||||
docText = Utils::get<MarkupContent>(*doc).content();
|
||||
if (docText.contains("Annotation: qt_signal"))
|
||||
return SpecialQtType::Signal;
|
||||
if (docText.contains("Annotation: qt_slot"))
|
||||
return SpecialQtType::Slot;
|
||||
return SpecialQtType::None;
|
||||
}
|
||||
|
||||
QIcon ClangdCompletionItem::icon() const
|
||||
{
|
||||
const SpecialQtType qtType = getQtType(item());
|
||||
switch (qtType) {
|
||||
case SpecialQtType::Signal:
|
||||
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Signal);
|
||||
case SpecialQtType::Slot:
|
||||
// FIXME: Add visibility info to completion item tags in clangd?
|
||||
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::SlotPublic);
|
||||
case SpecialQtType::None:
|
||||
break;
|
||||
}
|
||||
return LanguageClientCompletionItem::icon();
|
||||
}
|
||||
|
||||
MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
||||
const AstHandler &astHandler,
|
||||
AstCallbackMode callbackMode, const Range &range)
|
||||
|
||||
@@ -86,6 +86,8 @@ public:
|
||||
static QString displayNameFromDocumentSymbol(LanguageServerProtocol::SymbolKind kind,
|
||||
const QString &name, const QString &detail);
|
||||
|
||||
static void handleUiHeaderChange(const QString &fileName);
|
||||
|
||||
signals:
|
||||
void indexingFinished();
|
||||
void foundReferences(const QList<Core::SearchResultItem> &items);
|
||||
|
||||
@@ -155,9 +155,10 @@ static ::Utils::ProcessLinkCallback extendedCallback(::Utils::ProcessLinkCallbac
|
||||
// If globalFollowSymbol finds nothing follow to the declaration.
|
||||
return [original_callback = std::move(callback), result](const ::Utils::Link &link) {
|
||||
if (link.linkTextStart < 0 && result.isResultOnlyForFallBack) {
|
||||
return original_callback(::Utils::Link(::Utils::FilePath::fromString(result.fileName),
|
||||
result.startLine,
|
||||
result.startColumn - 1));
|
||||
return original_callback(Utils::Link(
|
||||
Utils::FilePath::fromString(result.fileName).cleanPath(),
|
||||
result.startLine,
|
||||
result.startColumn - 1));
|
||||
}
|
||||
return original_callback(link);
|
||||
};
|
||||
@@ -242,7 +243,7 @@ void ClangFollowSymbol::findLink(const CppEditor::CursorInEditor &data,
|
||||
symbolFinder,
|
||||
inNextSplit);
|
||||
} else {
|
||||
callback(Link(Utils::FilePath::fromString(result.fileName),
|
||||
callback(Link(Utils::FilePath::fromString(result.fileName).cleanPath(),
|
||||
result.startLine,
|
||||
result.startColumn - 1));
|
||||
}
|
||||
|
||||
@@ -608,6 +608,7 @@ void ClangModelManagerSupport::onAbstractEditorSupportContentsUpdated(const QStr
|
||||
|
||||
const QString mappedPath = m_uiHeaderOnDiskManager.write(filePath, content);
|
||||
m_communicator.unsavedFilesUpdated(mappedPath, content, 0);
|
||||
ClangdClient::handleUiHeaderChange(Utils::FilePath::fromString(filePath).fileName());
|
||||
}
|
||||
|
||||
void ClangModelManagerSupport::onAbstractEditorSupportRemoved(const QString &filePath)
|
||||
@@ -618,6 +619,7 @@ void ClangModelManagerSupport::onAbstractEditorSupportRemoved(const QString &fil
|
||||
const QString mappedPath = m_uiHeaderOnDiskManager.remove(filePath);
|
||||
const QString projectPartId = projectPartIdForFile(filePath);
|
||||
m_communicator.unsavedFilesRemoved({{mappedPath, projectPartId}});
|
||||
ClangdClient::handleUiHeaderChange(Utils::FilePath::fromString(filePath).fileName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1488,9 +1488,15 @@ void ClangdTestCompletion::testCompleteIncludeDirective()
|
||||
getProposal("includeDirectiveCompletion.cpp", proposal);
|
||||
|
||||
QVERIFY(proposal);
|
||||
QVERIFY(hasItem(proposal, " file.h>"));
|
||||
QVERIFY(hasItem(proposal, " otherFile.h>"));
|
||||
QVERIFY(hasItem(proposal, " mylib/"));
|
||||
if (client()->versionNumber() < QVersionNumber(14)) {
|
||||
QVERIFY(hasItem(proposal, "file.h"));
|
||||
QVERIFY(hasItem(proposal, "otherFile.h"));
|
||||
QVERIFY(hasItem(proposal, "mylib/"));
|
||||
} else {
|
||||
QVERIFY(hasItem(proposal, " file.h>"));
|
||||
QVERIFY(hasItem(proposal, " otherFile.h>"));
|
||||
QVERIFY(hasItem(proposal, " mylib/"));
|
||||
}
|
||||
QVERIFY(!hasSnippet(proposal, "class "));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user