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/editormanager/editormanager.h>
|
||||||
#include <coreplugin/find/searchresultitem.h>
|
#include <coreplugin/find/searchresultitem.h>
|
||||||
#include <coreplugin/find/searchresultwindow.h>
|
#include <coreplugin/find/searchresultwindow.h>
|
||||||
|
#include <cplusplus/AST.h>
|
||||||
|
#include <cplusplus/ASTPath.h>
|
||||||
#include <cplusplus/FindUsages.h>
|
#include <cplusplus/FindUsages.h>
|
||||||
#include <cplusplus/Icons.h>
|
#include <cplusplus/Icons.h>
|
||||||
#include <cplusplus/MatchingText.h>
|
#include <cplusplus/MatchingText.h>
|
||||||
@@ -1090,7 +1092,9 @@ public:
|
|||||||
|
|
||||||
void handleDeclDefSwitchReplies();
|
void handleDeclDefSwitchReplies();
|
||||||
|
|
||||||
|
static CppEditor::CppEditorWidget *widgetFromDocument(const TextDocument *doc);
|
||||||
QString searchTermFromCursor(const QTextCursor &cursor) const;
|
QString searchTermFromCursor(const QTextCursor &cursor) const;
|
||||||
|
static QTextCursor adjustedCursor(const QTextCursor &cursor, const TextDocument *doc);
|
||||||
|
|
||||||
void setHelpItemForTooltip(const MessageId &token, const QString &fqn = {},
|
void setHelpItemForTooltip(const MessageId &token, const QString &fqn = {},
|
||||||
HelpItem::Category category = HelpItem::Unknown,
|
HelpItem::Category category = HelpItem::Unknown,
|
||||||
@@ -1380,22 +1384,23 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
|
|||||||
if (searchTerm.isEmpty())
|
if (searchTerm.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const QTextCursor adjustedCursor = Private::adjustedCursor(cursor, document);
|
||||||
const bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences();
|
const bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences();
|
||||||
|
|
||||||
// If it's a "normal" symbol, go right ahead.
|
// If it's a "normal" symbol, go right ahead.
|
||||||
if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) {
|
if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) {
|
||||||
return c.isLetterOrNumber() || c == '_';
|
return c.isLetterOrNumber() || c == '_';
|
||||||
})) {
|
})) {
|
||||||
d->findUsages(document, cursor, searchTerm, replacement, categorize);
|
d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise get the proper spelling of the search term from clang, so we can put it into the
|
// Otherwise get the proper spelling of the search term from clang, so we can put it into the
|
||||||
// search widget.
|
// search widget.
|
||||||
const TextDocumentIdentifier docId(DocumentUri::fromFilePath(document->filePath()));
|
const TextDocumentIdentifier docId(DocumentUri::fromFilePath(document->filePath()));
|
||||||
const TextDocumentPositionParams params(docId, Range(cursor).start());
|
const TextDocumentPositionParams params(docId, Range(adjustedCursor).start());
|
||||||
SymbolInfoRequest symReq(params);
|
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) {
|
(const SymbolInfoRequest::Response &response) {
|
||||||
if (!doc)
|
if (!doc)
|
||||||
return;
|
return;
|
||||||
@@ -1408,7 +1413,7 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
|
|||||||
const SymbolDetails &sd = list->first();
|
const SymbolDetails &sd = list->first();
|
||||||
if (sd.name().isEmpty())
|
if (sd.name().isEmpty())
|
||||||
return;
|
return;
|
||||||
d->findUsages(doc.data(), cursor, sd.name(), replacement, categorize);
|
d->findUsages(doc.data(), adjustedCursor, sd.name(), replacement, categorize);
|
||||||
});
|
});
|
||||||
sendContent(symReq);
|
sendContent(symReq);
|
||||||
}
|
}
|
||||||
@@ -1444,6 +1449,12 @@ void ClangdClient::handleDocumentClosed(TextDocument *doc)
|
|||||||
d->virtualRanges.remove(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()
|
const LanguageClient::Client::CustomInspectorTabs ClangdClient::createCustomInspectorTabs()
|
||||||
{
|
{
|
||||||
return {std::make_pair(new MemoryUsageWidget(this), tr("Memory Usage"))};
|
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));
|
QTC_ASSERT(documentOpen(document), openDocument(document));
|
||||||
|
const QTextCursor adjustedCursor = Private::adjustedCursor(cursor, document);
|
||||||
if (!resolveTarget) {
|
if (!resolveTarget) {
|
||||||
d->followSymbolData.reset();
|
d->followSymbolData.reset();
|
||||||
symbolSupport().findLinkAt(document, cursor, std::move(callback), false);
|
symbolSupport().findLinkAt(document, adjustedCursor, std::move(callback), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
||||||
<< cursor.blockNumber() << cursor.positionInBlock();
|
<< adjustedCursor.blockNumber() << adjustedCursor.positionInBlock();
|
||||||
d->followSymbolData.emplace(this, ++d->nextJobId, cursor, editorWidget,
|
d->followSymbolData.emplace(this, ++d->nextJobId, adjustedCursor, editorWidget,
|
||||||
DocumentUri::fromFilePath(document->filePath()),
|
DocumentUri::fromFilePath(document->filePath()),
|
||||||
std::move(callback), openInSplit);
|
std::move(callback), openInSplit);
|
||||||
|
|
||||||
@@ -1809,7 +1821,7 @@ void ClangdClient::followSymbol(TextDocument *document,
|
|||||||
if (d->followSymbolData->cursorNode)
|
if (d->followSymbolData->cursorNode)
|
||||||
d->handleGotoDefinitionResult();
|
d->handleGotoDefinitionResult();
|
||||||
};
|
};
|
||||||
symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true);
|
symbolSupport().findLinkAt(document, adjustedCursor, std::move(gotoDefCallback), true);
|
||||||
|
|
||||||
if (versionNumber() < QVersionNumber(12)) {
|
if (versionNumber() < QVersionNumber(12)) {
|
||||||
d->followSymbolData->cursorNode.emplace(AstNode());
|
d->followSymbolData->cursorNode.emplace(AstNode());
|
||||||
@@ -1825,7 +1837,8 @@ void ClangdClient::followSymbol(TextDocument *document,
|
|||||||
if (d->followSymbolData->defLink.hasValidTarget())
|
if (d->followSymbolData->defLink.hasValidTarget())
|
||||||
d->handleGotoDefinitionResult();
|
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,
|
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
|
||||||
@@ -2347,6 +2360,13 @@ void ClangdClient::Private::handleDeclDefSwitchReplies()
|
|||||||
switchDeclDefData.reset();
|
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
|
QString ClangdClient::Private::searchTermFromCursor(const QTextCursor &cursor) const
|
||||||
{
|
{
|
||||||
QTextCursor termCursor(cursor);
|
QTextCursor termCursor(cursor);
|
||||||
@@ -2354,6 +2374,36 @@ QString ClangdClient::Private::searchTermFromCursor(const QTextCursor &cursor) c
|
|||||||
return termCursor.selectedText();
|
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,
|
void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const QString &fqn,
|
||||||
HelpItem::Category category,
|
HelpItem::Category category,
|
||||||
const QString &type)
|
const QString &type)
|
||||||
@@ -2704,12 +2754,10 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
|
|||||||
if (clangdLogAst().isDebugEnabled())
|
if (clangdLogAst().isDebugEnabled())
|
||||||
ast.print();
|
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(),
|
const auto runner = [tokens, filePath = doc->filePath(),
|
||||||
text = doc->document()->toPlainText(), ast,
|
text = doc->document()->toPlainText(), ast,
|
||||||
w = QPointer(editorWidget), rev = doc->document()->revision(),
|
w = QPointer<TextEditorWidget>(widgetFromDocument(doc)),
|
||||||
|
rev = doc->document()->revision(),
|
||||||
clangdVersion = q->versionNumber()] {
|
clangdVersion = q->versionNumber()] {
|
||||||
return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, w, rev,
|
return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, w, rev,
|
||||||
clangdVersion);
|
clangdVersion);
|
||||||
|
@@ -102,6 +102,8 @@ private:
|
|||||||
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override;
|
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override;
|
||||||
void handleDocumentOpened(TextEditor::TextDocument *doc) override;
|
void handleDocumentOpened(TextEditor::TextDocument *doc) override;
|
||||||
void handleDocumentClosed(TextEditor::TextDocument *doc) override;
|
void handleDocumentClosed(TextEditor::TextDocument *doc) override;
|
||||||
|
QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||||
|
TextEditor::TextDocument *doc) override;
|
||||||
const CustomInspectorTabs createCustomInspectorTabs() override;
|
const CustomInspectorTabs createCustomInspectorTabs() override;
|
||||||
|
|
||||||
class Private;
|
class Private;
|
||||||
|
@@ -523,8 +523,10 @@ void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget)
|
|||||||
if (m_highlightRequests.contains(widget))
|
if (m_highlightRequests.contains(widget))
|
||||||
cancelRequest(m_highlightRequests.take(widget));
|
cancelRequest(m_highlightRequests.take(widget));
|
||||||
|
|
||||||
|
const QTextCursor adjustedCursor = adjustedCursorForHighlighting(widget->textCursor(),
|
||||||
|
widget->textDocument());
|
||||||
DocumentHighlightsRequest request(
|
DocumentHighlightsRequest request(
|
||||||
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor())));
|
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position{adjustedCursor}));
|
||||||
auto connection = connect(widget, &QObject::destroyed, this, [this, widget]() {
|
auto connection = connect(widget, &QObject::destroyed, this, [this, widget]() {
|
||||||
if (m_highlightRequests.contains(widget))
|
if (m_highlightRequests.contains(widget))
|
||||||
cancelRequest(m_highlightRequests.take(widget));
|
cancelRequest(m_highlightRequests.take(widget));
|
||||||
@@ -1570,4 +1572,11 @@ bool Client::sendWorkspceFolderChanges() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextCursor Client::adjustedCursorForHighlighting(const QTextCursor &cursor,
|
||||||
|
TextEditor::TextDocument *doc)
|
||||||
|
{
|
||||||
|
Q_UNUSED(doc)
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
@@ -248,6 +248,8 @@ private:
|
|||||||
|
|
||||||
virtual void handleDocumentClosed(TextEditor::TextDocument *) {}
|
virtual void handleDocumentClosed(TextEditor::TextDocument *) {}
|
||||||
virtual void handleDocumentOpened(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 &,
|
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
|
||||||
LanguageServerProtocol::ResponseHandlers,
|
LanguageServerProtocol::ResponseHandlers,
|
||||||
|
Reference in New Issue
Block a user