forked from qt-creator/qt-creator
ClangCodeModel: Work around clangd cursor issue
If the cursor is right before the "." in a member access expression, clangd interprets it as belonging to the member instead of the base expression, which leads to unexpected behavior. Work around this by sending a cursor position one to the left of the real one to clangd in such cases. Change-Id: I429ee9189760ccb02d231acfcb94ab6cfde3cd8d Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -38,6 +38,8 @@
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/find/searchresultitem.h>
|
||||
#include <coreplugin/find/searchresultwindow.h>
|
||||
#include <cplusplus/AST.h>
|
||||
#include <cplusplus/ASTPath.h>
|
||||
#include <cplusplus/FindUsages.h>
|
||||
#include <cplusplus/Icons.h>
|
||||
#include <cplusplus/MatchingText.h>
|
||||
@@ -1090,7 +1092,9 @@ public:
|
||||
|
||||
void handleDeclDefSwitchReplies();
|
||||
|
||||
static CppEditor::CppEditorWidget *widgetFromDocument(const TextDocument *doc);
|
||||
QString searchTermFromCursor(const QTextCursor &cursor) const;
|
||||
static QTextCursor adjustedCursor(const QTextCursor &cursor, const TextDocument *doc);
|
||||
|
||||
void setHelpItemForTooltip(const MessageId &token, const QString &fqn = {},
|
||||
HelpItem::Category category = HelpItem::Unknown,
|
||||
@@ -1380,22 +1384,23 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
|
||||
if (searchTerm.isEmpty())
|
||||
return;
|
||||
|
||||
const QTextCursor adjustedCursor = Private::adjustedCursor(cursor, document);
|
||||
const bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences();
|
||||
|
||||
// If it's a "normal" symbol, go right ahead.
|
||||
if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) {
|
||||
return c.isLetterOrNumber() || c == '_';
|
||||
})) {
|
||||
d->findUsages(document, cursor, searchTerm, replacement, categorize);
|
||||
d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise get the proper spelling of the search term from clang, so we can put it into the
|
||||
// search widget.
|
||||
const TextDocumentIdentifier docId(DocumentUri::fromFilePath(document->filePath()));
|
||||
const TextDocumentPositionParams params(docId, Range(cursor).start());
|
||||
const TextDocumentPositionParams params(docId, Range(adjustedCursor).start());
|
||||
SymbolInfoRequest symReq(params);
|
||||
symReq.setResponseCallback([this, doc = QPointer(document), cursor, replacement, categorize]
|
||||
symReq.setResponseCallback([this, doc = QPointer(document), adjustedCursor, replacement, categorize]
|
||||
(const SymbolInfoRequest::Response &response) {
|
||||
if (!doc)
|
||||
return;
|
||||
@@ -1408,7 +1413,7 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
|
||||
const SymbolDetails &sd = list->first();
|
||||
if (sd.name().isEmpty())
|
||||
return;
|
||||
d->findUsages(doc.data(), cursor, sd.name(), replacement, categorize);
|
||||
d->findUsages(doc.data(), adjustedCursor, sd.name(), replacement, categorize);
|
||||
});
|
||||
sendContent(symReq);
|
||||
}
|
||||
@@ -1444,6 +1449,12 @@ void ClangdClient::handleDocumentClosed(TextDocument *doc)
|
||||
d->virtualRanges.remove(doc);
|
||||
}
|
||||
|
||||
QTextCursor ClangdClient::adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||
TextEditor::TextDocument *doc)
|
||||
{
|
||||
return Private::adjustedCursor(cursor, doc);
|
||||
}
|
||||
|
||||
const LanguageClient::Client::CustomInspectorTabs ClangdClient::createCustomInspectorTabs()
|
||||
{
|
||||
return {std::make_pair(new MemoryUsageWidget(this), tr("Memory Usage"))};
|
||||
@@ -1782,15 +1793,16 @@ void ClangdClient::followSymbol(TextDocument *document,
|
||||
)
|
||||
{
|
||||
QTC_ASSERT(documentOpen(document), openDocument(document));
|
||||
const QTextCursor adjustedCursor = Private::adjustedCursor(cursor, document);
|
||||
if (!resolveTarget) {
|
||||
d->followSymbolData.reset();
|
||||
symbolSupport().findLinkAt(document, cursor, std::move(callback), false);
|
||||
symbolSupport().findLinkAt(document, adjustedCursor, std::move(callback), false);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
||||
<< cursor.blockNumber() << cursor.positionInBlock();
|
||||
d->followSymbolData.emplace(this, ++d->nextJobId, cursor, editorWidget,
|
||||
<< adjustedCursor.blockNumber() << adjustedCursor.positionInBlock();
|
||||
d->followSymbolData.emplace(this, ++d->nextJobId, adjustedCursor, editorWidget,
|
||||
DocumentUri::fromFilePath(document->filePath()),
|
||||
std::move(callback), openInSplit);
|
||||
|
||||
@@ -1809,7 +1821,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
||||
if (d->followSymbolData->cursorNode)
|
||||
d->handleGotoDefinitionResult();
|
||||
};
|
||||
symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true);
|
||||
symbolSupport().findLinkAt(document, adjustedCursor, std::move(gotoDefCallback), true);
|
||||
|
||||
if (versionNumber() < QVersionNumber(12)) {
|
||||
d->followSymbolData->cursorNode.emplace(AstNode());
|
||||
@@ -1825,7 +1837,8 @@ void ClangdClient::followSymbol(TextDocument *document,
|
||||
if (d->followSymbolData->defLink.hasValidTarget())
|
||||
d->handleGotoDefinitionResult();
|
||||
};
|
||||
d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::AlwaysAsync, Range(cursor));
|
||||
d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::AlwaysAsync,
|
||||
Range(adjustedCursor));
|
||||
}
|
||||
|
||||
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
|
||||
@@ -2347,6 +2360,13 @@ void ClangdClient::Private::handleDeclDefSwitchReplies()
|
||||
switchDeclDefData.reset();
|
||||
}
|
||||
|
||||
CppEditor::CppEditorWidget *ClangdClient::Private::widgetFromDocument(const TextDocument *doc)
|
||||
{
|
||||
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
|
||||
[doc](const IEditor *editor) { return editor->document() == doc; });
|
||||
return qobject_cast<CppEditor::CppEditorWidget *>(TextEditorWidget::fromEditor(editor));
|
||||
}
|
||||
|
||||
QString ClangdClient::Private::searchTermFromCursor(const QTextCursor &cursor) const
|
||||
{
|
||||
QTextCursor termCursor(cursor);
|
||||
@@ -2354,6 +2374,36 @@ QString ClangdClient::Private::searchTermFromCursor(const QTextCursor &cursor) c
|
||||
return termCursor.selectedText();
|
||||
}
|
||||
|
||||
// https://github.com/clangd/clangd/issues/936
|
||||
QTextCursor ClangdClient::Private::adjustedCursor(const QTextCursor &cursor,
|
||||
const TextDocument *doc)
|
||||
{
|
||||
CppEditor::CppEditorWidget * const widget = widgetFromDocument(doc);
|
||||
if (!widget)
|
||||
return cursor;
|
||||
const Document::Ptr cppDoc = widget->semanticInfo().doc;
|
||||
if (!cppDoc)
|
||||
return cursor;
|
||||
const QList<AST *> astPath = ASTPath(cppDoc)(cursor);
|
||||
for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
|
||||
const MemberAccessAST * const memberAccess = (*it)->asMemberAccess();
|
||||
if (!memberAccess)
|
||||
continue;
|
||||
const TranslationUnit * const tu = cppDoc->translationUnit();
|
||||
if (tu->tokenAt(memberAccess->access_token).kind() != T_DOT)
|
||||
return cursor;
|
||||
int dotLine, dotColumn;
|
||||
tu->getTokenPosition(memberAccess->access_token, &dotLine, &dotColumn);
|
||||
const int dotPos = Utils::Text::positionInText(doc->document(), dotLine, dotColumn);
|
||||
if (dotPos != cursor.position())
|
||||
return cursor;
|
||||
QTextCursor c = cursor;
|
||||
c.setPosition(cursor.position() - 1);
|
||||
return c;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const QString &fqn,
|
||||
HelpItem::Category category,
|
||||
const QString &type)
|
||||
@@ -2704,12 +2754,10 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
|
||||
if (clangdLogAst().isDebugEnabled())
|
||||
ast.print();
|
||||
|
||||
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
|
||||
[doc](const IEditor *editor) { return editor->document() == doc; });
|
||||
const auto editorWidget = TextEditorWidget::fromEditor(editor);
|
||||
const auto runner = [tokens, filePath = doc->filePath(),
|
||||
text = doc->document()->toPlainText(), ast,
|
||||
w = QPointer(editorWidget), rev = doc->document()->revision(),
|
||||
w = QPointer<TextEditorWidget>(widgetFromDocument(doc)),
|
||||
rev = doc->document()->revision(),
|
||||
clangdVersion = q->versionNumber()] {
|
||||
return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, w, rev,
|
||||
clangdVersion);
|
||||
|
@@ -102,6 +102,8 @@ private:
|
||||
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override;
|
||||
void handleDocumentOpened(TextEditor::TextDocument *doc) override;
|
||||
void handleDocumentClosed(TextEditor::TextDocument *doc) override;
|
||||
QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||
TextEditor::TextDocument *doc) override;
|
||||
const CustomInspectorTabs createCustomInspectorTabs() override;
|
||||
|
||||
class Private;
|
||||
|
@@ -523,8 +523,10 @@ void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget)
|
||||
if (m_highlightRequests.contains(widget))
|
||||
cancelRequest(m_highlightRequests.take(widget));
|
||||
|
||||
const QTextCursor adjustedCursor = adjustedCursorForHighlighting(widget->textCursor(),
|
||||
widget->textDocument());
|
||||
DocumentHighlightsRequest request(
|
||||
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor())));
|
||||
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position{adjustedCursor}));
|
||||
auto connection = connect(widget, &QObject::destroyed, this, [this, widget]() {
|
||||
if (m_highlightRequests.contains(widget))
|
||||
cancelRequest(m_highlightRequests.take(widget));
|
||||
@@ -1570,4 +1572,11 @@ bool Client::sendWorkspceFolderChanges() const
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextCursor Client::adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||
TextEditor::TextDocument *doc)
|
||||
{
|
||||
Q_UNUSED(doc)
|
||||
return cursor;
|
||||
}
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
@@ -248,6 +248,8 @@ private:
|
||||
|
||||
virtual void handleDocumentClosed(TextEditor::TextDocument *) {}
|
||||
virtual void handleDocumentOpened(TextEditor::TextDocument *) {}
|
||||
virtual QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||
TextEditor::TextDocument *doc);
|
||||
|
||||
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
|
||||
LanguageServerProtocol::ResponseHandlers,
|
||||
|
Reference in New Issue
Block a user