forked from qt-creator/qt-creator
CppEditor: Consider symbol occurrences in comments
... when renaming. For local renaming, we consider only function parameters. Task-number: QTCREATORBUG-12051 Change-Id: I7948d69f11b97663c9bd747ae6241a82dd9bdd82 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -8,58 +8,35 @@
|
||||
#include <cplusplus/Overview.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
#include <QTextBlock>
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace CPlusPlus {
|
||||
|
||||
QList<Token> commentsForDeclaration(const Symbol *symbol, const Snapshot &snapshot,
|
||||
const QTextDocument &textDoc)
|
||||
static QString nameFromSymbol(const Symbol *symbol)
|
||||
{
|
||||
// Set up cpp document.
|
||||
const Document::Ptr cppDoc = snapshot.preprocessedDocument(textDoc.toPlainText().toUtf8(),
|
||||
symbol->filePath());
|
||||
cppDoc->parse();
|
||||
TranslationUnit * const tu = cppDoc->translationUnit();
|
||||
if (!tu || !tu->isParsed())
|
||||
const QStringList symbolParts = Overview().prettyName(symbol->name())
|
||||
.split("::", Qt::SkipEmptyParts);
|
||||
if (symbolParts.isEmpty())
|
||||
return {};
|
||||
return symbolParts.last();
|
||||
}
|
||||
|
||||
// Find the symbol declaration's AST node.
|
||||
// We stop at the last declaration node that precedes the symbol, except:
|
||||
// - For parameter declarations, we just continue, because we are interested in the function.
|
||||
// - If the declaration node is preceded directly by another one, we choose that one instead,
|
||||
// because with nested declarations we want the outer one (e.g. templates).
|
||||
int line, column;
|
||||
tu->getTokenPosition(symbol->sourceLocation(), &line, &column);
|
||||
const QList<AST *> astPath = ASTPath(cppDoc)(line, column);
|
||||
if (astPath.isEmpty())
|
||||
return {};
|
||||
if (astPath.last()->firstToken() != symbol->sourceLocation())
|
||||
return {};
|
||||
const AST *declAst = nullptr;
|
||||
bool needsSymbolReference = false;
|
||||
bool isParameter = false;
|
||||
for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) {
|
||||
AST * const node = *it;
|
||||
if (node->asParameterDeclaration()) {
|
||||
needsSymbolReference = true;
|
||||
isParameter = true;
|
||||
continue;
|
||||
}
|
||||
if (node->asDeclaration()) {
|
||||
declAst = node;
|
||||
continue;
|
||||
}
|
||||
if (declAst)
|
||||
break;
|
||||
}
|
||||
if (!declAst)
|
||||
static QList<Token> commentsForDeclaration(
|
||||
const AST *decl, const QString &symbolName, const QTextDocument &textDoc,
|
||||
const Document::Ptr &cppDoc, bool isParameter)
|
||||
{
|
||||
if (symbolName.isEmpty())
|
||||
return {};
|
||||
|
||||
// Get the list of all tokens (including comments) and find the declaration start token there.
|
||||
const Token &declToken = tu->tokenAt(declAst->firstToken());
|
||||
TranslationUnit * const tu = cppDoc->translationUnit();
|
||||
QTC_ASSERT(tu && tu->isParsed(), return {});
|
||||
const Token &declToken = tu->tokenAt(decl->firstToken());
|
||||
std::vector<Token> allTokens = tu->allTokens();
|
||||
QTC_ASSERT(!allTokens.empty(), return {});
|
||||
int tokenPos = -1;
|
||||
@@ -86,6 +63,7 @@ QList<Token> commentsForDeclaration(const Symbol *symbol, const Snapshot &snapsh
|
||||
const auto blockForTokenEnd = [&](const Token &tok) {
|
||||
return textDoc.findBlock(tu->getTokenEndPositionInDocument(tok, &textDoc));
|
||||
};
|
||||
bool needsSymbolReference = isParameter;
|
||||
for (int i = tokenPos - 1; i >= 0; --i) {
|
||||
const Token &tok = allTokens.at(i);
|
||||
if (!tok.isComment())
|
||||
@@ -127,7 +105,6 @@ QList<Token> commentsForDeclaration(const Symbol *symbol, const Snapshot &snapsh
|
||||
return tokenList();
|
||||
|
||||
// b)
|
||||
const QString symbolName = Overview().prettyName(symbol->name());
|
||||
const Kind tokenKind = comments.first().token.kind();
|
||||
const bool isDoxygenComment = tokenKind == T_DOXY_COMMENT || tokenKind == T_CPP_DOXY_COMMENT;
|
||||
const QRegularExpression symbolRegExp(QString("%1\\b%2\\b").arg(
|
||||
@@ -142,4 +119,57 @@ QList<Token> commentsForDeclaration(const Symbol *symbol, const Snapshot &snapsh
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
QList<Token> commentsForDeclaration(const Symbol *symbol, const QTextDocument &textDoc,
|
||||
const Document::Ptr &cppDoc)
|
||||
{
|
||||
QTC_ASSERT(cppDoc->translationUnit() && cppDoc->translationUnit()->isParsed(), return {});
|
||||
Utils::Text::Position pos;
|
||||
cppDoc->translationUnit()->getTokenPosition(symbol->sourceLocation(), &pos.line, &pos.column);
|
||||
--pos.column;
|
||||
return commentsForDeclaration(nameFromSymbol(symbol), pos, textDoc, cppDoc);
|
||||
}
|
||||
|
||||
QList<Token> commentsForDeclaration(const QString &symbolName, const Utils::Text::Position &pos,
|
||||
const QTextDocument &textDoc, const Document::Ptr &cppDoc)
|
||||
{
|
||||
if (symbolName.isEmpty())
|
||||
return {};
|
||||
|
||||
// Find the symbol declaration's AST node.
|
||||
// We stop at the last declaration node that precedes the symbol, except:
|
||||
// - For parameter declarations, we just continue, because we are interested in the function.
|
||||
// - If the declaration node is preceded directly by another one, we choose that one instead,
|
||||
// because with nested declarations we want the outer one (e.g. templates).
|
||||
const QList<AST *> astPath = ASTPath(cppDoc)(pos.line, pos.column + 1);
|
||||
if (astPath.isEmpty())
|
||||
return {};
|
||||
const AST *declAst = nullptr;
|
||||
bool isParameter = false;
|
||||
for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) {
|
||||
AST * const node = *it;
|
||||
if (node->asParameterDeclaration()) {
|
||||
isParameter = true;
|
||||
continue;
|
||||
}
|
||||
if (node->asDeclaration()) {
|
||||
declAst = node;
|
||||
continue;
|
||||
}
|
||||
if (declAst)
|
||||
break;
|
||||
}
|
||||
if (!declAst)
|
||||
return {};
|
||||
|
||||
return commentsForDeclaration(declAst, symbolName, textDoc, cppDoc, isParameter);
|
||||
}
|
||||
|
||||
QList<Token> commentsForDeclaration(const Symbol *symbol, const AST *decl,
|
||||
const QTextDocument &textDoc, const Document::Ptr &cppDoc)
|
||||
{
|
||||
return commentsForDeclaration(decl, nameFromSymbol(symbol), textDoc, cppDoc,
|
||||
symbol->asArgument());
|
||||
}
|
||||
|
||||
} // namespace CPlusPlus
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <cplusplus/Token.h>
|
||||
|
||||
#include <QList>
|
||||
@@ -11,11 +12,24 @@ QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils { namespace Text { class Position; } }
|
||||
|
||||
namespace CPlusPlus {
|
||||
class Snapshot;
|
||||
class AST;
|
||||
class Symbol;
|
||||
|
||||
QList<Token> CPLUSPLUS_EXPORT commentsForDeclaration(const Symbol *symbol,
|
||||
const Snapshot &snapshot,
|
||||
const QTextDocument &textDoc);
|
||||
const QTextDocument &textDoc,
|
||||
const Document::Ptr &cppDoc);
|
||||
|
||||
QList<Token> CPLUSPLUS_EXPORT commentsForDeclaration(const Symbol *symbol,
|
||||
const AST *decl,
|
||||
const QTextDocument &textDoc,
|
||||
const Document::Ptr &cppDoc);
|
||||
|
||||
QList<Token> CPLUSPLUS_EXPORT commentsForDeclaration(const QString &symbolName,
|
||||
const Utils::Text::Position &pos,
|
||||
const QTextDocument &textDoc,
|
||||
const Document::Ptr &cppDoc);
|
||||
|
||||
} // namespace CPlusPlus
|
||||
|
@@ -96,6 +96,11 @@ private:
|
||||
|
||||
using SearchResultItems = QList<SearchResultItem>;
|
||||
|
||||
inline size_t qHash(const SearchResultItem &item)
|
||||
{
|
||||
return item.mainRange().begin.line << 16 | item.mainRange().begin.column;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
Q_DECLARE_METATYPE(Utils::SearchResultItem)
|
||||
|
@@ -402,6 +402,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
|
||||
setCompletionAssistProvider(new ClangdCompletionAssistProvider(this));
|
||||
setQuickFixAssistProvider(new ClangdQuickFixProvider(this));
|
||||
symbolSupport().setLimitRenamingToProjects(true);
|
||||
symbolSupport().setRenameResultsEnhancer([](const SearchResultItems &symbolOccurrencesInCode) {
|
||||
return CppEditor::symbolOccurrencesInDeclarationComments(symbolOccurrencesInCode);
|
||||
});
|
||||
if (!project) {
|
||||
QJsonObject initOptions;
|
||||
const Utils::FilePath includeDir
|
||||
@@ -752,6 +755,13 @@ bool ClangdClient::fileBelongsToProject(const Utils::FilePath &filePath) const
|
||||
return Client::fileBelongsToProject(filePath);
|
||||
}
|
||||
|
||||
QList<Text::Range> ClangdClient::additionalDocumentHighlights(
|
||||
TextEditorWidget *editorWidget, const QTextCursor &cursor)
|
||||
{
|
||||
return CppEditor::symbolOccurrencesInDeclarationComments(
|
||||
qobject_cast<CppEditor::CppEditorWidget *>(editorWidget), cursor);
|
||||
}
|
||||
|
||||
RefactoringChangesData *ClangdClient::createRefactoringChangesBackend() const
|
||||
{
|
||||
return new CppEditor::CppRefactoringChangesData(
|
||||
@@ -1056,9 +1066,11 @@ void ClangdClient::switchHeaderSource(const Utils::FilePath &filePath, bool inNe
|
||||
sendMessage(req);
|
||||
}
|
||||
|
||||
void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cursor,
|
||||
CppEditor::RenameCallback &&callback)
|
||||
void ClangdClient::findLocalUsages(CppEditor::CppEditorWidget *editorWidget,
|
||||
const QTextCursor &cursor, CppEditor::RenameCallback &&callback)
|
||||
{
|
||||
QTC_ASSERT(editorWidget, return);
|
||||
TextDocument * const document = editorWidget->textDocument();
|
||||
QTC_ASSERT(documentOpen(document), openDocument(document));
|
||||
|
||||
qCDebug(clangdLog) << "local references requested" << document->filePath()
|
||||
@@ -1076,7 +1088,7 @@ void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cu
|
||||
return;
|
||||
}
|
||||
|
||||
d->findLocalRefs = new ClangdFindLocalReferences(this, document, cursor, callback);
|
||||
d->findLocalRefs = new ClangdFindLocalReferences(this, editorWidget, cursor, callback);
|
||||
connect(d->findLocalRefs, &ClangdFindLocalReferences::done, this, [this] {
|
||||
d->findLocalRefs->deleteLater();
|
||||
d->findLocalRefs = nullptr;
|
||||
|
@@ -74,7 +74,7 @@ public:
|
||||
const Utils::LinkHandler &callback);
|
||||
void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit);
|
||||
|
||||
void findLocalUsages(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||
void findLocalUsages(CppEditor::CppEditorWidget *editorWidget, const QTextCursor &cursor,
|
||||
CppEditor::RenameCallback &&callback);
|
||||
|
||||
void gatherHelpItemForTooltip(
|
||||
@@ -148,6 +148,9 @@ private:
|
||||
bool referencesShadowFile(const TextEditor::TextDocument *doc,
|
||||
const Utils::FilePath &candidate) override;
|
||||
bool fileBelongsToProject(const Utils::FilePath &filePath) const override;
|
||||
QList<Utils::Text::Range> additionalDocumentHighlights(
|
||||
TextEditor::TextEditorWidget *editorWidget, const QTextCursor &cursor) override;
|
||||
|
||||
|
||||
class Private;
|
||||
class VirtualFunctionAssistProcessor;
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <cplusplus/FindUsages.h>
|
||||
|
||||
#include <cppeditor/cppcodemodelsettings.h>
|
||||
#include <cppeditor/cppeditorwidget.h>
|
||||
#include <cppeditor/cppfindreferences.h>
|
||||
#include <cppeditor/cpptoolsreuse.h>
|
||||
|
||||
@@ -670,10 +671,10 @@ ClangdFindReferences::CheckUnusedData::~CheckUnusedData()
|
||||
class ClangdFindLocalReferences::Private
|
||||
{
|
||||
public:
|
||||
Private(ClangdFindLocalReferences *q, TextDocument *document, const QTextCursor &cursor,
|
||||
Private(ClangdFindLocalReferences *q, CppEditorWidget *editorWidget, const QTextCursor &cursor,
|
||||
const RenameCallback &callback)
|
||||
: q(q), document(document), cursor(cursor), callback(callback),
|
||||
uri(client()->hostPathToServerUri(document->filePath())),
|
||||
: q(q), editorWidget(editorWidget), document(editorWidget->textDocument()), cursor(cursor),
|
||||
callback(callback), uri(client()->hostPathToServerUri(document->filePath())),
|
||||
revision(document->document()->revision())
|
||||
{}
|
||||
|
||||
@@ -685,6 +686,7 @@ public:
|
||||
void finish();
|
||||
|
||||
ClangdFindLocalReferences * const q;
|
||||
const QPointer<CppEditorWidget> editorWidget;
|
||||
const QPointer<TextDocument> document;
|
||||
const QTextCursor cursor;
|
||||
RenameCallback callback;
|
||||
@@ -694,9 +696,9 @@ public:
|
||||
};
|
||||
|
||||
ClangdFindLocalReferences::ClangdFindLocalReferences(
|
||||
ClangdClient *client, TextDocument *document, const QTextCursor &cursor,
|
||||
ClangdClient *client, CppEditorWidget *editorWidget, const QTextCursor &cursor,
|
||||
const RenameCallback &callback)
|
||||
: QObject(client), d(new Private(this, document, cursor, callback))
|
||||
: QObject(client), d(new Private(this, editorWidget, cursor, callback))
|
||||
{
|
||||
d->findDefinition();
|
||||
}
|
||||
@@ -780,7 +782,7 @@ void ClangdFindLocalReferences::Private::handleReferences(const QList<Location>
|
||||
return loc.toLink(mapper);
|
||||
};
|
||||
|
||||
const Utils::Links links = Utils::transform(references, transformLocation);
|
||||
Utils::Links links = Utils::transform(references, transformLocation);
|
||||
|
||||
// The callback only uses the symbol length, so we just create a dummy.
|
||||
// Note that the calculation will be wrong for identifiers with
|
||||
@@ -788,7 +790,27 @@ void ClangdFindLocalReferences::Private::handleReferences(const QList<Location>
|
||||
QString symbol;
|
||||
if (!references.isEmpty()) {
|
||||
const Range r = references.first().range();
|
||||
symbol = QString(r.end().character() - r.start().character(), 'x');
|
||||
const Position pos = r.start();
|
||||
symbol = QString(r.end().character() - pos.character(), 'x');
|
||||
if (editorWidget && document) {
|
||||
QTextCursor cursor(document->document());
|
||||
cursor.setPosition(Text::positionInText(document->document(), pos.line() + 1,
|
||||
pos.character() + 1));
|
||||
const QList<Text::Range> occurrencesInComments
|
||||
= symbolOccurrencesInDeclarationComments(editorWidget, cursor);
|
||||
for (const Text::Range &range : occurrencesInComments) {
|
||||
static const auto cmp = [](const Link &l, const Text::Range &r) {
|
||||
if (l.targetLine < r.begin.line)
|
||||
return true;
|
||||
if (l.targetLine > r.begin.line)
|
||||
return false;
|
||||
return l.targetColumn < r.begin.column;
|
||||
};
|
||||
const auto it = std::lower_bound(links.begin(), links.end(), range, cmp);
|
||||
links.emplace(it, links.first().targetFilePath, range.begin.line,
|
||||
range.begin.column);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(symbol, links, revision);
|
||||
callback = {};
|
||||
|
@@ -48,8 +48,8 @@ class ClangdFindLocalReferences : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClangdFindLocalReferences(ClangdClient *client, TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor,
|
||||
explicit ClangdFindLocalReferences(
|
||||
ClangdClient *client, CppEditor::CppEditorWidget *editorWidget, const QTextCursor &cursor,
|
||||
const CppEditor::RenameCallback &callback);
|
||||
~ClangdFindLocalReferences();
|
||||
|
||||
|
@@ -309,7 +309,7 @@ void ClangModelManagerSupport::startLocalRenaming(const CursorInEditor &data,
|
||||
{
|
||||
if (ClangdClient * const client = clientForFile(data.filePath());
|
||||
client && client->reachable()) {
|
||||
client->findLocalUsages(data.textDocument(), data.cursor(),
|
||||
client->findLocalUsages(data.editorWidget(), data.cursor(),
|
||||
std::move(renameSymbolsCallback));
|
||||
return;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <cplusplus/FindUsages.h>
|
||||
#include <cppeditor/cppcodemodelsettings.h>
|
||||
#include <cppeditor/cppeditorwidget.h>
|
||||
#include <cppeditor/cpptoolsreuse.h>
|
||||
#include <cppeditor/cpptoolstestcase.h>
|
||||
#include <cppeditor/semantichighlighter.h>
|
||||
@@ -536,6 +537,8 @@ void ClangdTestLocalReferences::test_data()
|
||||
QTest::newRow("overloaded operators arguments from outside") << 171 << 7
|
||||
<< QList<Range>{{171, 6, 1}, {172, 6, 1}, {172, 11, 1},
|
||||
{173, 6, 1}, {173, 9, 1}};
|
||||
QTest::newRow("documented function parameter") << 181 << 32
|
||||
<< QList<Range>{{177, 10, 6}, {179, 9, 6}, {181, 31, 6}, {183, 6, 6}, {184, 17, 6}};
|
||||
}
|
||||
|
||||
void ClangdTestLocalReferences::test()
|
||||
@@ -546,6 +549,11 @@ void ClangdTestLocalReferences::test()
|
||||
|
||||
TextEditor::TextDocument * const doc = document("references.cpp");
|
||||
QVERIFY(doc);
|
||||
const QList<BaseTextEditor *> editors = BaseTextEditor::textEditorsForDocument(doc);
|
||||
QCOMPARE(editors.size(), 1);
|
||||
const auto editorWidget = qobject_cast<CppEditor::CppEditorWidget *>(
|
||||
editors.first()->editorWidget());
|
||||
QVERIFY(editorWidget);
|
||||
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
@@ -561,7 +569,7 @@ void ClangdTestLocalReferences::test()
|
||||
QTextCursor cursor(doc->document());
|
||||
const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn);
|
||||
cursor.setPosition(pos);
|
||||
client()->findLocalUsages(doc, cursor, std::move(handler));
|
||||
client()->findLocalUsages(editorWidget, cursor, std::move(handler));
|
||||
timer.start(10000);
|
||||
loop.exec();
|
||||
QVERIFY(timer.isActive());
|
||||
|
@@ -172,3 +172,14 @@ int testOperator() {
|
||||
vec[n] = n * 100;
|
||||
vec(n, n) = 100;
|
||||
}
|
||||
|
||||
/*
|
||||
* @param param1
|
||||
* @param param2
|
||||
* @note param1 and param2 should be the same.
|
||||
*/
|
||||
void funcWithParamComments(int param1, int param2)
|
||||
{
|
||||
if (param1 != param2)
|
||||
param2 = param1;
|
||||
}
|
||||
|
@@ -82,9 +82,26 @@ int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeIt
|
||||
}
|
||||
|
||||
int SearchResultTreeItem::insertionIndex(const Utils::SearchResultItem &item,
|
||||
SearchResultTreeItem **existingItem) const
|
||||
SearchResultTreeItem **existingItem,
|
||||
SearchResult::AddMode mode) const
|
||||
{
|
||||
switch (mode) {
|
||||
case SearchResult::AddSortedByContent:
|
||||
return insertionIndex(item.lineText(), existingItem);
|
||||
case SearchResult::AddSortedByPosition:
|
||||
break;
|
||||
case Core::SearchResult::AddOrdered:
|
||||
QTC_ASSERT(false, return 0);
|
||||
}
|
||||
|
||||
static const auto cmp = [](const SearchResultTreeItem *a, const Utils::Text::Position b) {
|
||||
return a->item.mainRange().begin < b;
|
||||
};
|
||||
const auto insertionPosition =
|
||||
std::lower_bound(m_children.begin(), m_children.end(), item.mainRange().begin, cmp);
|
||||
if (existingItem)
|
||||
*existingItem = nullptr;
|
||||
return insertionPosition - m_children.begin();
|
||||
}
|
||||
|
||||
void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child)
|
||||
|
@@ -21,7 +21,8 @@ public:
|
||||
SearchResultTreeItem *parent() const;
|
||||
SearchResultTreeItem *childAt(int index) const;
|
||||
int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const;
|
||||
int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem) const;
|
||||
int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem,
|
||||
SearchResult::AddMode mode) const;
|
||||
void insertChild(int index, SearchResultTreeItem *child);
|
||||
void insertChild(int index, const Utils::SearchResultItem &item);
|
||||
void appendChild(const Utils::SearchResultItem &item);
|
||||
|
@@ -402,10 +402,10 @@ void SearchResultTreeModel::addResultsToCurrentParent(const SearchResultItems &i
|
||||
m_currentParent->appendChild(item);
|
||||
}
|
||||
endInsertRows();
|
||||
} else if (mode == SearchResult::AddSorted) {
|
||||
} else {
|
||||
for (const SearchResultItem &item : items) {
|
||||
SearchResultTreeItem *existingItem;
|
||||
const int insertionIndex = m_currentParent->insertionIndex(item, &existingItem);
|
||||
const int insertionIndex = m_currentParent->insertionIndex(item, &existingItem, mode);
|
||||
if (existingItem) {
|
||||
existingItem->setGenerated(false);
|
||||
existingItem->item = item;
|
||||
|
@@ -478,7 +478,7 @@ void SearchResultWidget::doReplace()
|
||||
{
|
||||
m_infoBar.clear();
|
||||
setShowReplaceUI(false);
|
||||
emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(),
|
||||
emit replaceButtonClicked(m_replaceTextEdit->text(), items(true),
|
||||
m_preserveCaseSupported && m_preserveCaseCheck->isChecked());
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ void SearchResultWidget::searchAgain()
|
||||
emit searchAgainRequested();
|
||||
}
|
||||
|
||||
SearchResultItems SearchResultWidget::checkedItems() const
|
||||
SearchResultItems SearchResultWidget::items(bool checkedOnly) const
|
||||
{
|
||||
SearchResultItems result;
|
||||
SearchResultFilterModel *model = m_searchResultTreeView->model();
|
||||
@@ -508,7 +508,7 @@ SearchResultItems SearchResultWidget::checkedItems() const
|
||||
const QModelIndex textIndex = model->index(rowIndex, 0, fileIndex);
|
||||
const SearchResultTreeItem * const rowItem = model->itemForIndex(textIndex);
|
||||
QTC_ASSERT(rowItem != nullptr, continue);
|
||||
if (rowItem->checkState())
|
||||
if (!checkedOnly || rowItem->checkState())
|
||||
result << rowItem->item;
|
||||
}
|
||||
}
|
||||
|
@@ -71,6 +71,7 @@ public:
|
||||
bool hasFilter() const;
|
||||
void showFilterWidget(QWidget *parent);
|
||||
void setReplaceEnabled(bool enabled);
|
||||
Utils::SearchResultItems items(bool checkedOnly) const;
|
||||
|
||||
public slots:
|
||||
void finishSearch(bool canceled, const QString &reason);
|
||||
@@ -103,7 +104,6 @@ private:
|
||||
void continueAfterSizeWarning();
|
||||
void cancelAfterSizeWarning();
|
||||
|
||||
Utils::SearchResultItems checkedItems() const;
|
||||
void updateMatchesFoundLabel();
|
||||
|
||||
SearchResultTreeView *m_searchResultTreeView = nullptr;
|
||||
|
@@ -900,6 +900,11 @@ void Core::SearchResult::makeNonInteractive(const std::function<void ()> &callba
|
||||
m_finishedHandler = callback;
|
||||
}
|
||||
|
||||
Utils::SearchResultItems SearchResult::allItems() const
|
||||
{
|
||||
return m_widget->items(false);
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
#include "searchresultwindow.moc"
|
||||
|
@@ -43,7 +43,8 @@ class CORE_EXPORT SearchResult : public QObject
|
||||
|
||||
public:
|
||||
enum AddMode {
|
||||
AddSorted,
|
||||
AddSortedByContent,
|
||||
AddSortedByPosition,
|
||||
AddOrdered
|
||||
};
|
||||
|
||||
@@ -57,6 +58,7 @@ public:
|
||||
void setAdditionalReplaceWidget(QWidget *widget);
|
||||
void makeNonInteractive(const std::function<void()> &callback);
|
||||
bool isInteractive() const { return !m_finishedHandler; }
|
||||
Utils::SearchResultItems allItems() const;
|
||||
|
||||
public slots:
|
||||
void addResult(const Utils::SearchResultItem &item);
|
||||
|
@@ -600,6 +600,10 @@ static void displayResults(SearchResult *search,
|
||||
|
||||
static void searchFinished(SearchResult *search, QFutureWatcher<CPlusPlus::Usage> *watcher)
|
||||
{
|
||||
if (!watcher->isCanceled() && search->supportsReplace()) {
|
||||
search->addResults(symbolOccurrencesInDeclarationComments(search->allItems()),
|
||||
SearchResult::AddSortedByPosition);
|
||||
}
|
||||
search->finishSearch(watcher->isCanceled());
|
||||
|
||||
CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
|
||||
|
@@ -4,8 +4,14 @@
|
||||
#include "cpplocalsymbols.h"
|
||||
|
||||
#include "cppsemanticinfo.h"
|
||||
#include "cpptoolsreuse.h"
|
||||
#include "semantichighlighter.h"
|
||||
|
||||
#include <cplusplus/declarationcomments.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
|
||||
namespace CppEditor::Internal {
|
||||
@@ -16,7 +22,7 @@ class FindLocalSymbols: protected ASTVisitor
|
||||
{
|
||||
public:
|
||||
explicit FindLocalSymbols(Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit())
|
||||
: ASTVisitor(doc->translationUnit()), _doc(doc)
|
||||
{ }
|
||||
|
||||
// local and external uses.
|
||||
@@ -38,6 +44,42 @@ public:
|
||||
accept(ast);
|
||||
}
|
||||
}
|
||||
|
||||
if (localUses.isEmpty())
|
||||
return;
|
||||
|
||||
// Look for parameter occurrences in function comments.
|
||||
const TextEditor::TextDocument * const editorDoc
|
||||
= TextEditor::TextDocument::textDocumentForFilePath(_doc->filePath());
|
||||
if (!editorDoc)
|
||||
return;
|
||||
QTextDocument * const textDoc = editorDoc->document();
|
||||
if (!textDoc)
|
||||
return;
|
||||
const QString &content = textDoc->toPlainText();
|
||||
const QStringView docView(content);
|
||||
for (auto it = localUses.begin(); it != localUses.end(); ++it) {
|
||||
Symbol * const symbol = it.key();
|
||||
if (!symbol->asArgument())
|
||||
continue;
|
||||
const QList<Token> commentTokens = commentsForDeclaration(symbol, ast, *textDoc, _doc);
|
||||
if (commentTokens.isEmpty())
|
||||
continue;
|
||||
const QString symbolName = Overview().prettyName(symbol->name());
|
||||
for (const Token &tok : commentTokens) {
|
||||
const int commentPos = translationUnit()->getTokenPositionInDocument(tok, textDoc);
|
||||
const int commentEndPos = translationUnit()->getTokenEndPositionInDocument(
|
||||
tok, textDoc);
|
||||
const QStringView commentView = docView.mid(commentPos, commentEndPos - commentPos);
|
||||
const QList<Utils::Text::Range> ranges = symbolOccurrencesInText(
|
||||
*textDoc, commentView, commentPos, symbolName);
|
||||
for (const Utils::Text::Range &range : ranges) {
|
||||
it.value().append(HighlightingResult(range.begin.line, range.begin.column + 1,
|
||||
symbolName.size(),
|
||||
SemanticHighlighter::LocalUse));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -275,6 +317,7 @@ protected:
|
||||
|
||||
private:
|
||||
QList<Scope *> _scopeStack;
|
||||
Document::Ptr _doc;
|
||||
};
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
@@ -82,19 +82,19 @@ void MyClass::run() {}
|
||||
origHeaderClassName.insert(classOffset + 6, '@');
|
||||
const QByteArray newHeaderClassName = R"cpp(
|
||||
/**
|
||||
* \brief MyClass
|
||||
* \brief MyNewClass
|
||||
*/
|
||||
class MyNewClass {
|
||||
/** \brief MyClass::MyClass */
|
||||
/** \brief MyNewClass::MyNewClass */
|
||||
MyNewClass() {}
|
||||
~MyNewClass();
|
||||
/** \brief MyClass::run */
|
||||
/** \brief MyNewClass::run */
|
||||
void run();
|
||||
};
|
||||
)cpp";
|
||||
const QByteArray newSourceClassName = R"cpp(
|
||||
#include "file.h"
|
||||
/** \brief MyClass::~MyClass */
|
||||
/** \brief MyNewClass::~MyNewClass */
|
||||
MyNewClass::~MyNewClass() {}
|
||||
|
||||
void MyNewClass::run() {}
|
||||
@@ -115,7 +115,7 @@ class MyClass {
|
||||
/** \brief MyClass::MyClass */
|
||||
MyClass() {}
|
||||
~MyClass();
|
||||
/** \brief MyClass::run */
|
||||
/** \brief MyClass::runAgain */
|
||||
void runAgain();
|
||||
};
|
||||
)cpp";
|
||||
|
@@ -5,10 +5,12 @@
|
||||
|
||||
#include "clangdiagnosticconfigsmodel.h"
|
||||
#include "cppautocompleter.h"
|
||||
#include "cppcanonicalsymbol.h"
|
||||
#include "cppcodemodelsettings.h"
|
||||
#include "cppcompletionassist.h"
|
||||
#include "cppeditorconstants.h"
|
||||
#include "cppeditorplugin.h"
|
||||
#include "cppeditorwidget.h"
|
||||
#include "cppeditortr.h"
|
||||
#include "cppfilesettingspage.h"
|
||||
#include "cpphighlighter.h"
|
||||
@@ -28,20 +30,27 @@
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include <cplusplus/BackwardsScanner.h>
|
||||
#include <cplusplus/declarationcomments.h>
|
||||
#include <cplusplus/LookupContext.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <cplusplus/SimpleLexer.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/textfileformat.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
#include <QHash>
|
||||
#include <QRegularExpression>
|
||||
#include <QSet>
|
||||
#include <QStringView>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace Utils;
|
||||
|
||||
@@ -626,6 +635,200 @@ QString preferredCxxSourceSuffix(ProjectExplorer::Project *project)
|
||||
return Internal::CppEditorPlugin::fileSettings(project).sourceSuffix;
|
||||
}
|
||||
|
||||
SearchResultItems symbolOccurrencesInDeclarationComments(
|
||||
const Utils::SearchResultItems &symbolOccurrencesInCode)
|
||||
{
|
||||
if (symbolOccurrencesInCode.isEmpty())
|
||||
return {};
|
||||
|
||||
// When using clangd, this function gets called every time the replacement string changes,
|
||||
// so cache the results.
|
||||
static QHash<SearchResultItems, SearchResultItems> resultCache;
|
||||
if (const auto it = resultCache.constFind(symbolOccurrencesInCode);
|
||||
it != resultCache.constEnd()) {
|
||||
return it.value();
|
||||
}
|
||||
if (resultCache.size() > 5)
|
||||
resultCache.clear();
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
Snapshot snapshot = CppModelManager::snapshot();
|
||||
std::vector<std::unique_ptr<QTextDocument>> docPool;
|
||||
using FileData = std::tuple<QTextDocument *, QString, Document::Ptr, QList<Token>>;
|
||||
QHash<FilePath, FileData> dataPerFile;
|
||||
QString symbolName;
|
||||
const auto fileData = [&](const FilePath &filePath) -> FileData & {
|
||||
auto &data = dataPerFile[filePath];
|
||||
auto &[doc, content, cppDoc, allCommentTokens] = data;
|
||||
if (!doc) {
|
||||
if (TextEditor::TextDocument * const textDoc
|
||||
= TextEditor::TextDocument::textDocumentForFilePath(filePath)) {
|
||||
doc = textDoc->document();
|
||||
} else {
|
||||
std::unique_ptr<QTextDocument> newDoc = std::make_unique<QTextDocument>();
|
||||
if (const auto content = TextFileFormat::readFile(
|
||||
filePath, Core::EditorManager::defaultTextCodec())) {
|
||||
newDoc->setPlainText(content.value());
|
||||
}
|
||||
doc = newDoc.get();
|
||||
docPool.push_back(std::move(newDoc));
|
||||
}
|
||||
content = doc->toPlainText();
|
||||
cppDoc = snapshot.preprocessedDocument(content.toUtf8(), filePath);
|
||||
cppDoc->check();
|
||||
}
|
||||
return data;
|
||||
};
|
||||
static const auto addToken = [](QList<Token> &tokens, const Token &tok) {
|
||||
if (!Utils::contains(tokens, [&tok](const Token &t) {
|
||||
return t.byteOffset == tok.byteOffset; })) {
|
||||
tokens << tok;
|
||||
}
|
||||
};
|
||||
|
||||
// Collect comment blocks associated with replace locations.
|
||||
Symbol *canonicalSymbol = nullptr;
|
||||
for (const SearchResultItem &item : symbolOccurrencesInCode) {
|
||||
const FilePath filePath = FilePath::fromUserInput(item.path().last());
|
||||
auto &[doc, content, cppDoc, allCommentTokens] = fileData(filePath);
|
||||
const Text::Range &range = item.mainRange();
|
||||
if (symbolName.isEmpty()) {
|
||||
const int symbolStartPos = Utils::Text::positionInText(doc, range.begin.line,
|
||||
range.begin.column + 1);
|
||||
const int symbolEndPos = Utils::Text::positionInText(doc, range.end.line,
|
||||
range.end.column + 1);
|
||||
symbolName = content.mid(symbolStartPos, symbolEndPos - symbolStartPos);
|
||||
}
|
||||
const QList<Token> commentTokens = commentsForDeclaration(symbolName, range.begin,
|
||||
*doc, cppDoc);
|
||||
for (const Token &tok : commentTokens)
|
||||
addToken(allCommentTokens, tok);
|
||||
|
||||
if (!canonicalSymbol) {
|
||||
QTextCursor cursor(doc);
|
||||
cursor.setPosition(Text::positionInText(doc, range.begin.line, range.begin.column + 1));
|
||||
canonicalSymbol = Internal::CanonicalSymbol(cppDoc, snapshot)(cursor);
|
||||
}
|
||||
|
||||
// We hook in between the end of the "regular" search and (possibly non-interactive)
|
||||
// actions on it, so we must run synchronously in the UI thread and therefore be fast.
|
||||
// If we notice we are lagging, just abort, as renaming the comments is not
|
||||
// required for code correctness.
|
||||
if (timer.elapsed() > 1000) {
|
||||
resultCache.insert(symbolOccurrencesInCode, {});
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// If the symbol is a class, collect all comment blocks in the class body.
|
||||
if (Class * const klass = canonicalSymbol ? canonicalSymbol->asClass() : nullptr) {
|
||||
auto &[_1, _2, symbolCppDoc, commentTokens] = fileData(canonicalSymbol->filePath());
|
||||
TranslationUnit * const tu = symbolCppDoc->translationUnit();
|
||||
for (int i = 0; i < tu->commentCount(); ++i) {
|
||||
const Token &tok = tu->commentAt(i);
|
||||
if (tok.bytesBegin() < klass->startOffset())
|
||||
continue;
|
||||
if (tok.bytesBegin() >= klass->endOffset())
|
||||
break;
|
||||
addToken(commentTokens, tok);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new replace items for occurrences of the symbol name in collected comment blocks.
|
||||
SearchResultItems commentItems;
|
||||
for (auto it = dataPerFile.cbegin(); it != dataPerFile.cend(); ++it) {
|
||||
const auto &[doc, content, cppDoc, commentTokens] = it.value();
|
||||
const QStringView docView(content);
|
||||
for (const Token &tok : commentTokens) {
|
||||
const int tokenStartPos = cppDoc->translationUnit()->getTokenPositionInDocument(
|
||||
tok, doc);
|
||||
const int tokenEndPos = cppDoc->translationUnit()->getTokenEndPositionInDocument(
|
||||
tok, doc);
|
||||
const QStringView tokenView = docView.mid(tokenStartPos, tokenEndPos - tokenStartPos);
|
||||
const QList<Text::Range> ranges = symbolOccurrencesInText(
|
||||
*doc, tokenView, tokenStartPos, symbolName);
|
||||
for (const Text::Range &range : ranges) {
|
||||
SearchResultItem item;
|
||||
item.setUseTextEditorFont(true);
|
||||
item.setFilePath(it.key());
|
||||
item.setMainRange(range);
|
||||
item.setLineText(doc->findBlockByNumber(range.begin.line - 1).text());
|
||||
commentItems << item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultCache.insert(symbolOccurrencesInCode, commentItems);
|
||||
return commentItems;
|
||||
}
|
||||
|
||||
QList<Text::Range> symbolOccurrencesInText(const QTextDocument &doc, QStringView text, int offset,
|
||||
const QString &symbolName)
|
||||
{
|
||||
QList<Text::Range> ranges;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
index = text.indexOf(symbolName, index);
|
||||
if (index == -1)
|
||||
break;
|
||||
|
||||
// Prevent substring matching.
|
||||
const auto checkAdjacent = [&](int i) {
|
||||
if (i == -1 || i == text.size())
|
||||
return true;
|
||||
const QChar c = text.at(i);
|
||||
if (c.isLetterOrNumber() || c == '_') {
|
||||
index += symbolName.length();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!checkAdjacent(index - 1))
|
||||
continue;
|
||||
if (!checkAdjacent(index + symbolName.length()))
|
||||
continue;
|
||||
|
||||
const Text::Position startPos = Text::Position::fromPositionInDocument(&doc, offset + index);
|
||||
index += symbolName.length();
|
||||
const Text::Position endPos = Text::Position::fromPositionInDocument(&doc, offset + index);
|
||||
ranges << Text::Range{startPos, endPos};
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
QList<Text::Range> symbolOccurrencesInDeclarationComments(CppEditorWidget *editorWidget,
|
||||
const QTextCursor &cursor)
|
||||
{
|
||||
if (!editorWidget)
|
||||
return {};
|
||||
const SemanticInfo &semanticInfo = editorWidget->semanticInfo();
|
||||
const Document::Ptr &cppDoc = semanticInfo.doc;
|
||||
if (!cppDoc)
|
||||
return {};
|
||||
const Symbol * const symbol = Internal::CanonicalSymbol(cppDoc, semanticInfo.snapshot)(cursor);
|
||||
if (!symbol || !symbol->asArgument())
|
||||
return {};
|
||||
const QTextDocument * const textDoc = editorWidget->textDocument()->document();
|
||||
QTC_ASSERT(textDoc, return {});
|
||||
const QList<Token> comments = commentsForDeclaration(symbol, *textDoc, cppDoc);
|
||||
if (comments.isEmpty())
|
||||
return {};
|
||||
QList<Text::Range> ranges;
|
||||
const QString &content = textDoc->toPlainText();
|
||||
const QStringView docView = QStringView(content);
|
||||
const QString symbolName = Overview().prettyName(symbol->name());
|
||||
for (const Token &tok : comments) {
|
||||
const int tokenStartPos = cppDoc->translationUnit()->getTokenPositionInDocument(
|
||||
tok, textDoc);
|
||||
const int tokenEndPos = cppDoc->translationUnit()->getTokenEndPositionInDocument(
|
||||
tok, textDoc);
|
||||
const QStringView tokenView = docView.mid(tokenStartPos, tokenEndPos - tokenStartPos);
|
||||
ranges << symbolOccurrencesInText(*textDoc, tokenView, tokenStartPos, symbolName);
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
|
||||
void decorateCppEditor(TextEditor::TextEditorWidget *editor)
|
||||
|
@@ -12,6 +12,8 @@
|
||||
#include <texteditor/quickfix.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/searchresultitem.h>
|
||||
|
||||
#include <cplusplus/ASTVisitor.h>
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <cplusplus/Token.h>
|
||||
@@ -23,9 +25,10 @@ class LookupContext;
|
||||
} // namespace CPlusPlus
|
||||
|
||||
namespace TextEditor { class AssistInterface; }
|
||||
namespace Utils { namespace Text { class Range; } }
|
||||
|
||||
namespace CppEditor {
|
||||
|
||||
class CppEditorWidget;
|
||||
class CppRefactoringFile;
|
||||
class ProjectInfo;
|
||||
class CppCompletionAssistProcessor;
|
||||
@@ -71,6 +74,14 @@ QString CPPEDITOR_EXPORT preferredCxxHeaderSuffix(ProjectExplorer::Project *proj
|
||||
QString CPPEDITOR_EXPORT preferredCxxSourceSuffix(ProjectExplorer::Project *project);
|
||||
bool CPPEDITOR_EXPORT preferLowerCaseFileNames(ProjectExplorer::Project *project);
|
||||
|
||||
|
||||
QList<Utils::Text::Range> CPPEDITOR_EXPORT symbolOccurrencesInText(
|
||||
const QTextDocument &doc, QStringView text, int offset, const QString &symbolName);
|
||||
Utils::SearchResultItems CPPEDITOR_EXPORT
|
||||
symbolOccurrencesInDeclarationComments(const Utils::SearchResultItems &symbolOccurrencesInCode);
|
||||
QList<Utils::Text::Range> CPPEDITOR_EXPORT symbolOccurrencesInDeclarationComments(
|
||||
CppEditorWidget *editorWidget, const QTextCursor &cursor);
|
||||
|
||||
UsePrecompiledHeaders CPPEDITOR_EXPORT getPchUsage();
|
||||
|
||||
int indexerFileSizeLimitInMb();
|
||||
|
@@ -138,7 +138,7 @@ void SymbolsFindFilter::addResults(QFutureWatcher<SearchResultItem> *watcher, in
|
||||
SearchResultItems items;
|
||||
for (int i = begin; i < end; ++i)
|
||||
items << watcher->resultAt(i);
|
||||
search->addResults(items, SearchResult::AddSorted);
|
||||
search->addResults(items, SearchResult::AddSortedByContent);
|
||||
}
|
||||
|
||||
void SymbolsFindFilter::finish(QFutureWatcher<SearchResultItem> *watcher)
|
||||
|
@@ -848,7 +848,7 @@ void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *w
|
||||
q->cancelRequest(m_highlightRequests.take(widget));
|
||||
});
|
||||
request.setResponseCallback(
|
||||
[widget, this, uri, connection]
|
||||
[widget, this, uri, connection, adjustedCursor]
|
||||
(const DocumentHighlightsRequest::Response &response)
|
||||
{
|
||||
m_highlightRequests.remove(widget);
|
||||
@@ -874,6 +874,30 @@ void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *w
|
||||
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
selections << selection;
|
||||
}
|
||||
if (!selections.isEmpty()) {
|
||||
const QList<Text::Range> extraRanges = q->additionalDocumentHighlights(
|
||||
widget, adjustedCursor);
|
||||
for (const Text::Range &range : extraRanges) {
|
||||
QTextEdit::ExtraSelection selection{widget->textCursor(), format};
|
||||
const Text::Position &startPos = range.begin;
|
||||
const Text::Position &endPos = range.end;
|
||||
const int start = Text::positionInText(document, startPos.line,
|
||||
startPos.column + 1);
|
||||
const int end = Text::positionInText(document, endPos.line,
|
||||
endPos.column + 1);
|
||||
if (start < 0 || end < 0 || start >= end)
|
||||
continue;
|
||||
selection.cursor.setPosition(start);
|
||||
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
static const auto cmp = [](const QTextEdit::ExtraSelection &s1,
|
||||
const QTextEdit::ExtraSelection &s2) {
|
||||
return s1.cursor.position() < s2.cursor.position();
|
||||
};
|
||||
const auto it = std::lower_bound(selections.begin(), selections.end(),
|
||||
selection, cmp);
|
||||
selections.insert(it, selection);
|
||||
}
|
||||
}
|
||||
widget->setExtraSelections(id, selections);
|
||||
});
|
||||
m_highlightRequests[widget] = request.id();
|
||||
|
@@ -16,6 +16,8 @@ class TextDocument;
|
||||
class TextEditorWidget;
|
||||
}
|
||||
|
||||
namespace Utils { namespace Text { class Range; } }
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
@@ -226,6 +228,8 @@ private:
|
||||
TextEditor::TextDocument *doc);
|
||||
virtual bool referencesShadowFile(const TextEditor::TextDocument *doc,
|
||||
const Utils::FilePath &candidate);
|
||||
virtual QList<Utils::Text::Range> additionalDocumentHighlights(
|
||||
TextEditor::TextEditorWidget *, const QTextCursor &) { return {}; }
|
||||
};
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
@@ -563,11 +563,22 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
|
||||
|
||||
const std::optional<WorkspaceEdit> &edits = response.result();
|
||||
if (edits.has_value()) {
|
||||
search->addResults(generateReplaceItems(*edits,
|
||||
search,
|
||||
m_limitRenamingToProjects,
|
||||
m_client->hostPathMapper()),
|
||||
Core::SearchResult::AddOrdered);
|
||||
const Utils::SearchResultItems items = generateReplaceItems(
|
||||
*edits, search, m_limitRenamingToProjects, m_client->hostPathMapper());
|
||||
search->addResults(items, Core::SearchResult::AddOrdered);
|
||||
if (m_renameResultsEnhancer) {
|
||||
Utils::SearchResultItems additionalItems = m_renameResultsEnhancer(items);
|
||||
for (Utils::SearchResultItem &item : additionalItems) {
|
||||
TextEdit edit;
|
||||
const Utils::Text::Position startPos = item.mainRange().begin;
|
||||
const Utils::Text::Position endPos = item.mainRange().end;
|
||||
edit.setRange({{startPos.line - 1, startPos.column},
|
||||
{endPos.line - 1, endPos.column}});
|
||||
edit.setNewText(search->textToReplace());
|
||||
item.setUserData(QVariant(edit));
|
||||
}
|
||||
search->addResults(additionalItems, Core::SearchResult::AddSortedByPosition);
|
||||
}
|
||||
qobject_cast<ReplaceWidget *>(search->additionalReplaceWidget())->showLabel(false);
|
||||
search->setReplaceEnabled(true);
|
||||
search->finishSearch(false);
|
||||
@@ -634,6 +645,11 @@ void SymbolSupport::setDefaultRenamingSymbolMapper(const SymbolMapper &mapper)
|
||||
m_defaultSymbolMapper = mapper;
|
||||
}
|
||||
|
||||
void SymbolSupport::setRenameResultsEnhancer(const RenameResultsEnhancer &enhancer)
|
||||
{
|
||||
m_renameResultsEnhancer = enhancer;
|
||||
}
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
#include <languageclientsymbolsupport.moc>
|
||||
|
@@ -51,6 +51,9 @@ public:
|
||||
|
||||
void setLimitRenamingToProjects(bool limit) { m_limitRenamingToProjects = limit; }
|
||||
|
||||
using RenameResultsEnhancer = std::function<Utils::SearchResultItems(const Utils::SearchResultItems &)>;
|
||||
void setRenameResultsEnhancer(const RenameResultsEnhancer &enhancer);
|
||||
|
||||
private:
|
||||
void handleFindReferencesResponse(
|
||||
const LanguageServerProtocol::FindReferencesRequest::Response &response,
|
||||
@@ -78,6 +81,7 @@ private:
|
||||
|
||||
Client *m_client = nullptr;
|
||||
SymbolMapper m_defaultSymbolMapper;
|
||||
RenameResultsEnhancer m_renameResultsEnhancer;
|
||||
QHash<Core::SearchResult *, LanguageServerProtocol::MessageId> m_renameRequestIds;
|
||||
bool m_limitRenamingToProjects = false;
|
||||
};
|
||||
|
@@ -155,7 +155,7 @@ void TestDeclarationComments::commentsForDecl()
|
||||
const Symbol * const symbol = finder.find();
|
||||
QVERIFY(symbol);
|
||||
|
||||
const QList<Token> commentTokens = commentsForDeclaration(symbol, m_snapshot, m_textDoc);
|
||||
const QList<Token> commentTokens = commentsForDeclaration(symbol, m_textDoc, m_cppDoc);
|
||||
if (expectedCommentPrefix.isEmpty()) {
|
||||
QVERIFY(commentTokens.isEmpty());
|
||||
return;
|
||||
|
Reference in New Issue
Block a user