Commit bec4f024 authored by Leandro Melo's avatar Leandro Melo

New code assist API

This is a re-work of our completion engine. Primary goals are:

- Allow the computation to run in a separate thread so the GUI is not locked.
- Support a model-based approach. QStrings are still needed (filtering, etc), but
internal structures are free to use more efficient representations.
- Unifiy all kinds of *assist* into a more reusable and extensible framework.
- Remove unnecessary dependencies on the text editor so we have more generic
and easily "plugable" components (still things to be resolved).
parent d835b769
......@@ -31,6 +31,7 @@
**************************************************************************/
#include "cppcompleteswitch.h"
#include "cppquickfixassistant.h"
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
......@@ -102,8 +103,11 @@ public:
class Operation: public CppQuickFixOperation
{
public:
Operation(const CppQuickFixState &state, int priority, CompoundStatementAST *compoundStatement, const QStringList &values)
: CppQuickFixOperation(state, priority)
Operation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
int priority,
CompoundStatementAST *compoundStatement,
const QStringList &values)
: CppQuickFixOperation(interface, priority)
, compoundStatement(compoundStatement)
, values(values)
{
......@@ -150,15 +154,15 @@ static Enum *findEnum(const QList<LookupItem> &results,
return 0;
}
static Enum *conditionEnum(const CppQuickFixState &state,
static Enum *conditionEnum(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
SwitchStatementAST *statement)
{
Block *block = statement->symbol;
Scope *scope = state.document()->scopeAt(block->line(), block->column());
Scope *scope = interface->semanticInfo().doc->scopeAt(block->line(), block->column());
TypeOfExpression typeOfExpression;
typeOfExpression.init(state.document(), state.snapshot());
typeOfExpression.init(interface->semanticInfo().doc, interface->snapshot());
const QList<LookupItem> results = typeOfExpression(statement->condition,
state.document(),
interface->semanticInfo().doc,
scope);
return findEnum(results, typeOfExpression.context());
......@@ -166,9 +170,10 @@ static Enum *conditionEnum(const CppQuickFixState &state,
} // end of anonymous namespace
QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQuickFixState &state)
QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(
const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = state.path();
const QList<AST *> &path = interface->path();
if (path.isEmpty())
return noResult(); // nothing to do
......@@ -178,13 +183,13 @@ QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQui
AST *ast = path.at(depth);
SwitchStatementAST *switchStatement = ast->asSwitchStatement();
if (switchStatement) {
if (!state.isCursorOn(switchStatement->switch_token) || !switchStatement->statement)
if (!interface->isCursorOn(switchStatement->switch_token) || !switchStatement->statement)
return noResult();
CompoundStatementAST *compoundStatement = switchStatement->statement->asCompoundStatement();
if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
return noResult();
// look if the condition's type is an enum
if (Enum *e = conditionEnum(state, switchStatement)) {
if (Enum *e = conditionEnum(interface, switchStatement)) {
// check the possible enum values
QStringList values;
Overview prettyPrint;
......@@ -195,8 +200,8 @@ QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQui
}
// Get the used values
Block *block = switchStatement->symbol;
CaseStatementCollector caseValues(state.document(), state.snapshot(),
state.document()->scopeAt(block->line(), block->column()));
CaseStatementCollector caseValues(interface->semanticInfo().doc, interface->snapshot(),
interface->semanticInfo().doc->scopeAt(block->line(), block->column()));
QStringList usedValues = caseValues(switchStatement);
// save the values that would be added
foreach (const QString &usedValue, usedValues)
......@@ -204,7 +209,7 @@ QList<CppQuickFixOperation::Ptr> CompleteSwitchCaseStatement::match(const CppQui
if (values.isEmpty())
return noResult();
else
return singleResult(new Operation(state, depth, compoundStatement, values));
return singleResult(new Operation(interface, depth, compoundStatement, values));
}
return noResult();
......
......@@ -46,7 +46,8 @@ namespace Internal {
class CompleteSwitchCaseStatement: public CppQuickFixFactory
{
public:
virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state);
virtual QList<CppQuickFixOperation::Ptr> match(
const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
};
} // namespace Internal
......
......@@ -35,11 +35,10 @@
#include "cppplugin.h"
#include "cpphighlighter.h"
#include "cppchecksymbols.h"
#include "cppquickfix.h"
#include "cpplocalsymbols.h"
#include "cppquickfixcollector.h"
#include "cppqtstyleindenter.h"
#include "cppautocompleter.h"
#include "cppquickfixassistant.h"
#include <AST.h>
#include <Control.h>
......@@ -66,6 +65,7 @@
#include <cpptools/cpptoolsplugin.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cppcodeformatter.h>
#include <cpptools/cppcompletionassist.h>
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
......@@ -83,6 +83,9 @@
#include <texteditor/fontsettings.h>
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
......@@ -1617,22 +1620,29 @@ void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
quickFixMenu->addAction(am->command(Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
CppQuickFixCollector *quickFixCollector = CppPlugin::instance()->quickFixCollector();
QSignalMapper mapper;
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
if (! isOutdated()) {
if (quickFixCollector->startCompletion(editor()) != -1) {
m_quickFixes = quickFixCollector->quickFixes();
if (! m_quickFixes.isEmpty())
quickFixMenu->addSeparator();
for (int index = 0; index < m_quickFixes.size(); ++index) {
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
QAction *action = quickFixMenu->addAction(op->description());
mapper.setMapping(action, index);
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
TextEditor::IAssistInterface *interface =
createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked);
if (interface) {
QScopedPointer<TextEditor::IAssistProcessor> processor(
CppPlugin::instance()->quickFixProvider()->createProcessor());
QScopedPointer<TextEditor::IAssistProposal> proposal(processor->perform(interface));
if (!proposal.isNull()) {
TextEditor::BasicProposalItemListModel *model =
static_cast<TextEditor::BasicProposalItemListModel *>(proposal->model());
for (int index = 0; index < model->size(); ++index) {
TextEditor::BasicProposalItem *item =
static_cast<TextEditor::BasicProposalItem *>(model->proposalItem(index));
TextEditor::QuickFixOperation::Ptr op =
item->data().value<TextEditor::QuickFixOperation::Ptr>();
m_quickFixes.append(op);
QAction *action = quickFixMenu->addAction(op->description());
mapper.setMapping(action, index);
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
}
delete model;
}
}
}
......@@ -1646,7 +1656,6 @@ void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
appendStandardContextMenuActions(menu);
menu->exec(e->globalPos());
quickFixCollector->cleanup();
m_quickFixes.clear();
delete menu;
}
......@@ -1921,6 +1930,7 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
}
setExtraSelections(UnusedSymbolSelection, unusedSelections);
if (! m_renameSelections.isEmpty())
......@@ -2205,4 +2215,32 @@ QVector<QString> CPPEditorWidget::highlighterFormatCategories()
return categories;
}
TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface(
TextEditor::AssistKind kind,
TextEditor::AssistReason reason) const
{
if (kind == TextEditor::Completion) {
QStringList includePaths;
QStringList frameworkPaths;
if (ProjectExplorer::Project *project =
ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject()) {
includePaths = m_modelManager->projectInfo(project).includePaths;
frameworkPaths = m_modelManager->projectInfo(project).frameworkPaths;
}
return new CppTools::Internal::CppCompletionAssistInterface(
document(),
position(),
editor()->file(),
reason,
m_modelManager->snapshot(),
includePaths,
frameworkPaths);
} else if (kind == TextEditor::QuickFix) {
if (!semanticInfo().doc || semanticInfo().revision != editorRevision())
return 0;
return new CppQuickFixAssistInterface(const_cast<CPPEditorWidget *>(this), reason);
}
return 0;
}
#include "cppeditor.moc"
......@@ -34,13 +34,13 @@
#define CPPEDITOR_H
#include "cppeditorenums.h"
#include "cppquickfix.h"
#include "cppsemanticinfo.h"
#include <cplusplus/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
#include <texteditor/basetexteditor.h>
#include <texteditor/quickfix.h>
#include <QtCore/QThread>
#include <QtCore/QMutex>
......@@ -189,6 +189,9 @@ public:
static QVector<QString> highlighterFormatCategories();
virtual TextEditor::IAssistInterface *createAssistInterface(TextEditor::AssistKind kind,
TextEditor::AssistReason reason) const;
Q_SIGNALS:
void outlineModelIndexChanged(const QModelIndex &index);
......
......@@ -14,7 +14,6 @@ HEADERS += cppplugin.h \
cppeditorenums.h \
cppeditor_global.h \
cppclasswizard.h \
cppquickfix.h \
cppchecksymbols.h \
cppsemanticinfo.h \
cppoutline.h \
......@@ -22,12 +21,13 @@ HEADERS += cppplugin.h \
cpplocalsymbols.h \
cpptypehierarchy.h \
cppelementevaluator.h \
cppquickfixcollector.h \
cppqtstyleindenter.h \
cppautocompleter.h \
cppcompleteswitch.h \
cppsnippetprovider.h \
cppinsertqtpropertymembers.h
cppinsertqtpropertymembers.h \
cppquickfixassistant.h \
cppquickfix.h
SOURCES += cppplugin.cpp \
cppeditor.cpp \
......@@ -35,7 +35,6 @@ SOURCES += cppplugin.cpp \
cpphoverhandler.cpp \
cppfilewizard.cpp \
cppclasswizard.cpp \
cppquickfix.cpp \
cppquickfixes.cpp \
cppchecksymbols.cpp \
cppsemanticinfo.cpp \
......@@ -44,12 +43,13 @@ SOURCES += cppplugin.cpp \
cpplocalsymbols.cpp \
cpptypehierarchy.cpp \
cppelementevaluator.cpp \
cppquickfixcollector.cpp \
cppqtstyleindenter.cpp \
cppautocompleter.cpp \
cppcompleteswitch.cpp \
cppsnippetprovider.cpp \
cppinsertqtpropertymembers.cpp
cppinsertqtpropertymembers.cpp \
cppquickfixassistant.cpp \
cppquickfix.cpp
RESOURCES += cppeditor.qrc
OTHER_FILES += CppEditor.mimetypes.xml
......@@ -31,6 +31,7 @@
**************************************************************************/
#include "cppinsertdecldef.h"
#include "cppquickfixassistant.h"
#include <CPlusPlus.h>
#include <cplusplus/ASTPath.h>
......@@ -53,11 +54,12 @@ namespace {
class InsertDeclOperation: public CppQuickFixOperation
{
public:
InsertDeclOperation(const CppQuickFixState &state, int priority,
InsertDeclOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
int priority,
const QString &targetFileName, const Class *targetSymbol,
InsertionPointLocator::AccessSpec xsSpec,
const QString &decl)
: CppQuickFixOperation(state, priority)
: CppQuickFixOperation(interface, priority)
, m_targetFileName(targetFileName)
, m_targetSymbol(targetSymbol)
, m_xsSpec(xsSpec)
......@@ -108,10 +110,11 @@ private:
} // anonymous namespace
QList<CppQuickFixOperation::Ptr> DeclFromDef::match(const CppQuickFixState &state)
QList<CppQuickFixOperation::Ptr> DeclFromDef::match(
const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = state.path();
const CppRefactoringFile &file = state.currentFile();
const QList<AST *> &path = interface->path();
const CppRefactoringFile &file = interface->currentFile();
FunctionDefinitionAST *funDef = 0;
int idx = 0;
......@@ -158,7 +161,7 @@ QList<CppQuickFixOperation::Ptr> DeclFromDef::match(const CppQuickFixState &stat
if (!q->base())
return noResult();
if (ClassOrNamespace *binding = state.context().lookupType(q->base(), enclosingScope)) {
if (ClassOrNamespace *binding = interface->context().lookupType(q->base(), enclosingScope)) {
foreach (Symbol *s, binding->symbols()) {
if (Class *matchingClass = s->asClass()) {
for (Symbol *s = matchingClass->find(q->identifier()); s; s = s->next()) {
......@@ -177,11 +180,11 @@ QList<CppQuickFixOperation::Ptr> DeclFromDef::match(const CppQuickFixState &stat
const QString fn = QString::fromUtf8(matchingClass->fileName(),
matchingClass->fileNameLength());
const QString decl = generateDeclaration(state,
const QString decl = generateDeclaration(interface,
method,
binding);
return singleResult(
new InsertDeclOperation(state, idx, fn, matchingClass,
new InsertDeclOperation(interface, idx, fn, matchingClass,
InsertionPointLocator::Public,
decl));
}
......@@ -191,7 +194,7 @@ QList<CppQuickFixOperation::Ptr> DeclFromDef::match(const CppQuickFixState &stat
return noResult();
}
QString DeclFromDef::generateDeclaration(const CppQuickFixState &,
QString DeclFromDef::generateDeclaration(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &,
Function *method,
ClassOrNamespace *targetBinding)
{
......@@ -214,9 +217,9 @@ namespace {
class InsertDefOperation: public CppQuickFixOperation
{
public:
InsertDefOperation(const CppQuickFixState &state, int priority,
InsertDefOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface, int priority,
Declaration *decl, const InsertionLocation &loc)
: CppQuickFixOperation(state, priority)
: CppQuickFixOperation(interface, priority)
, m_decl(decl)
, m_loc(loc)
{
......@@ -241,12 +244,12 @@ public:
//--
SubstitutionEnvironment env;
env.setContext(state().context());
env.setContext(assistInterface()->context());
env.switchScope(m_decl->enclosingScope());
UseQualifiedNames q;
env.enter(&q);
Control *control = state().context().control().data();
Control *control = assistInterface()->context().control().data();
FullySpecifiedType tn = rewriteType(m_decl->type(), &env, control);
QString name = oo(LookupContext::fullyQualifiedName(m_decl));
//--
......@@ -274,10 +277,11 @@ private:
} // anonymous namespace
QList<CppQuickFixOperation::Ptr> DefFromDecl::match(const CppQuickFixState &state)
QList<CppQuickFixOperation::Ptr> DefFromDecl::match(
const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = state.path();
const CppRefactoringFile &file = state.currentFile();
const QList<AST *> &path = interface->path();
const CppRefactoringFile &file = interface->currentFile();
int idx = path.size() - 1;
for (; idx >= 0; --idx) {
......@@ -292,12 +296,12 @@ QList<CppQuickFixOperation::Ptr> DefFromDecl::match(const CppQuickFixState &stat
&& decl->enclosingScope()->isClass()) {
DeclaratorAST *declarator = simpleDecl->declarator_list->value;
if (file.isCursorOn(declarator->core_declarator)) {
CppRefactoringChanges refactoring(state.snapshot());
CppRefactoringChanges refactoring(interface->snapshot());
InsertionPointLocator locator(&refactoring);
QList<CppQuickFixOperation::Ptr> results;
foreach (const InsertionLocation &loc, locator.methodDefinition(decl)) {
if (loc.isValid())
results.append(CppQuickFixOperation::Ptr(new InsertDefOperation(state, idx, decl, loc)));
results.append(CppQuickFixOperation::Ptr(new InsertDefOperation(interface, idx, decl, loc)));
}
return results;
}
......
......@@ -47,10 +47,11 @@ namespace Internal {
class DeclFromDef: public CppQuickFixFactory
{
public:
virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state);
virtual QList<CppQuickFixOperation::Ptr>
match(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
protected:
static QString generateDeclaration(const CppQuickFixState &state,
static QString generateDeclaration(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
CPlusPlus::Function *method,
CPlusPlus::ClassOrNamespace *targetBinding);
};
......@@ -58,7 +59,8 @@ protected:
class DefFromDecl: public CppQuickFixFactory
{
public:
virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state);
virtual QList<CppQuickFixOperation::Ptr>
match(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
};
} // namespace Internal
......
......@@ -31,6 +31,7 @@
**************************************************************************/
#include "cppinsertqtpropertymembers.h"
#include "cppquickfixassistant.h"
#include <AST.h>
#include <Token.h>
......@@ -38,6 +39,7 @@
#include <cpptools/insertionpointlocator.h>
#include <cpptools/cpprefactoringchanges.h>
#include <cppeditor/cppquickfix.h>
#include <coreplugin/ifile.h>
using namespace CPlusPlus;
using namespace CppTools;
......@@ -46,9 +48,10 @@ using namespace Utils;
using namespace CppEditor;
using namespace CppEditor::Internal;
QList<CppQuickFixOperation::Ptr> InsertQtPropertyMembers::match(const CppQuickFixState &state)
QList<CppQuickFixOperation::Ptr> InsertQtPropertyMembers::match(
const QSharedPointer<const CppQuickFixAssistInterface> &interface)
{
const QList<AST *> &path = state.path();
const QList<AST *> &path = interface->path();
if (path.isEmpty())
return noResult();
......@@ -67,8 +70,8 @@ QList<CppQuickFixOperation::Ptr> InsertQtPropertyMembers::match(const CppQuickFi
if (!klass)
return noResult();
CppRefactoringChanges refactoring(state.snapshot());
const CppRefactoringFile &file = refactoring.file(state.document()->fileName());
CppRefactoringChanges refactoring(interface->snapshot());
const CppRefactoringFile &file = refactoring.file(interface->file()->fileName());
const QString propertyName = file.textOf(qtPropertyDeclaration->property_name);
QString getterName;
QString setterName;
......@@ -116,16 +119,17 @@ QList<CppQuickFixOperation::Ptr> InsertQtPropertyMembers::match(const CppQuickFi
if (getterName.isEmpty() && setterName.isEmpty() && signalName.isEmpty())
return noResult();
return singleResult(new Operation(state, path.size() - 1, qtPropertyDeclaration, c,
return singleResult(new Operation(interface, path.size() - 1, qtPropertyDeclaration, c,
generateFlags,
getterName, setterName, signalName, storageName));
}
InsertQtPropertyMembers::Operation::Operation(
const CppQuickFixState &state, int priority, QtPropertyDeclarationAST *declaration, Class *klass,
const QSharedPointer<const CppQuickFixAssistInterface> &interface,
int priority, QtPropertyDeclarationAST *declaration, Class *klass,
int generateFlags, const QString &getterName, const QString &setterName, const QString &signalName,
const QString &storageName)
: CppQuickFixOperation(state, priority)
: CppQuickFixOperation(interface, priority)
, m_declaration(declaration)
, m_class(klass)
, m_generateFlags(generateFlags)
......
......@@ -62,7 +62,8 @@ class InsertQtPropertyMembers : public CppQuickFixFactory
Q_OBJECT
public:
virtual QList<CppQuickFixOperation::Ptr> match(const CppQuickFixState &state);
virtual QList<CppQuickFixOperation::Ptr>
match(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
private:
enum GenerateFlag {
......@@ -75,7 +76,8 @@ private:
class Operation: public CppQuickFixOperation
{
public:
Operation(const CppQuickFixState &state, int priority,
Operation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
int priority,
CPlusPlus::QtPropertyDeclarationAST *declaration, CPlusPlus::Class *klass,
int generateFlags,
const QString &getterName, const QString &setterName, const QString &signalName,
......
......@@ -37,11 +37,10 @@
#include "cppeditorenums.h"
#include "cppfilewizard.h"
#include "cpphoverhandler.h"
#include "cppquickfix.h"
#include "cppoutline.h"
#include "cppquickfixcollector.h"
#include "cpptypehierarchy.h"
#include "cppsnippetprovider.h"
#include "cppquickfixassistant.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
......@@ -54,7 +53,6 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/navigationwidget.h>
#include <texteditor/completionsupport.h>
#include <texteditor/fontsettings.h>
#include <texteditor/storagesettings.h>
#include <texteditor/texteditoractionhandler.h>
......@@ -75,6 +73,8 @@
using namespace CppEditor;
using namespace CppEditor::Internal;
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn);
enum { QUICKFIX_INTERVAL = 20 };
//////////////////////////// CppEditorFactory /////////////////////////////
......@@ -149,15 +149,10 @@ CppPlugin::CppPlugin() :
m_renameSymbolUnderCursorAction(0),
m_findUsagesAction(0),
m_updateCodeModelAction(0),
m_openTypeHierarchyAction(0)
m_openTypeHierarchyAction(0),
m_quickFixProvider(0)
{
m_instance = this;
m_quickFixCollector = 0;
m_quickFixTimer = new QTimer(this);
m_quickFixTimer->setInterval(20);
m_quickFixTimer->setSingleShot(true);
connect(m_quickFixTimer, SIGNAL(timeout()), this, SLOT(quickFixNow()));
}
CppPlugin::~CppPlugin()
......@@ -193,8 +188,10 @@ bool CppPlugin::sortedOutline() const
return m_sortedOutline;
}
CppQuickFixCollector *CppPlugin::quickFixCollector() const
{ return m_quickFixCollector; }
CppQuickFixAssistProvider *CppPlugin::quickFixProvider() const
{
return m_quickFixProvider;
}
bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage)
{
......@@ -209,9 +206,9 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess
addAutoReleasedObject(new CppTypeHierarchyFactory);
addAutoReleasedObject(new CppSnippetProvider);
m_quickFixCollector = new CppQuickFixCollector;