diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 5308d796d15c013952738980f59eb161c5cf9d76..7d576c6599f9d59d382ad7d0df777b5352bcb131 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 92950136e47246890d55b4c89f3bb5afe8b8c8bc..df1cd80253c1f5939f85e81599cd09ce135e5107 100644 --- a/src/plugins/cppeditor/cppquickfix.h +++ b/src/plugins/cppeditor/cppquickfix.h @@ -36,8 +36,11 @@ #include "cppeditor_global.h" #include <texteditor/quickfix.h> +namespace CPlusPlus { +class CppModelManagerInterface; +} + namespace CppTools { - class CppModelManagerInterface; class CppRefactoringFile; class CppRefactoringChanges; typedef QSharedPointer<CppRefactoringFile> CppRefactoringFilePtr; diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index f4ea4183c7464abc61473027e68a41dee2cbf099..baf506825989b4115a5345a0415fc773a6e115bd 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -56,12 +56,16 @@ #include <cplusplus/DependencyTable.h> #include <cplusplus/Overview.h> #include <cplusplus/TypeOfExpression.h> +#include <cplusplus/ModelManagerInterface.h> #include <cplusplus/CppRewriter.h> #include <cpptools/cpptoolsconstants.h> #include <cpptools/cpprefactoringchanges.h> #include <cpptools/insertionpointlocator.h> #include <cpptools/cpptoolsreuse.h> +#include <cpptools/cppclassesfilter.h> +#include <cpptools/searchsymbols.h> #include <extensionsystem/iplugin.h> +#include <extensionsystem/pluginmanager.h> #include <QtCore/QFileInfo> #include <QtGui/QApplication> @@ -1701,6 +1705,157 @@ private: }; }; +/** + * Adds an include for an undefined identifier. + */ +class IncludeAdder : public CppQuickFixFactory +{ +public: + virtual QList<CppQuickFixOperation::Ptr> match(const QSharedPointer<const CppQuickFixAssistInterface> &interface) + { + CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::instance()->getObject<CppClassesFilter>(); + if (!classesFilter) + return noResult(); + + const QList<AST *> &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<LookupItem> existingResults = interface->context().lookup(enclosingName->name, scope); + if (!existingResults.isEmpty()) + return noResult(); + + const QString &className = Overview()(innermostName->name); + if (className.isEmpty()) + return noResult(); + + QList<CppQuickFixOperation::Ptr> results; + + // find the include paths + QStringList includePaths; + CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); + QList<CppModelManagerInterface::ProjectInfo> 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<Locator::FilterEntry> dummyInterface; + QList<Locator::FilterEntry> matches = classesFilter->matchesFor(dummyInterface, className); + bool classExists = false; + foreach (const Locator::FilterEntry &entry, matches) { + const ModelItemInfo info = entry.internalData.value<ModelItemInfo>(); + 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 <QSomething> 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<const CppQuickFixAssistInterface> &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<Document::Include> 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 4c1e9e73d43aa59f4c2596803329a934ab796760..591e9829db2c186d45950a990efa36c372ec68fe 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 4f88f79a871509e85245912ce024afb6de8d5954..bb4efb5b6c00c0fcfa327b7f22e216a2858c22ca 100644 --- a/src/plugins/cpptools/cppclassesfilter.h +++ b/src/plugins/cpptools/cppclassesfilter.h @@ -33,17 +33,17 @@ #ifndef CPPCLASSESFILTER_H #define CPPCLASSESFILTER_H -#include <cpplocatorfilter.h> +#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 41bf6a38047a92e0f2058d73a48d5d10c26db5d7..0530da5ac87938fe32a7c006166523440b275335 100644 --- a/src/plugins/cpptools/cppcurrentdocumentfilter.cpp +++ b/src/plugins/cpptools/cppcurrentdocumentfilter.cpp @@ -111,7 +111,7 @@ QList<Locator::FilterEntry> CppCurrentDocumentFilter::matchesFor(QFutureInterfac void CppCurrentDocumentFilter::accept(Locator::FilterEntry selection) const { - ModelItemInfo info = qvariant_cast<CppTools::Internal::ModelItemInfo>(selection.internalData); + ModelItemInfo info = qvariant_cast<CppTools::ModelItemInfo>(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 24923bcb313e24d2550f47a3eb3f7a4a2cb43968..2f75a6cf66347311248b7e91e009ce95485e1a84 100644 --- a/src/plugins/cpptools/cpplocatorfilter.cpp +++ b/src/plugins/cpptools/cpplocatorfilter.cpp @@ -129,7 +129,7 @@ QList<Locator::FilterEntry> CppLocatorFilter::matchesFor(QFutureInterface<Locato void CppLocatorFilter::accept(Locator::FilterEntry selection) const { - ModelItemInfo info = qvariant_cast<CppTools::Internal::ModelItemInfo>(selection.internalData); + ModelItemInfo info = qvariant_cast<CppTools::ModelItemInfo>(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 5d8b3df991e1902c057f12689176b0b70e38c5b9..33f538fb63984dfac4a0f468e488aef412cee779 100644 --- a/src/plugins/cpptools/searchsymbols.cpp +++ b/src/plugins/cpptools/searchsymbols.cpp @@ -39,7 +39,7 @@ #include <QDebug> 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 6efdcce89d467bcce7dadbc190f8ca5c83e16e8b..5a2c7ba29ce97e928773edc6920556ef1212e08e 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 <cplusplus/CppDocument.h> #include <cplusplus/Icons.h> #include <cplusplus/Overview.h> @@ -48,9 +50,8 @@ #include <functional> 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