Files
qt-creator/src/plugins/cppeditor/cppeditor.cpp

2209 lines
69 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
2011-01-11 16:28:15 +01:00
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
2011-04-13 08:42:33 +02:00
** Contact: Nokia Corporation (info@qt.nokia.com)
2008-12-02 12:01:29 +01:00
**
**
** GNU Lesser General Public License Usage
**
2011-04-13 08:42:33 +02:00
** 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.
**
2010-12-17 16:01:08 +01:00
** In addition, as a special exception, Nokia gives you certain additional
2011-04-13 08:42:33 +02:00
** rights. These rights are described in the Nokia Qt LGPL Exception
2010-12-17 16:01:08 +01:00
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
2011-04-13 08:42:33 +02:00
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
2010-12-17 16:01:08 +01:00
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 15:08:31 +01:00
2008-12-02 12:01:29 +01:00
#include "cppeditor.h"
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
2010-07-13 15:25:05 +02:00
#include "cppchecksymbols.h"
#include "cppquickfix.h"
2010-07-15 16:03:48 +02:00
#include "cpplocalsymbols.h"
#include "cppquickfixcollector.h"
#include "cppqtstyleindenter.h"
#include "cppautocompleter.h"
2009-08-07 13:02:36 +02:00
2008-12-02 12:01:29 +01:00
#include <AST.h>
#include <Control.h>
2008-12-02 12:01:29 +01:00
#include <Token.h>
#include <Scope.h>
#include <Symbols.h>
#include <Names.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <ASTVisitor.h>
#include <SymbolVisitor.h>
#include <TranslationUnit.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/ModelManagerInterface.h>
2008-12-02 12:01:29 +01:00
#include <cplusplus/ExpressionUnderCursor.h>
2009-08-07 13:02:36 +02:00
#include <cplusplus/TypeOfExpression.h>
2008-12-02 12:01:29 +01:00
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/FastPreprocessor.h>
2010-07-13 15:25:05 +02:00
#include <cpptools/cpptoolsplugin.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cppcodeformatter.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/uniqueidmanager.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
2009-10-13 17:17:08 +02:00
#include <coreplugin/mimedatabase.h>
#include <utils/uncommentselection.h>
#include <extensionsystem/pluginmanager.h>
2008-12-02 12:01:29 +01:00
#include <projectexplorer/projectexplorerconstants.h>
#include <texteditor/basetextdocument.h>
#include <texteditor/basetextdocumentlayout.h>
2008-12-02 12:01:29 +01:00
#include <texteditor/fontsettings.h>
#include <texteditor/tabsettings.h>
2008-12-02 12:01:29 +01:00
#include <texteditor/texteditorconstants.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QStack>
#include <QtCore/QSettings>
#include <QtCore/QSignalMapper>
2008-12-02 12:01:29 +01:00
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QHeaderView>
2008-12-02 12:01:29 +01:00
#include <QtGui/QLayout>
#include <QtGui/QMenu>
#include <QtGui/QShortcut>
#include <QtGui/QTextEdit>
#include <QtGui/QComboBox>
2009-07-16 17:34:04 +02:00
#include <QtGui/QToolBar>
2008-12-02 12:01:29 +01:00
#include <QtGui/QTreeView>
#include <QtGui/QSortFilterProxyModel>
2008-12-02 12:01:29 +01:00
#include <sstream>
2009-06-24 16:40:30 +02:00
enum {
2010-07-19 14:13:06 +02:00
UPDATE_OUTLINE_INTERVAL = 500,
UPDATE_USES_INTERVAL = 500
2009-06-24 16:40:30 +02:00
};
using namespace CPlusPlus;
using namespace CppEditor::Internal;
namespace {
bool semanticHighlighterDisabled = qstrcmp(qVersion(), "4.7.0") == 0;
}
static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
const QList<CPlusPlus::Document::DiagnosticMessage> &msgs,
const QTextCharFormat &format)
{
QList<QTextEdit::ExtraSelection> selections;
foreach (const Document::DiagnosticMessage &m, msgs) {
const int pos = document->findBlockByNumber(m.line() - 1).position() + m.column() - 1;
if (pos < 0)
continue;
QTextCursor cursor(document);
cursor.setPosition(pos);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
QTextEdit::ExtraSelection sel;
sel.cursor = cursor;
sel.format = format;
sel.format.setToolTip(m.text());
selections.append(sel);
}
return selections;
}
2008-12-02 12:01:29 +01:00
namespace {
class OverviewTreeView : public QTreeView
{
public:
OverviewTreeView(QWidget *parent = 0)
: QTreeView(parent)
{
// TODO: Disable the root for all items (with a custom delegate?)
setRootIsDecorated(false);
}
void sync()
{
expandAll();
setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width()));
}
};
class OverviewProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
QSortFilterProxyModel(parent),
m_sourceModel(sourceModel)
{
setSourceModel(m_sourceModel);
}
bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
{
// ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
if (symbol && symbol->isGenerated())
return false;
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
private:
CPlusPlus::OverviewModel *m_sourceModel;
};
2009-06-24 16:40:30 +02:00
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
unsigned _line;
unsigned _column;
DeclarationAST *_functionDefinition;
2009-06-24 16:40:30 +02:00
public:
FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit),
_line(0), _column(0)
2009-06-24 16:40:30 +02:00
{ }
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
2009-06-24 16:40:30 +02:00
{
_functionDefinition = 0;
_line = line;
_column = column;
2009-06-24 16:40:30 +02:00
accept(ast);
return _functionDefinition;
}
protected:
virtual bool preVisit(AST *ast)
{
if (_functionDefinition)
return false;
else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
return checkDeclaration(def);
}
else if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
if (method->function_body)
return checkDeclaration(method);
2009-06-24 16:40:30 +02:00
}
return true;
}
private:
bool checkDeclaration(DeclarationAST *ast)
{
unsigned startLine, startColumn;
unsigned endLine, endColumn;
getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
if (_line < endLine || (_line == endLine && _column < endColumn)) {
_functionDefinition = ast;
return false;
}
}
return true;
}
2009-06-24 16:40:30 +02:00
};
class FindFunctionDefinitions: protected SymbolVisitor
{
2009-12-01 12:46:15 +01:00
const Name *_declarationName;
QList<Function *> *_functions;
public:
FindFunctionDefinitions()
: _declarationName(0),
_functions(0)
{ }
2009-12-01 12:46:15 +01:00
void operator()(const Name *declarationName, Scope *globals,
QList<Function *> *functions)
{
_declarationName = declarationName;
_functions = functions;
2010-08-11 12:26:02 +02:00
for (unsigned i = 0; i < globals->memberCount(); ++i) {
accept(globals->memberAt(i));
}
}
protected:
using SymbolVisitor::visit;
virtual bool visit(Function *function)
{
2009-12-01 12:46:15 +01:00
const Name *name = function->name();
if (const QualifiedNameId *q = name->asQualifiedNameId())
name = q->name();
if (_declarationName->isEqualTo(name))
_functions->append(function);
return false;
}
};
struct CanonicalSymbol
{
CPPEditorWidget *editor;
TypeOfExpression typeOfExpression;
SemanticInfo info;
CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
2010-06-29 17:47:59 +02:00
: editor(editor), info(info)
{
typeOfExpression.init(info.doc, info.snapshot);
}
const LookupContext &context() const
{
return typeOfExpression.context();
}
static inline bool isIdentifierChar(const QChar &ch)
{
return ch.isLetterOrNumber() || ch == QLatin1Char('_');
}
Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
{
return getScopeAndExpression(editor, info, cursor, code);
}
static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
const QTextCursor &cursor,
QString *code)
{
if (! info.doc)
return 0;
QTextCursor tc = cursor;
int line, col;
editor->convertPosition(tc.position(), &line, &col);
++col; // 1-based line and 1-based column
QTextDocument *document = editor->document();
int pos = tc.position();
if (! isIdentifierChar(document->characterAt(pos)))
if (! (pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
return 0;
while (isIdentifierChar(document->characterAt(pos)))
++pos;
tc.setPosition(pos);
ExpressionUnderCursor expressionUnderCursor;
*code = expressionUnderCursor(tc);
return info.doc->scopeAt(line, col);
}
Symbol *operator()(const QTextCursor &cursor)
{
QString code;
if (Scope *scope = getScopeAndExpression(cursor, &code))
return operator()(scope, code);
return 0;
}
Symbol *operator()(Scope *scope, const QString &code)
{
return canonicalSymbol(scope, code, typeOfExpression);
}
static Symbol *canonicalSymbol(Scope *scope, const QString &code, TypeOfExpression &typeOfExpression)
{
const QList<LookupItem> results = typeOfExpression(code, scope, TypeOfExpression::Preprocess);
2010-08-05 14:14:16 +02:00
for (int i = results.size() - 1; i != -1; --i) {
const LookupItem &r = results.at(i);
Symbol *decl = r.declaration();
2010-08-05 14:14:16 +02:00
if (! (decl && decl->enclosingScope()))
2010-08-05 14:14:16 +02:00
break;
if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
const Identifier *declId = decl->identifier();
const Identifier *classId = classScope->identifier();
if (classId && classId->isEqualTo(declId))
continue; // skip it, it's a ctor or a dtor.
else if (Function *funTy = r.declaration()->type()->asFunctionType()) {
if (funTy->isVirtual())
return r.declaration();
}
}
2010-08-05 14:14:16 +02:00
}
for (int i = 0; i < results.size(); ++i) {
const LookupItem &r = results.at(i);
if (r.declaration())
return r.declaration();
}
return 0;
}
};
int numberOfClosedEditors = 0;
2008-12-02 12:01:29 +01:00
} // end of anonymous namespace
CPPEditor::CPPEditor(CPPEditorWidget *editor)
: BaseTextEditor(editor)
2008-12-02 12:01:29 +01:00
{
m_context.add(CppEditor::Constants::C_CPPEDITOR);
m_context.add(ProjectExplorer::Constants::LANG_CXX);
m_context.add(TextEditor::Constants::C_TEXTEDITOR);
2008-12-02 12:01:29 +01:00
}
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
: TextEditor::BaseTextEditorWidget(parent)
, m_currentRenameSelection(NoCurrentRenameSelection)
, m_inRename(false)
, m_inRenameChanged(false)
, m_firstRenameChange(false)
, m_objcEnabled(false)
2008-12-02 12:01:29 +01:00
{
m_initialized = false;
qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
m_semanticHighlighter = new SemanticHighlighter(this);
m_semanticHighlighter->start();
2008-12-02 12:01:29 +01:00
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setCodeFoldingSupported(true);
setIndenter(new CppQtStyleIndenter);
setAutoCompleter(new CppAutoCompleter);
baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
2008-12-02 12:01:29 +01:00
m_modelManager = CppModelManagerInterface::instance();
2008-12-02 12:01:29 +01:00
if (m_modelManager) {
connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
}
m_highlightRevision = 0;
m_nextHighlightBlockNumber = 0;
connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
m_referencesRevision = 0;
m_referencesCursorPosition = 0;
connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
2008-12-02 12:01:29 +01:00
}
CPPEditorWidget::~CPPEditorWidget()
2008-12-02 12:01:29 +01:00
{
m_semanticHighlighter->abort();
m_semanticHighlighter->wait();
++numberOfClosedEditors;
if (numberOfClosedEditors == 5) {
m_modelManager->GC();
numberOfClosedEditors = 0;
}
2008-12-02 12:01:29 +01:00
}
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
2008-12-02 12:01:29 +01:00
{
CPPEditor *editable = new CPPEditor(this);
2008-12-02 12:01:29 +01:00
createToolBar(editable);
return editable;
}
void CPPEditorWidget::createToolBar(CPPEditor *editor)
2008-12-02 12:01:29 +01:00
{
m_outlineCombo = new QComboBox;
m_outlineCombo->setMinimumContentsLength(22);
// Make the combo box prefer to expand
QSizePolicy policy = m_outlineCombo->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Expanding);
m_outlineCombo->setSizePolicy(policy);
QTreeView *outlineView = new OverviewTreeView;
outlineView->header()->hide();
outlineView->setItemsExpandable(false);
m_outlineCombo->setView(outlineView);
m_outlineCombo->setMaxVisibleItems(40);
2008-12-02 12:01:29 +01:00
m_outlineModel = new OverviewModel(this);
m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
if (CppPlugin::instance()->sortedOutline())
m_proxyModel->sort(0, Qt::AscendingOrder);
else
m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_outlineCombo->setModel(m_proxyModel);
m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
m_sortAction->setCheckable(true);
m_sortAction->setChecked(sortedOutline());
connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
m_outlineCombo->addAction(m_sortAction);
2008-12-02 12:01:29 +01:00
m_updateOutlineTimer = new QTimer(this);
m_updateOutlineTimer->setSingleShot(true);
m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
m_updateOutlineIndexTimer = new QTimer(this);
m_updateOutlineIndexTimer->setSingleShot(true);
m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
2009-06-24 16:40:30 +02:00
m_updateUsesTimer = new QTimer(this);
m_updateUsesTimer->setSingleShot(true);
m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
2008-12-02 12:01:29 +01:00
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
// set up the semantic highlighter
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
connect(m_semanticHighlighter, SIGNAL(changed(CppEditor::Internal::SemanticInfo)),
this, SLOT(updateSemanticInfo(CppEditor::Internal::SemanticInfo)));
editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
2008-12-02 12:01:29 +01:00
}
void CPPEditorWidget::paste()
{
if (m_currentRenameSelection == NoCurrentRenameSelection) {
BaseTextEditorWidget::paste();
return;
}
startRename();
BaseTextEditorWidget::paste();
finishRename();
}
void CPPEditorWidget::cut()
{
if (m_currentRenameSelection == NoCurrentRenameSelection) {
BaseTextEditorWidget::cut();
return;
}
startRename();
BaseTextEditorWidget::cut();
finishRename();
}
CppModelManagerInterface *CPPEditorWidget::modelManager() const
{
return m_modelManager;
}
void CPPEditorWidget::setMimeType(const QString &mt)
{
BaseTextEditorWidget::setMimeType(mt);
setObjCEnabled(mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}
void CPPEditorWidget::setObjCEnabled(bool onoff)
{
m_objcEnabled = onoff;
}
bool CPPEditorWidget::isObjCEnabled() const
{ return m_objcEnabled; }
void CPPEditorWidget::startRename()
{
m_inRenameChanged = false;
}
void CPPEditorWidget::finishRename()
{
if (!m_inRenameChanged)
return;
m_inRename = true;
QTextCursor cursor = textCursor();
cursor.joinPreviousEditBlock();
cursor.setPosition(m_currentRenameSelectionEnd.position());
cursor.setPosition(m_currentRenameSelectionBegin.position(), QTextCursor::KeepAnchor);
m_renameSelections[m_currentRenameSelection].cursor = cursor;
QString text = cursor.selectedText();
for (int i = 0; i < m_renameSelections.size(); ++i) {
if (i == m_currentRenameSelection)
continue;
QTextEdit::ExtraSelection &s = m_renameSelections[i];
int pos = s.cursor.selectionStart();
s.cursor.removeSelectedText();
s.cursor.insertText(text);
s.cursor.setPosition(pos, QTextCursor::KeepAnchor);
}
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
cursor.endEditBlock();
m_inRename = false;
}
void CPPEditorWidget::abortRename()
{
if (m_currentRenameSelection <= NoCurrentRenameSelection)
return;
m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
m_currentRenameSelection = NoCurrentRenameSelection;
m_currentRenameSelectionBegin = QTextCursor();
m_currentRenameSelectionEnd = QTextCursor();
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
}
void CPPEditorWidget::rehighlight(bool force)
2010-08-03 11:53:01 +02:00
{
const SemanticHighlighter::Source source = currentSource(force);
m_semanticHighlighter->rehighlight(source);
}
void CPPEditorWidget::onDocumentUpdated(Document::Ptr doc)
2008-12-02 12:01:29 +01:00
{
if (doc->fileName() != file()->fileName())
return;
if (doc->editorRevision() != editorRevision())
return;
if (! m_initialized) {
m_initialized = true;
2010-08-03 11:53:01 +02:00
rehighlight(/* force = */ true);
}
m_updateOutlineTimer->start();
2008-12-02 12:01:29 +01:00
}
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
{
if (! doc)
return 0;
int line, col;
convertPosition(cursor.position(), &line, &col);
if (const Macro *macro = doc->findMacroDefinitionAt(line))
return macro;
if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position()))
return &use->macro();
return 0;
}
2009-10-05 15:17:25 +02:00
void CPPEditorWidget::findUsages()
{
SemanticInfo info = m_lastSemanticInfo;
info.snapshot = CppModelManagerInterface::instance()->snapshot();
info.snapshot.insert(info.doc);
CanonicalSymbol cs(this, info);
Symbol *canonicalSymbol = cs(textCursor());
if (canonicalSymbol) {
m_modelManager->findUsages(canonicalSymbol, cs.context());
} else if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
m_modelManager->findMacroUsages(*macro);
}
2009-10-05 15:17:25 +02:00
}
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
{
SemanticInfo info = m_lastSemanticInfo;
info.snapshot = CppModelManagerInterface::instance()->snapshot();
info.snapshot.insert(info.doc);
CanonicalSymbol cs(this, info);
if (Symbol *canonicalSymbol = cs(textCursor()))
if (canonicalSymbol->identifier() != 0)
m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
}
void CPPEditorWidget::renameUsages()
{
renameUsagesNow();
}
void CPPEditorWidget::markSymbolsNow()
{
if (m_references.isCanceled())
return;
else if (m_referencesCursorPosition != position())
return;
else if (m_referencesRevision != editorRevision())
return;
const SemanticInfo info = m_lastSemanticInfo;
TranslationUnit *unit = info.doc->translationUnit();
const QList<int> result = m_references.result();
QList<QTextEdit::ExtraSelection> selections;
foreach (int index, result) {
unsigned line, column;
unit->getTokenPosition(index, &line, &column);
if (column)
--column; // adjust the column position.
const int len = unit->tokenAt(index).f.length;
QTextCursor cursor(document()->findBlockByNumber(line - 1));
cursor.setPosition(cursor.position() + column);
cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
QTextEdit::ExtraSelection sel;
sel.format = m_occurrencesFormat;
sel.cursor = cursor;
selections.append(sel);
2009-08-07 13:02:36 +02:00
}
setExtraSelections(CodeSemanticsSelection, selections);
2009-08-07 13:02:36 +02:00
}
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc, Snapshot snapshot)
{
TypeOfExpression typeOfExpression;
snapshot.insert(doc);
typeOfExpression.init(doc, snapshot);
if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression)) {
return CppModelManagerInterface::instance()->references(canonicalSymbol, typeOfExpression.context());
}
return QList<int>();
}
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
{
abortRename();
if (! info.doc)
return;
CanonicalSymbol cs(this, info);
QString expression;
if (Scope *scope = cs.getScopeAndExpression(this, info, tc, &expression)) {
m_references.cancel();
m_referencesRevision = info.revision;
m_referencesCursorPosition = position();
m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc, info.snapshot);
m_referencesWatcher.setFuture(m_references);
} else {
const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);
if (! selections.isEmpty())
setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
}
}
void CPPEditorWidget::renameSymbolUnderCursor()
{
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
abortRename();
QTextCursor c = textCursor();
for (int i = 0; i < m_renameSelections.size(); ++i) {
QTextEdit::ExtraSelection s = m_renameSelections.at(i);
if (c.position() >= s.cursor.anchor()
&& c.position() <= s.cursor.position()) {
m_currentRenameSelection = i;
m_firstRenameChange = true;
m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
m_renameSelections[i].cursor.selectionStart());
m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
m_renameSelections[i].cursor.selectionEnd());
m_renameSelections[i].format = m_occurrenceRenameFormat;
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
break;
}
}
if (m_renameSelections.isEmpty())
2009-10-05 15:17:25 +02:00
renameUsages();
}
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
{
Q_UNUSED(position)
if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
return;
if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
// we are inserting at the beginning of the rename selection => expand
m_currentRenameSelectionBegin.setPosition(position);
m_renameSelections[m_currentRenameSelection].cursor.setPosition(position, QTextCursor::KeepAnchor);
}
// the condition looks odd, but keep in mind that the begin and end cursors do move automatically
m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
&& position + charsAdded <= m_currentRenameSelectionEnd.position());
if (!m_inRenameChanged)
abortRename();
if (charsRemoved > 0)
updateUses();
}
void CPPEditorWidget::updateFileName()
2008-12-02 12:01:29 +01:00
{ }
void CPPEditorWidget::jumpToOutlineElement(int)
2008-12-02 12:01:29 +01:00
{
QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
Symbol *symbol = m_outlineModel->symbolFromIndex(index);
2008-12-02 12:01:29 +01:00
if (! symbol)
return;
openCppEditorAt(linkToSymbol(symbol));
2008-12-02 12:01:29 +01:00
}
void CPPEditorWidget::setSortedOutline(bool sort)
{
if (sort != sortedOutline()) {
if (sort)
m_proxyModel->sort(0, Qt::AscendingOrder);
else
m_proxyModel->sort(-1, Qt::AscendingOrder);
bool block = m_sortAction->blockSignals(true);
m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
m_sortAction->blockSignals(block);
updateOutlineIndexNow();
}
}
bool CPPEditorWidget::sortedOutline() const
{
return (m_proxyModel->sortColumn() == 0);
}
void CPPEditorWidget::updateOutlineNow()
{
const Snapshot snapshot = m_modelManager->snapshot();
Document::Ptr document = snapshot.document(file()->fileName());
if (!document)
return;
if (document->editorRevision() != editorRevision()) {
m_updateOutlineTimer->start();
return;
}
m_outlineModel->rebuild(document);
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
treeView->sync();
updateOutlineIndexNow();
}
void CPPEditorWidget::updateOutlineIndex()
2008-12-02 12:01:29 +01:00
{
m_updateOutlineIndexTimer->start();
2009-06-24 16:40:30 +02:00
}
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
const SemanticInfo &semanticInfo,
QList<QTextEdit::ExtraSelection> *selections)
2009-06-25 16:11:28 +02:00
{
2009-09-23 18:06:59 +02:00
bool isUnused = false;
2009-10-05 14:08:10 +02:00
if (uses.size() == 1)
2009-09-23 18:06:59 +02:00
isUnused = true;
2009-06-26 09:48:40 +02:00
foreach (const SemanticInfo::Use &use, uses) {
2009-07-09 17:32:39 +02:00
QTextEdit::ExtraSelection sel;
2009-06-25 16:11:28 +02:00
2009-09-23 18:06:59 +02:00
if (isUnused)
sel.format = m_occurrencesUnusedFormat;
2009-09-23 18:06:59 +02:00
else
sel.format = m_occurrencesFormat;
2009-06-26 09:48:40 +02:00
const int anchor = document()->findBlockByNumber(use.line - 1).position() + use.column - 1;
2009-07-09 17:32:39 +02:00
const int position = anchor + use.length;
2009-06-25 16:11:28 +02:00
sel.cursor = QTextCursor(document());
2009-07-09 17:32:39 +02:00
sel.cursor.setPosition(anchor);
sel.cursor.setPosition(position, QTextCursor::KeepAnchor);
2009-06-25 16:11:28 +02:00
if (isUnused) {
if (semanticInfo.hasQ && sel.cursor.selectedText() == QLatin1String("q"))
continue; // skip q
else if (semanticInfo.hasD && sel.cursor.selectedText() == QLatin1String("d"))
continue; // skip d
}
2009-07-09 17:32:39 +02:00
selections->append(sel);
2009-06-25 16:11:28 +02:00
}
}
void CPPEditorWidget::updateOutlineIndexNow()
2009-06-24 16:40:30 +02:00
{
if (!m_outlineModel->document())
return;
if (m_outlineModel->document()->editorRevision() != editorRevision()) {
m_updateOutlineIndexTimer->start();
return;
}
m_updateOutlineIndexTimer->stop();
m_outlineModelIndex = QModelIndex(); //invalidate
QModelIndex comboIndex = outlineModelIndex();
2008-12-02 12:01:29 +01:00
if (comboIndex.isValid()) {
bool blocked = m_outlineCombo->blockSignals(true);
// There is no direct way to select a non-root item
m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
m_outlineCombo->setRootModelIndex(QModelIndex());
updateOutlineToolTip();
m_outlineCombo->blockSignals(blocked);
2008-12-02 12:01:29 +01:00
}
}
void CPPEditorWidget::updateOutlineToolTip()
{
m_outlineCombo->setToolTip(m_outlineCombo->currentText());
}
void CPPEditorWidget::updateUses()
{
if (editorRevision() != m_highlightRevision)
m_highlighter.cancel();
m_updateUsesTimer->start();
}
void CPPEditorWidget::updateUsesNow()
{
if (m_currentRenameSelection != NoCurrentRenameSelection)
return;
semanticRehighlight();
2008-12-02 12:01:29 +01:00
}
void CPPEditorWidget::highlightSymbolUsages(int from, int to)
{
if (editorRevision() != m_highlightRevision)
return; // outdated
else if (m_highlighter.isCanceled())
return; // aborted
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
Q_ASSERT(highlighter);
QTextDocument *doc = document();
if (m_nextHighlightBlockNumber >= doc->blockCount())
return;
2010-07-13 15:25:05 +02:00
QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
2010-08-10 12:28:41 +02:00
if (chunks.isEmpty())
return;
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
while (b.isValid() && it.hasNext()) {
it.next();
const int blockNumber = it.key();
Q_ASSERT(blockNumber < doc->blockCount());
while (m_nextHighlightBlockNumber < blockNumber) {
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
b = b.next();
++m_nextHighlightBlockNumber;
}
QList<QTextLayout::FormatRange> formats;
foreach (const SemanticInfo::Use &use, it.value()) {
QTextLayout::FormatRange formatRange;
2010-07-15 16:03:48 +02:00
switch (use.kind) {
case SemanticInfo::Use::Type:
formatRange.format = m_typeFormat;
break;
case SemanticInfo::Use::Field:
formatRange.format = m_fieldFormat;
2010-07-15 16:03:48 +02:00
break;
case SemanticInfo::Use::Local:
formatRange.format = m_localFormat;
2010-07-15 16:03:48 +02:00
break;
case SemanticInfo::Use::Static:
formatRange.format = m_staticFormat;
break;
2010-08-03 17:34:51 +02:00
case SemanticInfo::Use::VirtualMethod:
formatRange.format = m_virtualMethodFormat;
2010-08-03 17:34:51 +02:00
break;
2010-07-15 16:03:48 +02:00
default:
continue;
}
formatRange.start = use.column - 1;
formatRange.length = use.length;
formats.append(formatRange);
}
highlighter->setExtraAdditionalFormats(b, formats);
b = b.next();
++m_nextHighlightBlockNumber;
}
}
void CPPEditorWidget::finishHighlightSymbolUsages()
{
if (editorRevision() != m_highlightRevision)
return; // outdated
else if (m_highlighter.isCanceled())
return; // aborted
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
Q_ASSERT(highlighter);
QTextDocument *doc = document();
if (m_nextHighlightBlockNumber >= doc->blockCount())
return;
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
while (b.isValid()) {
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
b = b.next();
++m_nextHighlightBlockNumber;
}
}
void CPPEditorWidget::switchDeclarationDefinition()
2008-12-02 12:01:29 +01:00
{
if (! m_modelManager)
2008-12-02 12:01:29 +01:00
return;
const Snapshot snapshot = m_modelManager->snapshot();
if (Document::Ptr thisDocument = snapshot.document(file()->fileName())) {
int line = 0, positionInBlock = 0;
convertPosition(position(), &line, &positionInBlock);
2008-12-02 12:01:29 +01:00
Symbol *lastVisibleSymbol = thisDocument->lastVisibleSymbolAt(line, positionInBlock + 1);
if (! lastVisibleSymbol)
return;
2008-12-02 12:01:29 +01:00
Function *function = lastVisibleSymbol->asFunction();
if (! function)
function = lastVisibleSymbol->enclosingFunction();
2008-12-02 12:01:29 +01:00
if (function) {
LookupContext context(thisDocument, snapshot);
2008-12-02 12:01:29 +01:00
Function *functionDefinition = function->asFunction();
ClassOrNamespace *binding = context.lookupType(functionDefinition);
const QList<LookupItem> declarations = context.lookup(functionDefinition->name(), functionDefinition->enclosingScope());
QList<Symbol *> best;
foreach (const LookupItem &r, declarations) {
if (Symbol *decl = r.declaration()) {
if (Function *funTy = decl->type()->asFunctionType()) {
if (funTy->isEqualTo(function) && decl != function && binding == r.binding())
best.prepend(decl);
else
best.append(decl);
}
}
}
if (! best.isEmpty())
openCppEditorAt(linkToSymbol(best.first()));
} else if (lastVisibleSymbol && lastVisibleSymbol->isDeclaration() && lastVisibleSymbol->type()->isFunctionType()) {
if (Symbol *def = snapshot.findMatchingDefinition(lastVisibleSymbol))
openCppEditorAt(linkToSymbol(def));
}
2008-12-02 12:01:29 +01:00
}
}
static inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbols)
{
QList<LookupItem> candidates = resolvedSymbols;
LookupItem result = candidates.first();
const FullySpecifiedType ty = result.type().simplified();
if (ty->isForwardClassDeclarationType()) {
while (! candidates.isEmpty()) {
LookupItem r = candidates.takeFirst();
if (! r.type()->isForwardClassDeclarationType()) {
result = r;
break;
}
}
}
if (ty->isObjCForwardClassDeclarationType()) {
while (! candidates.isEmpty()) {
LookupItem r = candidates.takeFirst();
if (! r.type()->isObjCForwardClassDeclarationType()) {
result = r;
break;
}
}
}
if (ty->isObjCForwardProtocolDeclarationType()) {
while (! candidates.isEmpty()) {
LookupItem r = candidates.takeFirst();
if (! r.type()->isObjCForwardProtocolDeclarationType()) {
result = r;
break;
}
}
}
return result;
}
namespace {
QList<Declaration *> findMatchingDeclaration(const LookupContext &context,
Function *functionType)
{
QList<Declaration *> result;
Scope *enclosingScope = functionType->enclosingScope();
while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
enclosingScope = enclosingScope->enclosingScope();
Q_ASSERT(enclosingScope != 0);
const Name *functionName = functionType->name();
if (! functionName)
return result; // anonymous function names are not valid c++
ClassOrNamespace *binding = 0;
const QualifiedNameId *qName = functionName->asQualifiedNameId();
if (qName) {
if (qName->base())
binding = context.lookupType(qName->base(), enclosingScope);
functionName = qName->name();
}
if (!binding) { // declaration for a global function
binding = context.lookupType(enclosingScope);
if (!binding)
return result;
}
const Identifier *funcId = functionName->identifier();
if (!funcId) // E.g. operator, which we might be able to handle in the future...
return result;
QList<Declaration *> good, better, best;
foreach (Symbol *s, binding->symbols()) {
Class *matchingClass = s->asClass();
if (!matchingClass)
continue;
for (Symbol *s = matchingClass->find(funcId); s; s = s->next()) {
if (! s->name())
continue;
else if (! funcId->isEqualTo(s->identifier()))
continue;
else if (! s->type()->isFunctionType())
continue;
else if (Declaration *decl = s->asDeclaration()) {
if (Function *declFunTy = decl->type()->asFunctionType()) {
if (functionType->isEqualTo(declFunTy))
best.prepend(decl);
else if (functionType->argumentCount() == declFunTy->argumentCount() && result.isEmpty())
better.prepend(decl);
else
good.append(decl);
}
}
}
}
result.append(best);
result.append(better);
result.append(good);
return result;
}
} // end of anonymous namespace
CPPEditorWidget::Link CPPEditorWidget::attemptFuncDeclDef(const QTextCursor &cursor, const Document::Ptr &doc, Snapshot snapshot) const
{
snapshot.insert(doc);
Link result;
QList<AST *> path = ASTPath(doc)(cursor);
if (path.size() < 5)
return result;
NameAST *name = path.last()->asName();
if (!name)
return result;
if (QualifiedNameAST *qName = path.at(path.size() - 2)->asQualifiedName()) {
// TODO: check which part of the qualified name we're on
if (qName->unqualified_name != name)
return result;
}
for (int i = path.size() - 1; i != -1; --i) {
AST *node = path.at(i);
if (node->asParameterDeclaration() != 0)
return result;
}
AST *declParent = 0;
DeclaratorAST *decl = 0;
for (int i = path.size() - 2; i > 0; --i) {
if ((decl = path.at(i)->asDeclarator()) != 0) {
declParent = path.at(i - 1);
break;
}
}
if (!decl || !declParent)
return result;
if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value)
return result;
FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator();
if (!funcDecl)
return result;
Symbol *target = 0;
if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
QList<Declaration *> candidates = findMatchingDeclaration(LookupContext(doc, snapshot),
funDef->symbol);
if (!candidates.isEmpty()) // TODO: improve disambiguation
target = candidates.first();
} else if (declParent->asSimpleDeclaration()) {
target = snapshot.findMatchingDefinition(funcDecl->symbol);
}
if (target) {
result = linkToSymbol(target);
unsigned startLine, startColumn, endLine, endColumn;
doc->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, &startColumn);
doc->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, &endColumn);
QTextDocument *textDocument = cursor.document();
result.begin = textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1;
result.end = textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1;
}
return result;
}
CPPEditorWidget::Link CPPEditorWidget::findMacroLink(const QByteArray &name) const
{
if (! name.isEmpty()) {
if (Document::Ptr doc = m_lastSemanticInfo.doc) {
const Snapshot snapshot = m_modelManager->snapshot();
QSet<QString> processed;
return findMacroLink(name, doc, snapshot, &processed);
}
}
return Link();
}
CPPEditorWidget::Link CPPEditorWidget::findMacroLink(const QByteArray &name,
Document::Ptr doc,
const Snapshot &snapshot,
QSet<QString> *processed) const
{
if (doc && ! name.startsWith('<') && ! processed->contains(doc->fileName())) {
processed->insert(doc->fileName());
foreach (const Macro &macro, doc->definedMacros()) {
if (macro.name() == name) {
Link link;
link.fileName = macro.fileName();
link.line = macro.line();
return link;
}
}
const QList<Document::Include> includes = doc->includes();
for (int index = includes.size() - 1; index != -1; --index) {
const Document::Include &i = includes.at(index);
Link link = findMacroLink(name, snapshot.document(i.fileName()), snapshot, processed);
if (! link.fileName.isEmpty())
return link;
}
}
return Link();
}
QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor) const
{
macroCursor->movePosition(QTextCursor::StartOfWord);
macroCursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
return macroCursor->selectedText();
}
CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor,
bool resolveTarget)
2008-12-02 12:01:29 +01:00
{
Link link;
2008-12-02 12:01:29 +01:00
if (!m_modelManager)
return link;
2008-12-02 12:01:29 +01:00
const Snapshot snapshot = m_modelManager->snapshot();
if (m_lastSemanticInfo.doc){
Link l = attemptFuncDeclDef(cursor, m_lastSemanticInfo.doc, snapshot);
if (l.isValid()) {
return l;
}
}
int lineNumber = 0, positionInBlock = 0;
convertPosition(cursor.position(), &lineNumber, &positionInBlock);
Document::Ptr doc = snapshot.document(file()->fileName());
2008-12-02 12:01:29 +01:00
if (!doc)
return link;
const unsigned line = lineNumber;
const unsigned column = positionInBlock + 1;
QTextCursor tc = cursor;
// Make sure we're not at the start of a word
{
const QChar c = characterAt(tc.position());
if (c.isLetter() || c == QLatin1Char('_'))
tc.movePosition(QTextCursor::Right);
}
int beginOfToken = 0;
int endOfToken = 0;
SimpleLexer tokenize;
tokenize.setQtMocRunEnabled(true);
const QString blockText = cursor.block().text();
2010-06-29 17:57:15 +02:00
const QList<Token> tokens = tokenize(blockText, BackwardsScanner::previousBlockState(cursor.block()));
bool recognizedQtMethod = false;
for (int i = 0; i < tokens.size(); ++i) {
2010-06-29 17:57:15 +02:00
const Token &tk = tokens.at(i);
if (((unsigned) positionInBlock) >= tk.begin() && ((unsigned) positionInBlock) <= tk.end()) {
if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
&& (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
// token[i] == T_IDENTIFIER
// token[i + 1] == T_LPAREN
// token[.....] == ....
// token[i + n] == T_RPAREN
if (i + 1 < tokens.size() && tokens.at(i + 1).is(T_LPAREN)) {
// skip matched parenthesis
int j = i - 1;
int depth = 0;
for (; j < tokens.size(); ++j) {
if (tokens.at(j).is(T_LPAREN))
++depth;
else if (tokens.at(j).is(T_RPAREN)) {
if (! --depth)
break;
}
}
if (j < tokens.size()) {
QTextBlock block = cursor.block();
beginOfToken = block.position() + tokens.at(i).begin();
endOfToken = block.position() + tokens.at(i).end();
tc.setPosition(block.position() + tokens.at(j).end());
recognizedQtMethod = true;
}
}
}
break;
}
}
if (! recognizedQtMethod) {
const QTextBlock block = tc.block();
int pos = cursor.positionInBlock();
QChar ch = document()->characterAt(cursor.position());
if (pos > 0 && ! (ch.isLetterOrNumber() || ch == QLatin1Char('_')))
--pos; // positionInBlock points to a delimiter character.
const Token tk = SimpleLexer::tokenAt(block.text(), pos, BackwardsScanner::previousBlockState(block), true);
beginOfToken = block.position() + tk.begin();
endOfToken = block.position() + tk.end();
// Handle include directives
if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
const unsigned lineno = cursor.blockNumber() + 1;
foreach (const Document::Include &incl, doc->includes()) {
if (incl.line() == lineno && incl.resolved()) {
link.fileName = incl.fileName();
link.begin = beginOfToken + 1;
link.end = endOfToken - 1;
return link;
}
}
}
if (tk.isNot(T_IDENTIFIER) && tk.kind() < T_FIRST_QT_KEYWORD && tk.kind() > T_LAST_KEYWORD)
return link;
tc.setPosition(endOfToken);
}
// Find the last symbol up to the cursor position
Scope *scope = doc->scopeAt(line, column);
if (!scope)
return link;
// Evaluate the type of the expression under the cursor
2010-06-29 17:47:59 +02:00
ExpressionUnderCursor expressionUnderCursor;
QString expression = expressionUnderCursor(tc);
for (int pos = tc.position();; ++pos) {
const QChar ch = characterAt(pos);
if (ch.isSpace())
continue;
else {
if (ch == QLatin1Char('(') && ! expression.isEmpty()) {
tc.setPosition(pos);
if (TextEditor::TextBlockUserData::findNextClosingParenthesis(&tc, true)) {
expression.append(tc.selectedText());
}
}
break;
}
}
2008-12-02 12:01:29 +01:00
TypeOfExpression typeOfExpression;
2010-05-05 12:06:38 +02:00
typeOfExpression.init(doc, snapshot);
const QList<LookupItem> resolvedSymbols = typeOfExpression.reference(expression, scope, TypeOfExpression::Preprocess);
2008-12-02 12:01:29 +01:00
if (!resolvedSymbols.isEmpty()) {
LookupItem result = skipForwardDeclarations(resolvedSymbols);
foreach (const LookupItem &r, resolvedSymbols) {
if (Symbol *d = r.declaration()) {
if (d->isDeclaration() || d->isFunction()) {
if (file()->fileName() == QString::fromUtf8(d->fileName(), d->fileNameLength())) {
if (unsigned(lineNumber) == d->line() && unsigned(positionInBlock) >= d->column()) { // ### TODO: check the end
result = r; // take the symbol under cursor.
break;
}
}
}
}
}
if (Symbol *symbol = result.declaration()) {
2008-12-02 12:01:29 +01:00
Symbol *def = 0;
if (resolveTarget) {
Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column);
def = findDefinition(symbol, snapshot);
2008-12-02 12:01:29 +01:00
if (def == lastVisibleSymbol)
def = 0; // jump to declaration then.
if (symbol->isForwardClassDeclaration()) {
def = snapshot.findMatchingClassDeclaration(symbol);
}
}
link = linkToSymbol(def ? def : symbol);
link.begin = beginOfToken;
link.end = endOfToken;
return link;
2008-12-02 12:01:29 +01:00
}
}
// Handle macro uses
QTextCursor macroCursor = cursor;
const QByteArray name = identifierUnderCursor(&macroCursor).toLatin1();
link = findMacroLink(name);
if (! link.fileName.isEmpty()) {
link.begin = macroCursor.selectionStart();
link.end = macroCursor.selectionEnd();
return link;
}
return Link();
}
void CPPEditorWidget::jumpToDefinition()
{
openLink(findLinkAt(textCursor()));
2008-12-02 12:01:29 +01:00
}
Symbol *CPPEditorWidget::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
2008-12-02 12:01:29 +01:00
{
if (symbol->isFunction())
return 0; // symbol is a function definition.
2008-12-02 12:01:29 +01:00
else if (! symbol->type()->isFunctionType())
return 0; // not a function declaration
return snapshot.findMatchingDefinition(symbol);
2008-12-02 12:01:29 +01:00
}
unsigned CPPEditorWidget::editorRevision() const
{
return document()->revision();
}
bool CPPEditorWidget::isOutdated() const
{
if (m_lastSemanticInfo.revision != editorRevision())
return true;
return false;
}
SemanticInfo CPPEditorWidget::semanticInfo() const
2009-07-10 12:09:26 +02:00
{
return m_lastSemanticInfo;
}
CPlusPlus::OverviewModel *CPPEditorWidget::outlineModel() const
{
return m_outlineModel;
}
QModelIndex CPPEditorWidget::outlineModelIndex()
{
if (!m_outlineModelIndex.isValid()) {
int line = 0, column = 0;
convertPosition(position(), &line, &column);
m_outlineModelIndex = indexForPosition(line, column);
emit outlineModelIndexChanged(m_outlineModelIndex);
}
return m_outlineModelIndex;
}
bool CPPEditorWidget::event(QEvent *e)
{
switch (e->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != NoCurrentRenameSelection) {
e->accept();
return true;
}
break;
default:
break;
}
return BaseTextEditorWidget::event(e);
}
void CPPEditorWidget::performQuickFix(int index)
{
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
op->perform();
}
void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
2008-12-02 12:01:29 +01:00
{
// ### enable
// updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
QMenu *menu = new QMenu;
2008-12-02 12:01:29 +01:00
Core::ActionManager *am = Core::ICore::instance()->actionManager();
Core::ActionContainer *mcontext = am->actionContainer(Constants::M_CONTEXT);
2008-12-02 12:01:29 +01:00
QMenu *contextMenu = mcontext->menu();
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()));
}
}
}
foreach (QAction *action, contextMenu->actions()) {
2008-12-02 12:01:29 +01:00
menu->addAction(action);
if (action->objectName() == Constants::M_REFACTORING_MENU_INSERTION_POINT)
menu->addMenu(quickFixMenu);
}
2008-12-02 12:01:29 +01:00
appendStandardContextMenuActions(menu);
2008-12-02 12:01:29 +01:00
menu->exec(e->globalPos());
quickFixCollector->cleanup();
m_quickFixes.clear();
2008-12-02 12:01:29 +01:00
delete menu;
}
void CPPEditorWidget::keyPressEvent(QKeyEvent *e)
{
if (m_currentRenameSelection == NoCurrentRenameSelection) {
TextEditor::BaseTextEditorWidget::keyPressEvent(e);
return;
}
QTextCursor cursor = textCursor();
const QTextCursor::MoveMode moveMode =
(e->modifiers() & Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor;
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
abortRename();
e->accept();
return;
case Qt::Key_Home: {
// Send home to start of name when within the name and not at the start
if (cursor.position() > m_currentRenameSelectionBegin.position()
&& cursor.position() <= m_currentRenameSelectionEnd.position()) {
cursor.setPosition(m_currentRenameSelectionBegin.position(), moveMode);
setTextCursor(cursor);
e->accept();
return;
}
break;
}
case Qt::Key_End: {
// Send end to end of name when within the name and not at the end
if (cursor.position() >= m_currentRenameSelectionBegin.position()
&& cursor.position() < m_currentRenameSelectionEnd.position()) {
cursor.setPosition(m_currentRenameSelectionEnd.position(), moveMode);
setTextCursor(cursor);
e->accept();
return;
}
break;
}
case Qt::Key_Backspace: {
if (cursor.position() == m_currentRenameSelectionBegin.position()
&& !cursor.hasSelection()) {
// Eat backspace at start of name when there is no selection
e->accept();
return;
}
break;
}
case Qt::Key_Delete: {
if (cursor.position() == m_currentRenameSelectionEnd.position()
&& !cursor.hasSelection()) {
// Eat delete at end of name when there is no selection
e->accept();
return;
}
break;
}
default: {
break;
}
} // switch
startRename();
bool wantEditBlock = (cursor.position() >= m_currentRenameSelectionBegin.position()
&& cursor.position() <= m_currentRenameSelectionEnd.position());
if (wantEditBlock) {
// possible change inside rename selection
if (m_firstRenameChange)
cursor.beginEditBlock();
else
cursor.joinPreviousEditBlock();
m_firstRenameChange = false;
}
TextEditor::BaseTextEditorWidget::keyPressEvent(e);
if (wantEditBlock)
2010-01-06 14:34:09 +01:00
cursor.endEditBlock();
finishRename();
}
Core::IEditor *CPPEditor::duplicate(QWidget *parent)
2008-12-02 12:01:29 +01:00
{
CPPEditorWidget *newEditor = new CPPEditorWidget(parent);
newEditor->duplicateFrom(editorWidget());
2008-12-02 12:01:29 +01:00
CppPlugin::instance()->initializeEditor(newEditor);
return newEditor->editor();
2008-12-02 12:01:29 +01:00
}
QString CPPEditor::id() const
2008-12-02 12:01:29 +01:00
{
return QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
2008-12-02 12:01:29 +01:00
}
bool CPPEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
2009-10-13 17:17:08 +02:00
{
bool b = TextEditor::BaseTextEditor::open(errorString, fileName, realFileName);
editorWidget()->setMimeType(Core::ICore::instance()->mimeDatabase()->findByFile(QFileInfo(fileName)).type());
2009-10-13 17:17:08 +02:00
return b;
}
void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
2008-12-02 12:01:29 +01:00
{
TextEditor::BaseTextEditorWidget::setFontSettings(fs);
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
2008-12-02 12:01:29 +01:00
if (!highlighter)
return;
const QVector<QTextCharFormat> formats = fs.toTextCharFormats(highlighterFormatCategories());
2008-12-02 12:01:29 +01:00
highlighter->setFormats(formats.constBegin(), formats.constEnd());
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
2009-09-23 18:06:59 +02:00
m_occurrencesUnusedFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_UNUSED));
m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
m_occurrencesUnusedFormat.clearForeground();
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
2010-05-25 14:53:21 +02:00
m_typeFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
m_localFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
m_fieldFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD));
m_staticFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC));
m_virtualMethodFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD));
m_keywordFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_KEYWORD));
// only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
m_occurrencesFormat.clearForeground();
m_occurrenceRenameFormat.clearForeground();
// Clear all additional formats since they may have changed
QTextBlock b = document()->firstBlock();
while (b.isValid()) {
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
b = b.next();
}
// This also triggers an update of the additional formats
highlighter->rehighlight();
2008-12-02 12:01:29 +01:00
}
void CPPEditorWidget::setTabSettings(const TextEditor::TabSettings &ts)
{
CppTools::QtStyleCodeFormatter formatter;
formatter.invalidateCache(document());
TextEditor::BaseTextEditorWidget::setTabSettings(ts);
}
void CPPEditorWidget::unCommentSelection()
2008-12-02 12:01:29 +01:00
{
Utils::unCommentSelection(this);
2008-12-02 12:01:29 +01:00
}
CPPEditorWidget::Link CPPEditorWidget::linkToSymbol(CPlusPlus::Symbol *symbol)
{
if (!symbol)
return Link();
const QString fileName = QString::fromUtf8(symbol->fileName(),
symbol->fileNameLength());
unsigned line = symbol->line();
unsigned column = symbol->column();
2009-03-04 15:47:26 +01:00
2009-03-03 13:46:37 +01:00
if (column)
--column;
2009-03-04 15:47:26 +01:00
if (symbol->isGenerated())
column = 0;
2009-03-03 13:46:37 +01:00
return Link(fileName, line, column);
}
bool CPPEditorWidget::openCppEditorAt(const Link &link)
{
if (link.fileName.isEmpty())
return false;
if (baseTextDocument()->fileName() == link.fileName) {
Core::EditorManager *editorManager = Core::EditorManager::instance();
editorManager->cutForwardNavigationHistory();
editorManager->addCurrentPositionToNavigationHistory();
gotoLine(link.line, link.column);
setFocus();
return true;
}
return TextEditor::BaseTextEditorWidget::openEditorAt(link.fileName,
link.line,
link.column,
Constants::CPPEDITOR_ID);
2008-12-02 12:01:29 +01:00
}
void CPPEditorWidget::semanticRehighlight()
{
m_semanticHighlighter->rehighlight(currentSource());
}
void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
{
if (semanticInfo.revision != editorRevision()) {
2009-07-10 12:09:26 +02:00
// got outdated semantic info
semanticRehighlight();
return;
}
2010-05-21 15:45:18 +02:00
const SemanticInfo previousSemanticInfo = m_lastSemanticInfo;
m_lastSemanticInfo = semanticInfo; // update the semantic info
2009-07-10 12:09:26 +02:00
int line = 0, column = 0;
convertPosition(position(), &line, &column);
2009-11-30 15:21:16 +01:00
QList<QTextEdit::ExtraSelection> unusedSelections;
2009-10-05 14:08:10 +02:00
m_renameSelections.clear();
m_currentRenameSelection = NoCurrentRenameSelection;
SemanticInfo::LocalUseIterator it(semanticInfo.localUses);
while (it.hasNext()) {
it.next();
const QList<SemanticInfo::Use> &uses = it.value();
bool good = false;
foreach (const SemanticInfo::Use &use, uses) {
unsigned l = line;
unsigned c = column + 1; // convertCursorPosition() returns a 0-based column number.
if (l == use.line && c >= use.column && c <= (use.column + use.length)) {
good = true;
break;
}
}
if (uses.size() == 1)
2009-10-05 14:08:10 +02:00
// it's an unused declaration
highlightUses(uses, semanticInfo, &unusedSelections);
else if (good && m_renameSelections.isEmpty())
highlightUses(uses, semanticInfo, &m_renameSelections);
}
2010-05-21 15:45:18 +02:00
if (m_lastSemanticInfo.forced || previousSemanticInfo.revision != semanticInfo.revision) {
QTextCharFormat diagnosticMessageFormat;
diagnosticMessageFormat.setUnderlineColor(Qt::darkYellow); // ### hardcoded
diagnosticMessageFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); // ### hardcoded
setExtraSelections(UndefinedSymbolSelection, createSelections(document(),
semanticInfo.diagnosticMessages,
diagnosticMessageFormat));
m_highlighter.cancel();
if (! semanticHighlighterDisabled && semanticInfo.doc) {
if (Core::EditorManager::instance()->currentEditor() == editor()) {
LookupContext context(semanticInfo.doc, semanticInfo.snapshot);
CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
m_highlighter = f;
m_highlightRevision = semanticInfo.revision;
m_nextHighlightBlockNumber = 0;
m_highlightWatcher.setFuture(m_highlighter);
}
}
#if 0 // ### TODO: enable objc semantic highlighting
setExtraSelections(ObjCSelection, createSelections(document(),
semanticInfo.objcKeywords,
m_keywordFormat));
#endif
2010-05-21 15:45:18 +02:00
}
2009-11-30 15:21:16 +01:00
setExtraSelections(UnusedSymbolSelection, unusedSelections);
2010-05-25 15:55:12 +02:00
if (! m_renameSelections.isEmpty())
setExtraSelections(CodeSemanticsSelection, m_renameSelections); // ###
else {
markSymbols(textCursor(), semanticInfo);
}
2010-05-21 15:45:18 +02:00
m_lastSemanticInfo.forced = false; // clear the forced flag
}
namespace {
class FindObjCKeywords: public ASTVisitor
{
public:
FindObjCKeywords(TranslationUnit *unit)
: ASTVisitor(unit)
{}
QList<SemanticInfo::Use> operator()()
{
_keywords.clear();
accept(translationUnit()->ast());
return _keywords;
}
virtual bool visit(ObjCClassDeclarationAST *ast)
{
addToken(ast->interface_token);
addToken(ast->implementation_token);
addToken(ast->end_token);
return true;
}
virtual bool visit(ObjCClassForwardDeclarationAST *ast)
{ addToken(ast->class_token); return true; }
virtual bool visit(ObjCProtocolDeclarationAST *ast)
{ addToken(ast->protocol_token); addToken(ast->end_token); return true; }
virtual bool visit(ObjCProtocolForwardDeclarationAST *ast)
{ addToken(ast->protocol_token); return true; }
virtual bool visit(ObjCProtocolExpressionAST *ast)
{ addToken(ast->protocol_token); return true; }
virtual bool visit(ObjCTypeNameAST *) { return true; }
virtual bool visit(ObjCEncodeExpressionAST *ast)
{ addToken(ast->encode_token); return true; }
virtual bool visit(ObjCSelectorExpressionAST *ast)
{ addToken(ast->selector_token); return true; }
virtual bool visit(ObjCVisibilityDeclarationAST *ast)
{ addToken(ast->visibility_token); return true; }
virtual bool visit(ObjCPropertyAttributeAST *ast)
{
const Identifier *attrId = identifier(ast->attribute_identifier_token);
if (attrId == control()->objcAssignId()
|| attrId == control()->objcCopyId()
|| attrId == control()->objcGetterId()
|| attrId == control()->objcNonatomicId()
|| attrId == control()->objcReadonlyId()
|| attrId == control()->objcReadwriteId()
|| attrId == control()->objcRetainId()
|| attrId == control()->objcSetterId())
addToken(ast->attribute_identifier_token);
return true;
}
virtual bool visit(ObjCPropertyDeclarationAST *ast)
{ addToken(ast->property_token); return true; }
virtual bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast)
{ addToken(ast->synthesized_token); return true; }
virtual bool visit(ObjCDynamicPropertiesDeclarationAST *ast)
{ addToken(ast->dynamic_token); return true; }
virtual bool visit(ObjCFastEnumerationAST *ast)
{ addToken(ast->for_token); addToken(ast->in_token); return true; }
virtual bool visit(ObjCSynchronizedStatementAST *ast)
{ addToken(ast->synchronized_token); return true; }
protected:
void addToken(unsigned token)
{
if (token) {
SemanticInfo::Use use;
getTokenStartPosition(token, &use.line, &use.column);
use.length = tokenAt(token).length();
_keywords.append(use);
}
}
private:
QList<SemanticInfo::Use> _keywords;
};
} // anonymous namespace
SemanticHighlighter::Source CPPEditorWidget::currentSource(bool force)
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
const Snapshot snapshot = m_modelManager->snapshot();
const QString fileName = file()->fileName();
QString code;
if (force || m_lastSemanticInfo.revision != editorRevision())
code = toPlainText(); // get the source code only when needed.
const unsigned revision = editorRevision();
SemanticHighlighter::Source source(snapshot, fileName, code,
line, column, revision);
source.force = force;
return source;
}
SemanticHighlighter::SemanticHighlighter(QObject *parent)
: QThread(parent),
m_done(false)
{
}
SemanticHighlighter::~SemanticHighlighter()
{
}
void SemanticHighlighter::abort()
{
QMutexLocker locker(&m_mutex);
m_done = true;
m_condition.wakeOne();
}
void SemanticHighlighter::rehighlight(const Source &source)
{
QMutexLocker locker(&m_mutex);
m_source = source;
m_condition.wakeOne();
}
bool SemanticHighlighter::isOutdated()
{
QMutexLocker locker(&m_mutex);
const bool outdated = ! m_source.fileName.isEmpty() || m_done;
return outdated;
}
void SemanticHighlighter::run()
{
setPriority(QThread::LowestPriority);
forever {
m_mutex.lock();
2009-07-10 12:17:57 +02:00
while (! (m_done || ! m_source.fileName.isEmpty()))
m_condition.wait(&m_mutex);
const bool done = m_done;
const Source source = m_source;
m_source.clear();
m_mutex.unlock();
if (done)
break;
const SemanticInfo info = semanticInfo(source);
if (! isOutdated()) {
m_mutex.lock();
m_lastSemanticInfo = info;
m_mutex.unlock();
emit changed(info);
}
}
}
SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
{
m_mutex.lock();
const int revision = m_lastSemanticInfo.revision;
m_mutex.unlock();
Snapshot snapshot;
Document::Ptr doc;
2010-05-21 15:45:18 +02:00
QList<Document::DiagnosticMessage> diagnosticMessages;
2010-07-15 16:03:48 +02:00
QList<SemanticInfo::Use> objcKeywords;
if (! source.force && revision == source.revision) {
m_mutex.lock();
2010-05-21 15:45:18 +02:00
snapshot = m_lastSemanticInfo.snapshot; // ### TODO: use the new snapshot.
doc = m_lastSemanticInfo.doc;
2010-05-21 15:45:18 +02:00
diagnosticMessages = m_lastSemanticInfo.diagnosticMessages;
objcKeywords = m_lastSemanticInfo.objcKeywords;
m_mutex.unlock();
2009-09-24 12:19:54 +02:00
}
if (! doc) {
snapshot = source.snapshot;
const QByteArray preprocessedCode = snapshot.preprocessedCode(source.code, source.fileName);
doc = snapshot.documentFromSource(preprocessedCode, source.fileName);
doc->control()->setTopLevelDeclarationProcessor(this);
doc->check();
2010-05-21 15:45:18 +02:00
#if 0
if (TranslationUnit *unit = doc->translationUnit()) {
2010-07-05 17:43:13 +02:00
FindObjCKeywords findObjCKeywords(unit); // ### remove me
objcKeywords = findObjCKeywords();
}
#endif
}
TranslationUnit *translationUnit = doc->translationUnit();
AST *ast = translationUnit->ast();
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(translationUnit);
DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
2010-07-15 16:03:48 +02:00
const LocalSymbols useTable(doc, currentFunctionDefinition);
SemanticInfo semanticInfo;
semanticInfo.revision = source.revision;
2009-07-10 11:59:01 +02:00
semanticInfo.snapshot = snapshot;
semanticInfo.doc = doc;
2010-07-15 16:03:48 +02:00
semanticInfo.localUses = useTable.uses;
semanticInfo.hasQ = useTable.hasQ;
semanticInfo.hasD = useTable.hasD;
2010-05-21 15:45:18 +02:00
semanticInfo.forced = source.force;
semanticInfo.diagnosticMessages = diagnosticMessages;
semanticInfo.objcKeywords = objcKeywords;
return semanticInfo;
}
QModelIndex CPPEditorWidget::indexForPosition(int line, int column, const QModelIndex &rootIndex) const
{
QModelIndex lastIndex = rootIndex;
const int rowCount = m_outlineModel->rowCount(rootIndex);
for (int row = 0; row < rowCount; ++row) {
const QModelIndex index = m_outlineModel->index(row, 0, rootIndex);
Symbol *symbol = m_outlineModel->symbolFromIndex(index);
if (symbol && symbol->line() > unsigned(line))
break;
lastIndex = index;
}
if (lastIndex != rootIndex) {
// recurse
lastIndex = indexForPosition(line, column, lastIndex);
}
return lastIndex;
}
QVector<QString> CPPEditorWidget::highlighterFormatCategories()
{
static QVector<QString> categories;
if (categories.isEmpty()) {
categories << QLatin1String(TextEditor::Constants::C_NUMBER)
<< QLatin1String(TextEditor::Constants::C_STRING)
<< QLatin1String(TextEditor::Constants::C_TYPE)
<< QLatin1String(TextEditor::Constants::C_KEYWORD)
<< QLatin1String(TextEditor::Constants::C_OPERATOR)
<< QLatin1String(TextEditor::Constants::C_PREPROCESSOR)
<< QLatin1String(TextEditor::Constants::C_LABEL)
<< QLatin1String(TextEditor::Constants::C_COMMENT)
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_COMMENT)
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_TAG)
<< QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
}
return categories;
}
#include "cppeditor.moc"