From 96ef6c797b9ae8f87a0701b2e559d90c1b7b6141 Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Wed, 2 Aug 2017 14:39:45 +0200 Subject: [PATCH] CppEditor: split CppEditor and CppEditorWidget Change-Id: Id3c815184f7f3bace0276e947f6b6f76e61ec6de Reviewed-by: Nikolai Kosjar --- src/plugins/cppeditor/cppautocompleter.cpp | 1 + .../cppeditor/cppcodemodelinspectordialog.cpp | 2 +- src/plugins/cppeditor/cppdoxygen_test.cpp | 2 + src/plugins/cppeditor/cppeditor.cpp | 992 +--------------- src/plugins/cppeditor/cppeditor.h | 114 -- src/plugins/cppeditor/cppeditor.pro | 2 + src/plugins/cppeditor/cppeditor.qbs | 2 + src/plugins/cppeditor/cppeditorplugin.cpp | 2 +- src/plugins/cppeditor/cppeditortestcase.cpp | 1 + src/plugins/cppeditor/cppeditortestcase.h | 2 - src/plugins/cppeditor/cppeditorwidget.cpp | 1031 +++++++++++++++++ src/plugins/cppeditor/cppeditorwidget.h | 145 +++ .../cppeditor/cppfollowsymbolundercursor.cpp | 2 +- .../cppeditor/cppfunctiondecldeflink.cpp | 2 +- src/plugins/cppeditor/cppincludehierarchy.cpp | 1 + .../cppeditor/cppincludehierarchy_test.cpp | 2 + src/plugins/cppeditor/cppoutline.h | 1 + src/plugins/cppeditor/cppparsecontext.cpp | 2 +- .../cppeditor/cpppreprocessordialog.cpp | 1 + src/plugins/cppeditor/cppquickfix_test.cpp | 2 + .../cppeditor/cppquickfixassistant.cpp | 3 +- src/plugins/cppeditor/cppquickfixes.cpp | 2 +- src/plugins/cppeditor/cpptypehierarchy.cpp | 1 + .../cppeditor/cppuseselections_test.cpp | 1 + .../cppeditor/cppuseselectionsupdater.cpp | 2 +- .../cppeditor/fileandtokenactions_test.cpp | 1 + .../followsymbol_switchmethoddecldef_test.cpp | 2 + 27 files changed, 1207 insertions(+), 1114 deletions(-) create mode 100644 src/plugins/cppeditor/cppeditorwidget.cpp create mode 100644 src/plugins/cppeditor/cppeditorwidget.h diff --git a/src/plugins/cppeditor/cppautocompleter.cpp b/src/plugins/cppeditor/cppautocompleter.cpp index 8c1315b973a..6bc60d2093a 100644 --- a/src/plugins/cppeditor/cppautocompleter.cpp +++ b/src/plugins/cppeditor/cppautocompleter.cpp @@ -86,6 +86,7 @@ QString CppAutoCompleter::insertParagraphSeparator(const QTextCursor &cursor) co #ifdef WITH_TESTS #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorconstants.h" #include "cppeditorplugin.h" diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp index fed7c4c8549..6283d43d497 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -25,7 +25,7 @@ #include "cppcodemodelinspectordialog.h" #include "ui_cppcodemodelinspectordialog.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 6dde8cb4c09..639e0d5b8ae 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.cpp +++ b/src/plugins/cppeditor/cppdoxygen_test.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ +#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppdoxygen_test.h" #include "cppeditortestcase.h" diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 1c68cf5ab5e..a2b622a2f94 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -24,81 +24,12 @@ ****************************************************************************/ #include "cppeditor.h" - #include "cppautocompleter.h" -#include "cppdocumentationcommenthelper.h" -#include "cppeditorconstants.h" -#include "cppeditordocument.h" -#include "cppeditorplugin.h" -#include "cppfollowsymbolundercursor.h" #include "cpphighlighter.h" -#include "cpplocalrenaming.h" -#include "cppminimizableinfobars.h" -#include "cpppreprocessordialog.h" -#include "cppquickfixassistant.h" -#include "cppuseselectionsupdater.h" -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 }; - -using namespace Core; -using namespace CPlusPlus; -using namespace CppTools; -using namespace TextEditor; namespace CppEditor { namespace Internal { @@ -108,931 +39,12 @@ CppEditor::CppEditor() addContext(ProjectExplorer::Constants::CXX_LANGUAGE_ID); } -void CppEditor::decorateEditor(TextEditorWidget *editor) +void CppEditor::decorateEditor(TextEditor::TextEditorWidget *editor) { editor->textDocument()->setSyntaxHighlighter(new CppHighlighter); editor->textDocument()->setIndenter(new CppTools::CppQtStyleIndenter); editor->setAutoCompleter(new CppAutoCompleter); } -class CppEditorWidgetPrivate -{ -public: - CppEditorWidgetPrivate(CppEditorWidget *q); - -public: - QPointer m_modelManager; - - CppEditorDocument *m_cppEditorDocument; - CppEditorOutline *m_cppEditorOutline; - - QTimer m_updateFunctionDeclDefLinkTimer; - - CppLocalRenaming m_localRenaming; - - SemanticInfo m_lastSemanticInfo; - - CppUseSelectionsUpdater m_useSelectionsUpdater; - - FunctionDeclDefLinkFinder *m_declDefLinkFinder; - QSharedPointer m_declDefLink; - - QScopedPointer m_followSymbolUnderCursor; - - QAction *m_parseContextAction = nullptr; - ParseContextWidget *m_parseContextWidget = nullptr; - QToolButton *m_preprocessorButton = nullptr; - MinimizableInfoBars::Actions m_showInfoBarActions; - - CppSelectionChanger m_cppSelectionChanger; -}; - -CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) - : m_modelManager(CppModelManager::instance()) - , m_cppEditorDocument(qobject_cast(q->textDocument())) - , m_cppEditorOutline(new CppEditorOutline(q)) - , m_localRenaming(q) - , m_useSelectionsUpdater(q) - , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) - , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q)) - , m_cppSelectionChanger() -{ -} - -CppEditorWidget::CppEditorWidget() - : d(new CppEditorWidgetPrivate(this)) -{ - qRegisterMetaType("CppTools::SemanticInfo"); -} - -void CppEditorWidget::finalizeInitialization() -{ - d->m_cppEditorDocument = qobject_cast(textDocument()); - - setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID); - - // function combo box sorting - connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged, - outline(), &CppEditorOutline::setSorted); - - connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated, - this, &CppEditorWidget::onCodeWarningsUpdated); - connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated, - this, &CppEditorWidget::onIfdefedOutBlocksUpdated); - connect(d->m_cppEditorDocument, &CppEditorDocument::cppDocumentUpdated, - this, &CppEditorWidget::onCppDocumentUpdated); - connect(d->m_cppEditorDocument, &CppEditorDocument::semanticInfoUpdated, - this, [this](const CppTools::SemanticInfo &info) { updateSemanticInfo(info); }); - - connect(d->m_declDefLinkFinder, &FunctionDeclDefLinkFinder::foundLink, - this, &CppEditorWidget::onFunctionDeclDefLinkFound); - - connect(&d->m_useSelectionsUpdater, - &CppUseSelectionsUpdater::selectionsForVariableUnderCursorUpdated, - &d->m_localRenaming, - &CppLocalRenaming::updateSelectionsForVariableUnderCursor); - - connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished, this, - [this] (SemanticInfo::LocalUseMap localUses) { - d->m_lastSemanticInfo.localUsesUpdated = true; - d->m_lastSemanticInfo.localUses = localUses; - }); - - connect(document(), &QTextDocument::contentsChange, - &d->m_localRenaming, &CppLocalRenaming::onContentsChangeOfEditorWidgetDocument); - connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] { - cppEditorDocument()->recalculateSemanticInfoDetached(); - }); - connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally, - this, &CppEditorWidget::processKeyNormally); - connect(this, &QPlainTextEdit::cursorPositionChanged, - d->m_cppEditorOutline, &CppEditorOutline::updateIndex); - - connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged, this, - [this](bool customSettings) { - updateWidgetHighlighting(d->m_preprocessorButton, customSettings); - }); - - // set up function declaration - definition link - d->m_updateFunctionDeclDefLinkTimer.setSingleShot(true); - d->m_updateFunctionDeclDefLinkTimer.setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL); - connect(&d->m_updateFunctionDeclDefLinkTimer, &QTimer::timeout, - this, &CppEditorWidget::updateFunctionDeclDefLinkNow); - connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CppEditorWidget::updateFunctionDeclDefLink); - connect(this, &QPlainTextEdit::textChanged, this, &CppEditorWidget::updateFunctionDeclDefLink); - - // set up the use highlighitng - connect(this, &CppEditorWidget::cursorPositionChanged, this, [this]() { - if (!d->m_localRenaming.isActive()) - d->m_useSelectionsUpdater.scheduleUpdate(); - - // Notify selection expander about the changed cursor. - d->m_cppSelectionChanger.onCursorPositionChanged(textCursor()); - }); - - // Toolbar: Outline/Overview combo box - insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget()); - - // Toolbar: Parse context - ParseContextModel &parseContextModel = cppEditorDocument()->parseContextModel(); - d->m_parseContextWidget = new ParseContextWidget(parseContextModel, this); - d->m_parseContextAction = insertExtraToolBarWidget(TextEditorWidget::Left, - d->m_parseContextWidget); - d->m_parseContextAction->setVisible(false); - connect(&parseContextModel, &ParseContextModel::updated, - this, [this](bool areMultipleAvailable) { - d->m_parseContextAction->setVisible(areMultipleAvailable); - }); - - // Toolbar: '#' Button - // TODO: Make "Additional Preprocessor Directives" also useful with Clang Code Model. - if (!d->m_modelManager->isClangCodeModelActive()) { - d->m_preprocessorButton = new QToolButton(this); - d->m_preprocessorButton->setText(QLatin1String("#")); - Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG); - connect(cmd, &Command::keySequenceChanged, - this, &CppEditorWidget::updatePreprocessorButtonTooltip); - updatePreprocessorButtonTooltip(); - connect(d->m_preprocessorButton, &QAbstractButton::clicked, - this, &CppEditorWidget::showPreProcessorWidget); - - insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton); - } - - // Toolbar: Actions to show minimized info bars - d->m_showInfoBarActions = MinimizableInfoBars::createShowInfoBarActions([this](QWidget *w) { - return this->insertExtraToolBarWidget(TextEditorWidget::Left, w); - }); - connect(&cppEditorDocument()->minimizableInfoBars(), &MinimizableInfoBars::showAction, - this, &CppEditorWidget::onShowInfoBarAction); -} - -void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *other) -{ - QTC_ASSERT(other, return); - CppEditorWidget *cppEditorWidget = qobject_cast(other); - QTC_ASSERT(cppEditorWidget, return); - - if (cppEditorWidget->isSemanticInfoValidExceptLocalUses()) - updateSemanticInfo(cppEditorWidget->semanticInfo()); - d->m_cppEditorOutline->update(); - const Id selectionKind = CodeWarningsSelection; - setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind)); - - if (isWidgetHighlighted(cppEditorWidget->d->m_preprocessorButton)) - updateWidgetHighlighting(d->m_preprocessorButton, true); - - d->m_parseContextWidget->syncToModel(); - d->m_parseContextAction->setVisible( - d->m_cppEditorDocument->parseContextModel().areMultipleAvailable()); -} - -CppEditorWidget::~CppEditorWidget() -{ - // non-inline destructor, see section "Forward Declared Pointers" of QScopedPointer. -} - -CppEditorDocument *CppEditorWidget::cppEditorDocument() const -{ - return d->m_cppEditorDocument; -} - -CppEditorOutline *CppEditorWidget::outline() const -{ - return d->m_cppEditorOutline; -} - -void CppEditorWidget::paste() -{ - if (d->m_localRenaming.handlePaste()) - return; - - TextEditorWidget::paste(); -} - -void CppEditorWidget::cut() -{ - if (d->m_localRenaming.handleCut()) - return; - - TextEditorWidget::cut(); -} - -void CppEditorWidget::selectAll() -{ - if (d->m_localRenaming.handleSelectAll()) - return; - - TextEditorWidget::selectAll(); -} - -void CppEditorWidget::onCppDocumentUpdated() -{ - d->m_cppEditorOutline->update(); -} - -void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, - const QList selections, - const TextEditor::RefactorMarkers &refactorMarkers) -{ - if (revision != documentRevision()) - return; - - setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); - setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers); -} - -void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, - const QList ifdefedOutBlocks) -{ - if (revision != documentRevision()) - return; - setIfdefedOutBlocks(ifdefedOutBlocks); -} - -void CppEditorWidget::onShowInfoBarAction(const Id &id, bool show) -{ - QAction *action = d->m_showInfoBarActions.value(id); - QTC_ASSERT(action, return); - action->setVisible(show); -} - -void CppEditorWidget::findUsages() -{ - if (!d->m_modelManager) - return; - - SemanticInfo info = d->m_lastSemanticInfo; - info.snapshot = CppModelManager::instance()->snapshot(); - info.snapshot.insert(info.doc); - - if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) { - d->m_modelManager->findMacroUsages(*macro); - } else { - CanonicalSymbol cs(info.doc, info.snapshot); - Symbol *canonicalSymbol = cs(textCursor()); - if (canonicalSymbol) - d->m_modelManager->findUsages(canonicalSymbol, cs.context()); - } -} - -void CppEditorWidget::renameUsages(const QString &replacement) -{ - if (!d->m_modelManager) - return; - - SemanticInfo info = d->m_lastSemanticInfo; - info.snapshot = CppModelManager::instance()->snapshot(); - info.snapshot.insert(info.doc); - - if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) { - d->m_modelManager->renameMacroUsages(*macro, replacement); - } else { - CanonicalSymbol cs(info.doc, info.snapshot); - if (Symbol *canonicalSymbol = cs(textCursor())) - if (canonicalSymbol->identifier() != 0) - d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement); - } -} - -bool CppEditorWidget::selectBlockUp() -{ - if (!behaviorSettings().m_smartSelectionChanging) - return TextEditorWidget::selectBlockUp(); - - QTextCursor cursor = textCursor(); - d->m_cppSelectionChanger.startChangeSelection(); - const bool changed = - d->m_cppSelectionChanger.changeSelection( - CppSelectionChanger::ExpandSelection, - cursor, - d->m_lastSemanticInfo.doc); - if (changed) - setTextCursor(cursor); - d->m_cppSelectionChanger.stopChangeSelection(); - - return changed; -} - -bool CppEditorWidget::selectBlockDown() -{ - if (!behaviorSettings().m_smartSelectionChanging) - return TextEditorWidget::selectBlockDown(); - - QTextCursor cursor = textCursor(); - d->m_cppSelectionChanger.startChangeSelection(); - const bool changed = - d->m_cppSelectionChanger.changeSelection( - CppSelectionChanger::ShrinkSelection, - cursor, - d->m_lastSemanticInfo.doc); - if (changed) - setTextCursor(cursor); - d->m_cppSelectionChanger.stopChangeSelection(); - - return changed; -} - -void CppEditorWidget::updateWidgetHighlighting(QWidget *widget, bool highlight) -{ - if (!widget) - return; - - widget->setProperty("highlightWidget", highlight); - widget->update(); -} - -bool CppEditorWidget::isWidgetHighlighted(QWidget *widget) -{ - return widget ? widget->property("highlightWidget").toBool() : false; -} - -void CppEditorWidget::renameSymbolUnderCursor() -{ - if (refactoringEngine()) - renameSymbolUnderCursorClang(); - else - renameSymbolUnderCursorBuiltin(); -} - -void CppEditorWidget::renameSymbolUnderCursorBuiltin() -{ - updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(), - /*updateUseSelectionSynchronously=*/ true); - - if (!d->m_localRenaming.start()) // Rename local symbol - renameUsages(); // Rename non-local symbol or macro -} - -namespace { - -QList fetchProjectParts(CppTools::CppModelManager *modelManager, - const Utils::FileName &filePath) -{ - QList projectParts = modelManager->projectPart(filePath); - - if (projectParts.isEmpty()) - projectParts = modelManager->projectPartFromDependencies(filePath); - if (projectParts.isEmpty()) - projectParts.append(modelManager->fallbackProjectPart()); - - return projectParts; -} - -ProjectPart *findProjectPartForCurrentProject(const QList &projectParts, - ProjectExplorer::Project *currentProject) -{ - auto found = std::find_if(projectParts.cbegin(), - projectParts.cend(), - [&] (const CppTools::ProjectPart::Ptr &projectPart) { - return projectPart->project == currentProject; - }); - - if (found != projectParts.cend()) - return (*found).data(); - - return 0; -} - -} - -ProjectPart *CppEditorWidget::projectPart() const -{ - if (!d->m_modelManager) - return 0; - - auto projectParts = fetchProjectParts(d->m_modelManager, textDocument()->filePath()); - - return findProjectPartForCurrentProject(projectParts, - ProjectExplorer::ProjectTree::currentProject()); -} - -namespace { - -using ClangBackEnd::V2::SourceLocationContainer; -using TextEditor::Convenience::selectAt; - -QTextCharFormat occurrencesTextCharFormat() -{ - using TextEditor::TextEditorSettings; - - return TextEditorSettings::fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES); -} - -QList -sourceLocationsToExtraSelections(const std::vector &sourceLocations, - uint selectionLength, - CppEditorWidget *cppEditorWidget) -{ - const auto textCharFormat = occurrencesTextCharFormat(); - - QList selections; - selections.reserve(int(sourceLocations.size())); - - auto sourceLocationToExtraSelection = [&] (const SourceLocationContainer &sourceLocation) { - QTextEdit::ExtraSelection selection; - - selection.cursor = selectAt(cppEditorWidget->textCursor(), - sourceLocation.line(), - sourceLocation.column(), - selectionLength); - selection.format = textCharFormat; - - return selection; - }; - - - std::transform(sourceLocations.begin(), - sourceLocations.end(), - std::back_inserter(selections), - sourceLocationToExtraSelection); - - return selections; -}; - -} - -void CppEditorWidget::renameSymbolUnderCursorClang() -{ - using ClangBackEnd::SourceLocationsContainer; - - ProjectPart *theProjectPart = projectPart(); - if (refactoringEngine()->isUsable() && theProjectPart) { - d->m_useSelectionsUpdater.abortSchedule(); - - QPointer cppEditorWidget = this; - - auto renameSymbols = [=] (const QString &symbolName, - const SourceLocationsContainer &sourceLocations, - int revision) { - if (cppEditorWidget) { - viewport()->setCursor(Qt::IBeamCursor); - - if (revision == document()->revision()) { - auto selections = sourceLocationsToExtraSelections(sourceLocations.sourceLocationContainers(), - symbolName.size(), - cppEditorWidget); - setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, - selections); - d->m_localRenaming.updateSelectionsForVariableUnderCursor(selections); - if (!d->m_localRenaming.start()) - renameUsages(); - } - } - }; - - refactoringEngine()->startLocalRenaming(textCursor(), - textDocument()->filePath(), - document()->revision(), - theProjectPart, - std::move(renameSymbols)); - - viewport()->setCursor(Qt::BusyCursor); - } -} - -void CppEditorWidget::updatePreprocessorButtonTooltip() -{ - if (!d->m_preprocessorButton) - return; - - Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG); - QTC_ASSERT(cmd, return); - d->m_preprocessorButton->setToolTip(cmd->action()->toolTip()); -} - -void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit) -{ - if (!d->m_modelManager) - return; - - if (!d->m_lastSemanticInfo.doc) - return; - - // Find function declaration or definition under cursor - Function *functionDefinitionSymbol = 0; - Symbol *functionDeclarationSymbol = 0; - - ASTPath astPathFinder(d->m_lastSemanticInfo.doc); - const QList astPath = astPathFinder(textCursor()); - - for (int i = 0, size = astPath.size(); i < size; ++i) { - AST *ast = astPath.at(i); - if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) { - if ((functionDefinitionSymbol = functionDefinitionAST->symbol)) - break; // Function definition found! - } else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) { - if (List *symbols = simpleDeclaration->symbols) { - if (Symbol *symbol = symbols->value) { - if (symbol->isDeclaration() && symbol->type()->isFunctionType()) { - functionDeclarationSymbol = symbol; - break; // Function declaration found! - } - } - } - } - } - - // Link to function definition/declaration - CppEditorWidget::Link symbolLink; - if (functionDeclarationSymbol) { - symbolLink = linkToSymbol(d->m_modelManager->symbolFinder() - ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot())); - } else if (functionDefinitionSymbol) { - const Snapshot snapshot = d->m_modelManager->snapshot(); - LookupContext context(d->m_lastSemanticInfo.doc, snapshot); - ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol); - const QList declarations = context.lookup(functionDefinitionSymbol->name(), - functionDefinitionSymbol->enclosingScope()); - - QList best; - foreach (const LookupItem &r, declarations) { - if (Symbol *decl = r.declaration()) { - if (Function *funTy = decl->type()->asFunctionType()) { - if (funTy->match(functionDefinitionSymbol)) { - if (decl != functionDefinitionSymbol && binding == r.binding()) - best.prepend(decl); - else - best.append(decl); - } - } - } - } - - if (best.isEmpty()) - return; - symbolLink = linkToSymbol(best.first()); - } - - // Open Editor at link position - if (symbolLink.hasValidTarget()) - openLink(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit()); -} - -CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget, - bool inNextSplit) -{ - if (!d->m_modelManager) - return Link(); - - return d->m_followSymbolUnderCursor->findLink(cursor, resolveTarget, - d->m_modelManager->snapshot(), - d->m_lastSemanticInfo.doc, - d->m_modelManager->symbolFinder(), - inNextSplit); -} - -unsigned CppEditorWidget::documentRevision() const -{ - return document()->revision(); -} - -static bool isClangFixItAvailableMarker(const RefactorMarker &marker) -{ - return marker.data.toString() - == QLatin1String(CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID); -} - -RefactorMarkers CppEditorWidget::refactorMarkersWithoutClangMarkers() const -{ - RefactorMarkers clearedRefactorMarkers; - - foreach (const RefactorMarker &marker, refactorMarkers()) { - if (isClangFixItAvailableMarker(marker)) - continue; - - clearedRefactorMarkers.append(marker); - } - - return clearedRefactorMarkers; -} - -RefactoringEngineInterface *CppEditorWidget::refactoringEngine() const -{ - return CppTools::CppModelManager::refactoringEngine(); -} - -bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const -{ - return d->m_lastSemanticInfo.doc - && d->m_lastSemanticInfo.revision == documentRevision() - && !d->m_lastSemanticInfo.snapshot.isEmpty(); -} - -bool CppEditorWidget::isSemanticInfoValid() const -{ - return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated; -} - -SemanticInfo CppEditorWidget::semanticInfo() const -{ - return d->m_lastSemanticInfo; -} - -bool CppEditorWidget::event(QEvent *e) -{ - switch (e->type()) { - case QEvent::ShortcutOverride: - // handle escape manually if a rename is active - if (static_cast(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) { - e->accept(); - return true; - } - break; - default: - break; - } - - return TextEditorWidget::event(e); -} - -void CppEditorWidget::processKeyNormally(QKeyEvent *e) -{ - TextEditorWidget::keyPressEvent(e); -} - -void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e) -{ - // ### enable - // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); - - QPointer menu(new QMenu(this)); - - ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT); - QMenu *contextMenu = mcontext->menu(); - - QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu); - quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action()); - - if (isSemanticInfoValidExceptLocalUses()) { - d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous); - AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked); - if (interface) { - QScopedPointer processor( - CppEditorPlugin::instance()->quickFixProvider()->createProcessor()); - QScopedPointer proposal(processor->perform(interface)); - if (!proposal.isNull()) { - auto model = static_cast(proposal->model()); - for (int index = 0; index < model->size(); ++index) { - auto item = static_cast(model->proposalItem(index)); - QuickFixOperation::Ptr op = item->data().value(); - QAction *action = quickFixMenu->addAction(op->description()); - connect(action, &QAction::triggered, this, [op] { op->perform(); }); - } - delete model; - } - } - } - - foreach (QAction *action, contextMenu->actions()) { - menu->addAction(action); - if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)) - menu->addMenu(quickFixMenu); - } - - appendStandardContextMenuActions(menu); - - menu->exec(e->globalPos()); - if (!menu) - return; - delete menu; -} - -void CppEditorWidget::keyPressEvent(QKeyEvent *e) -{ - if (d->m_localRenaming.handleKeyPressEvent(e)) - return; - - if (handleStringSplitting(e)) - return; - - if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - if (trySplitComment(this, semanticInfo().snapshot)) { - e->accept(); - return; - } - } - - TextEditorWidget::keyPressEvent(e); -} - -bool CppEditorWidget::handleStringSplitting(QKeyEvent *e) const -{ - if (!TextEditorSettings::completionSettings().m_autoSplitStrings) - return false; - - if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - QTextCursor cursor = textCursor(); - - const Kind stringKind = CPlusPlus::MatchingText::stringKindAtCursor(cursor); - if (stringKind >= T_FIRST_STRING_LITERAL && stringKind < T_FIRST_RAW_STRING_LITERAL) { - cursor.beginEditBlock(); - if (cursor.positionInBlock() > 0 - && cursor.block().text().at(cursor.positionInBlock() - 1) == QLatin1Char('\\')) { - // Already escaped: simply go back to line, but do not indent. - cursor.insertText(QLatin1String("\n")); - } else if (e->modifiers() & Qt::ShiftModifier) { - // With 'shift' modifier, escape the end of line character - // and start at beginning of next line. - cursor.insertText(QLatin1String("\\\n")); - } else { - // End the current string, and start a new one on the line, properly indented. - cursor.insertText(QLatin1String("\"\n\"")); - textDocument()->autoIndent(cursor); - } - cursor.endEditBlock(); - e->accept(); - return true; - } - } - - return false; -} - -void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &) -{ - QtStyleCodeFormatter formatter; - formatter.invalidateCache(document()); -} - -void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo, - bool updateUseSelectionSynchronously) -{ - if (semanticInfo.revision != documentRevision()) - return; - - d->m_lastSemanticInfo = semanticInfo; - - if (!d->m_localRenaming.isActive()) { - const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously - ? CppUseSelectionsUpdater::Synchronous - : CppUseSelectionsUpdater::Asynchronous; - d->m_useSelectionsUpdater.update(type); - } - - // schedule a check for a decl/def link - updateFunctionDeclDefLink(); -} - -AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const -{ - if (kind == Completion) { - if (CppCompletionAssistProvider *cap = - qobject_cast(cppEditorDocument()->completionAssistProvider())) { - LanguageFeatures features = LanguageFeatures::defaultFeatures(); - if (Document::Ptr doc = d->m_lastSemanticInfo.doc) - features = doc->languageFeatures(); - features.objCEnabled |= cppEditorDocument()->isObjCEnabled(); - return cap->createAssistInterface( - textDocument()->filePath().toString(), - this, - features, - position(), - reason); - } - } else if (kind == QuickFix) { - if (isSemanticInfoValid()) - return new CppQuickFixInterface(const_cast(this), reason); - } else { - return TextEditorWidget::createAssistInterface(kind, reason); - } - return 0; -} - -QSharedPointer CppEditorWidget::declDefLink() const -{ - return d->m_declDefLink; -} - -void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker) -{ - if (marker.data.canConvert()) { - applyDeclDefLinkChanges(true); - } else if (isClangFixItAvailableMarker(marker)) { - int line, column; - if (Convenience::convertPosition(document(), marker.cursor.position(), &line, &column)) { - setTextCursor(marker.cursor); - invokeAssist(TextEditor::QuickFix); - } - } -} - -void CppEditorWidget::updateFunctionDeclDefLink() -{ - const int pos = textCursor().selectionStart(); - - // if there's already a link, abort it if the cursor is outside or the name changed - // (adding a prefix is an exception since the user might type a return type) - if (d->m_declDefLink - && (pos < d->m_declDefLink->linkSelection.selectionStart() - || pos > d->m_declDefLink->linkSelection.selectionEnd() - || !d->m_declDefLink->nameSelection.selectedText().trimmed() - .endsWith(d->m_declDefLink->nameInitial))) { - abortDeclDefLink(); - return; - } - - // don't start a new scan if there's one active and the cursor is already in the scanned area - const QTextCursor scannedSelection = d->m_declDefLinkFinder->scannedSelection(); - if (!scannedSelection.isNull() - && scannedSelection.selectionStart() <= pos - && scannedSelection.selectionEnd() >= pos) { - return; - } - - d->m_updateFunctionDeclDefLinkTimer.start(); -} - -void CppEditorWidget::updateFunctionDeclDefLinkNow() -{ - IEditor *editor = EditorManager::currentEditor(); - if (!editor || editor->widget() != this) - return; - - const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot; - const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc; - - if (d->m_declDefLink) { - // update the change marker - const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot); - if (changes.isEmpty()) - d->m_declDefLink->hideMarker(this); - else - d->m_declDefLink->showMarker(this); - return; - } - - if (!isSemanticInfoValidExceptLocalUses()) - return; - - Snapshot snapshot = CppModelManager::instance()->snapshot(); - snapshot.insert(semanticDoc); - - d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot); -} - -void CppEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer link) -{ - abortDeclDefLink(); - d->m_declDefLink = link; - IDocument *targetDocument = DocumentModel::documentForFilePath( d->m_declDefLink->targetFile->fileName()); - if (textDocument() != targetDocument) { - if (auto textDocument = qobject_cast(targetDocument)) - connect(textDocument, &IDocument::contentsChanged, - this, &CppEditorWidget::abortDeclDefLink); - } - -} - -void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch) -{ - if (!d->m_declDefLink) - return; - d->m_declDefLink->apply(this, jumpToMatch); - abortDeclDefLink(); - updateFunctionDeclDefLink(); -} - -FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate() -{ - return d->m_followSymbolUnderCursor.data(); -} - -void CppEditorWidget::encourageApply() -{ - if (d->m_localRenaming.encourageApply()) - return; - - TextEditorWidget::encourageApply(); -} - -void CppEditorWidget::abortDeclDefLink() -{ - if (!d->m_declDefLink) - return; - - IDocument *targetDocument = DocumentModel::documentForFilePath(d->m_declDefLink->targetFile->fileName()); - if (textDocument() != targetDocument) { - if (auto textDocument = qobject_cast(targetDocument)) - disconnect(textDocument, &IDocument::contentsChanged, - this, &CppEditorWidget::abortDeclDefLink); - } - - d->m_declDefLink->hideMarker(this); - d->m_declDefLink.clear(); -} - -void CppEditorWidget::showPreProcessorWidget() -{ - const QString filePath = textDocument()->filePath().toString(); - - CppPreProcessorDialog dialog(filePath, this); - if (dialog.exec() == QDialog::Accepted) { - const QByteArray extraDirectives = dialog.extraPreprocessorDirectives().toUtf8(); - cppEditorDocument()->setExtraPreprocessorDirectives(extraDirectives); - cppEditorDocument()->scheduleProcessDocument(); - } -} - } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 5dc6ee7d49c..fdc19cb6c30 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -25,28 +25,11 @@ #pragma once -#include "cppfunctiondecldeflink.h" - #include -#include - -namespace CppTools { -class CppEditorOutline; -class RefactoringEngineInterface; -class SemanticInfo; -class ProjectPart; -} - namespace CppEditor { namespace Internal { -class CppEditorDocument; - -class CppEditorWidgetPrivate; -class FollowSymbolUnderCursor; -class FunctionDeclDefLink; - class CppEditor : public TextEditor::BaseTextEditor { Q_OBJECT @@ -55,103 +38,6 @@ public: CppEditor(); static void decorateEditor(TextEditor::TextEditorWidget *editor); - -}; - -class CppEditorWidget : public TextEditor::TextEditorWidget -{ - Q_OBJECT - -public: - CppEditorWidget(); - ~CppEditorWidget() override; - - CppEditorDocument *cppEditorDocument() const; - CppTools::CppEditorOutline *outline() const; - - CppTools::SemanticInfo semanticInfo() const; - bool isSemanticInfoValidExceptLocalUses() const; - bool isSemanticInfoValid() const; - - QSharedPointer declDefLink() const; - void applyDeclDefLinkChanges(bool jumpToMatch); - - TextEditor::AssistInterface *createAssistInterface( - TextEditor::AssistKind kind, - TextEditor::AssistReason reason) const override; - - FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests - - void encourageApply() override; - - void paste() override; - void cut() override; - void selectAll() override; - - void switchDeclarationDefinition(bool inNextSplit); - void showPreProcessorWidget(); - - void findUsages(); - void renameSymbolUnderCursor(); - void renameUsages(const QString &replacement = QString()); - - bool selectBlockUp() override; - bool selectBlockDown() override; - - static void updateWidgetHighlighting(QWidget *widget, bool highlight); - static bool isWidgetHighlighted(QWidget *widget); - -protected: - bool event(QEvent *e) override; - void contextMenuEvent(QContextMenuEvent *) override; - void keyPressEvent(QKeyEvent *e) override; - bool handleStringSplitting(QKeyEvent *e) const; - - Link findLinkAt(const QTextCursor &, bool resolveTarget = true, - bool inNextSplit = false) override; - - void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker) override; - - void slotCodeStyleSettingsChanged(const QVariant &) override; - -private: - void updateFunctionDeclDefLink(); - void updateFunctionDeclDefLinkNow(); - void abortDeclDefLink(); - void onFunctionDeclDefLinkFound(QSharedPointer link); - - void onCppDocumentUpdated(); - - void onCodeWarningsUpdated(unsigned revision, - const QList selections, - const TextEditor::RefactorMarkers &refactorMarkers); - void onIfdefedOutBlocksUpdated(unsigned revision, - const QList ifdefedOutBlocks); - - void onShowInfoBarAction(const Core::Id &id, bool show); - - void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo, - bool updateUseSelectionSynchronously = false); - void updatePreprocessorButtonTooltip(); - - void processKeyNormally(QKeyEvent *e); - - void finalizeInitialization() override; - void finalizeInitializationAfterDuplication(TextEditorWidget *other) override; - - unsigned documentRevision() const; - - TextEditor::RefactorMarkers refactorMarkersWithoutClangMarkers() const; - - CppTools::RefactoringEngineInterface *refactoringEngine() const; - - void renameSymbolUnderCursorClang(); - void renameSymbolUnderCursorBuiltin(); - - CppTools::ProjectPart *projectPart() const; - -private: - QScopedPointer d; }; } // namespace Internal diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 8efb3cd4f0d..2bac48cff21 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -11,6 +11,7 @@ HEADERS += \ cppeditorconstants.h \ cppeditorenums.h \ cppeditorplugin.h \ + cppeditorwidget.h \ cppelementevaluator.h \ cppfollowsymbolundercursor.h \ cppfunctiondecldeflink.h \ @@ -39,6 +40,7 @@ SOURCES += \ cppeditor.cpp \ cppeditordocument.cpp \ cppeditorplugin.cpp \ + cppeditorwidget.cpp \ cppelementevaluator.cpp \ cppfollowsymbolundercursor.cpp \ cppfunctiondecldeflink.cpp \ diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 32a403ea4c7..1e87ebdfb40 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -29,6 +29,8 @@ QtcPlugin { "cppdocumentationcommenthelper.h", "cppeditor.cpp", "cppeditor.h", + "cppeditorwidget.cpp", + "cppeditorwidget.h", "cppeditor.qrc", "cppeditor_global.h", "cppeditorconstants.h", diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 1fec96aae6e..3f352f1ffe8 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -28,7 +28,7 @@ #include "cppautocompleter.h" #include "cppcodemodelinspectordialog.h" #include "cppeditorconstants.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include "cpphighlighter.h" #include "cpphoverhandler.h" diff --git a/src/plugins/cppeditor/cppeditortestcase.cpp b/src/plugins/cppeditor/cppeditortestcase.cpp index 7e62dbbc470..027523a02d7 100644 --- a/src/plugins/cppeditor/cppeditortestcase.cpp +++ b/src/plugins/cppeditor/cppeditortestcase.cpp @@ -26,6 +26,7 @@ #include "cppeditortestcase.h" #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include diff --git a/src/plugins/cppeditor/cppeditortestcase.h b/src/plugins/cppeditor/cppeditortestcase.h index dafca4ec14f..6eb9821bddb 100644 --- a/src/plugins/cppeditor/cppeditortestcase.h +++ b/src/plugins/cppeditor/cppeditortestcase.h @@ -25,8 +25,6 @@ #pragma once -#include "cppeditor.h" - #include #include diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp new file mode 100644 index 00000000000..f48648a44ec --- /dev/null +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -0,0 +1,1031 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cppeditorwidget.h" + +#include "cppautocompleter.h" +#include "cppdocumentationcommenthelper.h" +#include "cppeditorconstants.h" +#include "cppeditordocument.h" +#include "cppeditorplugin.h" +#include "cppfollowsymbolundercursor.h" +#include "cppfunctiondecldeflink.h" +#include "cpphighlighter.h" +#include "cpplocalrenaming.h" +#include "cppminimizableinfobars.h" +#include "cpppreprocessordialog.h" +#include "cppquickfixassistant.h" +#include "cppuseselectionsupdater.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 }; + +using namespace Core; +using namespace CPlusPlus; +using namespace CppTools; +using namespace TextEditor; + +namespace CppEditor { +namespace Internal { + +class CppEditorWidgetPrivate +{ +public: + CppEditorWidgetPrivate(CppEditorWidget *q); + +public: + QPointer m_modelManager; + + CppEditorDocument *m_cppEditorDocument; + CppEditorOutline *m_cppEditorOutline; + + QTimer m_updateFunctionDeclDefLinkTimer; + + CppLocalRenaming m_localRenaming; + + SemanticInfo m_lastSemanticInfo; + + CppUseSelectionsUpdater m_useSelectionsUpdater; + + FunctionDeclDefLinkFinder *m_declDefLinkFinder; + QSharedPointer m_declDefLink; + + QScopedPointer m_followSymbolUnderCursor; + + QAction *m_parseContextAction = nullptr; + ParseContextWidget *m_parseContextWidget = nullptr; + QToolButton *m_preprocessorButton = nullptr; + MinimizableInfoBars::Actions m_showInfoBarActions; + + CppSelectionChanger m_cppSelectionChanger; +}; + +CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) + : m_modelManager(CppModelManager::instance()) + , m_cppEditorDocument(qobject_cast(q->textDocument())) + , m_cppEditorOutline(new CppEditorOutline(q)) + , m_localRenaming(q) + , m_useSelectionsUpdater(q) + , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) + , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q)) + , m_cppSelectionChanger() +{} + +CppEditorWidget::CppEditorWidget() + : d(new CppEditorWidgetPrivate(this)) +{ + qRegisterMetaType("CppTools::SemanticInfo"); +} + +void CppEditorWidget::finalizeInitialization() +{ + d->m_cppEditorDocument = qobject_cast(textDocument()); + + setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID); + + // clang-format off + // function combo box sorting + connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged, + outline(), &CppEditorOutline::setSorted); + + connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated, + this, &CppEditorWidget::onCodeWarningsUpdated); + connect(d->m_cppEditorDocument, &CppEditorDocument::ifdefedOutBlocksUpdated, + this, &CppEditorWidget::onIfdefedOutBlocksUpdated); + connect(d->m_cppEditorDocument, &CppEditorDocument::cppDocumentUpdated, + this, &CppEditorWidget::onCppDocumentUpdated); + connect(d->m_cppEditorDocument, &CppEditorDocument::semanticInfoUpdated, + this, [this](const CppTools::SemanticInfo &info) { updateSemanticInfo(info); }); + + connect(d->m_declDefLinkFinder, &FunctionDeclDefLinkFinder::foundLink, + this, &CppEditorWidget::onFunctionDeclDefLinkFound); + + connect(&d->m_useSelectionsUpdater, + &CppUseSelectionsUpdater::selectionsForVariableUnderCursorUpdated, + &d->m_localRenaming, + &CppLocalRenaming::updateSelectionsForVariableUnderCursor); + + connect(&d->m_useSelectionsUpdater, &CppUseSelectionsUpdater::finished, this, + [this] (SemanticInfo::LocalUseMap localUses) { + d->m_lastSemanticInfo.localUsesUpdated = true; + d->m_lastSemanticInfo.localUses = localUses; + }); + + connect(document(), &QTextDocument::contentsChange, + &d->m_localRenaming, &CppLocalRenaming::onContentsChangeOfEditorWidgetDocument); + connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] { + cppEditorDocument()->recalculateSemanticInfoDetached(); + }); + connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally, + this, &CppEditorWidget::processKeyNormally); + connect(this, &QPlainTextEdit::cursorPositionChanged, + d->m_cppEditorOutline, &CppEditorOutline::updateIndex); + + connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged, this, + [this](bool customSettings) { + updateWidgetHighlighting(d->m_preprocessorButton, customSettings); + }); + + // set up function declaration - definition link + d->m_updateFunctionDeclDefLinkTimer.setSingleShot(true); + d->m_updateFunctionDeclDefLinkTimer.setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL); + connect(&d->m_updateFunctionDeclDefLinkTimer, &QTimer::timeout, + this, &CppEditorWidget::updateFunctionDeclDefLinkNow); + connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CppEditorWidget::updateFunctionDeclDefLink); + connect(this, &QPlainTextEdit::textChanged, this, &CppEditorWidget::updateFunctionDeclDefLink); + + // set up the use highlighitng + connect(this, &CppEditorWidget::cursorPositionChanged, this, [this]() { + if (!d->m_localRenaming.isActive()) + d->m_useSelectionsUpdater.scheduleUpdate(); + + // Notify selection expander about the changed cursor. + d->m_cppSelectionChanger.onCursorPositionChanged(textCursor()); + }); + + // Toolbar: Outline/Overview combo box + insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget()); + + // Toolbar: Parse context + ParseContextModel &parseContextModel = cppEditorDocument()->parseContextModel(); + d->m_parseContextWidget = new ParseContextWidget(parseContextModel, this); + d->m_parseContextAction = insertExtraToolBarWidget(TextEditorWidget::Left, + d->m_parseContextWidget); + d->m_parseContextAction->setVisible(false); + connect(&parseContextModel, &ParseContextModel::updated, + this, [this](bool areMultipleAvailable) { + d->m_parseContextAction->setVisible(areMultipleAvailable); + }); + // clang-format on + // Toolbar: '#' Button + // TODO: Make "Additional Preprocessor Directives" also useful with Clang Code Model. + if (!d->m_modelManager->isClangCodeModelActive()) { + d->m_preprocessorButton = new QToolButton(this); + d->m_preprocessorButton->setText(QLatin1String("#")); + Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG); + connect(cmd, &Command::keySequenceChanged, + this, &CppEditorWidget::updatePreprocessorButtonTooltip); + updatePreprocessorButtonTooltip(); + connect(d->m_preprocessorButton, &QAbstractButton::clicked, + this, &CppEditorWidget::showPreProcessorWidget); + + insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton); + } + + // Toolbar: Actions to show minimized info bars + d->m_showInfoBarActions = MinimizableInfoBars::createShowInfoBarActions([this](QWidget *w) { + return this->insertExtraToolBarWidget(TextEditorWidget::Left, w); + }); + connect(&cppEditorDocument()->minimizableInfoBars(), &MinimizableInfoBars::showAction, + this, &CppEditorWidget::onShowInfoBarAction); +} + +void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *other) +{ + QTC_ASSERT(other, return); + CppEditorWidget *cppEditorWidget = qobject_cast(other); + QTC_ASSERT(cppEditorWidget, return); + + if (cppEditorWidget->isSemanticInfoValidExceptLocalUses()) + updateSemanticInfo(cppEditorWidget->semanticInfo()); + d->m_cppEditorOutline->update(); + const Id selectionKind = CodeWarningsSelection; + setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind)); + + if (isWidgetHighlighted(cppEditorWidget->d->m_preprocessorButton)) + updateWidgetHighlighting(d->m_preprocessorButton, true); + + d->m_parseContextWidget->syncToModel(); + d->m_parseContextAction->setVisible( + d->m_cppEditorDocument->parseContextModel().areMultipleAvailable()); +} + +CppEditorWidget::~CppEditorWidget() +{ + // non-inline destructor, see section "Forward Declared Pointers" of QScopedPointer. +} + +CppEditorDocument *CppEditorWidget::cppEditorDocument() const +{ + return d->m_cppEditorDocument; +} + +CppEditorOutline *CppEditorWidget::outline() const +{ + return d->m_cppEditorOutline; +} + +void CppEditorWidget::paste() +{ + if (d->m_localRenaming.handlePaste()) + return; + + TextEditorWidget::paste(); +} + +void CppEditorWidget::cut() +{ + if (d->m_localRenaming.handleCut()) + return; + + TextEditorWidget::cut(); +} + +void CppEditorWidget::selectAll() +{ + if (d->m_localRenaming.handleSelectAll()) + return; + + TextEditorWidget::selectAll(); +} + +void CppEditorWidget::onCppDocumentUpdated() +{ + d->m_cppEditorOutline->update(); +} + +void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, + const QList selections, + const TextEditor::RefactorMarkers &refactorMarkers) +{ + if (revision != documentRevision()) + return; + + setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); + setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers); +} + +void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, + const QList ifdefedOutBlocks) +{ + if (revision != documentRevision()) + return; + setIfdefedOutBlocks(ifdefedOutBlocks); +} + +void CppEditorWidget::onShowInfoBarAction(const Id &id, bool show) +{ + QAction *action = d->m_showInfoBarActions.value(id); + QTC_ASSERT(action, return); + action->setVisible(show); +} + +void CppEditorWidget::findUsages() +{ + if (!d->m_modelManager) + return; + + SemanticInfo info = d->m_lastSemanticInfo; + info.snapshot = CppModelManager::instance()->snapshot(); + info.snapshot.insert(info.doc); + + if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) { + d->m_modelManager->findMacroUsages(*macro); + } else { + CanonicalSymbol cs(info.doc, info.snapshot); + Symbol *canonicalSymbol = cs(textCursor()); + if (canonicalSymbol) + d->m_modelManager->findUsages(canonicalSymbol, cs.context()); + } +} + +void CppEditorWidget::renameUsages(const QString &replacement) +{ + if (!d->m_modelManager) + return; + + SemanticInfo info = d->m_lastSemanticInfo; + info.snapshot = CppModelManager::instance()->snapshot(); + info.snapshot.insert(info.doc); + + if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) { + d->m_modelManager->renameMacroUsages(*macro, replacement); + } else { + CanonicalSymbol cs(info.doc, info.snapshot); + if (Symbol *canonicalSymbol = cs(textCursor())) + if (canonicalSymbol->identifier() != 0) + d->m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement); + } +} + +bool CppEditorWidget::selectBlockUp() +{ + if (!behaviorSettings().m_smartSelectionChanging) + return TextEditorWidget::selectBlockUp(); + + QTextCursor cursor = textCursor(); + d->m_cppSelectionChanger.startChangeSelection(); + const bool changed = d->m_cppSelectionChanger + .changeSelection(CppSelectionChanger::ExpandSelection, + cursor, + d->m_lastSemanticInfo.doc); + if (changed) + setTextCursor(cursor); + d->m_cppSelectionChanger.stopChangeSelection(); + + return changed; +} + +bool CppEditorWidget::selectBlockDown() +{ + if (!behaviorSettings().m_smartSelectionChanging) + return TextEditorWidget::selectBlockDown(); + + QTextCursor cursor = textCursor(); + d->m_cppSelectionChanger.startChangeSelection(); + const bool changed = d->m_cppSelectionChanger + .changeSelection(CppSelectionChanger::ShrinkSelection, + cursor, + d->m_lastSemanticInfo.doc); + if (changed) + setTextCursor(cursor); + d->m_cppSelectionChanger.stopChangeSelection(); + + return changed; +} + +void CppEditorWidget::updateWidgetHighlighting(QWidget *widget, bool highlight) +{ + if (!widget) + return; + + widget->setProperty("highlightWidget", highlight); + widget->update(); +} + +bool CppEditorWidget::isWidgetHighlighted(QWidget *widget) +{ + return widget ? widget->property("highlightWidget").toBool() : false; +} + +void CppEditorWidget::renameSymbolUnderCursor() +{ + if (refactoringEngine()) + renameSymbolUnderCursorClang(); + else + renameSymbolUnderCursorBuiltin(); +} + +void CppEditorWidget::renameSymbolUnderCursorBuiltin() +{ + updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(), + /*updateUseSelectionSynchronously=*/true); + + if (!d->m_localRenaming.start()) // Rename local symbol + renameUsages(); // Rename non-local symbol or macro +} + +namespace { + +QList fetchProjectParts(CppTools::CppModelManager *modelManager, + const Utils::FileName &filePath) +{ + QList projectParts = modelManager->projectPart(filePath); + + if (projectParts.isEmpty()) + projectParts = modelManager->projectPartFromDependencies(filePath); + if (projectParts.isEmpty()) + projectParts.append(modelManager->fallbackProjectPart()); + + return projectParts; +} + +ProjectPart *findProjectPartForCurrentProject(const QList &projectParts, + ProjectExplorer::Project *currentProject) +{ + auto found = std::find_if(projectParts.cbegin(), + projectParts.cend(), + [&](const CppTools::ProjectPart::Ptr &projectPart) { + return projectPart->project == currentProject; + }); + + if (found != projectParts.cend()) + return (*found).data(); + + return 0; +} + +} // namespace + +ProjectPart *CppEditorWidget::projectPart() const +{ + if (!d->m_modelManager) + return 0; + + auto projectParts = fetchProjectParts(d->m_modelManager, textDocument()->filePath()); + + return findProjectPartForCurrentProject(projectParts, + ProjectExplorer::ProjectTree::currentProject()); +} + +namespace { + +using ClangBackEnd::V2::SourceLocationContainer; +using TextEditor::Convenience::selectAt; + +QTextCharFormat occurrencesTextCharFormat() +{ + using TextEditor::TextEditorSettings; + + return TextEditorSettings::fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES); +} + +QList sourceLocationsToExtraSelections( + const std::vector &sourceLocations, + uint selectionLength, + CppEditorWidget *cppEditorWidget) +{ + const auto textCharFormat = occurrencesTextCharFormat(); + + QList selections; + selections.reserve(int(sourceLocations.size())); + + auto sourceLocationToExtraSelection = [&](const SourceLocationContainer &sourceLocation) { + QTextEdit::ExtraSelection selection; + + selection.cursor = selectAt(cppEditorWidget->textCursor(), + sourceLocation.line(), + sourceLocation.column(), + selectionLength); + selection.format = textCharFormat; + + return selection; + }; + + std::transform(sourceLocations.begin(), + sourceLocations.end(), + std::back_inserter(selections), + sourceLocationToExtraSelection); + + return selections; +}; + +} + +void CppEditorWidget::renameSymbolUnderCursorClang() +{ + using ClangBackEnd::SourceLocationsContainer; + + ProjectPart *theProjectPart = projectPart(); + if (refactoringEngine()->isUsable() && theProjectPart) { + d->m_useSelectionsUpdater.abortSchedule(); + + QPointer cppEditorWidget = this; + + auto renameSymbols = [=](const QString &symbolName, + const SourceLocationsContainer &sourceLocations, + int revision) { + if (cppEditorWidget) { + viewport()->setCursor(Qt::IBeamCursor); + + if (revision == document()->revision()) { + auto selections + = sourceLocationsToExtraSelections(sourceLocations.sourceLocationContainers(), + symbolName.size(), + cppEditorWidget); + setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, + selections); + d->m_localRenaming.updateSelectionsForVariableUnderCursor(selections); + if (!d->m_localRenaming.start()) + renameUsages(); + } + } + }; + + refactoringEngine()->startLocalRenaming(textCursor(), + textDocument()->filePath(), + document()->revision(), + theProjectPart, + std::move(renameSymbols)); + + viewport()->setCursor(Qt::BusyCursor); + } +} + +void CppEditorWidget::updatePreprocessorButtonTooltip() +{ + if (!d->m_preprocessorButton) + return; + + Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG); + QTC_ASSERT(cmd, return ); + d->m_preprocessorButton->setToolTip(cmd->action()->toolTip()); +} + +void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit) +{ + if (!d->m_modelManager) + return; + + if (!d->m_lastSemanticInfo.doc) + return; + + // Find function declaration or definition under cursor + Function *functionDefinitionSymbol = 0; + Symbol *functionDeclarationSymbol = 0; + + ASTPath astPathFinder(d->m_lastSemanticInfo.doc); + const QList astPath = astPathFinder(textCursor()); + + for (int i = 0, size = astPath.size(); i < size; ++i) { + AST *ast = astPath.at(i); + if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) { + if ((functionDefinitionSymbol = functionDefinitionAST->symbol)) + break; // Function definition found! + } else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) { + if (List *symbols = simpleDeclaration->symbols) { + if (Symbol *symbol = symbols->value) { + if (symbol->isDeclaration() && symbol->type()->isFunctionType()) { + functionDeclarationSymbol = symbol; + break; // Function declaration found! + } + } + } + } + } + + // Link to function definition/declaration + CppEditorWidget::Link symbolLink; + if (functionDeclarationSymbol) { + symbolLink = linkToSymbol( + d->m_modelManager->symbolFinder() + ->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot())); + } else if (functionDefinitionSymbol) { + const Snapshot snapshot = d->m_modelManager->snapshot(); + LookupContext context(d->m_lastSemanticInfo.doc, snapshot); + ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol); + const QList declarations + = context.lookup(functionDefinitionSymbol->name(), + functionDefinitionSymbol->enclosingScope()); + + QList best; + foreach (const LookupItem &r, declarations) { + if (Symbol *decl = r.declaration()) { + if (Function *funTy = decl->type()->asFunctionType()) { + if (funTy->match(functionDefinitionSymbol)) { + if (decl != functionDefinitionSymbol && binding == r.binding()) + best.prepend(decl); + else + best.append(decl); + } + } + } + } + + if (best.isEmpty()) + return; + symbolLink = linkToSymbol(best.first()); + } + + // Open Editor at link position + if (symbolLink.hasValidTarget()) + openLink(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit()); +} + +CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, + bool resolveTarget, + bool inNextSplit) +{ + if (!d->m_modelManager) + return Link(); + + return d->m_followSymbolUnderCursor->findLink(cursor, + resolveTarget, + d->m_modelManager->snapshot(), + d->m_lastSemanticInfo.doc, + d->m_modelManager->symbolFinder(), + inNextSplit); +} + +unsigned CppEditorWidget::documentRevision() const +{ + return document()->revision(); +} + +static bool isClangFixItAvailableMarker(const RefactorMarker &marker) +{ + return marker.data.toString() + == QLatin1String(CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID); +} + +RefactorMarkers CppEditorWidget::refactorMarkersWithoutClangMarkers() const +{ + RefactorMarkers clearedRefactorMarkers; + + foreach (const RefactorMarker &marker, refactorMarkers()) { + if (isClangFixItAvailableMarker(marker)) + continue; + + clearedRefactorMarkers.append(marker); + } + + return clearedRefactorMarkers; +} + +RefactoringEngineInterface *CppEditorWidget::refactoringEngine() const +{ + return CppTools::CppModelManager::refactoringEngine(); +} + +bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const +{ + return d->m_lastSemanticInfo.doc && d->m_lastSemanticInfo.revision == documentRevision() + && !d->m_lastSemanticInfo.snapshot.isEmpty(); +} + +bool CppEditorWidget::isSemanticInfoValid() const +{ + return isSemanticInfoValidExceptLocalUses() && d->m_lastSemanticInfo.localUsesUpdated; +} + +SemanticInfo CppEditorWidget::semanticInfo() const +{ + return d->m_lastSemanticInfo; +} + +bool CppEditorWidget::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::ShortcutOverride: + // handle escape manually if a rename is active + if (static_cast(e)->key() == Qt::Key_Escape && d->m_localRenaming.isActive()) { + e->accept(); + return true; + } + break; + default: + break; + } + + return TextEditorWidget::event(e); +} + +void CppEditorWidget::processKeyNormally(QKeyEvent *e) +{ + TextEditorWidget::keyPressEvent(e); +} + +void CppEditorWidget::contextMenuEvent(QContextMenuEvent *e) +{ + // ### enable + // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); + + QPointer menu(new QMenu(this)); + + ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT); + QMenu *contextMenu = mcontext->menu(); + + QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu); + quickFixMenu->addAction(ActionManager::command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action()); + + if (isSemanticInfoValidExceptLocalUses()) { + d->m_useSelectionsUpdater.update(CppUseSelectionsUpdater::Synchronous); + AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked); + if (interface) { + QScopedPointer processor( + CppEditorPlugin::instance()->quickFixProvider()->createProcessor()); + QScopedPointer proposal(processor->perform(interface)); + if (!proposal.isNull()) { + auto model = static_cast(proposal->model()); + for (int index = 0; index < model->size(); ++index) { + auto item = static_cast(model->proposalItem(index)); + QuickFixOperation::Ptr op = item->data().value(); + QAction *action = quickFixMenu->addAction(op->description()); + connect(action, &QAction::triggered, this, [op] { op->perform(); }); + } + delete model; + } + } + } + + foreach (QAction *action, contextMenu->actions()) { + menu->addAction(action); + if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)) + menu->addMenu(quickFixMenu); + } + + appendStandardContextMenuActions(menu); + + menu->exec(e->globalPos()); + if (!menu) + return; + delete menu; +} + +void CppEditorWidget::keyPressEvent(QKeyEvent *e) +{ + if (d->m_localRenaming.handleKeyPressEvent(e)) + return; + + if (handleStringSplitting(e)) + return; + + if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { + if (trySplitComment(this, semanticInfo().snapshot)) { + e->accept(); + return; + } + } + + TextEditorWidget::keyPressEvent(e); +} + +bool CppEditorWidget::handleStringSplitting(QKeyEvent *e) const +{ + if (!TextEditorSettings::completionSettings().m_autoSplitStrings) + return false; + + if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { + QTextCursor cursor = textCursor(); + + const Kind stringKind = CPlusPlus::MatchingText::stringKindAtCursor(cursor); + if (stringKind >= T_FIRST_STRING_LITERAL && stringKind < T_FIRST_RAW_STRING_LITERAL) { + cursor.beginEditBlock(); + if (cursor.positionInBlock() > 0 + && cursor.block().text().at(cursor.positionInBlock() - 1) == QLatin1Char('\\')) { + // Already escaped: simply go back to line, but do not indent. + cursor.insertText(QLatin1String("\n")); + } else if (e->modifiers() & Qt::ShiftModifier) { + // With 'shift' modifier, escape the end of line character + // and start at beginning of next line. + cursor.insertText(QLatin1String("\\\n")); + } else { + // End the current string, and start a new one on the line, properly indented. + cursor.insertText(QLatin1String("\"\n\"")); + textDocument()->autoIndent(cursor); + } + cursor.endEditBlock(); + e->accept(); + return true; + } + } + + return false; +} + +void CppEditorWidget::slotCodeStyleSettingsChanged(const QVariant &) +{ + QtStyleCodeFormatter formatter; + formatter.invalidateCache(document()); +} + +void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo, + bool updateUseSelectionSynchronously) +{ + if (semanticInfo.revision != documentRevision()) + return; + + d->m_lastSemanticInfo = semanticInfo; + + if (!d->m_localRenaming.isActive()) { + const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously + ? CppUseSelectionsUpdater::Synchronous + : CppUseSelectionsUpdater::Asynchronous; + d->m_useSelectionsUpdater.update(type); + } + + // schedule a check for a decl/def link + updateFunctionDeclDefLink(); +} + +AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const +{ + if (kind == Completion) { + if (CppCompletionAssistProvider *cap = qobject_cast( + cppEditorDocument()->completionAssistProvider())) { + LanguageFeatures features = LanguageFeatures::defaultFeatures(); + if (Document::Ptr doc = d->m_lastSemanticInfo.doc) + features = doc->languageFeatures(); + features.objCEnabled |= cppEditorDocument()->isObjCEnabled(); + return cap->createAssistInterface(textDocument()->filePath().toString(), + this, + features, + position(), + reason); + } + } else if (kind == QuickFix) { + if (isSemanticInfoValid()) + return new CppQuickFixInterface(const_cast(this), reason); + } else { + return TextEditorWidget::createAssistInterface(kind, reason); + } + return 0; +} + +QSharedPointer CppEditorWidget::declDefLink() const +{ + return d->m_declDefLink; +} + +void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker) +{ + if (marker.data.canConvert()) { + applyDeclDefLinkChanges(true); + } else if (isClangFixItAvailableMarker(marker)) { + int line, column; + if (Convenience::convertPosition(document(), marker.cursor.position(), &line, &column)) { + setTextCursor(marker.cursor); + invokeAssist(TextEditor::QuickFix); + } + } +} + +void CppEditorWidget::updateFunctionDeclDefLink() +{ + const int pos = textCursor().selectionStart(); + + // if there's already a link, abort it if the cursor is outside or the name changed + // (adding a prefix is an exception since the user might type a return type) + if (d->m_declDefLink + && (pos < d->m_declDefLink->linkSelection.selectionStart() + || pos > d->m_declDefLink->linkSelection.selectionEnd() + || !d->m_declDefLink->nameSelection.selectedText().trimmed().endsWith( + d->m_declDefLink->nameInitial))) { + abortDeclDefLink(); + return; + } + + // don't start a new scan if there's one active and the cursor is already in the scanned area + const QTextCursor scannedSelection = d->m_declDefLinkFinder->scannedSelection(); + if (!scannedSelection.isNull() && scannedSelection.selectionStart() <= pos + && scannedSelection.selectionEnd() >= pos) { + return; + } + + d->m_updateFunctionDeclDefLinkTimer.start(); +} + +void CppEditorWidget::updateFunctionDeclDefLinkNow() +{ + IEditor *editor = EditorManager::currentEditor(); + if (!editor || editor->widget() != this) + return; + + const Snapshot semanticSnapshot = d->m_lastSemanticInfo.snapshot; + const Document::Ptr semanticDoc = d->m_lastSemanticInfo.doc; + + if (d->m_declDefLink) { + // update the change marker + const Utils::ChangeSet changes = d->m_declDefLink->changes(semanticSnapshot); + if (changes.isEmpty()) + d->m_declDefLink->hideMarker(this); + else + d->m_declDefLink->showMarker(this); + return; + } + + if (!isSemanticInfoValidExceptLocalUses()) + return; + + Snapshot snapshot = CppModelManager::instance()->snapshot(); + snapshot.insert(semanticDoc); + + d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot); +} + +void CppEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer link) +{ + abortDeclDefLink(); + d->m_declDefLink = link; + IDocument *targetDocument = DocumentModel::documentForFilePath( + d->m_declDefLink->targetFile->fileName()); + if (textDocument() != targetDocument) { + if (auto textDocument = qobject_cast(targetDocument)) + connect(textDocument, + &IDocument::contentsChanged, + this, + &CppEditorWidget::abortDeclDefLink); + } +} + +void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch) +{ + if (!d->m_declDefLink) + return; + d->m_declDefLink->apply(this, jumpToMatch); + abortDeclDefLink(); + updateFunctionDeclDefLink(); +} + +FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate() +{ + return d->m_followSymbolUnderCursor.data(); +} + +void CppEditorWidget::encourageApply() +{ + if (d->m_localRenaming.encourageApply()) + return; + + TextEditorWidget::encourageApply(); +} + +void CppEditorWidget::abortDeclDefLink() +{ + if (!d->m_declDefLink) + return; + + IDocument *targetDocument = DocumentModel::documentForFilePath( + d->m_declDefLink->targetFile->fileName()); + if (textDocument() != targetDocument) { + if (auto textDocument = qobject_cast(targetDocument)) + disconnect(textDocument, + &IDocument::contentsChanged, + this, + &CppEditorWidget::abortDeclDefLink); + } + + d->m_declDefLink->hideMarker(this); + d->m_declDefLink.clear(); +} + +void CppEditorWidget::showPreProcessorWidget() +{ + const QString filePath = textDocument()->filePath().toString(); + + CppPreProcessorDialog dialog(filePath, this); + if (dialog.exec() == QDialog::Accepted) { + const QByteArray extraDirectives = dialog.extraPreprocessorDirectives().toUtf8(); + cppEditorDocument()->setExtraPreprocessorDirectives(extraDirectives); + cppEditorDocument()->scheduleProcessDocument(); + } +} + +} // namespace Internal +} // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h new file mode 100644 index 00000000000..8e5be90dde9 --- /dev/null +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +namespace CppTools { +class CppEditorOutline; +class RefactoringEngineInterface; +class SemanticInfo; +class ProjectPart; +} + +namespace CppEditor { +namespace Internal { + +class CppEditorDocument; + +class CppEditorWidgetPrivate; +class FollowSymbolUnderCursor; +class FunctionDeclDefLink; + +class CppEditorWidget : public TextEditor::TextEditorWidget +{ + Q_OBJECT + +public: + CppEditorWidget(); + ~CppEditorWidget() override; + + CppEditorDocument *cppEditorDocument() const; + CppTools::CppEditorOutline *outline() const; + + CppTools::SemanticInfo semanticInfo() const; + bool isSemanticInfoValidExceptLocalUses() const; + bool isSemanticInfoValid() const; + + QSharedPointer declDefLink() const; + void applyDeclDefLinkChanges(bool jumpToMatch); + + TextEditor::AssistInterface *createAssistInterface( + TextEditor::AssistKind kind, + TextEditor::AssistReason reason) const override; + + FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests + + void encourageApply() override; + + void paste() override; + void cut() override; + void selectAll() override; + + void switchDeclarationDefinition(bool inNextSplit); + void showPreProcessorWidget(); + + void findUsages(); + void renameSymbolUnderCursor(); + void renameUsages(const QString &replacement = QString()); + + bool selectBlockUp() override; + bool selectBlockDown() override; + + static void updateWidgetHighlighting(QWidget *widget, bool highlight); + static bool isWidgetHighlighted(QWidget *widget); + +protected: + bool event(QEvent *e) override; + void contextMenuEvent(QContextMenuEvent *) override; + void keyPressEvent(QKeyEvent *e) override; + bool handleStringSplitting(QKeyEvent *e) const; + + Link findLinkAt(const QTextCursor &, bool resolveTarget = true, + bool inNextSplit = false) override; + + void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker) override; + + void slotCodeStyleSettingsChanged(const QVariant &) override; + +private: + void updateFunctionDeclDefLink(); + void updateFunctionDeclDefLinkNow(); + void abortDeclDefLink(); + void onFunctionDeclDefLinkFound(QSharedPointer link); + + void onCppDocumentUpdated(); + + void onCodeWarningsUpdated(unsigned revision, + const QList selections, + const TextEditor::RefactorMarkers &refactorMarkers); + void onIfdefedOutBlocksUpdated(unsigned revision, + const QList ifdefedOutBlocks); + + void onShowInfoBarAction(const Core::Id &id, bool show); + + void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo, + bool updateUseSelectionSynchronously = false); + void updatePreprocessorButtonTooltip(); + + void processKeyNormally(QKeyEvent *e); + + void finalizeInitialization() override; + void finalizeInitializationAfterDuplication(TextEditorWidget *other) override; + + unsigned documentRevision() const; + + TextEditor::RefactorMarkers refactorMarkersWithoutClangMarkers() const; + + CppTools::RefactoringEngineInterface *refactoringEngine() const; + + void renameSymbolUnderCursorClang(); + void renameSymbolUnderCursorBuiltin(); + + CppTools::ProjectPart *projectPart() const; + +private: + QScopedPointer d; +}; + +} // namespace Internal +} // namespace CppEditor diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 4999a791a03..a33304cfcbd 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -24,7 +24,7 @@ ****************************************************************************/ #include "cppfollowsymbolundercursor.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include "cppvirtualfunctionassistprovider.h" diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index 9eaf09ab11c..86bdf2a0cf7 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -25,7 +25,7 @@ #include "cppfunctiondecldeflink.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppquickfixassistant.h" #include diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index 6988d2a74db..9baa067af12 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -26,6 +26,7 @@ #include "cppincludehierarchy.h" #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorconstants.h" #include "cppeditorplugin.h" #include "cppelementevaluator.h" diff --git a/src/plugins/cppeditor/cppincludehierarchy_test.cpp b/src/plugins/cppeditor/cppincludehierarchy_test.cpp index 95995e732a7..33a58400cc7 100644 --- a/src/plugins/cppeditor/cppincludehierarchy_test.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy_test.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ +#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" #include "cppincludehierarchy.h" diff --git a/src/plugins/cppeditor/cppoutline.h b/src/plugins/cppeditor/cppoutline.h index 9e28506027a..2176fde2e20 100644 --- a/src/plugins/cppeditor/cppoutline.h +++ b/src/plugins/cppeditor/cppoutline.h @@ -26,6 +26,7 @@ #pragma once #include "cppeditor.h" +#include "cppeditorwidget.h" #include diff --git a/src/plugins/cppeditor/cppparsecontext.cpp b/src/plugins/cppeditor/cppparsecontext.cpp index 1519f0e71af..b3a3f1b1a3d 100644 --- a/src/plugins/cppeditor/cppparsecontext.cpp +++ b/src/plugins/cppeditor/cppparsecontext.cpp @@ -25,7 +25,7 @@ #include "cppparsecontext.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include #include diff --git a/src/plugins/cppeditor/cpppreprocessordialog.cpp b/src/plugins/cppeditor/cpppreprocessordialog.cpp index 167e373a2da..f66ee92a89c 100644 --- a/src/plugins/cppeditor/cpppreprocessordialog.cpp +++ b/src/plugins/cppeditor/cpppreprocessordialog.cpp @@ -27,6 +27,7 @@ #include "ui_cpppreprocessordialog.h" #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorconstants.h" #include diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 7882fef70ee..bfc35a67c4d 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ +#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" #include "cppquickfixassistant.h" diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index bde1f759bc6..8ded187d889 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -26,7 +26,8 @@ #include "cppquickfixassistant.h" #include "cppeditorconstants.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" +#include "cppquickfixes.h" #include #include diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index cc74b811051..fc4ed5a2ac6 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -25,7 +25,7 @@ #include "cppquickfixes.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include "cppfunctiondecldeflink.h" #include "cppquickfixassistant.h" diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp index 47ca64f9a26..cac2269de6a 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.cpp +++ b/src/plugins/cppeditor/cpptypehierarchy.cpp @@ -27,6 +27,7 @@ #include "cppeditorconstants.h" #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppelementevaluator.h" #include "cppeditorplugin.h" diff --git a/src/plugins/cppeditor/cppuseselections_test.cpp b/src/plugins/cppeditor/cppuseselections_test.cpp index 943f1d4ec69..8de8048a892 100644 --- a/src/plugins/cppeditor/cppuseselections_test.cpp +++ b/src/plugins/cppeditor/cppuseselections_test.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index ac071a02b01..44012b1c95f 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -25,7 +25,7 @@ #include "cppuseselectionsupdater.h" -#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditordocument.h" #include diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp index 9bf053bb941..463172d45bd 100644 --- a/src/plugins/cppeditor/fileandtokenactions_test.cpp +++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" #include "cppquickfix.h" diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 4fbd7c554d7..87e0b59bdde 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ +#include "cppeditor.h" +#include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" #include "cppelementevaluator.h"