From dc72cff71aa9ba035c195273cb9b3b923948ff9e Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Fri, 25 Nov 2011 12:05:58 +0100 Subject: [PATCH] C++: Add basic 'insert #include' quick fix. Change-Id: I3a2fef56d6d1871ea4bbb139f9bdd2bc44dd0123 Reviewed-by: Leandro Melo --- src/plugins/cppeditor/cppeditor.h | 2 +- src/plugins/cppeditor/cppquickfix.h | 5 +- src/plugins/cppeditor/cppquickfixes.cpp | 156 ++++++++++++++++++ src/plugins/cpptools/cppclassesfilter.cpp | 1 + src/plugins/cpptools/cppclassesfilter.h | 9 +- .../cpptools/cppcurrentdocumentfilter.cpp | 2 +- src/plugins/cpptools/cpplocatorfilter.cpp | 2 +- src/plugins/cpptools/searchsymbols.cpp | 2 +- src/plugins/cpptools/searchsymbols.h | 10 +- 9 files changed, 174 insertions(+), 15 deletions(-) diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 5308d796d1..7d576c6599 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -58,10 +58,10 @@ QT_END_NAMESPACE namespace CPlusPlus { class OverviewModel; class Symbol; +class CppModelManagerInterface; } namespace CppTools { -class CppModelManagerInterface; class CppCodeStyleSettings; class CppRefactoringFile; } diff --git a/src/plugins/cppeditor/cppquickfix.h b/src/plugins/cppeditor/cppquickfix.h index 92950136e4..df1cd80253 100644 --- a/src/plugins/cppeditor/cppquickfix.h +++ b/src/plugins/cppeditor/cppquickfix.h @@ -36,8 +36,11 @@ #include "cppeditor_global.h" #include +namespace CPlusPlus { +class CppModelManagerInterface; +} + namespace CppTools { - class CppModelManagerInterface; class CppRefactoringFile; class CppRefactoringChanges; typedef QSharedPointer CppRefactoringFilePtr; diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index f4ea4183c7..baf5068259 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -56,12 +56,16 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include +#include #include #include @@ -1701,6 +1705,157 @@ private: }; }; +/** + * Adds an include for an undefined identifier. + */ +class IncludeAdder : public CppQuickFixFactory +{ +public: + virtual QList match(const QSharedPointer &interface) + { + CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::instance()->getObject(); + if (!classesFilter) + return noResult(); + + const QList &path = interface->path(); + + if (path.isEmpty()) + return noResult(); + + // find the largest enclosing Name + const NameAST *enclosingName = 0; + const SimpleNameAST *innermostName = 0; + for (int i = path.size() - 1; i >= 0; --i) { + if (NameAST *nameAst = path.at(i)->asName()) { + enclosingName = nameAst; + if (!innermostName) { + innermostName = nameAst->asSimpleName(); + if (!innermostName) + return noResult(); + } + } else { + break; + } + } + if (!enclosingName || !enclosingName->name) + return noResult(); + + // find the enclosing scope + unsigned line, column; + const Document::Ptr &doc = interface->semanticInfo().doc; + doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column); + Scope *scope = doc->scopeAt(line, column); + if (!scope) + return noResult(); + + // check if the name resolves to something + QList existingResults = interface->context().lookup(enclosingName->name, scope); + if (!existingResults.isEmpty()) + return noResult(); + + const QString &className = Overview()(innermostName->name); + if (className.isEmpty()) + return noResult(); + + QList results; + + // find the include paths + QStringList includePaths; + CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); + QList projectInfos = modelManager->projectInfos(); + bool inProject = false; + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { + if (info.sourceFiles.contains(doc->fileName())) { + inProject = true; + includePaths += info.includePaths; + } + } + if (!inProject) { + // better use all include paths than none + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) + includePaths += info.includePaths; + } + + // find a include file through the locator + QFutureInterface dummyInterface; + QList matches = classesFilter->matchesFor(dummyInterface, className); + bool classExists = false; + foreach (const Locator::FilterEntry &entry, matches) { + const ModelItemInfo info = entry.internalData.value(); + if (info.symbolName != className) + continue; + classExists = true; + const QString &fileName = info.fileName; + const QFileInfo fileInfo(fileName); + + // find the shortest way to include fileName given the includePaths + QString shortestInclude; + + if (fileInfo.path() == QFileInfo(doc->fileName()).path()) { + shortestInclude = QString("\"%1\"").arg(fileInfo.fileName()); + } else { + foreach (const QString &includePath, includePaths) { + if (!fileName.startsWith(includePath)) + continue; + QString relativePath = fileName.mid(includePath.size()); + if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/')) + relativePath = relativePath.mid(1); + if (shortestInclude.isEmpty() || relativePath.size() + 2 < shortestInclude.size()) + shortestInclude = QString("<%1>").arg(relativePath); + } + } + + if (!shortestInclude.isEmpty()) + results += CppQuickFixOperation::Ptr(new Operation(interface, 0, shortestInclude)); + } + + // for QSomething, propose a include -- if such a class was in the locator + if (classExists + && className.size() > 2 + && className.at(0) == QLatin1Char('Q') + && className.at(1).isUpper()) { + results += CppQuickFixOperation::Ptr(new Operation(interface, 1, QString("<%1>").arg(className))); + } + + return results; + } + +private: + class Operation: public CppQuickFixOperation + { + public: + Operation(const QSharedPointer &interface, int priority, const QString &include) + : CppQuickFixOperation(interface, priority) + , m_include(include) + { + setDescription(QApplication::translate("CppTools::QuickFix", + "Add #include %1").arg(m_include)); + } + + virtual void performChanges(const CppRefactoringFilePtr &file, + const CppRefactoringChanges &) + { + // find location of last include in file + QList includes = file->cppDocument()->includes(); + unsigned lastIncludeLine = 0; + foreach (const Document::Include &include, includes) { + if (include.line() > lastIncludeLine) + lastIncludeLine = include.line(); + } + + // add include + const int insertPos = file->position(lastIncludeLine + 1, 1) - 1; + ChangeSet changes; + changes.insert(insertPos, QString("\n#include %1").arg(m_include)); + file->setChangeSet(changes); + file->apply(); + } + + private: + QString m_include; + }; +}; + } // end of anonymous namespace void registerQuickFixes(ExtensionSystem::IPlugin *plugIn) @@ -1725,4 +1880,5 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn) plugIn->addAutoReleasedObject(new DeclFromDef); plugIn->addAutoReleasedObject(new DefFromDecl); plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges); + plugIn->addAutoReleasedObject(new IncludeAdder); } diff --git a/src/plugins/cpptools/cppclassesfilter.cpp b/src/plugins/cpptools/cppclassesfilter.cpp index 4c1e9e73d4..591e9829db 100644 --- a/src/plugins/cpptools/cppclassesfilter.cpp +++ b/src/plugins/cpptools/cppclassesfilter.cpp @@ -32,6 +32,7 @@ #include "cppclassesfilter.h" +using namespace CppTools; using namespace CppTools::Internal; CppClassesFilter::CppClassesFilter(CppModelManager *manager) diff --git a/src/plugins/cpptools/cppclassesfilter.h b/src/plugins/cpptools/cppclassesfilter.h index 4f88f79a87..bb4efb5b6c 100644 --- a/src/plugins/cpptools/cppclassesfilter.h +++ b/src/plugins/cpptools/cppclassesfilter.h @@ -33,17 +33,17 @@ #ifndef CPPCLASSESFILTER_H #define CPPCLASSESFILTER_H -#include +#include "cpptools_global.h" +#include "cpplocatorfilter.h" namespace CppTools { -namespace Internal { -class CppClassesFilter : public CppLocatorFilter +class CPPTOOLS_EXPORT CppClassesFilter : public Internal::CppLocatorFilter { Q_OBJECT public: - CppClassesFilter(CppModelManager *manager); + CppClassesFilter(Internal::CppModelManager *manager); ~CppClassesFilter(); QString displayName() const { return tr("Classes"); } @@ -51,7 +51,6 @@ public: Priority priority() const { return Medium; } }; -} // namespace Internal } // namespace CppTools #endif // CPPCLASSESFILTER_H diff --git a/src/plugins/cpptools/cppcurrentdocumentfilter.cpp b/src/plugins/cpptools/cppcurrentdocumentfilter.cpp index 41bf6a3804..0530da5ac8 100644 --- a/src/plugins/cpptools/cppcurrentdocumentfilter.cpp +++ b/src/plugins/cpptools/cppcurrentdocumentfilter.cpp @@ -111,7 +111,7 @@ QList CppCurrentDocumentFilter::matchesFor(QFutureInterfac void CppCurrentDocumentFilter::accept(Locator::FilterEntry selection) const { - ModelItemInfo info = qvariant_cast(selection.internalData); + ModelItemInfo info = qvariant_cast(selection.internalData); TextEditor::BaseTextEditorWidget::openEditorAt(info.fileName, info.line, info.column, Core::Id(), Core::EditorManager::ModeSwitch); } diff --git a/src/plugins/cpptools/cpplocatorfilter.cpp b/src/plugins/cpptools/cpplocatorfilter.cpp index 24923bcb31..2f75a6cf66 100644 --- a/src/plugins/cpptools/cpplocatorfilter.cpp +++ b/src/plugins/cpptools/cpplocatorfilter.cpp @@ -129,7 +129,7 @@ QList CppLocatorFilter::matchesFor(QFutureInterface(selection.internalData); + ModelItemInfo info = qvariant_cast(selection.internalData); TextEditor::BaseTextEditorWidget::openEditorAt(info.fileName, info.line, info.column, Core::Id(), Core::EditorManager::ModeSwitch); } diff --git a/src/plugins/cpptools/searchsymbols.cpp b/src/plugins/cpptools/searchsymbols.cpp index 5d8b3df991..33f538fb63 100644 --- a/src/plugins/cpptools/searchsymbols.cpp +++ b/src/plugins/cpptools/searchsymbols.cpp @@ -39,7 +39,7 @@ #include using namespace CPlusPlus; -using namespace CppTools::Internal; +using namespace CppTools; SearchSymbols::SymbolTypes SearchSymbols::AllTypes = SearchSymbols::Classes diff --git a/src/plugins/cpptools/searchsymbols.h b/src/plugins/cpptools/searchsymbols.h index 6efdcce89d..5a2c7ba29c 100644 --- a/src/plugins/cpptools/searchsymbols.h +++ b/src/plugins/cpptools/searchsymbols.h @@ -33,6 +33,8 @@ #ifndef SEARCHSYMBOLS_H #define SEARCHSYMBOLS_H +#include "cpptools_global.h" + #include #include #include @@ -48,9 +50,8 @@ #include namespace CppTools { -namespace Internal { -struct ModelItemInfo +struct CPPTOOLS_EXPORT ModelItemInfo { enum ItemType { Enum, Class, Method, Declaration }; @@ -180,10 +181,9 @@ private: bool separateScope; }; -} // namespace Internal } // namespace CppTools -Q_DECLARE_OPERATORS_FOR_FLAGS(CppTools::Internal::SearchSymbols::SymbolTypes) -Q_DECLARE_METATYPE(CppTools::Internal::ModelItemInfo) +Q_DECLARE_OPERATORS_FOR_FLAGS(CppTools::SearchSymbols::SymbolTypes) +Q_DECLARE_METATYPE(CppTools::ModelItemInfo) #endif // SEARCHSYMBOLS_H -- GitLab