// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppquickfixassistant.h" #include "cppeditorwidget.h" #include "cppmodelmanager.h" #include "cppquickfix.h" #include "cpprefactoringchanges.h" #include #include #include #include #include #include using namespace CPlusPlus; using namespace TextEditor; namespace CppEditor::Internal { // CppQuickFixAssistProvider class CppQuickFixAssistProvider final : public IAssistProvider { public: class CppQuickFixAssistProcessor final : public IAssistProcessor { IAssistProposal *perform() final { return GenericProposal::createProposal(interface(), quickFixOperations(interface())); } }; TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const final { return new CppQuickFixAssistProcessor; } }; IAssistProvider &cppQuickFixAssistProvider() { static CppQuickFixAssistProvider theCppQuickFixAssistProvider; return theCppQuickFixAssistProvider; } // CppQuickFixAssistInterface CppQuickFixInterface::CppQuickFixInterface(CppEditorWidget *editor, AssistReason reason) : AssistInterface(editor->textCursor(), editor->textDocument()->filePath(), reason) , m_editor(editor) , m_semanticInfo(editor->semanticInfo()) , m_snapshot(CppModelManager::snapshot()) , m_currentFile(CppRefactoringChanges::file(editor, m_semanticInfo.doc)) , m_context(m_semanticInfo.doc, m_snapshot) { QTC_CHECK(m_semanticInfo.doc); QTC_CHECK(m_semanticInfo.doc->translationUnit()); QTC_CHECK(m_semanticInfo.doc->translationUnit()->ast()); ASTPath astPath(m_semanticInfo.doc); m_path = astPath(adjustedCursor()); } const QList &CppQuickFixInterface::path() const { return m_path; } Snapshot CppQuickFixInterface::snapshot() const { return m_snapshot; } SemanticInfo CppQuickFixInterface::semanticInfo() const { return m_semanticInfo; } const LookupContext &CppQuickFixInterface::context() const { return m_context; } CppEditorWidget *CppQuickFixInterface::editor() const { return m_editor; } CppRefactoringFilePtr CppQuickFixInterface::currentFile() const { return m_currentFile; } bool CppQuickFixInterface::isCursorOn(unsigned tokenIndex) const { return currentFile()->isCursorOn(tokenIndex); } 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); if (!cppInterface) return {}; QuickFixOperations quickFixes; for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories()) factory->match(*cppInterface, quickFixes); return quickFixes; } } // namespace CppEditor::Internal