Introduced the quick fix engine

This commit is contained in:
Roberto Raggi
2009-11-13 16:14:26 +01:00
parent e35a754dc9
commit c13b8697d2
10 changed files with 258 additions and 443 deletions

View File

@@ -44,273 +44,6 @@
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 RewriteConditional: public QuickFixOperation
{
QString _source;
BinaryExpressionAST *_binaryExpression;
public:
RewriteConditional(const QString &source, BinaryExpressionAST *node,
Document::Ptr doc, const Snapshot &snapshot)
: QuickFixOperation(doc, snapshot),
_source(source),
_binaryExpression(node)
{ }
virtual QString description() const
{ return QString::fromUtf8("Rewrite conditional (%1)").arg(_source.simplified()); }
virtual void apply(QTextCursor tc)
{
setTextCursor(tc);
tc.beginEditBlock();
UnaryExpressionAST *left_unary_expr = _binaryExpression->left_expression->asUnaryExpression();
UnaryExpressionAST *right_unary_expr = _binaryExpression->right_expression->asUnaryExpression();
QTextCursor left_not_op = cursor(left_unary_expr->unary_op_token);
QTextCursor right_not_op = cursor(right_unary_expr->unary_op_token);
QTextCursor log_and_op = cursor(_binaryExpression->binary_op_token);
QTextCursor begin_of_expr = moveAtStartOfToken(_binaryExpression->firstToken());
QTextCursor end_of_expr = moveAtEndOfToken(_binaryExpression->lastToken() - 1);
left_not_op.removeSelectedText();
right_not_op.removeSelectedText();
log_and_op.insertText(QLatin1String("||"));
begin_of_expr.insertText(QLatin1String("!("));
end_of_expr.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),
_line(0), _column(0)
{ }
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;
}
QTextCursor 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 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.f.length - 1);
return tc;
}
virtual bool visit(BinaryExpressionAST *ast)
{
if (ast->left_expression && ast->right_expression && tokenKind(ast->binary_op_token) == T_AMPER_AMPER &&
checkPosition(ast)) {
UnaryExpressionAST *left_unary_expr = ast->left_expression->asUnaryExpression();
UnaryExpressionAST *right_unary_expr = ast->right_expression->asUnaryExpression();
if (left_unary_expr && left_unary_expr->expression && tokenKind(left_unary_expr->unary_op_token) == T_NOT &&
right_unary_expr && right_unary_expr->expression && tokenKind(right_unary_expr->unary_op_token) == T_NOT) {
// replace !a && !b with !(a || b)
QTextCursor beg = moveAtStartOfToken(ast->firstToken());
QTextCursor end = moveAtEndOfToken(ast->lastToken() - 1);
beg.setPosition(end.position(), QTextCursor::KeepAnchor);
QString source = beg.selectedText();
QuickFixOperationPtr op(new RewriteConditional(source, ast, _doc, _snapshot));
_quickFixes.append(op);
return true;
}
}
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.f.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.f.length - 1);
return tc;
}
CppEditorSupport::CppEditorSupport(CppModelManager *modelManager)
: QObject(modelManager),
_modelManager(modelManager),
@@ -320,14 +53,6 @@ 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);
#ifdef QTCREATOR_WITH_QUICKFIX
connect(_quickFixTimer, SIGNAL(timeout()), this, SLOT(checkDocumentNow()));
#endif
}
CppEditorSupport::~CppEditorSupport()
@@ -344,12 +69,6 @@ void CppEditorSupport::setTextEditor(TextEditor::ITextEditor *textEditor)
return;
connect(_textEditor, SIGNAL(contentsChanged()), this, SIGNAL(contentsChanged()));
#ifdef QTCREATOR_WITH_QUICKFIX
connect(qobject_cast<TextEditor::BaseTextEditor *>(_textEditor->widget()), SIGNAL(cursorPositionChanged()),
this, SLOT(checkDocument()));
#endif
connect(this, SIGNAL(contentsChanged()), this, SLOT(updateDocument()));
updateDocument();
@@ -396,37 +115,3 @@ 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 QString plainText = contents();
const QString fileName = _textEditor->file()->fileName();
const QByteArray preprocessedCode = snapshot.preprocessedCode(plainText, fileName);
if (Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, fileName)) {
doc->parse();
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;
}
}
}