Files
qt-creator/src/plugins/cpptools/cpptoolsplugin.cpp

542 lines
19 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 15:08:31 +01:00
#include "cpptoolsplugin.h"
#include "completionsettingspage.h"
#include "cppfilesettingspage.h"
#include "cppclassesfilter.h"
2008-12-02 12:01:29 +01:00
#include "cppcodecompletion.h"
#include "cppfunctionsfilter.h"
2008-12-02 12:01:29 +01:00
#include "cppmodelmanager.h"
#include "cpptoolsconstants.h"
#include "cppquickopenfilter.h"
2009-06-02 14:56:03 +02:00
#include <extensionsystem/pluginmanager.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/actionmanager/actionmanager.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/editormanager/editormanager.h>
2009-06-02 14:56:03 +02:00
#include <coreplugin/progressmanager/progressmanager.h>
2008-12-02 12:01:29 +01:00
#include <cppeditor/cppeditorconstants.h>
2009-06-02 14:56:03 +02:00
#include <QtCore/QtConcurrentRun>
#include <QtCore/QFutureSynchronizer>
#include <qtconcurrent/runextensions.h>
#include <find/ifindfilter.h>
#include <find/searchresultwindow.h>
#include <utils/filesearch.h>
#include <Control.h>
#include <AST.h>
#include <ASTVisitor.h>
#include <TranslationUnit.h>
#include <cplusplus/PreprocessorEnvironment.h>
#include <cplusplus/pp.h>
#include <QtCore/QtPlugin>
2008-12-02 12:01:29 +01:00
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
2008-12-02 12:01:29 +01:00
#include <QtGui/QMenu>
#include <QtGui/QAction>
2009-06-02 14:56:03 +02:00
#include <sstream>
2008-12-02 12:01:29 +01:00
using namespace CppTools::Internal;
2009-06-02 14:56:03 +02:00
using namespace CPlusPlus;
2008-12-02 12:01:29 +01:00
enum { debug = 0 };
CppToolsPlugin *CppToolsPlugin::m_instance = 0;
2009-06-02 14:56:03 +02:00
namespace {
class SimpleClient: public Client
{
Environment _env;
QPointer<CppModelManager> _modelManager;
Snapshot _snapshot;
Preprocessor _preproc;
QSet<QString> _merged;
public:
SimpleClient(QPointer<CppModelManager> modelManager)
: _modelManager(modelManager),
_snapshot(_modelManager->snapshot()),
_preproc(this, &_env)
{ }
QByteArray run(QString fileName, const QByteArray &source)
{
const QByteArray preprocessed = _preproc(fileName, source);
return preprocessed;
}
virtual void sourceNeeded(QString &fileName, IncludeType, unsigned)
{ mergeEnvironment(fileName); }
virtual void macroAdded(const Macro &) {}
virtual void startExpandingMacro(unsigned,
const Macro &,
const QByteArray &,
const QVector<MacroArgumentReference> &) {}
virtual void stopExpandingMacro(unsigned, const Macro &) {}
virtual void startSkippingBlocks(unsigned) {}
virtual void stopSkippingBlocks(unsigned) {}
void mergeEnvironment(const QString &fileName)
{
if (! _merged.contains(fileName)) {
_merged.insert(fileName);
if (Document::Ptr doc = _snapshot.value(fileName)) {
foreach (const Document::Include &i, doc->includes())
mergeEnvironment(i.fileName());
_env.addMacros(doc->definedMacros());
}
}
}
};
class FindClass: protected ASTVisitor
{
QFutureInterface<Core::Utils::FileSearchResult> &_future;
Document::Ptr _doc;
Snapshot _snapshot;
Document::Ptr _thisDocument;
2009-06-02 14:56:03 +02:00
QByteArray _source;
QString _text;
QTextDocument::FindFlags _findFlags;
public:
FindClass(QFutureInterface<Core::Utils::FileSearchResult> &future, Document::Ptr doc, Snapshot snapshot)
: ASTVisitor(doc->control()),
_future(future),
_doc(doc),
_snapshot(snapshot)
{
_thisDocument = _snapshot.value(_doc->fileName());
2009-06-02 14:56:03 +02:00
}
void operator()(AST *ast, const QByteArray &source, const QString &text,
QTextDocument::FindFlags findFlags)
{
_source = source;
_text = text;
_findFlags = findFlags;
accept(ast);
}
protected:
using ASTVisitor::visit;
virtual bool visit(ClassSpecifierAST *ast)
{
if (ast->name) {
Qt::CaseSensitivity cs = Qt::CaseInsensitive;
if (_findFlags & QTextDocument::FindCaseSensitively)
cs = Qt::CaseSensitive;
Token start = tokenAt(ast->name->firstToken());
Token end = tokenAt(ast->name->lastToken() - 1);
const QString className = QString::fromUtf8(_source.constData() + start.begin(),
end.end() - start.begin());
int idx = className.indexOf(_text, 0, cs);
2009-06-02 14:56:03 +02:00
if (idx != -1) {
const char *beg = _source.constData();
const char *cp = beg + start.offset;
for (; cp != beg - 1; --cp) {
if (*cp == '\n')
break;
}
++cp;
const char *lineEnd = cp + 1;
for (; *lineEnd; ++lineEnd) {
if (*lineEnd == '\n')
break;
}
const QString matchingLine = QString::fromUtf8(cp, lineEnd - cp);
unsigned line, col;
getTokenStartPosition(ast->name->firstToken(), &line, &col);
_future.reportResult(Core::Utils::FileSearchResult(QDir::toNativeSeparators(_doc->fileName()),
line, matchingLine,
col + idx - 1, _text.length()));
}
}
return true;
}
};
static void searchClassDeclarations(QFutureInterface<Core::Utils::FileSearchResult> &future,
QPointer<CppModelManager> modelManager,
QString text,
QTextDocument::FindFlags findFlags)
{
const Snapshot snapshot = modelManager->snapshot();
future.setProgressRange(0, snapshot.size());
future.setProgressValue(0);
int progress = 0;
foreach (Document::Ptr doc, snapshot) {
const QString fileName = doc->fileName();
QFile file(fileName);
if (! file.open(QFile::ReadOnly))
continue;
const QString contents = QTextStream(&file).readAll();
SimpleClient r(modelManager);
const QByteArray source = r.run(fileName, contents.toUtf8());
Document::Ptr newDoc = Document::create(fileName);
newDoc->setSource(source);
newDoc->parse();
FindClass findClass(future, newDoc, snapshot);
findClass(newDoc->translationUnit()->ast(), source, text, findFlags);
future.setProgressValue(++progress);
}
}
} // end of anonymous namespace
FindClassDeclarations::FindClassDeclarations(CppModelManager *modelManager)
: _modelManager(modelManager),
_resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>())
{
m_watcher.setPendingResultsLimit(1);
connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(displayResult(int)));
connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
}
void FindClassDeclarations::findAll(const QString &txt, QTextDocument::FindFlags findFlags)
{
_resultWindow->clearContents();
_resultWindow->popup(true);
Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
QFuture<Core::Utils::FileSearchResult> result =
QtConcurrent::run(&searchClassDeclarations, _modelManager, txt, findFlags);
m_watcher.setFuture(result);
Core::FutureProgress *progress = progressManager->addTask(result, tr("Search class"),
CppTools::Constants::TASK_INDEX,
Core::ProgressManager::CloseOnSuccess);
connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
}
void FindClassDeclarations::displayResult(int index)
{
Core::Utils::FileSearchResult result = m_watcher.future().resultAt(index);
Find::ResultWindowItem *item = _resultWindow->addResult(result.fileName,
result.lineNumber,
result.matchingLine,
result.matchStart,
result.matchLength);
if (item)
connect(item, SIGNAL(activated(const QString&,int,int)),
this, SLOT(openEditor(const QString&,int,int)));
}
void FindClassDeclarations::searchFinished()
{
emit changed();
}
void FindClassDeclarations::openEditor(const QString &fileName, int line, int column)
{
TextEditor::BaseTextEditor::openEditorAt(fileName, line, column);
}
CppToolsPlugin::CppToolsPlugin() :
m_context(-1),
m_modelManager(0),
m_fileSettings(new CppFileSettings)
2008-12-02 12:01:29 +01:00
{
m_instance = this;
}
CppToolsPlugin::~CppToolsPlugin()
{
m_instance = 0;
m_modelManager = 0; // deleted automatically
}
bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
2008-12-02 12:01:29 +01:00
{
Q_UNUSED(arguments);
Q_UNUSED(error);
Core::ICore *core = Core::ICore::instance();
Core::ActionManager *am = core->actionManager();
2008-12-02 12:01:29 +01:00
// Objects
m_modelManager = new CppModelManager(this);
addAutoReleasedObject(m_modelManager);
m_completion = new CppCodeCompletion(m_modelManager);
addAutoReleasedObject(m_completion);
2008-12-02 12:01:29 +01:00
CppQuickOpenFilter *quickOpenFilter = new CppQuickOpenFilter(m_modelManager,
core->editorManager());
2008-12-02 12:01:29 +01:00
addAutoReleasedObject(quickOpenFilter);
addAutoReleasedObject(new CppClassesFilter(m_modelManager, core->editorManager()));
addAutoReleasedObject(new CppFunctionsFilter(m_modelManager, core->editorManager()));
addAutoReleasedObject(new CompletionSettingsPage(m_completion));
addAutoReleasedObject(new CppFileSettingsPage(m_fileSettings));
2008-12-02 12:01:29 +01:00
2009-06-02 14:56:03 +02:00
addAutoReleasedObject(new FindClassDeclarations(m_modelManager));
2008-12-02 12:01:29 +01:00
// Menus
Core::ActionContainer *mtools = am->actionContainer(Core::Constants::M_TOOLS);
Core::ActionContainer *mcpptools = am->createMenu(CppTools::Constants::M_TOOLS_CPP);
2008-12-02 12:01:29 +01:00
QMenu *menu = mcpptools->menu();
menu->setTitle(tr("&C++"));
menu->setEnabled(true);
mtools->addMenu(mcpptools);
// Actions
m_context = core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
2008-12-02 12:01:29 +01:00
QList<int> context = QList<int>() << m_context;
QAction *switchAction = new QAction(tr("Switch Header/Source"), this);
Core::Command *command = am->registerAction(switchAction, Constants::SWITCH_HEADER_SOURCE, context);
2008-12-02 12:01:29 +01:00
command->setDefaultKeySequence(QKeySequence(Qt::Key_F4));
mcpptools->addAction(command);
connect(switchAction, SIGNAL(triggered()), this, SLOT(switchHeaderSource()));
// Restore settings
QSettings *settings = Core::ICore::instance()->settings();
settings->beginGroup(QLatin1String("CppTools"));
settings->beginGroup(QLatin1String("Completion"));
const bool caseSensitive = settings->value(QLatin1String("CaseSensitive"), true).toBool();
m_completion->setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
m_completion->setAutoInsertBrackets(settings->value(QLatin1String("AutoInsertBraces"), true).toBool());
m_completion->setPartialCompletionEnabled(settings->value(QLatin1String("PartiallyComplete"), true).toBool());
settings->endGroup();
settings->endGroup();
2008-12-02 12:01:29 +01:00
return true;
}
void CppToolsPlugin::extensionsInitialized()
{
// The Cpp editor plugin, which is loaded later on, registers the Cpp mime types,
// so, apply settings here
m_fileSettings->fromSettings(Core::ICore::instance()->settings());
if (!m_fileSettings->applySuffixesToMimeDB())
qWarning("Unable to apply cpp suffixes to mime database (cpp mime types not found).\n");
2008-12-02 12:01:29 +01:00
}
void CppToolsPlugin::shutdown()
{
// Save settings
QSettings *settings = Core::ICore::instance()->settings();
settings->beginGroup(QLatin1String("CppTools"));
settings->beginGroup(QLatin1String("Completion"));
settings->setValue(QLatin1String("CaseSensitive"), m_completion->caseSensitivity() == Qt::CaseSensitive);
settings->setValue(QLatin1String("AutoInsertBraces"), m_completion->autoInsertBrackets());
settings->setValue(QLatin1String("PartiallyComplete"), m_completion->isPartialCompletionEnabled());
settings->endGroup();
settings->endGroup();
}
2008-12-02 12:01:29 +01:00
void CppToolsPlugin::switchHeaderSource()
{
Core::EditorManager *editorManager = Core::EditorManager::instance();
Core::IEditor *editor = editorManager->currentEditor();
2008-12-02 12:01:29 +01:00
QString otherFile = correspondingHeaderOrSource(editor->file()->fileName());
if (!otherFile.isEmpty()) {
editorManager->openEditor(otherFile);
editorManager->ensureEditorManagerVisible();
2008-12-02 12:01:29 +01:00
}
}
QFileInfo CppToolsPlugin::findFile(const QDir &dir, const QString &name,
const ProjectExplorer::Project *project) const
{
if (debug)
qDebug() << Q_FUNC_INFO << dir << name;
QFileInfo fileInSameDir(dir, name);
if (project && !fileInSameDir.isFile()) {
2008-12-02 12:01:29 +01:00
QString pattern = QString(1, QLatin1Char('/'));
pattern += name;
const QStringList projectFiles = project->files(ProjectExplorer::Project::AllFiles);
const QStringList::const_iterator pcend = projectFiles.constEnd();
for (QStringList::const_iterator it = projectFiles.constBegin(); it != pcend; ++it)
if (it->endsWith(pattern))
return QFileInfo(*it);
return QFileInfo();
}
return fileInSameDir;
2008-12-02 12:01:29 +01:00
}
// Figure out file type
enum FileType {
HeaderFile,
C_SourceFile,
CPP_SourceFile,
ObjectiveCPP_SourceFile,
UnknownType
};
2008-12-02 12:01:29 +01:00
static inline FileType fileType(const Core::MimeDatabase *mimeDatase, const QFileInfo & fi)
{
const Core::MimeType mimeType = mimeDatase->findByFile(fi);
if (!mimeType)
return UnknownType;
const QString typeName = mimeType.type();
if (typeName == QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE))
return C_SourceFile;
if (typeName == QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE))
return CPP_SourceFile;
if (typeName == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE))
return ObjectiveCPP_SourceFile;
2008-12-02 12:01:29 +01:00
if (typeName == QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)
|| typeName == QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE))
return HeaderFile;
return UnknownType;
}
// Return the suffixes that should be checked when trying to find a
// source belonging to a header and vice versa
static QStringList matchingCandidateSuffixes(const Core::MimeDatabase *mimeDatase, FileType type)
{
switch (type) {
case UnknownType:
break;
case HeaderFile: // Note that C/C++ headers are undistinguishable
return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)).suffixes()
+ mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)).suffixes()
+ mimeDatase->findByType(QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)).suffixes();
2008-12-02 12:01:29 +01:00
case C_SourceFile:
return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)).suffixes();
case CPP_SourceFile:
case ObjectiveCPP_SourceFile:
2008-12-02 12:01:29 +01:00
return mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)).suffixes();
}
return QStringList();
}
QString CppToolsPlugin::correspondingHeaderOrSourceI(const QString &fileName) const
{
const Core::ICore *core = Core::ICore::instance();
2008-12-02 12:01:29 +01:00
const Core::MimeDatabase *mimeDatase = core->mimeDatabase();
ProjectExplorer::ProjectExplorerPlugin *explorer =
ProjectExplorer::ProjectExplorerPlugin::instance();
2008-12-02 12:01:29 +01:00
ProjectExplorer::Project *project = (explorer ? explorer->currentProject() : 0);
const QFileInfo fi(fileName);
const FileType type = fileType(mimeDatase, fi);
if (debug)
qDebug() << Q_FUNC_INFO << fileName << type;
if (type == UnknownType)
return QString();
const QDir absoluteDir = fi.absoluteDir();
const QString baseName = fi.completeBaseName();
2008-12-02 12:01:29 +01:00
const QStringList suffixes = matchingCandidateSuffixes(mimeDatase, type);
const QString privateHeaderSuffix = QLatin1String("_p");
const QChar dot = QLatin1Char('.');
QStringList candidates;
// Check base matches 'source.h'-> 'source.cpp' and vice versa
const QStringList::const_iterator scend = suffixes.constEnd();
for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) {
QString candidate = baseName;
candidate += dot;
candidate += *it;
const QFileInfo candidateFi = findFile(absoluteDir, candidate, project);
if (candidateFi.isFile())
return candidateFi.absoluteFilePath();
}
if (type == HeaderFile) {
// 'source_p.h': try 'source.cpp'
if (baseName.endsWith(privateHeaderSuffix)) {
QString sourceBaseName = baseName;
sourceBaseName.truncate(sourceBaseName.size() - privateHeaderSuffix.size());
for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) {
QString candidate = sourceBaseName;
candidate += dot;
candidate += *it;
const QFileInfo candidateFi = findFile(absoluteDir, candidate, project);
if (candidateFi.isFile())
return candidateFi.absoluteFilePath();
}
}
} else {
// 'source.cpp': try 'source_p.h'
const QStringList::const_iterator scend = suffixes.constEnd();
for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) {
QString candidate = baseName;
candidate += privateHeaderSuffix;
candidate += dot;
candidate += *it;
const QFileInfo candidateFi = findFile(absoluteDir, candidate, project);
if (candidateFi.isFile())
return candidateFi.absoluteFilePath();
}
}
return QString();
}
QString CppToolsPlugin::correspondingHeaderOrSource(const QString &fileName) const
{
const QString rc = correspondingHeaderOrSourceI(fileName);
if (debug)
qDebug() << Q_FUNC_INFO << fileName << rc;
return rc;
}
Q_EXPORT_PLUGIN(CppToolsPlugin)