diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp index d5f299750ee..0e396166ce3 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp @@ -26,6 +26,9 @@ #include "AST.h" #include "Literals.h" #include "DiagnosticClient.h" + +#include + #include #include #include @@ -382,6 +385,13 @@ void TranslationUnit::getTokenPosition(int index, const StringLiteral **fileName) const { return getPosition(tokenAt(index).utf16charsBegin(), line, column, fileName); } +int TranslationUnit::getTokenPositionInDocument(int index, const QTextDocument *doc) const +{ + int line, column; + getTokenPosition(index, &line, &column); + return Utils::Text::positionInText(doc, line, column); +} + void TranslationUnit::getTokenStartPosition(int index, int *line, int *column, const StringLiteral **fileName) const diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.h b/src/libs/3rdparty/cplusplus/TranslationUnit.h index 7422885a7e6..38c680994e9 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.h +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.h @@ -28,6 +28,10 @@ #include #include +QT_BEGIN_NAMESPACE +class QTextDocument; +QT_END_NAMESPACE + namespace CPlusPlus { class CPLUSPLUS_EXPORT TranslationUnit @@ -124,6 +128,8 @@ public: int *column = nullptr, const StringLiteral **fileName = nullptr) const; + int getTokenPositionInDocument(int index, const QTextDocument *doc) const; + void pushLineOffset(int offset); void pushPreprocessorLine(int utf16charOffset, int line, diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index 5310ef7f7a7..7b336349942 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -86,7 +86,7 @@ CppQuickFixInterface::CppQuickFixInterface(CppEditorWidget *editor, AssistReason QTC_CHECK(m_semanticInfo.doc->translationUnit()); QTC_CHECK(m_semanticInfo.doc->translationUnit()->ast()); ASTPath astPath(m_semanticInfo.doc); - m_path = astPath(editor->textCursor()); + m_path = astPath(adjustedCursor()); } const QList &CppQuickFixInterface::path() const @@ -129,6 +129,50 @@ bool CppQuickFixInterface::isCursorOn(const AST *ast) const return currentFile()->isCursorOn(ast); } +// Some users like to select identifiers and expect the quickfix to apply to the selection. +// However, as the cursor position is at the end of the selection, it can happen that +// the quickfix is applied to the following token instead; see e.g. QTCREATORBUG-27886. +// We try to detect this condition: If there is a selection *and* this selection +// corresponds to a C++ token, we move the cursor to that token's position. +QTextCursor CppQuickFixInterface::adjustedCursor() +{ + QTextCursor cursor = this->cursor(); + if (!cursor.hasSelection()) + return cursor; + + const TranslationUnit * const tu = m_semanticInfo.doc->translationUnit(); + const int selStart = cursor.selectionStart(); + const int selEnd = cursor.selectionEnd(); + const QTextDocument * const doc = m_editor->textDocument()->document(); + + // Binary search for matching token. + for (int l = 0, u = tu->tokenCount() - 1; l <= u; ) { + const int i = (l + u) / 2; + const int tokenPos = tu->getTokenPositionInDocument(i, doc); + if (selStart < tokenPos) { + u = i - 1; + continue; + } + if (selStart > tokenPos) { + l = i + 1; + continue; + } + + // Selection does not end at token end. + if (tokenPos + tu->tokenAt(i).utf16chars() != selEnd) + break; + + cursor.setPosition(selStart); + + // Try not to have the cursor "at the edge", in order to prevent potential ambiguities. + if (selEnd - selStart > 1) + cursor.setPosition(cursor.position() + 1); + + return cursor; + } + return cursor; +} + QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface) { const auto cppInterface = dynamic_cast(interface); diff --git a/src/plugins/cppeditor/cppquickfixassistant.h b/src/plugins/cppeditor/cppquickfixassistant.h index 0824b82cd1f..c3964d854f3 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.h +++ b/src/plugins/cppeditor/cppquickfixassistant.h @@ -33,7 +33,6 @@ #include - namespace CppEditor { class CppEditorWidget; class CppRefactoringFile; @@ -59,6 +58,8 @@ public: bool isBaseObject() const override { return false; } private: + QTextCursor adjustedCursor(); + CppEditorWidget *m_editor; SemanticInfo m_semanticInfo; CPlusPlus::Snapshot m_snapshot;