2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2013-01-28 17:12:19 +01:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2008-12-02 15:08:31 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cppeditor.h"
|
2013-03-27 18:54:03 +01:00
|
|
|
|
2013-09-20 10:32:41 +02:00
|
|
|
#include "cppautocompleter.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cppeditorconstants.h"
|
2013-03-19 13:44:02 +01:00
|
|
|
#include "cppeditorplugin.h"
|
2013-09-20 10:32:41 +02:00
|
|
|
#include "cppfollowsymbolundercursor.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cpphighlighter.h"
|
2013-08-20 07:46:19 +02:00
|
|
|
#include "cpppreprocessoradditionwidget.h"
|
2011-04-15 16:19:23 +02:00
|
|
|
#include "cppquickfixassistant.h"
|
2009-08-07 13:02:36 +02:00
|
|
|
|
2013-03-27 18:54:03 +01:00
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
|
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
2013-04-17 10:58:20 +02:00
|
|
|
#include <cpptools/cpptoolseditorsupport.h>
|
2010-07-13 15:25:05 +02:00
|
|
|
#include <cpptools/cpptoolsplugin.h>
|
2010-06-18 12:31:13 +02:00
|
|
|
#include <cpptools/cpptoolsconstants.h>
|
2012-02-07 15:09:08 +01:00
|
|
|
#include <cpptools/cppchecksymbols.h>
|
2010-07-05 13:48:53 +02:00
|
|
|
#include <cpptools/cppcodeformatter.h>
|
2013-08-30 12:55:06 +02:00
|
|
|
#include <cpptools/cppcompletionassistprovider.h>
|
2012-02-07 15:09:08 +01:00
|
|
|
#include <cpptools/cpphighlightingsupport.h>
|
|
|
|
|
#include <cpptools/cpplocalsymbols.h>
|
2011-02-03 15:48:14 +01:00
|
|
|
#include <cpptools/cppqtstyleindenter.h>
|
2011-11-14 13:14:39 +01:00
|
|
|
#include <cpptools/cpptoolsreuse.h>
|
2011-12-07 15:05:02 +01:00
|
|
|
#include <cpptools/doxygengenerator.h>
|
|
|
|
|
#include <cpptools/cpptoolssettings.h>
|
2012-01-20 14:43:21 +01:00
|
|
|
#include <cpptools/symbolfinder.h>
|
2013-08-20 07:46:19 +02:00
|
|
|
#include <cpptools/cppmodelmanager.h>
|
|
|
|
|
#include <projectexplorer/session.h>
|
|
|
|
|
#include <projectexplorer/projectnodes.h>
|
|
|
|
|
#include <projectexplorer/nodesvisitor.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <texteditor/basetextdocument.h>
|
2010-12-10 10:32:46 +01:00
|
|
|
#include <texteditor/basetextdocumentlayout.h>
|
2013-03-27 18:54:03 +01:00
|
|
|
#include <texteditor/codeassist/basicproposalitem.h>
|
|
|
|
|
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
|
|
|
|
|
#include <texteditor/codeassist/genericproposal.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <texteditor/fontsettings.h>
|
2011-08-10 09:50:04 +02:00
|
|
|
#include <texteditor/refactoroverlay.h>
|
2013-03-27 18:54:03 +01:00
|
|
|
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <cplusplus/ASTPath.h>
|
|
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
|
|
|
|
#include <cplusplus/OverviewModel.h>
|
|
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QTimer>
|
2013-02-18 15:12:09 +01:00
|
|
|
#include <QPointer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QSignalMapper>
|
|
|
|
|
#include <QAction>
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QTextEdit>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QTreeView>
|
|
|
|
|
#include <QSortFilterProxyModel>
|
2013-08-20 07:46:19 +02:00
|
|
|
#include <QToolButton>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
enum {
|
2010-07-19 14:13:06 +02:00
|
|
|
UPDATE_OUTLINE_INTERVAL = 500,
|
2011-08-10 09:50:04 +02:00
|
|
|
UPDATE_USES_INTERVAL = 500,
|
|
|
|
|
UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200
|
2009-06-24 16:40:30 +02:00
|
|
|
};
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
using namespace CPlusPlus;
|
2012-02-07 15:09:08 +01:00
|
|
|
using namespace CppTools;
|
2009-07-09 12:14:00 +02:00
|
|
|
using namespace CppEditor::Internal;
|
|
|
|
|
|
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();
|
2011-05-25 13:22:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void adjustWidth()
|
|
|
|
|
{
|
2012-01-24 15:36:40 +01:00
|
|
|
const int w = Core::ICore::mainWindow()->geometry().width();
|
2011-05-25 13:22:22 +02:00
|
|
|
setMaximumWidth(w);
|
|
|
|
|
setMinimumWidth(qMin(qMax(sizeHintForColumn(0), minimumSizeHint().width()), w));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class OverviewCombo : public QComboBox
|
|
|
|
|
{
|
|
|
|
|
public:
|
2012-06-15 14:43:12 +02:00
|
|
|
OverviewCombo(QWidget *parent = 0) : QComboBox(parent), m_skipNextHide(false)
|
2011-05-25 13:22:22 +02:00
|
|
|
{}
|
|
|
|
|
|
2012-06-15 14:43:12 +02:00
|
|
|
bool eventFilter(QObject* object, QEvent* event)
|
|
|
|
|
{
|
|
|
|
|
if (event->type() == QEvent::MouseButtonPress && object == view()->viewport()) {
|
|
|
|
|
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
|
|
|
|
QModelIndex index = view()->indexAt(mouseEvent->pos());
|
|
|
|
|
if (!view()->visualRect(index).contains(mouseEvent->pos()))
|
|
|
|
|
m_skipNextHide = true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-25 13:22:22 +02:00
|
|
|
void showPopup()
|
|
|
|
|
{
|
|
|
|
|
static_cast<OverviewTreeView *>(view())->adjustWidth();
|
|
|
|
|
QComboBox::showPopup();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2012-06-15 14:43:12 +02:00
|
|
|
|
|
|
|
|
virtual void hidePopup()
|
|
|
|
|
{
|
|
|
|
|
if (m_skipNextHide)
|
|
|
|
|
m_skipNextHide = false;
|
|
|
|
|
else
|
|
|
|
|
QComboBox::hidePopup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool m_skipNextHide;
|
2008-12-02 12:01:29 +01:00
|
|
|
};
|
|
|
|
|
|
2010-07-08 17:03:38 +02:00
|
|
|
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-01-28 23:46:43 +01:00
|
|
|
class FindFunctionDefinitions: protected SymbolVisitor
|
|
|
|
|
{
|
2009-12-01 12:46:15 +01:00
|
|
|
const Name *_declarationName;
|
2009-01-28 23:46:43 +01:00
|
|
|
QList<Function *> *_functions;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FindFunctionDefinitions()
|
|
|
|
|
: _declarationName(0),
|
|
|
|
|
_functions(0)
|
|
|
|
|
{ }
|
|
|
|
|
|
2009-12-01 12:46:15 +01:00
|
|
|
void operator()(const Name *declarationName, Scope *globals,
|
2009-01-28 23:46:43 +01:00
|
|
|
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));
|
2009-01-28 23:46:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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())
|
2010-07-12 13:41:54 +02:00
|
|
|
name = q->name();
|
2009-01-28 23:46:43 +01:00
|
|
|
|
|
|
|
|
if (_declarationName->isEqualTo(name))
|
|
|
|
|
_functions->append(function);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
struct CanonicalSymbol
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditorWidget *editor;
|
2010-05-31 12:09:28 +02:00
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
SemanticInfo info;
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CanonicalSymbol(CPPEditorWidget *editor, const SemanticInfo &info)
|
2010-06-29 17:47:59 +02:00
|
|
|
: editor(editor), info(info)
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
|
|
|
|
typeOfExpression.init(info.doc, info.snapshot);
|
2013-04-02 23:04:12 +02:00
|
|
|
typeOfExpression.setExpandTemplates(true);
|
2010-05-31 12:09:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const LookupContext &context() const
|
|
|
|
|
{
|
|
|
|
|
return typeOfExpression.context();
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
static inline bool isIdentifierChar(const QChar &ch)
|
2010-06-01 12:16:13 +02:00
|
|
|
{
|
|
|
|
|
return ch.isLetterOrNumber() || ch == QLatin1Char('_');
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code)
|
|
|
|
|
{
|
|
|
|
|
return getScopeAndExpression(editor, info, cursor, code);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
static Scope *getScopeAndExpression(CPPEditorWidget *editor, const SemanticInfo &info,
|
2010-07-19 12:17:08 +02:00
|
|
|
const QTextCursor &cursor,
|
|
|
|
|
QString *code)
|
2010-05-31 12:09:28 +02:00
|
|
|
{
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!info.doc)
|
2010-05-31 12:09:28 +02:00
|
|
|
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();
|
2010-06-01 12:16:13 +02:00
|
|
|
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!isIdentifierChar(document->characterAt(pos)))
|
|
|
|
|
if (!(pos > 0 && isIdentifierChar(document->characterAt(pos - 1))))
|
2010-06-01 12:16:13 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
while (isIdentifierChar(document->characterAt(pos)))
|
2010-05-31 12:09:28 +02:00
|
|
|
++pos;
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
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);
|
2010-05-31 12:09:28 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Symbol *operator()(Scope *scope, const QString &code)
|
|
|
|
|
{
|
|
|
|
|
return canonicalSymbol(scope, code, typeOfExpression);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-30 14:50:47 +02:00
|
|
|
static Symbol *canonicalSymbol(Scope *scope, const QString &code,
|
|
|
|
|
TypeOfExpression &typeOfExpression)
|
2010-07-19 12:17:08 +02:00
|
|
|
{
|
2012-01-12 17:35:37 +01:00
|
|
|
const QList<LookupItem> results =
|
|
|
|
|
typeOfExpression(code.toUtf8(), scope, TypeOfExpression::Preprocess);
|
2010-07-19 12:17:08 +02:00
|
|
|
|
2010-08-05 14:14:16 +02:00
|
|
|
for (int i = results.size() - 1; i != -1; --i) {
|
|
|
|
|
const LookupItem &r = results.at(i);
|
2010-08-17 16:09:24 +02:00
|
|
|
Symbol *decl = r.declaration();
|
2010-08-05 14:14:16 +02:00
|
|
|
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!(decl && decl->enclosingScope()))
|
2010-08-05 14:14:16 +02:00
|
|
|
break;
|
|
|
|
|
|
2010-08-26 16:16:22 +02:00
|
|
|
if (Class *classScope = r.declaration()->enclosingScope()->asClass()) {
|
2010-08-17 16:09:24 +02:00
|
|
|
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.
|
|
|
|
|
|
2013-07-17 00:01:45 +03:00
|
|
|
if (Function *funTy = r.declaration()->type()->asFunctionType()) {
|
2010-08-17 16:09:24 +02:00
|
|
|
if (funTy->isVirtual())
|
|
|
|
|
return r.declaration();
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-05 14:14:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < results.size(); ++i) {
|
2010-05-31 12:09:28 +02:00
|
|
|
const LookupItem &r = results.at(i);
|
|
|
|
|
|
|
|
|
|
if (r.declaration())
|
|
|
|
|
return r.declaration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-07-19 12:17:08 +02:00
|
|
|
|
2010-05-31 12:09:28 +02:00
|
|
|
};
|
|
|
|
|
|
2013-02-21 05:45:44 +01:00
|
|
|
/// Check if previous line is a CppStyle Doxygen Comment
|
|
|
|
|
bool isPreviousLineCppStyleComment(const QTextCursor &cursor)
|
|
|
|
|
{
|
2013-05-05 22:43:52 +03:00
|
|
|
const QTextBlock ¤tBlock = cursor.block();
|
|
|
|
|
if (!currentBlock.isValid())
|
|
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
const QTextBlock &actual = currentBlock.previous();
|
|
|
|
|
if (!actual.isValid())
|
|
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
const QString text = actual.text().trimmed();
|
|
|
|
|
if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
|
|
|
|
|
return true;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if next line is a CppStyle Doxygen Comment
|
|
|
|
|
bool isNextLineCppStyleComment(const QTextCursor &cursor)
|
|
|
|
|
{
|
2013-05-05 22:43:52 +03:00
|
|
|
const QTextBlock ¤tBlock = cursor.block();
|
|
|
|
|
if (!currentBlock.isValid())
|
|
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
const QTextBlock &actual = currentBlock.next();
|
|
|
|
|
if (!actual.isValid())
|
|
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
const QString text = actual.text().trimmed();
|
|
|
|
|
if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
|
|
|
|
|
return true;
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2013-05-05 22:43:52 +03:00
|
|
|
return false;
|
2013-02-21 05:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if line is a CppStyle Doxygen comment and the cursor is after the comment
|
|
|
|
|
bool isCursorAfterCppComment(const QTextCursor &cursor, const QTextDocument *doc)
|
|
|
|
|
{
|
|
|
|
|
QTextCursor cursorFirstNonBlank(cursor);
|
|
|
|
|
cursorFirstNonBlank.movePosition(QTextCursor::StartOfLine);
|
|
|
|
|
while (doc->characterAt(cursorFirstNonBlank.position()).isSpace()
|
|
|
|
|
&& cursorFirstNonBlank.movePosition(QTextCursor::NextCharacter)) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QTextBlock& block = cursorFirstNonBlank.block();
|
|
|
|
|
const QString text = block.text().trimmed();
|
|
|
|
|
if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")))
|
|
|
|
|
return (cursor.position() >= cursorFirstNonBlank.position() + 3);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isCppStyleContinuation(const QTextCursor& cursor)
|
|
|
|
|
{
|
|
|
|
|
return (isPreviousLineCppStyleComment(cursor) || isNextLineCppStyleComment(cursor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoxygenGenerator::DocumentationStyle doxygenStyle(const QTextCursor &cursor,
|
|
|
|
|
const QTextDocument *doc)
|
|
|
|
|
{
|
|
|
|
|
const int pos = cursor.position();
|
|
|
|
|
|
2013-02-21 15:00:58 +01:00
|
|
|
QString comment = QString(doc->characterAt(pos - 3))
|
2013-02-21 05:45:44 +01:00
|
|
|
+ doc->characterAt(pos - 2)
|
|
|
|
|
+ doc->characterAt(pos - 1);
|
|
|
|
|
|
|
|
|
|
if (comment == QLatin1String("/**"))
|
|
|
|
|
return CppTools::DoxygenGenerator::JavaStyle;
|
|
|
|
|
else if (comment == QLatin1String("/*!"))
|
|
|
|
|
return CppTools::DoxygenGenerator::QtStyle;
|
|
|
|
|
else if (comment == QLatin1String("///"))
|
|
|
|
|
return CppTools::DoxygenGenerator::CppStyleA;
|
|
|
|
|
else
|
|
|
|
|
return CppTools::DoxygenGenerator::CppStyleB;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool handleDoxygenCppStyleContinuation(QTextCursor &cursor,
|
|
|
|
|
QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
const int blockPos = cursor.positionInBlock();
|
|
|
|
|
const QString &text = cursor.block().text();
|
|
|
|
|
int offset = 0;
|
|
|
|
|
for (; offset < blockPos; ++offset) {
|
|
|
|
|
if (!text.at(offset).isSpace())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the line does not start with the comment we don't
|
|
|
|
|
// consider it as a continuation. Handles situations like:
|
|
|
|
|
// void d(); ///<enter>
|
|
|
|
|
if (!(text.trimmed().startsWith(QLatin1String("///"))
|
2013-05-05 22:43:52 +03:00
|
|
|
|| text.startsWith(QLatin1String("//!")))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-02-21 05:45:44 +01:00
|
|
|
|
|
|
|
|
QString newLine(QLatin1Char('\n'));
|
|
|
|
|
newLine.append(QString(offset, QLatin1Char(' '))); // indent correctly
|
|
|
|
|
|
|
|
|
|
const QString commentMarker = text.mid(offset, 3);
|
|
|
|
|
newLine.append(commentMarker);
|
|
|
|
|
|
|
|
|
|
cursor.insertText(newLine);
|
|
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool handleDoxygenContinuation(QTextCursor &cursor,
|
|
|
|
|
QKeyEvent *e,
|
|
|
|
|
const QTextDocument *doc,
|
|
|
|
|
const bool enableDoxygen,
|
|
|
|
|
const bool leadingAsterisks)
|
|
|
|
|
{
|
|
|
|
|
// It might be a continuation if:
|
|
|
|
|
// a) current line starts with /// or //! and cursor is positioned after the comment
|
|
|
|
|
// b) current line is in the middle of a multi-line Qt or Java style comment
|
|
|
|
|
|
|
|
|
|
if (enableDoxygen && !cursor.atEnd() && isCursorAfterCppComment(cursor, doc))
|
|
|
|
|
return handleDoxygenCppStyleContinuation(cursor, e);
|
|
|
|
|
|
|
|
|
|
if (!leadingAsterisks)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// We continue the comment if the cursor is after a comment's line asterisk and if
|
|
|
|
|
// there's no asterisk immediately after the cursor (that would already be considered
|
|
|
|
|
// a leading asterisk).
|
|
|
|
|
int offset = 0;
|
|
|
|
|
const int blockPos = cursor.positionInBlock();
|
|
|
|
|
const QString &text = cursor.block().text();
|
|
|
|
|
for (; offset < blockPos; ++offset) {
|
|
|
|
|
if (!text.at(offset).isSpace())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (offset < blockPos
|
|
|
|
|
&& (text.at(offset) == QLatin1Char('*')
|
|
|
|
|
|| (offset < blockPos - 1
|
|
|
|
|
&& text.at(offset) == QLatin1Char('/')
|
|
|
|
|
&& text.at(offset + 1) == QLatin1Char('*')))) {
|
|
|
|
|
int followinPos = blockPos;
|
|
|
|
|
for (; followinPos < text.length(); ++followinPos) {
|
|
|
|
|
if (!text.at(followinPos).isSpace())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (followinPos == text.length()
|
|
|
|
|
|| text.at(followinPos) != QLatin1Char('*')) {
|
|
|
|
|
QString newLine(QLatin1Char('\n'));
|
|
|
|
|
QTextCursor c(cursor);
|
|
|
|
|
c.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset);
|
|
|
|
|
newLine.append(c.selectedText());
|
|
|
|
|
if (text.at(offset) == QLatin1Char('/')) {
|
|
|
|
|
newLine.append(QLatin1String(" *"));
|
|
|
|
|
} else {
|
|
|
|
|
int start = offset;
|
|
|
|
|
while (offset < blockPos && text.at(offset) == QLatin1Char('*'))
|
|
|
|
|
++offset;
|
|
|
|
|
newLine.append(QString(offset - start, QLatin1Char('*')));
|
|
|
|
|
}
|
|
|
|
|
cursor.insertText(newLine);
|
|
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditor::CPPEditor(CPPEditorWidget *editor)
|
|
|
|
|
: BaseTextEditor(editor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-25 17:37:59 +02: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
|
|
|
}
|
|
|
|
|
|
2012-01-24 11:07:26 +01:00
|
|
|
Q_GLOBAL_STATIC(CppTools::SymbolFinder, symbolFinder)
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditorWidget::CPPEditorWidget(QWidget *parent)
|
|
|
|
|
: TextEditor::BaseTextEditorWidget(parent)
|
2010-07-19 13:58:43 +02:00
|
|
|
, m_currentRenameSelection(NoCurrentRenameSelection)
|
2009-07-01 18:41:04 +02:00
|
|
|
, m_inRename(false)
|
2009-12-03 18:35:36 +01:00
|
|
|
, m_inRenameChanged(false)
|
|
|
|
|
, m_firstRenameChange(false)
|
2010-06-18 12:31:13 +02:00
|
|
|
, m_objcEnabled(false)
|
2011-12-07 15:05:02 +01:00
|
|
|
, m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
|
2013-07-04 20:11:10 +02:00
|
|
|
, m_followSymbolUnderCursor(new FollowSymbolUnderCursor(this))
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2012-02-07 15:09:08 +01:00
|
|
|
qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
|
2009-07-09 12:14:00 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
|
setMarksVisible(true);
|
2009-03-16 17:23:50 +01:00
|
|
|
setCodeFoldingSupported(true);
|
2011-02-03 15:48:14 +01:00
|
|
|
setIndenter(new CppTools::CppQtStyleIndenter);
|
2010-11-08 16:11:26 +01:00
|
|
|
setAutoCompleter(new CppAutoCompleter);
|
2010-11-10 17:00:07 +01:00
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-12-03 13:49:35 +01:00
|
|
|
m_modelManager = CppModelManagerInterface::instance();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (m_modelManager) {
|
2013-04-17 10:58:20 +02:00
|
|
|
CppEditorSupport *editorSupport = m_modelManager->cppEditorSupport(editor());
|
|
|
|
|
connect(editorSupport, SIGNAL(documentUpdated()),
|
|
|
|
|
this, SLOT(onDocumentUpdated()));
|
|
|
|
|
connect(editorSupport, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
|
|
|
|
|
this, SLOT(updateSemanticInfo(CppTools::SemanticInfo)));
|
2013-05-16 13:40:15 +02:00
|
|
|
connect(editorSupport, SIGNAL(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)),
|
|
|
|
|
this, SLOT(highlighterStarted(QFuture<TextEditor::HighlightingResult>*,uint)));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-07-05 18:52:52 +02:00
|
|
|
|
2010-07-13 14:37:31 +02:00
|
|
|
m_highlightRevision = 0;
|
2013-04-30 14:50:47 +02:00
|
|
|
connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)),
|
|
|
|
|
SLOT(highlightSymbolUsages(int,int)));
|
|
|
|
|
connect(&m_highlightWatcher, SIGNAL(finished()),
|
|
|
|
|
SLOT(finishHighlightSymbolUsages()));
|
2010-07-19 12:17:08 +02:00
|
|
|
|
|
|
|
|
m_referencesRevision = 0;
|
|
|
|
|
m_referencesCursorPosition = 0;
|
|
|
|
|
connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow()));
|
2011-08-10 09:50:04 +02:00
|
|
|
|
|
|
|
|
connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
|
|
|
|
|
this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));
|
|
|
|
|
|
|
|
|
|
m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this);
|
|
|
|
|
connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)),
|
|
|
|
|
this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>)));
|
2011-12-07 15:05:02 +01:00
|
|
|
|
|
|
|
|
connect(CppTools::CppToolsSettings::instance(),
|
|
|
|
|
SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)),
|
|
|
|
|
this,
|
|
|
|
|
SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings)));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditorWidget::~CPPEditorWidget()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-05-24 13:04:40 +02:00
|
|
|
if (m_modelManager)
|
2013-07-11 11:13:07 +02:00
|
|
|
m_modelManager->deleteCppEditorSupport(editor());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditor *CPPEditorWidget::createEditor()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditor *editable = new CPPEditor(this);
|
2008-12-02 12:01:29 +01:00
|
|
|
createToolBar(editable);
|
|
|
|
|
return editable;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-25 15:58:02 +01:00
|
|
|
void CPPEditorWidget::createToolBar(CPPEditor *editor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-05-25 13:22:22 +02:00
|
|
|
m_outlineCombo = new OverviewCombo;
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setMinimumContentsLength(22);
|
2008-12-17 11:54:47 +01:00
|
|
|
|
|
|
|
|
// Make the combo box prefer to expand
|
2010-07-13 11:37:06 +02:00
|
|
|
QSizePolicy policy = m_outlineCombo->sizePolicy();
|
2008-12-17 11:54:47 +01:00
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setSizePolicy(policy);
|
2008-12-17 11:54:47 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
QTreeView *outlineView = new OverviewTreeView;
|
|
|
|
|
outlineView->header()->hide();
|
2012-06-15 14:43:12 +02:00
|
|
|
outlineView->setItemsExpandable(true);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setView(outlineView);
|
2011-03-24 12:38:42 +01:00
|
|
|
m_outlineCombo->setMaxVisibleItems(40);
|
2012-06-15 14:43:12 +02:00
|
|
|
outlineView->viewport()->installEventFilter(m_outlineCombo);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModel = new OverviewModel(this);
|
|
|
|
|
m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
|
2013-03-19 13:44:02 +01:00
|
|
|
if (CppEditorPlugin::instance()->sortedOutline())
|
2009-04-27 17:22:49 +02:00
|
|
|
m_proxyModel->sort(0, Qt::AscendingOrder);
|
|
|
|
|
else
|
2010-07-13 11:37:06 +02:00
|
|
|
m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
|
2009-04-27 17:22:49 +02:00
|
|
|
m_proxyModel->setDynamicSortFilter(true);
|
|
|
|
|
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setModel(m_proxyModel);
|
2009-04-27 17:22:49 +02:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
|
|
|
m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
|
2009-04-27 17:22:49 +02:00
|
|
|
m_sortAction->setCheckable(true);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_sortAction->setChecked(sortedOutline());
|
2013-03-19 13:44:02 +01:00
|
|
|
connect(m_sortAction, SIGNAL(toggled(bool)),
|
|
|
|
|
CppEditorPlugin::instance(), SLOT(setSortedOutline(bool)));
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->addAction(m_sortAction);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-13 11:19:31 +02:00
|
|
|
m_updateOutlineTimer = new QTimer(this);
|
|
|
|
|
m_updateOutlineTimer->setSingleShot(true);
|
|
|
|
|
m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
|
connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
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
|
|
|
|
2009-07-01 17:57:00 +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()));
|
|
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
m_updateFunctionDeclDefLinkTimer = new QTimer(this);
|
|
|
|
|
m_updateFunctionDeclDefLinkTimer->setSingleShot(true);
|
|
|
|
|
m_updateFunctionDeclDefLinkTimer->setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL);
|
2013-04-30 14:50:47 +02:00
|
|
|
connect(m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()),
|
|
|
|
|
this, SLOT(updateFunctionDeclDefLinkNow()));
|
2011-08-10 09:50:04 +02:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
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()));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2013-06-25 15:47:01 +02:00
|
|
|
// set up slots to document changes
|
|
|
|
|
updateContentsChangedSignal();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
// set up function declaration - definition link
|
|
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink()));
|
|
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink()));
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
// set up the semantic highlighter
|
|
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
|
|
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
|
|
|
|
|
|
2013-08-20 07:46:19 +02:00
|
|
|
QToolButton *hashButton = new QToolButton(this);
|
|
|
|
|
hashButton->setText(QLatin1String("#"));
|
|
|
|
|
connect(hashButton, SIGNAL(clicked()), this, SLOT(showPreProcessorWidget()));
|
|
|
|
|
editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, hashButton);
|
2011-02-25 15:58:02 +01:00
|
|
|
editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::paste()
|
2009-12-03 18:35:36 +01:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::paste();
|
2009-12-03 18:35:36 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startRename();
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::paste();
|
2009-12-03 18:35:36 +01:00
|
|
|
finishRename();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::cut()
|
2009-12-03 18:35:36 +01:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::cut();
|
2009-12-03 18:35:36 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startRename();
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::cut();
|
2009-12-03 18:35:36 +01:00
|
|
|
finishRename();
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-18 12:09:41 +01:00
|
|
|
void CPPEditorWidget::selectAll()
|
|
|
|
|
{
|
|
|
|
|
// if we are currently renaming a symbol
|
|
|
|
|
// and the cursor is over that symbol, select just that symbol
|
|
|
|
|
if (m_currentRenameSelection != NoCurrentRenameSelection) {
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
int selectionBegin = m_currentRenameSelectionBegin.position();
|
|
|
|
|
int selectionEnd = m_currentRenameSelectionEnd.position();
|
|
|
|
|
|
|
|
|
|
if (cursor.position() >= selectionBegin
|
|
|
|
|
&& cursor.position() <= selectionEnd) {
|
|
|
|
|
cursor.setPosition(selectionBegin);
|
|
|
|
|
cursor.setPosition(selectionEnd, QTextCursor::KeepAnchor);
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseTextEditorWidget::selectAll();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::setMimeType(const QString &mt)
|
2010-06-18 12:31:13 +02:00
|
|
|
{
|
2013-08-20 07:46:19 +02:00
|
|
|
const QString &fileName = editor()->document()->filePath();
|
|
|
|
|
// Check if this editor belongs to a project
|
|
|
|
|
QList<ProjectPart::Ptr> projectParts = m_modelManager->projectPart(fileName);
|
|
|
|
|
if (projectParts.isEmpty())
|
|
|
|
|
projectParts = m_modelManager->projectPartFromDependencies(fileName);
|
|
|
|
|
if (!projectParts.isEmpty()) {
|
|
|
|
|
if (ProjectExplorer::Project *project = projectParts.first()->project) {
|
|
|
|
|
QByteArray additionalDefines = project->additionalCppDefines()
|
|
|
|
|
.value(projectParts.first()->projectFile).toByteArray();
|
|
|
|
|
m_modelManager->cppEditorSupport(editor())->snapshotUpdater()->setEditorDefines(
|
|
|
|
|
additionalDefines);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::setMimeType(mt);
|
2013-05-02 11:24:34 +02:00
|
|
|
setObjCEnabled(mt == QLatin1String(CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE)
|
|
|
|
|
|| mt == QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
|
2010-06-18 12:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::setObjCEnabled(bool onoff)
|
2010-06-18 12:31:13 +02:00
|
|
|
{
|
|
|
|
|
m_objcEnabled = onoff;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
bool CPPEditorWidget::isObjCEnabled() const
|
2010-06-18 12:31:13 +02:00
|
|
|
{ return m_objcEnabled; }
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::startRename()
|
2009-12-03 18:35:36 +01:00
|
|
|
{
|
|
|
|
|
m_inRenameChanged = false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::finishRename()
|
2009-12-03 18:35:36 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::abortRename()
|
2009-07-01 18:41:04 +02:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection <= NoCurrentRenameSelection)
|
2009-12-03 18:35:36 +01:00
|
|
|
return;
|
2009-12-03 19:23:29 +01:00
|
|
|
m_renameSelections[m_currentRenameSelection].format = m_occurrencesFormat;
|
2010-07-19 13:58:43 +02:00
|
|
|
m_currentRenameSelection = NoCurrentRenameSelection;
|
2009-12-03 18:35:36 +01:00
|
|
|
m_currentRenameSelectionBegin = QTextCursor();
|
|
|
|
|
m_currentRenameSelectionEnd = QTextCursor();
|
2009-07-01 18:41:04 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
2011-11-14 13:22:44 +01:00
|
|
|
|
2011-11-18 10:16:49 +01:00
|
|
|
semanticRehighlight(/* force = */ true);
|
2009-07-01 18:41:04 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-17 10:58:20 +02:00
|
|
|
/// \brief Called by \c CppEditorSupport when the document corresponding to the
|
|
|
|
|
/// file in this editor is updated.
|
|
|
|
|
void CPPEditorWidget::onDocumentUpdated()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-13 11:19:31 +02:00
|
|
|
m_updateOutlineTimer->start();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
const Macro *CPPEditorWidget::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
|
2009-12-21 14:54:10 +01:00
|
|
|
{
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!doc)
|
2009-12-21 14:54:10 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
int line, col;
|
|
|
|
|
convertPosition(cursor.position(), &line, &col);
|
|
|
|
|
|
2012-03-08 15:53:28 +01:00
|
|
|
if (const Macro *macro = doc->findMacroDefinitionAt(line)) {
|
|
|
|
|
QTextCursor macroCursor = cursor;
|
|
|
|
|
const QByteArray name = identifierUnderCursor(¯oCursor).toLatin1();
|
|
|
|
|
if (macro->name() == name)
|
|
|
|
|
return macro;
|
|
|
|
|
} else if (const Document::MacroUse *use = doc->findMacroUseAt(cursor.position())) {
|
2009-12-21 14:54:10 +01:00
|
|
|
return &use->macro();
|
2012-03-08 15:53:28 +01:00
|
|
|
}
|
2009-12-21 14:54:10 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-10-05 15:17:25 +02:00
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::findUsages()
|
2010-01-29 21:33:57 +01:00
|
|
|
{
|
2013-10-02 09:26:50 +02:00
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-08-05 13:59:09 +02:00
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
2010-12-03 13:49:35 +01:00
|
|
|
info.snapshot = CppModelManagerInterface::instance()->snapshot();
|
2010-08-05 13:59:09 +02:00
|
|
|
info.snapshot.insert(info.doc);
|
2010-05-31 12:09:28 +02:00
|
|
|
|
2012-03-08 15:53:28 +01:00
|
|
|
if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
|
2009-12-21 14:54:10 +01:00
|
|
|
m_modelManager->findMacroUsages(*macro);
|
2012-03-08 15:53:28 +01:00
|
|
|
} else {
|
|
|
|
|
CanonicalSymbol cs(this, info);
|
|
|
|
|
Symbol *canonicalSymbol = cs(textCursor());
|
|
|
|
|
if (canonicalSymbol)
|
|
|
|
|
m_modelManager->findUsages(canonicalSymbol, cs.context());
|
2009-10-09 11:33:31 +02:00
|
|
|
}
|
2009-10-05 15:17:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-05 13:59:09 +02:00
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::renameUsagesNow(const QString &replacement)
|
2010-08-05 13:59:09 +02:00
|
|
|
{
|
2013-10-02 09:26:50 +02:00
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-08-05 13:59:09 +02:00
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
2010-12-03 13:49:35 +01:00
|
|
|
info.snapshot = CppModelManagerInterface::instance()->snapshot();
|
2010-08-05 13:59:09 +02:00
|
|
|
info.snapshot.insert(info.doc);
|
|
|
|
|
|
2012-03-17 13:26:27 +01:00
|
|
|
if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
|
|
|
|
|
m_modelManager->renameMacroUsages(*macro, replacement);
|
|
|
|
|
} else {
|
|
|
|
|
CanonicalSymbol cs(this, info);
|
|
|
|
|
if (Symbol *canonicalSymbol = cs(textCursor()))
|
|
|
|
|
if (canonicalSymbol->identifier() != 0)
|
|
|
|
|
m_modelManager->renameUsages(canonicalSymbol, cs.context(), replacement);
|
|
|
|
|
}
|
2010-08-05 13:59:09 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::renameUsages()
|
2009-09-30 13:25:40 +02:00
|
|
|
{
|
2009-10-15 13:59:04 +02:00
|
|
|
renameUsagesNow();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::markSymbolsNow()
|
2009-10-09 11:33:31 +02:00
|
|
|
{
|
2010-07-19 12:17:08 +02:00
|
|
|
if (m_references.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
else if (m_referencesCursorPosition != position())
|
|
|
|
|
return;
|
|
|
|
|
else if (m_referencesRevision != editorRevision())
|
|
|
|
|
return;
|
2009-10-06 16:00:32 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
const SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
TranslationUnit *unit = info.doc->translationUnit();
|
|
|
|
|
const QList<int> result = m_references.result();
|
2009-09-30 13:25:40 +02:00
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
foreach (int index, result) {
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
unit->getTokenPosition(index, &line, &column);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
if (column)
|
|
|
|
|
--column; // adjust the column position.
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
const int len = unit->tokenAt(index).f.length;
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
QTextCursor cursor(document()->findBlockByNumber(line - 1));
|
|
|
|
|
cursor.setPosition(cursor.position() + column);
|
|
|
|
|
cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2010-07-19 12:17:08 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.format = m_occurrencesFormat;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
selections.append(sel);
|
2009-09-30 13:25:40 +02:00
|
|
|
|
2009-08-07 13:02:36 +02:00
|
|
|
}
|
2009-10-09 11:33:31 +02:00
|
|
|
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
2009-08-07 13:02:36 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-30 14:50:47 +02:00
|
|
|
static QList<int> lazyFindReferences(Scope *scope, QString code, Document::Ptr doc,
|
|
|
|
|
Snapshot snapshot)
|
2010-07-19 12:17:08 +02:00
|
|
|
{
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
2010-08-10 14:40:27 +02:00
|
|
|
snapshot.insert(doc);
|
|
|
|
|
typeOfExpression.init(doc, snapshot);
|
2013-01-19 13:17:34 +01:00
|
|
|
// make possible to instantiate templates
|
|
|
|
|
typeOfExpression.setExpandTemplates(true);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (Symbol *canonicalSymbol = CanonicalSymbol::canonicalSymbol(scope, code, typeOfExpression))
|
2013-04-30 14:50:47 +02:00
|
|
|
return CppModelManagerInterface::instance()->references(canonicalSymbol,
|
|
|
|
|
typeOfExpression.context());
|
2010-07-19 12:17:08 +02:00
|
|
|
return QList<int>();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::markSymbols(const QTextCursor &tc, const SemanticInfo &info)
|
2010-07-19 12:17:08 +02:00
|
|
|
{
|
|
|
|
|
abortRename();
|
|
|
|
|
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!info.doc)
|
2010-07-19 12:17:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2013-01-17 14:44:22 +01:00
|
|
|
if (const Macro *macro = findCanonicalMacro(textCursor(), info.doc)) {
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
|
|
|
|
|
//Macro definition
|
|
|
|
|
if (macro->fileName() == info.doc->fileName()) {
|
|
|
|
|
QTextCursor cursor(document());
|
|
|
|
|
cursor.setPosition(macro->offset());
|
2013-04-30 14:50:47 +02:00
|
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
|
|
|
|
|
macro->name().length());
|
2013-01-17 14:44:22 +01:00
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.format = m_occurrencesFormat;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Other macro uses
|
2013-03-08 09:55:44 +01:00
|
|
|
foreach (const Document::MacroUse &use, info.doc->macroUses()) {
|
2013-01-17 14:44:22 +01:00
|
|
|
if (use.macro().line() != macro->line()
|
|
|
|
|
|| use.macro().offset() != macro->offset()
|
|
|
|
|
|| use.macro().length() != macro->length()
|
|
|
|
|
|| use.macro().fileName() != macro->fileName())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor(document());
|
|
|
|
|
cursor.setPosition(use.begin());
|
|
|
|
|
cursor.setPosition(use.end(), QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.format = m_occurrencesFormat;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
}
|
2010-07-20 14:59:53 +02:00
|
|
|
|
2013-01-17 14:44:22 +01:00
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
|
|
|
|
} else {
|
|
|
|
|
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();
|
2013-04-30 14:50:47 +02:00
|
|
|
m_references = QtConcurrent::run(&lazyFindReferences, scope, expression, info.doc,
|
|
|
|
|
info.snapshot);
|
2013-01-17 14:44:22 +01:00
|
|
|
m_referencesWatcher.setFuture(m_references);
|
|
|
|
|
} else {
|
|
|
|
|
const QList<QTextEdit::ExtraSelection> selections = extraSelections(CodeSemanticsSelection);
|
|
|
|
|
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!selections.isEmpty())
|
2013-01-17 14:44:22 +01:00
|
|
|
setExtraSelections(CodeSemanticsSelection, QList<QTextEdit::ExtraSelection>());
|
|
|
|
|
}
|
2010-07-19 12:17:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::renameSymbolUnderCursor()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2013-10-02 09:26:50 +02:00
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-04-17 10:58:20 +02:00
|
|
|
CppEditorSupport *edSup = m_modelManager->cppEditorSupport(editor());
|
|
|
|
|
updateSemanticInfo(edSup->recalculateSemanticInfo(/* emitSignalWhenFinished = */ false));
|
2009-12-03 18:35:36 +01:00
|
|
|
abortRename();
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
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()
|
2009-07-02 12:21:00 +02:00
|
|
|
&& c.position() <= s.cursor.position()) {
|
2009-07-01 17:57:00 +02:00
|
|
|
m_currentRenameSelection = i;
|
2009-12-03 18:35:36 +01:00
|
|
|
m_firstRenameChange = true;
|
|
|
|
|
m_currentRenameSelectionBegin = QTextCursor(c.document()->docHandle(),
|
|
|
|
|
m_renameSelections[i].cursor.selectionStart());
|
|
|
|
|
m_currentRenameSelectionEnd = QTextCursor(c.document()->docHandle(),
|
2013-04-30 14:50:47 +02:00
|
|
|
m_renameSelections[i].cursor.selectionEnd());
|
2009-07-14 10:34:17 +02:00
|
|
|
m_renameSelections[i].format = m_occurrenceRenameFormat;
|
2009-07-01 17:57:00 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-29 16:40:32 +02:00
|
|
|
|
|
|
|
|
if (m_renameSelections.isEmpty())
|
2009-10-05 15:17:25 +02:00
|
|
|
renameUsages();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int charsAdded)
|
2009-07-01 18:41:04 +02:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection || m_inRename)
|
2009-07-02 18:38:21 +02:00
|
|
|
return;
|
|
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
if (position + charsAdded == m_currentRenameSelectionBegin.position()) {
|
|
|
|
|
// we are inserting at the beginning of the rename selection => expand
|
|
|
|
|
m_currentRenameSelectionBegin.setPosition(position);
|
2013-04-30 14:50:47 +02:00
|
|
|
m_renameSelections[m_currentRenameSelection].cursor.setPosition(position,
|
|
|
|
|
QTextCursor::KeepAnchor);
|
2009-12-03 18:35:36 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-30 14:50:47 +02:00
|
|
|
// the condition looks odd, but keep in mind that the begin
|
|
|
|
|
// and end cursors do move automatically
|
2009-12-03 18:35:36 +01:00
|
|
|
m_inRenameChanged = (position >= m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& position + charsAdded <= m_currentRenameSelectionEnd.position());
|
|
|
|
|
|
|
|
|
|
if (!m_inRenameChanged)
|
2009-07-01 18:41:04 +02:00
|
|
|
abortRename();
|
|
|
|
|
|
|
|
|
|
if (charsRemoved > 0)
|
|
|
|
|
updateUses();
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 18:50:47 +02:00
|
|
|
void CPPEditorWidget::jumpToOutlineElement(int index)
|
|
|
|
|
{
|
|
|
|
|
QModelIndex modelIndex = m_outlineCombo->view()->currentIndex();
|
|
|
|
|
// When the user clicks on an item in the combo box,
|
|
|
|
|
// the view's currentIndex is updated, so we want to use that.
|
|
|
|
|
// When the scroll wheel was used on the combo box,
|
|
|
|
|
// the view's currentIndex is not updated,
|
2013-10-07 13:34:40 +02:00
|
|
|
// but the passed index to this function is correct.
|
2012-09-20 18:50:47 +02:00
|
|
|
// So, if the view has a current index, we reset it, to be able
|
|
|
|
|
// to distinguish wheel events later
|
|
|
|
|
if (modelIndex.isValid())
|
|
|
|
|
m_outlineCombo->view()->setCurrentIndex(QModelIndex());
|
|
|
|
|
else
|
|
|
|
|
modelIndex = m_proxyModel->index(index, 0); // toplevel index
|
|
|
|
|
QModelIndex sourceIndex = m_proxyModel->mapToSource(modelIndex);
|
|
|
|
|
Symbol *symbol = m_outlineModel->symbolFromIndex(sourceIndex);
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!symbol)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
openCppEditorAt(linkToSymbol(symbol));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::setSortedOutline(bool sort)
|
2009-04-27 17:22:49 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (sort != sortedOutline()) {
|
2009-04-27 17:22:49 +02:00
|
|
|
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);
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineIndexNow();
|
2009-04-27 17:22:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
bool CPPEditorWidget::sortedOutline() const
|
2009-04-27 17:22:49 +02:00
|
|
|
{
|
|
|
|
|
return (m_proxyModel->sortColumn() == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateOutlineNow()
|
2010-07-13 11:19:31 +02:00
|
|
|
{
|
2013-10-02 09:26:50 +02:00
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-13 11:19:31 +02:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
2013-07-04 13:30:26 +02:00
|
|
|
Document::Ptr document = snapshot.document(editorDocument()->filePath());
|
2010-07-13 11:19:31 +02:00
|
|
|
|
|
|
|
|
if (!document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (document->editorRevision() != editorRevision()) {
|
|
|
|
|
m_updateOutlineTimer->start();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModel->rebuild(document);
|
2010-07-13 11:19:31 +02:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
|
2010-07-13 11:19:31 +02:00
|
|
|
treeView->sync();
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineIndexNow();
|
2010-07-13 11:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateOutlineIndex()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
m_updateOutlineIndexTimer->start();
|
2009-06-24 16:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::highlightUses(const QList<SemanticInfo::Use> &uses,
|
2013-02-14 12:48:48 +01:00
|
|
|
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
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
foreach (const SemanticInfo::Use &use, uses) {
|
2013-02-13 15:01:48 +01:00
|
|
|
if (use.isInvalid())
|
|
|
|
|
continue;
|
2009-06-25 16:11:28 +02:00
|
|
|
|
2013-02-13 15:01:48 +01:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2009-09-23 18:06:59 +02:00
|
|
|
if (isUnused)
|
2009-09-24 10:05:33 +02:00
|
|
|
sel.format = m_occurrencesUnusedFormat;
|
2009-09-23 18:06:59 +02:00
|
|
|
else
|
2009-09-24 10:05:33 +02:00
|
|
|
sel.format = m_occurrencesFormat;
|
2009-06-26 09:48:40 +02:00
|
|
|
|
2009-09-24 10:05:33 +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
|
|
|
|
2009-09-24 10:05:33 +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
|
|
|
|
2009-07-09 17:32:39 +02:00
|
|
|
selections->append(sel);
|
2009-06-25 16:11:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateOutlineIndexNow()
|
2009-06-24 16:40:30 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (!m_outlineModel->document())
|
2009-12-15 15:52:55 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
if (m_outlineModel->document()->editorRevision() != editorRevision()) {
|
|
|
|
|
m_updateOutlineIndexTimer->start();
|
2009-12-15 15:52:55 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_updateOutlineIndexTimer->stop();
|
2009-12-15 15:52:55 +01:00
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModelIndex = QModelIndex(); //invalidate
|
|
|
|
|
QModelIndex comboIndex = outlineModelIndex();
|
2010-07-13 11:44:05 +02:00
|
|
|
|
2010-07-08 16:00:41 +02:00
|
|
|
if (comboIndex.isValid()) {
|
2010-07-13 11:37:06 +02:00
|
|
|
bool blocked = m_outlineCombo->blockSignals(true);
|
2010-07-13 11:44:05 +02:00
|
|
|
|
|
|
|
|
// There is no direct way to select a non-root item
|
|
|
|
|
m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
|
2010-07-13 11:44:05 +02:00
|
|
|
m_outlineCombo->setRootModelIndex(QModelIndex());
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
updateOutlineToolTip();
|
2010-07-13 11:44:05 +02:00
|
|
|
|
|
|
|
|
m_outlineCombo->blockSignals(blocked);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateOutlineToolTip()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineCombo->setToolTip(m_outlineCombo->currentText());
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateUses()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2010-07-13 14:37:31 +02:00
|
|
|
if (editorRevision() != m_highlightRevision)
|
|
|
|
|
m_highlighter.cancel();
|
2013-05-06 14:59:15 +02:00
|
|
|
|
|
|
|
|
// Block premature semantic info calculation when editor is created.
|
2013-10-02 09:26:50 +02:00
|
|
|
if (m_modelManager && m_modelManager->cppEditorSupport(editor())->initialized())
|
2013-05-06 14:59:15 +02:00
|
|
|
m_updateUsesTimer->start();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateUsesNow()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection != NoCurrentRenameSelection)
|
2009-07-01 17:57:00 +02:00
|
|
|
return;
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
semanticRehighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::highlightSymbolUsages(int from, int to)
|
2010-07-13 14:37:31 +02:00
|
|
|
{
|
|
|
|
|
if (editorRevision() != m_highlightRevision)
|
|
|
|
|
return; // outdated
|
|
|
|
|
|
|
|
|
|
else if (m_highlighter.isCanceled())
|
|
|
|
|
return; // aborted
|
|
|
|
|
|
2011-08-16 09:47:54 +02:00
|
|
|
TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
|
|
|
|
|
QTC_ASSERT(highlighter, return);
|
2010-07-15 16:03:48 +02:00
|
|
|
|
2011-08-16 09:47:54 +02:00
|
|
|
TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
|
|
|
|
|
highlighter, m_highlighter, from, to, m_semanticHighlightFormatMap);
|
2010-07-13 14:37:31 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::finishHighlightSymbolUsages()
|
2010-07-05 18:52:52 +02:00
|
|
|
{
|
2010-07-13 14:37:31 +02:00
|
|
|
if (editorRevision() != m_highlightRevision)
|
2010-07-05 18:52:52 +02:00
|
|
|
return; // outdated
|
|
|
|
|
|
2013-05-02 11:42:33 +02:00
|
|
|
if (m_highlighter.isCanceled())
|
2010-07-05 18:52:52 +02:00
|
|
|
return; // aborted
|
|
|
|
|
|
2011-08-16 09:47:54 +02:00
|
|
|
TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
|
|
|
|
|
QTC_ASSERT(highlighter, return);
|
2010-07-13 14:37:31 +02:00
|
|
|
|
2011-08-16 09:47:54 +02:00
|
|
|
TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
|
|
|
|
|
highlighter, m_highlighter);
|
2010-07-05 18:52:52 +02:00
|
|
|
}
|
|
|
|
|
|
2013-02-05 14:14:33 +01:00
|
|
|
void CPPEditorWidget::switchDeclarationDefinition(bool inNextSplit)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-04-26 17:35:55 +02:00
|
|
|
if (!m_modelManager)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2013-04-26 17:35:55 +02:00
|
|
|
if (!m_lastSemanticInfo.doc)
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2013-04-26 17:35:55 +02:00
|
|
|
// Find function declaration or definition under cursor
|
|
|
|
|
Function *functionDefinitionSymbol = 0;
|
|
|
|
|
Symbol *functionDeclarationSymbol = 0;
|
|
|
|
|
|
|
|
|
|
ASTPath astPathFinder(m_lastSemanticInfo.doc);
|
|
|
|
|
const QList<AST *> astPath = astPathFinder(textCursor());
|
|
|
|
|
|
|
|
|
|
for (int i = 0, size = astPath.size(); i < size; ++i) {
|
|
|
|
|
AST *ast = astPath.at(i);
|
|
|
|
|
if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) {
|
|
|
|
|
if ((functionDefinitionSymbol = functionDefinitionAST->symbol))
|
|
|
|
|
break; // Function definition found!
|
|
|
|
|
} else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
|
|
|
|
|
if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
|
|
|
|
|
if (Symbol *symbol = symbols->value) {
|
|
|
|
|
if (symbol->isDeclaration() && symbol->type()->isFunctionType()) {
|
|
|
|
|
functionDeclarationSymbol = symbol;
|
|
|
|
|
break; // Function declaration found!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2013-04-26 17:35:55 +02:00
|
|
|
// Link to function definition/declaration
|
|
|
|
|
CPPEditorWidget::Link symbolLink;
|
|
|
|
|
if (functionDeclarationSymbol) {
|
|
|
|
|
symbolLink = linkToSymbol(symbolFinder()
|
2013-09-26 14:44:03 +02:00
|
|
|
->findMatchingDefinition(functionDeclarationSymbol, m_modelManager->snapshot()));
|
2013-04-26 17:35:55 +02:00
|
|
|
} else if (functionDefinitionSymbol) {
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
LookupContext context(m_lastSemanticInfo.doc, snapshot);
|
|
|
|
|
ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
|
|
|
|
|
const QList<LookupItem> declarations = context.lookup(functionDefinitionSymbol->name(),
|
|
|
|
|
functionDefinitionSymbol->enclosingScope());
|
|
|
|
|
|
|
|
|
|
QList<Symbol *> best;
|
|
|
|
|
foreach (const LookupItem &r, declarations) {
|
|
|
|
|
if (Symbol *decl = r.declaration()) {
|
|
|
|
|
if (Function *funTy = decl->type()->asFunctionType()) {
|
|
|
|
|
if (funTy->isEqualTo(functionDefinitionSymbol)) {
|
|
|
|
|
if (decl != functionDefinitionSymbol && binding == r.binding())
|
|
|
|
|
best.prepend(decl);
|
|
|
|
|
else
|
|
|
|
|
best.append(decl);
|
2010-12-10 10:47:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-05-12 16:36:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-05 14:14:33 +01:00
|
|
|
|
2013-04-26 17:35:55 +02:00
|
|
|
if (best.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
symbolLink = linkToSymbol(best.first());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2013-04-26 17:35:55 +02:00
|
|
|
|
|
|
|
|
// Open Editor at link position
|
|
|
|
|
if (symbolLink.hasValidTarget())
|
|
|
|
|
openCppEditorAt(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2013-09-20 10:32:41 +02:00
|
|
|
QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor)
|
2010-12-10 11:50:36 +01:00
|
|
|
{
|
|
|
|
|
macroCursor->movePosition(QTextCursor::StartOfWord);
|
|
|
|
|
macroCursor->movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
return macroCursor->selectedText();
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-04 20:11:10 +02:00
|
|
|
CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
|
|
|
|
|
bool inNextSplit)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (!m_modelManager)
|
2013-09-20 10:32:41 +02:00
|
|
|
return Link();
|
2009-01-28 23:46:43 +01:00
|
|
|
|
2013-07-04 20:11:10 +02:00
|
|
|
return m_followSymbolUnderCursor->findLink(cursor, resolveTarget, m_modelManager->snapshot(),
|
|
|
|
|
m_lastSemanticInfo.doc, symbolFinder(), inNextSplit);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
unsigned CPPEditorWidget::editorRevision() const
|
2009-12-15 15:52:55 +01:00
|
|
|
{
|
|
|
|
|
return document()->revision();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
bool CPPEditorWidget::isOutdated() const
|
2009-11-18 15:06:26 +01:00
|
|
|
{
|
2009-12-15 15:52:55 +01:00
|
|
|
if (m_lastSemanticInfo.revision != editorRevision())
|
2009-11-18 15:06:26 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
SemanticInfo CPPEditorWidget::semanticInfo() const
|
2009-07-10 12:09:26 +02:00
|
|
|
{
|
|
|
|
|
return m_lastSemanticInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CPlusPlus::OverviewModel *CPPEditorWidget::outlineModel() const
|
2010-07-08 15:47:59 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
return m_outlineModel;
|
2010-07-08 15:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
QModelIndex CPPEditorWidget::outlineModelIndex()
|
2010-07-08 16:00:41 +02:00
|
|
|
{
|
2010-07-13 11:37:06 +02:00
|
|
|
if (!m_outlineModelIndex.isValid()) {
|
2010-07-12 16:40:15 +02:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
2010-07-13 11:37:06 +02:00
|
|
|
m_outlineModelIndex = indexForPosition(line, column);
|
|
|
|
|
emit outlineModelIndexChanged(m_outlineModelIndex);
|
2010-07-12 16:40:15 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
return m_outlineModelIndex;
|
2010-07-08 16:00:41 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
bool CPPEditorWidget::event(QEvent *e)
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
|
|
|
|
switch (e->type()) {
|
|
|
|
|
case QEvent::ShortcutOverride:
|
2011-09-05 10:33:17 +02:00
|
|
|
// handle escape manually if a rename is active
|
2011-08-10 09:50:04 +02:00
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape
|
2011-09-05 10:33:17 +02:00
|
|
|
&& m_currentRenameSelection != NoCurrentRenameSelection) {
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
return BaseTextEditorWidget::event(e);
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::performQuickFix(int index)
|
2009-11-23 16:55:04 +01:00
|
|
|
{
|
2010-06-03 14:45:55 +02:00
|
|
|
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
|
|
|
|
|
op->perform();
|
2009-11-23 16:55:04 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
// ### enable
|
|
|
|
|
// updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
|
|
|
|
|
2013-02-18 15:12:09 +01:00
|
|
|
QPointer<QMenu> menu(new QMenu(this));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-05-24 13:49:06 +02:00
|
|
|
Core::ActionContainer *mcontext = Core::ActionManager::actionContainer(Constants::M_CONTEXT);
|
2008-12-02 12:01:29 +01:00
|
|
|
QMenu *contextMenu = mcontext->menu();
|
|
|
|
|
|
2010-12-10 15:51:25 +01:00
|
|
|
QMenu *quickFixMenu = new QMenu(tr("&Refactor"), menu);
|
2013-04-30 14:50:47 +02:00
|
|
|
quickFixMenu->addAction(Core::ActionManager::command(
|
|
|
|
|
Constants::RENAME_SYMBOL_UNDER_CURSOR)->action());
|
2009-11-23 16:55:04 +01:00
|
|
|
|
|
|
|
|
QSignalMapper mapper;
|
|
|
|
|
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!isOutdated()) {
|
2011-04-15 16:19:23 +02:00
|
|
|
TextEditor::IAssistInterface *interface =
|
|
|
|
|
createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked);
|
|
|
|
|
if (interface) {
|
|
|
|
|
QScopedPointer<TextEditor::IAssistProcessor> processor(
|
2013-03-19 13:44:02 +01:00
|
|
|
CppEditorPlugin::instance()->quickFixProvider()->createProcessor());
|
2011-04-15 16:19:23 +02:00
|
|
|
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;
|
2009-11-23 16:55:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-15 14:33:44 +02:00
|
|
|
foreach (QAction *action, contextMenu->actions()) {
|
2008-12-02 12:01:29 +01:00
|
|
|
menu->addAction(action);
|
2012-01-11 14:26:09 +01:00
|
|
|
if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
|
2010-09-15 14:33:44 +02:00
|
|
|
menu->addMenu(quickFixMenu);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-11-02 14:02:18 +01:00
|
|
|
appendStandardContextMenuActions(menu);
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
menu->exec(e->globalPos());
|
2013-02-18 15:12:09 +01:00
|
|
|
if (!menu)
|
|
|
|
|
return;
|
2009-11-23 16:55:04 +01:00
|
|
|
m_quickFixes.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::keyPressEvent(QKeyEvent *e)
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2010-07-19 13:58:43 +02:00
|
|
|
if (m_currentRenameSelection == NoCurrentRenameSelection) {
|
2011-12-07 15:05:02 +01:00
|
|
|
if (!handleDocumentationComment(e))
|
|
|
|
|
TextEditor::BaseTextEditorWidget::keyPressEvent(e);
|
2009-07-01 17:57:00 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
// key handling for renames
|
|
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
2013-04-30 14:50:47 +02:00
|
|
|
const QTextCursor::MoveMode moveMode = (e->modifiers() & Qt::ShiftModifier)
|
|
|
|
|
? QTextCursor::KeepAnchor
|
|
|
|
|
: QTextCursor::MoveAnchor;
|
2009-07-01 17:57:00 +02:00
|
|
|
|
|
|
|
|
switch (e->key()) {
|
|
|
|
|
case Qt::Key_Enter:
|
|
|
|
|
case Qt::Key_Return:
|
|
|
|
|
case Qt::Key_Escape:
|
2009-07-01 18:41:04 +02:00
|
|
|
abortRename();
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
2009-07-01 18:41:04 +02:00
|
|
|
return;
|
2009-07-01 17:57:00 +02:00
|
|
|
case Qt::Key_Home: {
|
2009-07-02 15:09:02 +02:00
|
|
|
// Send home to start of name when within the name and not at the start
|
2009-12-03 18:35:36 +01:00
|
|
|
if (cursor.position() > m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& cursor.position() <= m_currentRenameSelectionEnd.position()) {
|
|
|
|
|
cursor.setPosition(m_currentRenameSelectionBegin.position(), moveMode);
|
2009-07-02 15:09:02 +02:00
|
|
|
setTextCursor(cursor);
|
2009-07-02 12:21:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
case Qt::Key_End: {
|
2009-07-02 15:09:02 +02:00
|
|
|
// Send end to end of name when within the name and not at the end
|
2009-12-03 18:35:36 +01:00
|
|
|
if (cursor.position() >= m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& cursor.position() < m_currentRenameSelectionEnd.position()) {
|
|
|
|
|
cursor.setPosition(m_currentRenameSelectionEnd.position(), moveMode);
|
2009-07-02 15:09:02 +02:00
|
|
|
setTextCursor(cursor);
|
2009-07-02 12:21:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
case Qt::Key_Backspace: {
|
2010-03-25 16:44:41 +01:00
|
|
|
if (cursor.position() == m_currentRenameSelectionBegin.position()
|
|
|
|
|
&& !cursor.hasSelection()) {
|
|
|
|
|
// Eat backspace at start of name when there is no selection
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Qt::Key_Delete: {
|
2010-03-25 16:44:41 +01:00
|
|
|
if (cursor.position() == m_currentRenameSelectionEnd.position()
|
|
|
|
|
&& !cursor.hasSelection()) {
|
|
|
|
|
// Eat delete at end of name when there is no selection
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-12-03 18:35:36 +01:00
|
|
|
} // switch
|
|
|
|
|
|
|
|
|
|
startRename();
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-12-03 18:35:36 +01:00
|
|
|
bool wantEditBlock = (cursor.position() >= m_currentRenameSelectionBegin.position()
|
2013-05-05 22:43:52 +03:00
|
|
|
&& cursor.position() <= m_currentRenameSelectionEnd.position());
|
2009-12-03 18:35:36 +01:00
|
|
|
|
|
|
|
|
if (wantEditBlock) {
|
|
|
|
|
// possible change inside rename selection
|
|
|
|
|
if (m_firstRenameChange)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
else
|
|
|
|
|
cursor.joinPreviousEditBlock();
|
|
|
|
|
m_firstRenameChange = false;
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::keyPressEvent(e);
|
2009-12-03 18:35:36 +01:00
|
|
|
if (wantEditBlock)
|
2010-01-06 14:34:09 +01:00
|
|
|
cursor.endEditBlock();
|
2009-12-03 18:35:36 +01:00
|
|
|
finishRename();
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
Core::IEditor *CPPEditor::duplicate(QWidget *parent)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditorWidget *newEditor = new CPPEditorWidget(parent);
|
|
|
|
|
newEditor->duplicateFrom(editorWidget());
|
2013-06-25 15:47:01 +02:00
|
|
|
// A new QTextDocument was set, so update our signal/slot connection to the new document
|
|
|
|
|
newEditor->updateContentsChangedSignal();
|
2013-03-19 13:44:02 +01:00
|
|
|
CppEditorPlugin::instance()->initializeEditor(newEditor);
|
2011-02-21 16:02:26 +01:00
|
|
|
return newEditor->editor();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-11-10 11:36:51 +01:00
|
|
|
Core::Id CPPEditor::id() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-08-30 16:38:57 +02:00
|
|
|
return CppEditor::Constants::CPPEDITOR_ID;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-05-10 20:43:03 +02:00
|
|
|
bool CPPEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
|
2009-10-13 17:17:08 +02:00
|
|
|
{
|
2013-05-24 08:56:20 +02:00
|
|
|
if (!TextEditor::BaseTextEditor::open(errorString, fileName, realFileName))
|
|
|
|
|
return false;
|
2013-08-30 16:38:57 +02:00
|
|
|
editorWidget()->setMimeType(Core::MimeDatabase::findByFile(QFileInfo(fileName)).type());
|
2013-05-24 08:56:20 +02:00
|
|
|
return true;
|
2009-10-13 17:17:08 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:28:19 +01:00
|
|
|
const Utils::CommentDefinition *CPPEditor::commentDefinition() const
|
|
|
|
|
{
|
|
|
|
|
return &m_commentDefinition;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-30 12:55:06 +02:00
|
|
|
TextEditor::CompletionAssistProvider *CPPEditor::completionAssistProvider()
|
|
|
|
|
{
|
|
|
|
|
return CppModelManagerInterface::instance()->cppEditorSupport(this)->completionAssistProvider();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::setFontSettings(fs);
|
2013-08-13 12:57:31 +02:00
|
|
|
TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!highlighter)
|
|
|
|
|
return;
|
|
|
|
|
|
2012-04-26 14:17:42 +02:00
|
|
|
m_occurrencesFormat = fs.toTextCharFormat(TextEditor::C_OCCURRENCES);
|
|
|
|
|
m_occurrencesUnusedFormat = fs.toTextCharFormat(TextEditor::C_OCCURRENCES_UNUSED);
|
2009-11-30 17:23:31 +01:00
|
|
|
m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
|
|
|
m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
|
|
|
|
|
m_occurrencesUnusedFormat.clearForeground();
|
|
|
|
|
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
|
2012-04-26 14:17:42 +02:00
|
|
|
m_occurrenceRenameFormat = fs.toTextCharFormat(TextEditor::C_OCCURRENCES_RENAME);
|
2013-04-16 16:48:10 +02:00
|
|
|
|
|
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::TypeUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_TYPE);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::LocalUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_LOCAL);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::FieldUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_FIELD);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::EnumerationUse] =
|
2012-08-27 10:42:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_ENUMERATION);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::VirtualMethodUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_VIRTUAL_METHOD);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::LabelUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_LABEL);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::MacroUse] =
|
2012-04-26 14:17:42 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_PREPROCESSOR);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::FunctionUse] =
|
2010-10-18 17:45:49 +02:00
|
|
|
fs.toTextCharFormat(TextEditor::C_FUNCTION);
|
2013-04-16 16:48:10 +02:00
|
|
|
m_semanticHighlightFormatMap[CppHighlightingSupport::PseudoKeywordUse] =
|
2012-06-25 23:49:17 +04:00
|
|
|
fs.toTextCharFormat(TextEditor::C_KEYWORD);
|
2012-04-26 14:17:42 +02:00
|
|
|
m_keywordFormat = fs.toTextCharFormat(TextEditor::C_KEYWORD);
|
2010-08-09 10:34:31 +02:00
|
|
|
|
2013-04-30 14:50:47 +02:00
|
|
|
// only set the background, we do not want to modify foreground properties
|
|
|
|
|
// set by the syntax highlighter or the link
|
2009-09-24 15:19:45 +02:00
|
|
|
m_occurrencesFormat.clearForeground();
|
|
|
|
|
m_occurrenceRenameFormat.clearForeground();
|
2010-07-15 17:01:37 +02:00
|
|
|
|
|
|
|
|
// Clear all additional formats since they may have changed
|
|
|
|
|
QTextBlock b = document()->firstBlock();
|
|
|
|
|
while (b.isValid()) {
|
2013-02-26 11:17:13 +01:00
|
|
|
QList<QTextLayout::FormatRange> noFormats;
|
|
|
|
|
highlighter->setExtraAdditionalFormats(b, noFormats);
|
2010-07-15 17:01:37 +02:00
|
|
|
b = b.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This also triggers an update of the additional formats
|
|
|
|
|
highlighter->rehighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::setTabSettings(const TextEditor::TabSettings &ts)
|
2010-07-05 13:48:53 +02:00
|
|
|
{
|
2010-07-07 11:01:38 +02:00
|
|
|
CppTools::QtStyleCodeFormatter formatter;
|
|
|
|
|
formatter.invalidateCache(document());
|
2010-07-05 13:48:53 +02:00
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::setTabSettings(ts);
|
2010-07-05 13:48:53 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::unCommentSelection()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-05 11:06:05 +02:00
|
|
|
Utils::unCommentSelection(this);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-03 15:48:14 +01:00
|
|
|
void CPPEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
|
|
|
|
|
{
|
|
|
|
|
CppTools::QtStyleCodeFormatter formatter;
|
|
|
|
|
formatter.invalidateCache(document());
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
CPPEditorWidget::Link CPPEditorWidget::linkToSymbol(CPlusPlus::Symbol *symbol)
|
2008-12-11 10:28:39 +01:00
|
|
|
{
|
2010-08-13 16:38:45 +02:00
|
|
|
if (!symbol)
|
|
|
|
|
return Link();
|
|
|
|
|
|
2013-02-06 14:23:18 +01:00
|
|
|
const QString filename = QString::fromUtf8(symbol->fileName(),
|
2009-03-23 17:20:19 +01:00
|
|
|
symbol->fileNameLength());
|
2013-02-06 14:23:18 +01:00
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
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
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
if (symbol->isGenerated())
|
2009-03-05 16:29:18 +01:00
|
|
|
column = 0;
|
2009-03-03 13:46:37 +01:00
|
|
|
|
2013-02-06 14:23:18 +01:00
|
|
|
return Link(filename, line, column);
|
2009-03-23 17:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-05 14:14:33 +01:00
|
|
|
bool CPPEditorWidget::openCppEditorAt(const Link &link, bool inNextSplit)
|
2009-03-23 17:20:19 +01:00
|
|
|
{
|
2013-02-05 14:14:33 +01:00
|
|
|
if (!link.hasValidTarget())
|
2009-03-23 17:20:19 +01:00
|
|
|
return false;
|
|
|
|
|
|
2013-05-31 12:52:53 +02:00
|
|
|
Core::EditorManager::OpenEditorFlags flags;
|
2013-05-31 15:16:06 +02:00
|
|
|
if (inNextSplit)
|
|
|
|
|
flags |= Core::EditorManager::OpenInOtherSplit;
|
2013-05-30 17:26:51 +02:00
|
|
|
return Core::EditorManager::openEditorAt(link.targetFileName,
|
|
|
|
|
link.targetLine,
|
|
|
|
|
link.targetColumn,
|
2013-05-31 15:16:06 +02:00
|
|
|
Constants::CPPEDITOR_ID,
|
|
|
|
|
flags);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
2011-11-14 13:34:41 +01:00
|
|
|
void CPPEditorWidget::semanticRehighlight(bool force)
|
2009-07-09 12:14:00 +02:00
|
|
|
{
|
2013-10-02 09:26:50 +02:00
|
|
|
if (m_modelManager)
|
|
|
|
|
m_modelManager->cppEditorSupport(editor())->recalculateSemanticInfoDetached(force);
|
2013-04-17 10:58:20 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-28 11:20:02 +03:00
|
|
|
void CPPEditorWidget::highlighterStarted(QFuture<TextEditor::HighlightingResult> *highlighter,
|
2013-04-17 10:58:20 +02:00
|
|
|
unsigned revision)
|
|
|
|
|
{
|
2013-04-28 11:20:02 +03:00
|
|
|
m_highlighter = *highlighter;
|
2013-04-17 10:58:20 +02:00
|
|
|
m_highlightRevision = revision;
|
|
|
|
|
m_highlightWatcher.setFuture(m_highlighter);
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
2009-07-09 12:14:00 +02:00
|
|
|
{
|
2009-12-15 15:52:55 +01:00
|
|
|
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
|
|
|
m_lastSemanticInfo = semanticInfo; // update the semantic info
|
2009-07-10 12:09:26 +02:00
|
|
|
|
2009-07-09 12:14:00 +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();
|
2010-07-19 13:58:43 +02:00
|
|
|
m_currentRenameSelection = NoCurrentRenameSelection;
|
2009-07-09 12:14:00 +02:00
|
|
|
|
2011-11-14 13:14:39 +01:00
|
|
|
// We can use the semanticInfo's snapshot (and avoid locking), but not its
|
|
|
|
|
// document, since it doesn't contain expanded macros.
|
2013-07-04 13:30:26 +02:00
|
|
|
LookupContext context(semanticInfo.snapshot.document(editorDocument()->filePath()),
|
2011-11-14 13:14:39 +01:00
|
|
|
semanticInfo.snapshot);
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 13:14:39 +01:00
|
|
|
if (uses.size() == 1) {
|
|
|
|
|
if (!CppTools::isOwnershipRAIIType(it.key(), context)) {
|
|
|
|
|
// it's an unused declaration
|
2013-02-14 12:48:48 +01:00
|
|
|
highlightUses(uses, &unusedSelections);
|
2011-11-14 13:14:39 +01:00
|
|
|
}
|
|
|
|
|
} else if (good && m_renameSelections.isEmpty()) {
|
2013-02-14 12:48:48 +01:00
|
|
|
highlightUses(uses, &m_renameSelections);
|
2011-11-14 13:14:39 +01:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2009-11-30 15:21:16 +01:00
|
|
|
setExtraSelections(UnusedSymbolSelection, unusedSelections);
|
2010-05-25 15:55:12 +02:00
|
|
|
|
2013-07-24 11:52:01 +02:00
|
|
|
if (!m_renameSelections.isEmpty())
|
2010-05-25 15:55:12 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections); // ###
|
2013-07-17 00:01:45 +03:00
|
|
|
else
|
2010-07-19 12:17:08 +02:00
|
|
|
markSymbols(textCursor(), semanticInfo);
|
2010-05-21 15:45:18 +02:00
|
|
|
|
|
|
|
|
m_lastSemanticInfo.forced = false; // clear the forced flag
|
2011-08-10 09:50:04 +02:00
|
|
|
|
|
|
|
|
// schedule a check for a decl/def link
|
|
|
|
|
updateFunctionDeclDefLink();
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-30 14:50:47 +02:00
|
|
|
QModelIndex CPPEditorWidget::indexForPosition(int line, int column,
|
|
|
|
|
const QModelIndex &rootIndex) const
|
2010-07-08 16:00:41 +02:00
|
|
|
{
|
|
|
|
|
QModelIndex lastIndex = rootIndex;
|
|
|
|
|
|
2010-07-13 11:37:06 +02:00
|
|
|
const int rowCount = m_outlineModel->rowCount(rootIndex);
|
2010-07-08 16:00:41 +02:00
|
|
|
for (int row = 0; row < rowCount; ++row) {
|
2010-07-13 11:37:06 +02:00
|
|
|
const QModelIndex index = m_outlineModel->index(row, 0, rootIndex);
|
|
|
|
|
Symbol *symbol = m_outlineModel->symbolFromIndex(index);
|
2010-07-08 16:00:41 +02:00
|
|
|
if (symbol && symbol->line() > unsigned(line))
|
|
|
|
|
break;
|
|
|
|
|
lastIndex = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastIndex != rootIndex) {
|
|
|
|
|
// recurse
|
|
|
|
|
lastIndex = indexForPosition(line, column, lastIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lastIndex;
|
|
|
|
|
}
|
2010-07-08 17:03:38 +02:00
|
|
|
|
2012-04-26 14:17:42 +02:00
|
|
|
QVector<TextEditor::TextStyle> CPPEditorWidget::highlighterFormatCategories()
|
2010-10-27 17:38:22 +02:00
|
|
|
{
|
2012-04-26 14:17:42 +02:00
|
|
|
static QVector<TextEditor::TextStyle> categories;
|
2010-10-27 17:38:22 +02:00
|
|
|
if (categories.isEmpty()) {
|
2012-04-26 14:17:42 +02:00
|
|
|
categories << TextEditor::C_NUMBER
|
|
|
|
|
<< TextEditor::C_STRING
|
|
|
|
|
<< TextEditor::C_TYPE
|
|
|
|
|
<< TextEditor::C_KEYWORD
|
|
|
|
|
<< TextEditor::C_OPERATOR
|
|
|
|
|
<< TextEditor::C_PREPROCESSOR
|
|
|
|
|
<< TextEditor::C_LABEL
|
|
|
|
|
<< TextEditor::C_COMMENT
|
|
|
|
|
<< TextEditor::C_DOXYGEN_COMMENT
|
|
|
|
|
<< TextEditor::C_DOXYGEN_TAG
|
|
|
|
|
<< TextEditor::C_VISUAL_WHITESPACE;
|
2010-10-27 17:38:22 +02:00
|
|
|
}
|
|
|
|
|
return categories;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-15 16:19:23 +02:00
|
|
|
TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface(
|
|
|
|
|
TextEditor::AssistKind kind,
|
|
|
|
|
TextEditor::AssistReason reason) const
|
|
|
|
|
{
|
|
|
|
|
if (kind == TextEditor::Completion) {
|
2013-08-30 13:13:44 +02:00
|
|
|
CppEditorSupport *ces = CppModelManagerInterface::instance()->cppEditorSupport(editor());
|
|
|
|
|
CppCompletionAssistProvider *cap = ces->completionAssistProvider();
|
2013-08-19 16:05:29 +02:00
|
|
|
if (cap) {
|
2013-08-30 13:13:44 +02:00
|
|
|
return cap->createAssistInterface(
|
2013-08-19 16:05:29 +02:00
|
|
|
ProjectExplorer::ProjectExplorerPlugin::currentProject(),
|
|
|
|
|
editor(), document(), position(), reason);
|
|
|
|
|
}
|
2011-04-15 16:19:23 +02:00
|
|
|
} else if (kind == TextEditor::QuickFix) {
|
2011-11-14 13:22:44 +01:00
|
|
|
if (!semanticInfo().doc || isOutdated())
|
2011-04-15 16:19:23 +02:00
|
|
|
return 0;
|
|
|
|
|
return new CppQuickFixAssistInterface(const_cast<CPPEditorWidget *>(this), reason);
|
2013-07-04 20:11:10 +02:00
|
|
|
} else {
|
|
|
|
|
return BaseTextEditorWidget::createAssistInterface(kind, reason);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-05 10:33:17 +02:00
|
|
|
QSharedPointer<FunctionDeclDefLink> CPPEditorWidget::declDefLink() const
|
|
|
|
|
{
|
|
|
|
|
return m_declDefLink;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
void CPPEditorWidget::onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker)
|
|
|
|
|
{
|
|
|
|
|
if (marker.data.canConvert<FunctionDeclDefLink::Marker>())
|
|
|
|
|
applyDeclDefLinkChanges(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::updateFunctionDeclDefLink()
|
|
|
|
|
{
|
|
|
|
|
const int pos = textCursor().selectionStart();
|
|
|
|
|
|
2011-08-25 13:22:21 +02:00
|
|
|
// if there's already a link, abort it if the cursor is outside or the name changed
|
2012-11-23 13:25:45 +01:00
|
|
|
// (adding a prefix is an exception since the user might type a return type)
|
2011-08-10 09:50:04 +02:00
|
|
|
if (m_declDefLink
|
|
|
|
|
&& (pos < m_declDefLink->linkSelection.selectionStart()
|
2011-08-25 13:22:21 +02:00
|
|
|
|| pos > m_declDefLink->linkSelection.selectionEnd()
|
2012-11-23 13:25:45 +01:00
|
|
|
|| !m_declDefLink->nameSelection.selectedText().trimmed()
|
|
|
|
|
.endsWith(m_declDefLink->nameInitial))) {
|
2011-08-10 09:50:04 +02:00
|
|
|
abortDeclDefLink();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// don't start a new scan if there's one active and the cursor is already in the scanned area
|
|
|
|
|
const QTextCursor scannedSelection = m_declDefLinkFinder->scannedSelection();
|
|
|
|
|
if (!scannedSelection.isNull()
|
|
|
|
|
&& scannedSelection.selectionStart() <= pos
|
|
|
|
|
&& scannedSelection.selectionEnd() >= pos) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_updateFunctionDeclDefLinkTimer->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::updateFunctionDeclDefLinkNow()
|
|
|
|
|
{
|
2012-05-08 09:43:14 +02:00
|
|
|
if (Core::EditorManager::currentEditor() != editor())
|
2011-08-10 09:50:04 +02:00
|
|
|
return;
|
|
|
|
|
if (m_declDefLink) {
|
|
|
|
|
// update the change marker
|
|
|
|
|
const Utils::ChangeSet changes = m_declDefLink->changes(m_lastSemanticInfo.snapshot);
|
|
|
|
|
if (changes.isEmpty())
|
|
|
|
|
m_declDefLink->hideMarker(this);
|
|
|
|
|
else
|
|
|
|
|
m_declDefLink->showMarker(this);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!m_lastSemanticInfo.doc || isOutdated())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Snapshot snapshot = CppModelManagerInterface::instance()->snapshot();
|
|
|
|
|
snapshot.insert(m_lastSemanticInfo.doc);
|
|
|
|
|
|
|
|
|
|
m_declDefLinkFinder->startFindLinkAt(textCursor(), m_lastSemanticInfo.doc, snapshot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link)
|
|
|
|
|
{
|
|
|
|
|
abortDeclDefLink();
|
|
|
|
|
m_declDefLink = link;
|
2013-07-18 09:40:50 +02:00
|
|
|
Core::IDocument *targetDocument = Core::EditorManager::documentModel()->documentForFilePath(
|
|
|
|
|
m_declDefLink->targetFile->fileName());
|
|
|
|
|
if (editorDocument() != targetDocument) {
|
|
|
|
|
if (TextEditor::BaseTextDocument *baseTextDocument = qobject_cast<TextEditor::BaseTextDocument *>(targetDocument))
|
|
|
|
|
connect(baseTextDocument->document(), SIGNAL(contentsChanged()),
|
|
|
|
|
this, SLOT(abortDeclDefLink()));
|
2011-08-17 12:21:04 +02:00
|
|
|
}
|
2013-07-18 09:40:50 +02:00
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
|
|
|
|
|
{
|
|
|
|
|
if (!m_declDefLink)
|
|
|
|
|
return;
|
|
|
|
|
m_declDefLink->apply(this, jumpToMatch);
|
2011-08-30 10:05:09 +02:00
|
|
|
abortDeclDefLink();
|
2011-08-10 09:50:04 +02:00
|
|
|
updateFunctionDeclDefLink();
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-25 15:47:01 +02:00
|
|
|
void CPPEditorWidget::updateContentsChangedSignal()
|
|
|
|
|
{
|
|
|
|
|
connect(document(), SIGNAL(contentsChange(int,int,int)),
|
|
|
|
|
this, SLOT(onContentsChanged(int,int,int)));
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-04 20:11:10 +02:00
|
|
|
FollowSymbolUnderCursor *CPPEditorWidget::followSymbolUnderCursorDelegate()
|
|
|
|
|
{
|
|
|
|
|
return m_followSymbolUnderCursor.data();
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
void CPPEditorWidget::abortDeclDefLink()
|
|
|
|
|
{
|
|
|
|
|
if (!m_declDefLink)
|
|
|
|
|
return;
|
2011-08-17 12:21:04 +02:00
|
|
|
|
2013-07-18 09:40:50 +02:00
|
|
|
Core::IDocument *targetDocument = Core::EditorManager::documentModel()->documentForFilePath(
|
|
|
|
|
m_declDefLink->targetFile->fileName());
|
|
|
|
|
if (editorDocument() != targetDocument) {
|
|
|
|
|
if (TextEditor::BaseTextDocument *baseTextDocument = qobject_cast<TextEditor::BaseTextDocument *>(targetDocument))
|
|
|
|
|
disconnect(baseTextDocument->document(), SIGNAL(contentsChanged()),
|
|
|
|
|
this, SLOT(abortDeclDefLink()));
|
2011-08-17 12:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-10 09:50:04 +02:00
|
|
|
m_declDefLink->hideMarker(this);
|
|
|
|
|
m_declDefLink.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-07 15:05:02 +01:00
|
|
|
bool CPPEditorWidget::handleDocumentationComment(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (!m_commentsSettings.m_enableDoxygen
|
|
|
|
|
&& !m_commentsSettings.m_leadingAsterisks) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e->key() == Qt::Key_Return
|
|
|
|
|
|| e->key() == Qt::Key_Enter) {
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
if (!autoCompleter()->isInComment(cursor))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// We are interested on two particular cases:
|
2013-02-21 05:45:44 +01:00
|
|
|
// 1) The cursor is right after a /**, /*!, /// or ///! and the user pressed enter.
|
|
|
|
|
// If Doxygen is enabled we need to generate an entire comment block.
|
2011-12-07 15:05:02 +01:00
|
|
|
// 2) The cursor is already in the middle of a multi-line comment and the user pressed
|
|
|
|
|
// enter. If leading asterisk(s) is set we need to write a comment continuation
|
|
|
|
|
// with those.
|
|
|
|
|
|
|
|
|
|
if (m_commentsSettings.m_enableDoxygen
|
|
|
|
|
&& cursor.positionInBlock() >= 3) {
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2011-12-07 15:05:02 +01:00
|
|
|
const int pos = cursor.position();
|
2013-02-21 05:45:44 +01:00
|
|
|
|
|
|
|
|
if (isStartOfDoxygenComment(cursor)) {
|
|
|
|
|
CppTools::DoxygenGenerator::DocumentationStyle style
|
|
|
|
|
= doxygenStyle(cursor, document());
|
|
|
|
|
|
|
|
|
|
// Check if we're already in a CppStyle Doxygen comment => continuation
|
|
|
|
|
// Needs special handling since CppStyle does not have start and end markers
|
|
|
|
|
if ((style == CppTools::DoxygenGenerator::CppStyleA
|
|
|
|
|
|| style == CppTools::DoxygenGenerator::CppStyleB)
|
|
|
|
|
&& isCppStyleContinuation(cursor)) {
|
|
|
|
|
return handleDoxygenCppStyleContinuation(cursor, e);
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-07 15:05:02 +01:00
|
|
|
CppTools::DoxygenGenerator doxygen;
|
2013-02-21 05:45:44 +01:00
|
|
|
doxygen.setStyle(style);
|
2011-12-07 15:05:02 +01:00
|
|
|
doxygen.setAddLeadingAsterisks(m_commentsSettings.m_leadingAsterisks);
|
|
|
|
|
doxygen.setGenerateBrief(m_commentsSettings.m_generateBrief);
|
|
|
|
|
doxygen.setStartComment(false);
|
|
|
|
|
|
|
|
|
|
// Move until we reach any possibly meaningful content.
|
2012-02-03 16:09:44 +02:00
|
|
|
while (document()->characterAt(cursor.position()).isSpace()
|
|
|
|
|
&& cursor.movePosition(QTextCursor::NextCharacter)) {
|
|
|
|
|
}
|
2011-12-07 15:05:02 +01:00
|
|
|
|
2012-02-10 16:02:01 +01:00
|
|
|
if (!cursor.atEnd()) {
|
|
|
|
|
const QString &comment = doxygen.generate(cursor);
|
|
|
|
|
if (!comment.isEmpty()) {
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.setPosition(pos);
|
|
|
|
|
cursor.insertText(comment);
|
|
|
|
|
cursor.setPosition(pos - 3, QTextCursor::KeepAnchor);
|
|
|
|
|
indent(document(), cursor, QChar::Null);
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-12-07 15:05:02 +01:00
|
|
|
}
|
2013-02-21 05:45:44 +01:00
|
|
|
|
2011-12-07 15:05:02 +01:00
|
|
|
}
|
2013-02-21 05:45:44 +01:00
|
|
|
} // right after first doxygen comment
|
2011-12-07 15:05:02 +01:00
|
|
|
|
2013-02-21 05:45:44 +01:00
|
|
|
return handleDoxygenContinuation(cursor,
|
|
|
|
|
e,
|
|
|
|
|
document(),
|
|
|
|
|
m_commentsSettings.m_enableDoxygen,
|
|
|
|
|
m_commentsSettings.m_leadingAsterisks);
|
|
|
|
|
}
|
2011-12-07 15:05:02 +01:00
|
|
|
|
2013-02-21 05:45:44 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2012-01-23 23:08:08 +01:00
|
|
|
|
2013-02-21 05:45:44 +01:00
|
|
|
bool CPPEditorWidget::isStartOfDoxygenComment(const QTextCursor &cursor) const
|
|
|
|
|
{
|
|
|
|
|
const int pos = cursor.position();
|
2013-04-18 18:21:17 +02:00
|
|
|
QString comment = QString(document()->characterAt(pos - 3))
|
|
|
|
|
+ document()->characterAt(pos - 2)
|
|
|
|
|
+ document()->characterAt(pos - 1);
|
2011-12-07 15:05:02 +01:00
|
|
|
|
2013-02-21 05:45:44 +01:00
|
|
|
if ((comment == QLatin1String("/**"))
|
|
|
|
|
|| (comment == QLatin1String("/*!"))
|
|
|
|
|
|| (comment == QLatin1String("///"))
|
|
|
|
|
|| (comment == QLatin1String("//!"))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-12-07 15:05:02 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::onCommentsSettingsChanged(const CppTools::CommentsSettings &settings)
|
|
|
|
|
{
|
|
|
|
|
m_commentsSettings = settings;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 07:46:19 +02:00
|
|
|
void CPPEditorWidget::showPreProcessorWidget()
|
|
|
|
|
{
|
|
|
|
|
const QString &fileName = editor()->document()->filePath();
|
|
|
|
|
// Check if this editor belongs to a project
|
|
|
|
|
|
|
|
|
|
QList<ProjectPart::Ptr> projectParts = m_modelManager->projectPart(fileName);
|
|
|
|
|
if (projectParts.isEmpty())
|
|
|
|
|
projectParts = m_modelManager->projectPartFromDependencies(fileName);
|
|
|
|
|
if (projectParts.isEmpty())
|
|
|
|
|
projectParts << m_modelManager->fallbackProjectPart();
|
|
|
|
|
|
|
|
|
|
PreProcessorAdditionPopUp::instance()->show(this, projectParts);
|
|
|
|
|
|
|
|
|
|
connect(PreProcessorAdditionPopUp::instance(),
|
|
|
|
|
SIGNAL(finished(QByteArray)),
|
|
|
|
|
SLOT(preProcessorWidgetFinished(QByteArray)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditorWidget::preProcessorWidgetFinished(const QByteArray &additionalDefines)
|
|
|
|
|
{
|
|
|
|
|
PreProcessorAdditionPopUp::instance()->disconnect(this);
|
|
|
|
|
QSharedPointer<SnapshotUpdater> updater
|
|
|
|
|
= m_modelManager->cppEditorSupport(editor())->snapshotUpdater();
|
|
|
|
|
updater->setEditorDefines(additionalDefines);
|
|
|
|
|
updater->update(m_modelManager->workingCopy());
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 18:54:03 +01:00
|
|
|
#include <cppeditor.moc>
|