forked from qt-creator/qt-creator
More work on `Quick Fix'.
This commit is contained in:
@@ -134,6 +134,11 @@ void CppPlugin::initializeEditor(CPPEditor *editor)
|
||||
// auto completion
|
||||
connect(editor, SIGNAL(requestAutoCompletion(ITextEditable*, bool)),
|
||||
TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(ITextEditable*, bool)));
|
||||
|
||||
// quick fix
|
||||
connect(editor, SIGNAL(requestQuickFix(ITextEditable*)),
|
||||
TextEditor::Internal::CompletionSupport::instance(), SLOT(quickFix(ITextEditable*)));
|
||||
|
||||
// method combo box sorting
|
||||
connect(this, SIGNAL(methodOverviewSortingChanged(bool)),
|
||||
editor, SLOT(setSortedMethodOverview(bool)));
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "cppcodecompletion.h"
|
||||
#include "cppmodelmanager.h"
|
||||
#include "cppdoxygen.h"
|
||||
#include "cpptoolseditorsupport.h"
|
||||
|
||||
#include <Control.h>
|
||||
#include <AST.h>
|
||||
@@ -421,6 +422,59 @@ void FunctionArgumentWidget::updateHintText()
|
||||
m_popupFrame->move(pos);
|
||||
}
|
||||
|
||||
CppQuickFixCollector::CppQuickFixCollector(CppModelManager *modelManager)
|
||||
: _modelManager(modelManager), _editor(0)
|
||||
{ }
|
||||
|
||||
CppQuickFixCollector::~CppQuickFixCollector()
|
||||
{ }
|
||||
|
||||
bool CppQuickFixCollector::supportsEditor(TextEditor::ITextEditable *editor)
|
||||
{ return _modelManager->isCppEditor(editor); }
|
||||
|
||||
bool CppQuickFixCollector::triggersCompletion(TextEditor::ITextEditable *)
|
||||
{ return false; }
|
||||
|
||||
int CppQuickFixCollector::startCompletion(TextEditor::ITextEditable *editor)
|
||||
{
|
||||
_editor = editor;
|
||||
|
||||
if (CppEditorSupport *extra = _modelManager->editorSupport(editor)) {
|
||||
const QList<QuickFixOperationPtr> quickFixes = extra->quickFixes();
|
||||
if (! quickFixes.isEmpty()) {
|
||||
int i = 0;
|
||||
foreach (QuickFixOperationPtr op, quickFixes) {
|
||||
TextEditor::CompletionItem item(this);
|
||||
item.m_text = op->description();
|
||||
item.m_data = QVariant::fromValue(i);
|
||||
_completions.append(item);
|
||||
++i;
|
||||
}
|
||||
return editor->position();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CppQuickFixCollector::completions(QList<TextEditor::CompletionItem> *completions)
|
||||
{
|
||||
completions->append(_completions);
|
||||
}
|
||||
|
||||
void CppQuickFixCollector::complete(const TextEditor::CompletionItem &item)
|
||||
{
|
||||
CppEditorSupport *extra = _modelManager->editorSupport(_editor);
|
||||
const QList<QuickFixOperationPtr> quickFixes = extra->quickFixes();
|
||||
QuickFixOperationPtr quickFix = quickFixes.at(item.m_data.toInt());
|
||||
TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(_editor->widget());
|
||||
quickFix->apply(ed->textCursor());
|
||||
}
|
||||
|
||||
void CppQuickFixCollector::cleanup()
|
||||
{
|
||||
_completions.clear();
|
||||
}
|
||||
|
||||
CppCodeCompletion::CppCodeCompletion(CppModelManager *manager)
|
||||
: ICompletionCollector(manager),
|
||||
m_manager(manager),
|
||||
|
||||
@@ -52,6 +52,27 @@ namespace Internal {
|
||||
class CppModelManager;
|
||||
class FunctionArgumentWidget;
|
||||
|
||||
class CppQuickFixCollector: public TextEditor::IQuickFixCollector
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CppQuickFixCollector(CppModelManager *modelManager);
|
||||
virtual ~CppQuickFixCollector();
|
||||
|
||||
virtual bool supportsEditor(TextEditor::ITextEditable *editor);
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual int startCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual void completions(QList<TextEditor::CompletionItem> *completions);
|
||||
virtual void complete(const TextEditor::CompletionItem &item);
|
||||
virtual void cleanup();
|
||||
|
||||
private:
|
||||
CppModelManager *_modelManager;
|
||||
TextEditor::ITextEditable *_editor;
|
||||
QList<TextEditor::CompletionItem> _completions;
|
||||
};
|
||||
|
||||
class CppCodeCompletion : public TextEditor::ICompletionCollector
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -86,6 +86,9 @@ public:
|
||||
|
||||
bool isCppEditor(Core::IEditor *editor) const; // ### private
|
||||
|
||||
CppEditorSupport *editorSupport(TextEditor::ITextEditor *editor) const
|
||||
{ return m_editorSupport.value(editor); }
|
||||
|
||||
void emitDocumentUpdated(CPlusPlus::Document::Ptr doc);
|
||||
|
||||
void stopEditorSelectionsUpdate()
|
||||
|
||||
@@ -33,9 +33,214 @@
|
||||
#include <texteditor/itexteditor.h>
|
||||
#include <texteditor/basetexteditor.h>
|
||||
|
||||
#include <QTimer>
|
||||
#include <AST.h>
|
||||
#include <ASTVisitor.h>
|
||||
#include <TranslationUnit.h>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
using namespace CppTools::Internal;
|
||||
using namespace CPlusPlus;
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
DEFAULT_QUICKFIX_INTERVAL = 500
|
||||
};
|
||||
|
||||
class QuickFixMark: public TextEditor::ITextMark
|
||||
{
|
||||
QIcon _icon;
|
||||
|
||||
public:
|
||||
QuickFixMark(QObject *parent)
|
||||
: TextEditor::ITextMark(parent),
|
||||
_icon(QLatin1String(":/core/images/redo.png")) // ### FIXME
|
||||
{ }
|
||||
|
||||
virtual ~QuickFixMark()
|
||||
{ }
|
||||
|
||||
virtual QIcon icon() const
|
||||
{ return _icon; }
|
||||
|
||||
virtual void updateLineNumber(int)
|
||||
{ }
|
||||
|
||||
virtual void updateBlock(const QTextBlock &)
|
||||
{ }
|
||||
|
||||
virtual void removedFromEditor()
|
||||
{ }
|
||||
|
||||
virtual void documentClosing()
|
||||
{ }
|
||||
};
|
||||
|
||||
class ReplaceCast: public QuickFixOperation
|
||||
{
|
||||
CastExpressionAST *_castExpression;
|
||||
|
||||
public:
|
||||
ReplaceCast(CastExpressionAST *node, Document::Ptr doc, const Snapshot &snapshot)
|
||||
: QuickFixOperation(doc, snapshot),
|
||||
_castExpression(node)
|
||||
{ }
|
||||
|
||||
virtual QString description() const
|
||||
{ return QLatin1String("Rewrite old C-style cast"); }
|
||||
|
||||
virtual void apply(QTextCursor tc)
|
||||
{
|
||||
setTextCursor(tc);
|
||||
|
||||
tc.beginEditBlock();
|
||||
|
||||
QTextCursor beginOfCast = cursor(_castExpression->lparen_token);
|
||||
QTextCursor endOfCast = cursor(_castExpression->rparen_token);
|
||||
QTextCursor beginOfExpr = moveAtStartOfToken(_castExpression->expression->firstToken());
|
||||
QTextCursor endOfExpr = moveAtEndOfToken(_castExpression->expression->lastToken() - 1);
|
||||
|
||||
beginOfCast.insertText(QLatin1String("reinterpret_cast<"));
|
||||
endOfCast.insertText(QLatin1String(">"));
|
||||
|
||||
beginOfExpr.insertText(QLatin1String("("));
|
||||
endOfExpr.insertText(QLatin1String(")"));
|
||||
|
||||
tc.endEditBlock();
|
||||
}
|
||||
};
|
||||
|
||||
class CheckDocument: protected ASTVisitor
|
||||
{
|
||||
QTextCursor _textCursor;
|
||||
Document::Ptr _doc;
|
||||
Snapshot _snapshot;
|
||||
unsigned _line;
|
||||
unsigned _column;
|
||||
QList<QuickFixOperationPtr> _quickFixes;
|
||||
|
||||
public:
|
||||
CheckDocument(Document::Ptr doc, Snapshot snapshot)
|
||||
: ASTVisitor(doc->control()), _doc(doc), _snapshot(snapshot)
|
||||
{ }
|
||||
|
||||
QList<QuickFixOperationPtr> operator()(QTextCursor tc)
|
||||
{
|
||||
_quickFixes.clear();
|
||||
_textCursor = tc;
|
||||
_line = tc.blockNumber() + 1;
|
||||
_column = tc.columnNumber() + 1;
|
||||
accept(_doc->translationUnit()->ast());
|
||||
return _quickFixes;
|
||||
}
|
||||
|
||||
protected:
|
||||
using ASTVisitor::visit;
|
||||
|
||||
bool checkPosition(AST *ast) const
|
||||
{
|
||||
unsigned startLine, startColumn;
|
||||
unsigned endLine, endColumn;
|
||||
|
||||
getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
|
||||
getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
|
||||
|
||||
if (_line < startLine || (_line == startLine && _column < startColumn))
|
||||
return false;
|
||||
else if (_line > endLine || (_line == endLine && _column >= endColumn))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
virtual bool visit(ForStatementAST *ast)
|
||||
{
|
||||
if (! checkPosition(ast))
|
||||
return true;
|
||||
|
||||
if (ast->initializer && ast->initializer->asDeclarationStatement() != 0) {
|
||||
if (checkPosition(ast->initializer)) {
|
||||
// move initializer
|
||||
_nodes.append(ast);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
virtual bool visit(CastExpressionAST *ast)
|
||||
{
|
||||
if (! checkPosition(ast))
|
||||
return true;
|
||||
|
||||
if (ast->type_id && ast->lparen_token && ast->rparen_token && ast->expression) {
|
||||
QuickFixOperationPtr op(new ReplaceCast(ast, _doc, _snapshot));
|
||||
_quickFixes.append(op);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
QuickFixOperation::QuickFixOperation(CPlusPlus::Document::Ptr doc, const CPlusPlus::Snapshot &snapshot)
|
||||
: _doc(doc), _snapshot(snapshot)
|
||||
{ }
|
||||
|
||||
QuickFixOperation::~QuickFixOperation()
|
||||
{ }
|
||||
|
||||
QTextCursor QuickFixOperation::textCursor() const
|
||||
{ return _textCursor; }
|
||||
|
||||
void QuickFixOperation::setTextCursor(const QTextCursor &tc)
|
||||
{ _textCursor = tc; }
|
||||
|
||||
const CPlusPlus::Token &QuickFixOperation::tokenAt(unsigned index) const
|
||||
{ return _doc->translationUnit()->tokenAt(index); }
|
||||
|
||||
void QuickFixOperation::getTokenStartPosition(unsigned index, unsigned *line, unsigned *column) const
|
||||
{ _doc->translationUnit()->getPosition(tokenAt(index).begin(), line, column); }
|
||||
|
||||
void QuickFixOperation::getTokenEndPosition(unsigned index, unsigned *line, unsigned *column) const
|
||||
{ _doc->translationUnit()->getPosition(tokenAt(index).end(), line, column); }
|
||||
|
||||
QTextCursor QuickFixOperation::cursor(unsigned index) const
|
||||
{
|
||||
const Token &tk = tokenAt(index);
|
||||
|
||||
unsigned line, col;
|
||||
getTokenStartPosition(index, &line, &col);
|
||||
QTextCursor tc = _textCursor;
|
||||
tc.setPosition(tc.document()->findBlockByNumber(line - 1).position() + col - 1);
|
||||
tc.setPosition(tc.position() + tk.length, QTextCursor::KeepAnchor);
|
||||
return tc;
|
||||
}
|
||||
|
||||
QTextCursor QuickFixOperation::moveAtStartOfToken(unsigned index) const
|
||||
{
|
||||
unsigned line, col;
|
||||
getTokenStartPosition(index, &line, &col);
|
||||
QTextCursor tc = _textCursor;
|
||||
tc.setPosition(tc.document()->findBlockByNumber(line - 1).position() + col - 1);
|
||||
return tc;
|
||||
}
|
||||
|
||||
QTextCursor QuickFixOperation::moveAtEndOfToken(unsigned index) const
|
||||
{
|
||||
const Token &tk = tokenAt(index);
|
||||
|
||||
unsigned line, col;
|
||||
getTokenStartPosition(index, &line, &col);
|
||||
QTextCursor tc = _textCursor;
|
||||
tc.setPosition(tc.document()->findBlockByNumber(line - 1).position() + col + tk.length - 1);
|
||||
return tc;
|
||||
}
|
||||
|
||||
CppEditorSupport::CppEditorSupport(CppModelManager *modelManager)
|
||||
: QObject(modelManager),
|
||||
@@ -46,6 +251,13 @@ CppEditorSupport::CppEditorSupport(CppModelManager *modelManager)
|
||||
_updateDocumentTimer->setSingleShot(true);
|
||||
_updateDocumentTimer->setInterval(_updateDocumentInterval);
|
||||
connect(_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
|
||||
|
||||
_quickFixMark = new QuickFixMark(this);
|
||||
|
||||
_quickFixTimer = new QTimer(this);
|
||||
_quickFixTimer->setSingleShot(true);
|
||||
_quickFixTimer->setInterval(DEFAULT_QUICKFIX_INTERVAL);
|
||||
connect(_quickFixTimer, SIGNAL(timeout()), this, SLOT(checkDocumentNow()));
|
||||
}
|
||||
|
||||
CppEditorSupport::~CppEditorSupport()
|
||||
@@ -62,7 +274,11 @@ void CppEditorSupport::setTextEditor(TextEditor::ITextEditor *textEditor)
|
||||
return;
|
||||
|
||||
connect(_textEditor, SIGNAL(contentsChanged()), this, SIGNAL(contentsChanged()));
|
||||
connect(qobject_cast<TextEditor::BaseTextEditor *>(_textEditor->widget()), SIGNAL(cursorPositionChanged()),
|
||||
this, SLOT(checkDocument()));
|
||||
|
||||
connect(this, SIGNAL(contentsChanged()), this, SLOT(updateDocument()));
|
||||
|
||||
updateDocument();
|
||||
}
|
||||
|
||||
@@ -107,5 +323,35 @@ void CppEditorSupport::updateDocumentNow()
|
||||
}
|
||||
}
|
||||
|
||||
void CppEditorSupport::checkDocument()
|
||||
{
|
||||
_quickFixTimer->start(DEFAULT_QUICKFIX_INTERVAL);
|
||||
}
|
||||
|
||||
void CppEditorSupport::checkDocumentNow()
|
||||
{
|
||||
_textEditor->markableInterface()->removeMark(_quickFixMark);
|
||||
_quickFixes.clear();
|
||||
|
||||
TextEditor::BaseTextEditor *ed =
|
||||
qobject_cast<TextEditor::BaseTextEditor *>(_textEditor->widget());
|
||||
|
||||
Snapshot snapshot = _modelManager->snapshot();
|
||||
const QByteArray plainText = contents();
|
||||
const QString fileName = _textEditor->file()->fileName();
|
||||
const QByteArray preprocessedCode = snapshot.preprocessedCode(plainText, fileName);
|
||||
|
||||
if (Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, fileName)) {
|
||||
CheckDocument checkDocument(doc, snapshot);
|
||||
QList<QuickFixOperationPtr> quickFixes = checkDocument(ed->textCursor());
|
||||
if (! quickFixes.isEmpty()) {
|
||||
int line, col;
|
||||
ed->convertPosition(ed->position(), &line, &col);
|
||||
|
||||
_textEditor->markableInterface()->addMark(_quickFixMark, line);
|
||||
_quickFixes = quickFixes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,13 +33,21 @@
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QFuture>
|
||||
#include <QSharedPointer>
|
||||
#include <QTextCursor.h>
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CPlusPlus {
|
||||
class AST;
|
||||
}
|
||||
|
||||
namespace TextEditor {
|
||||
class ITextEditor;
|
||||
class ITextMark;
|
||||
} // end of namespace TextEditor
|
||||
|
||||
namespace CppTools {
|
||||
@@ -47,6 +55,46 @@ namespace Internal {
|
||||
|
||||
class CppModelManager;
|
||||
|
||||
class QuickFixOperation;
|
||||
typedef QSharedPointer<QuickFixOperation> QuickFixOperationPtr;
|
||||
|
||||
class QuickFixOperation
|
||||
{
|
||||
Q_DISABLE_COPY(QuickFixOperation)
|
||||
|
||||
public:
|
||||
QuickFixOperation(CPlusPlus::Document::Ptr doc,
|
||||
const CPlusPlus::Snapshot &snapshot);
|
||||
|
||||
virtual ~QuickFixOperation();
|
||||
|
||||
virtual QString description() const = 0;
|
||||
virtual void apply(QTextCursor cursor) = 0;
|
||||
|
||||
CPlusPlus::Document::Ptr document() const { return _doc; }
|
||||
CPlusPlus::Snapshot snapshot() const { return _snapshot; }
|
||||
|
||||
QTextCursor textCursor() const;
|
||||
void setTextCursor(const QTextCursor &tc);
|
||||
|
||||
protected:
|
||||
const CPlusPlus::Token &tokenAt(unsigned index) const;
|
||||
void getTokenStartPosition(unsigned index, unsigned *line,
|
||||
unsigned *column) const;
|
||||
void getTokenEndPosition(unsigned index, unsigned *line,
|
||||
unsigned *column) const;
|
||||
|
||||
QTextCursor cursor(unsigned index) const;
|
||||
QTextCursor moveAtStartOfToken(unsigned index) const;
|
||||
QTextCursor moveAtEndOfToken(unsigned index) const;
|
||||
|
||||
private:
|
||||
CPlusPlus::AST *_node;
|
||||
CPlusPlus::Document::Ptr _doc;
|
||||
CPlusPlus::Snapshot _snapshot;
|
||||
QTextCursor _textCursor;
|
||||
};
|
||||
|
||||
class CppEditorSupport: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -55,6 +103,9 @@ public:
|
||||
CppEditorSupport(CppModelManager *modelManager);
|
||||
virtual ~CppEditorSupport();
|
||||
|
||||
QList<QuickFixOperationPtr> quickFixes() const
|
||||
{ return _quickFixes; }
|
||||
|
||||
TextEditor::ITextEditor *textEditor() const;
|
||||
void setTextEditor(TextEditor::ITextEditor *textEditor);
|
||||
|
||||
@@ -70,6 +121,9 @@ private Q_SLOTS:
|
||||
void updateDocument();
|
||||
void updateDocumentNow();
|
||||
|
||||
void checkDocument();
|
||||
void checkDocumentNow();
|
||||
|
||||
private:
|
||||
enum { UPDATE_DOCUMENT_DEFAULT_INTERVAL = 150 };
|
||||
|
||||
@@ -79,6 +133,10 @@ private:
|
||||
int _updateDocumentInterval;
|
||||
QFuture<void> _documentParser;
|
||||
QByteArray _cachedContents;
|
||||
|
||||
QTimer *_quickFixTimer;
|
||||
TextEditor::ITextMark *_quickFixMark;
|
||||
QList<QuickFixOperationPtr> _quickFixes;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -151,8 +151,12 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
|
||||
// Objects
|
||||
m_modelManager = new CppModelManager(this);
|
||||
addAutoReleasedObject(m_modelManager);
|
||||
|
||||
m_completion = new CppCodeCompletion(m_modelManager);
|
||||
addAutoReleasedObject(m_completion);
|
||||
|
||||
addAutoReleasedObject(new CppQuickFixCollector(m_modelManager));
|
||||
|
||||
CppQuickOpenFilter *quickOpenFilter = new CppQuickOpenFilter(m_modelManager,
|
||||
core->editorManager());
|
||||
addAutoReleasedObject(quickOpenFilter);
|
||||
|
||||
@@ -96,7 +96,6 @@ void CompletionSupport::autoComplete(ITextEditable *editor, bool forced)
|
||||
|
||||
void CompletionSupport::quickFix(ITextEditable *editor)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
autoComplete_helper(editor,
|
||||
/*forced = */ true,
|
||||
/*quickFix = */ true);
|
||||
|
||||
@@ -121,6 +121,9 @@ public:
|
||||
IQuickFixCollector(QObject *parent = 0) : ICompletionCollector(parent) {}
|
||||
virtual ~IQuickFixCollector() {}
|
||||
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *)
|
||||
{ return false; }
|
||||
|
||||
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &)
|
||||
{ return false; }
|
||||
};
|
||||
|
||||
@@ -137,6 +137,7 @@ bool TextEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
|
||||
// Make sure the shortcut still works when the quick fix widget is active
|
||||
quickFixShortcut->setContext(Qt::ApplicationShortcut);
|
||||
Core::Command *quickFixCommand = am->registerShortcut(quickFixShortcut, Constants::QUICKFIX_THIS, context);
|
||||
quickFixCommand->setDefaultKeySequence(QKeySequence(tr("Alt+Return")));
|
||||
connect(quickFixShortcut, SIGNAL(activated()), this, SLOT(invokeQuickFix()));
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user