2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Commercial Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01: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.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** If you are unsure which license is appropriate for your use, please
|
2009-08-14 09:30:56 +02:00
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 15:08:31 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cppeditor.h"
|
|
|
|
|
#include "cppeditorconstants.h"
|
|
|
|
|
#include "cppplugin.h"
|
|
|
|
|
#include "cpphighlighter.h"
|
|
|
|
|
|
2009-08-07 13:02:36 +02:00
|
|
|
#include <cpptools/cpptoolsplugin.h>
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <AST.h>
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <Control.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <Token.h>
|
|
|
|
|
#include <Scope.h>
|
|
|
|
|
#include <Symbols.h>
|
|
|
|
|
#include <Names.h>
|
|
|
|
|
#include <Control.h>
|
|
|
|
|
#include <CoreTypes.h>
|
|
|
|
|
#include <Literals.h>
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <PrettyPrinter.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <Semantic.h>
|
2009-01-28 23:46:43 +01:00
|
|
|
#include <SymbolVisitor.h>
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <TranslationUnit.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
2009-08-07 13:02:36 +02:00
|
|
|
#include <cplusplus/TypeOfExpression.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/LookupContext.h>
|
|
|
|
|
#include <cplusplus/Overview.h>
|
|
|
|
|
#include <cplusplus/OverviewModel.h>
|
|
|
|
|
#include <cplusplus/SimpleLexer.h>
|
2009-03-24 14:52:20 +01:00
|
|
|
#include <cplusplus/TokenUnderCursor.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/TypeOfExpression.h>
|
2009-09-17 15:44:54 +02:00
|
|
|
#include <cplusplus/MatchingText.h>
|
2009-09-21 15:11:18 +02:00
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
2009-09-30 17:15:31 +02:00
|
|
|
#include <cplusplus/FastPreprocessor.h>
|
2009-10-07 16:11:42 +02:00
|
|
|
#include <cplusplus/CppBindings.h>
|
2009-09-30 17:15:31 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cpptools/cppmodelmanagerinterface.h>
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/uniqueidmanager.h>
|
2009-01-13 13:39:31 +01:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2009-05-14 16:37:17 +02:00
|
|
|
#include <utils/uncommentselection.h>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <texteditor/basetextdocument.h>
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
|
#include <texteditor/textblockiterator.h>
|
|
|
|
|
#include <indenter.h>
|
|
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtCore/QTime>
|
|
|
|
|
#include <QtCore/QTimer>
|
2009-06-25 11:02:02 +02:00
|
|
|
#include <QtCore/QStack>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QAction>
|
2009-07-01 17:57:00 +02:00
|
|
|
#include <QtGui/QApplication>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <QtGui/QHeaderView>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QLayout>
|
|
|
|
|
#include <QtGui/QMenu>
|
|
|
|
|
#include <QtGui/QShortcut>
|
|
|
|
|
#include <QtGui/QTextEdit>
|
|
|
|
|
#include <QtGui/QComboBox>
|
2009-07-16 17:34:04 +02:00
|
|
|
#include <QtGui/QToolBar>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QTreeView>
|
2009-04-27 17:22:49 +02:00
|
|
|
#include <QtGui/QSortFilterProxyModel>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-05-11 17:00:29 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
enum {
|
2009-07-01 17:57:00 +02:00
|
|
|
UPDATE_METHOD_BOX_INTERVAL = 150,
|
|
|
|
|
UPDATE_USES_INTERVAL = 300
|
2009-06-24 16:40:30 +02:00
|
|
|
};
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
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();
|
|
|
|
|
setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width()));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
class FindScope: protected SymbolVisitor
|
2009-06-25 11:02:02 +02:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
TranslationUnit *_unit;
|
|
|
|
|
Scope *_scope;
|
|
|
|
|
unsigned _line;
|
|
|
|
|
unsigned _column;
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
public:
|
|
|
|
|
Scope *operator()(unsigned line, unsigned column,
|
|
|
|
|
Symbol *root, TranslationUnit *unit)
|
2009-06-25 11:02:02 +02:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
_unit = unit;
|
|
|
|
|
_scope = 0;
|
|
|
|
|
_line = line;
|
|
|
|
|
_column = column;
|
|
|
|
|
accept(root);
|
|
|
|
|
return _scope;
|
|
|
|
|
}
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
private:
|
|
|
|
|
using SymbolVisitor::visit;
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
virtual bool preVisit(Symbol *)
|
|
|
|
|
{ return ! _scope; }
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
virtual bool visit(Block *block)
|
|
|
|
|
{ return processScope(block->members()); }
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
virtual bool visit(Function *function)
|
|
|
|
|
{ return processScope(function->members()); }
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
bool processScope(Scope *scope)
|
|
|
|
|
{
|
|
|
|
|
if (_scope || ! scope)
|
|
|
|
|
return false;
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
|
|
|
|
|
accept(scope->symbolAt(i));
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
if (_scope)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
unsigned startOffset = scope->owner()->startOffset();
|
|
|
|
|
unsigned endOffset = scope->owner()->endOffset();
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
unsigned startLine, startColumn;
|
|
|
|
|
unsigned endLine, endColumn;
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
_unit->getPosition(startOffset, &startLine, &startColumn);
|
|
|
|
|
_unit->getPosition(endOffset, &endLine, &endColumn);
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
|
|
|
|
|
if (_line < endLine || (_line == endLine && _column < endColumn)) {
|
|
|
|
|
_scope = scope;
|
2009-06-25 11:02:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FindUses: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
Scope *_functionScope;
|
2009-06-25 11:02:02 +02:00
|
|
|
|
2009-07-08 13:50:49 +02:00
|
|
|
FindScope findScope;
|
|
|
|
|
|
2009-06-25 11:02:02 +02:00
|
|
|
public:
|
2009-06-25 16:11:28 +02:00
|
|
|
FindUses(Control *control)
|
2009-06-25 11:02:02 +02:00
|
|
|
: ASTVisitor(control)
|
|
|
|
|
{ }
|
|
|
|
|
|
2009-06-25 16:11:28 +02:00
|
|
|
// local and external uses.
|
2009-07-09 12:14:00 +02:00
|
|
|
SemanticInfo::LocalUseMap localUses;
|
2009-06-25 16:11:28 +02:00
|
|
|
|
|
|
|
|
void operator()(FunctionDefinitionAST *ast)
|
2009-06-25 11:02:02 +02:00
|
|
|
{
|
2009-06-25 16:11:28 +02:00
|
|
|
localUses.clear();
|
|
|
|
|
|
2009-06-25 11:02:02 +02:00
|
|
|
if (ast && ast->symbol) {
|
|
|
|
|
_functionScope = ast->symbol->members();
|
|
|
|
|
accept(ast);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using ASTVisitor::visit;
|
|
|
|
|
|
2009-06-26 09:48:40 +02:00
|
|
|
bool findMember(Scope *scope, NameAST *ast, unsigned line, unsigned column)
|
2009-06-25 11:02:02 +02:00
|
|
|
{
|
2009-06-26 09:48:40 +02:00
|
|
|
if (! (ast && ast->name))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Identifier *id = ast->name->identifier();
|
2009-06-25 11:02:02 +02:00
|
|
|
|
|
|
|
|
if (scope) {
|
|
|
|
|
for (Symbol *member = scope->lookat(id); member; member = member->next()) {
|
|
|
|
|
if (member->identifier() != id)
|
|
|
|
|
continue;
|
2009-06-25 15:18:19 +02:00
|
|
|
else if (member->line() < line || (member->line() == line && member->column() <= column)) {
|
2009-07-09 17:32:39 +02:00
|
|
|
localUses[member].append(SemanticInfo::Use(line, column, id->size()));
|
2009-06-25 11:02:02 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-06 11:53:01 +02:00
|
|
|
void searchUsesInTemplateArguments(NameAST *name)
|
|
|
|
|
{
|
|
|
|
|
if (! name)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
else if (TemplateIdAST *template_id = name->asTemplateId()) {
|
|
|
|
|
for (TemplateArgumentListAST *it = template_id->template_arguments; it; it = it->next) {
|
|
|
|
|
accept(it->template_argument);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-25 11:02:02 +02:00
|
|
|
virtual bool visit(SimpleNameAST *ast)
|
|
|
|
|
{
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
getTokenStartPosition(ast->firstToken(), &line, &column);
|
|
|
|
|
|
|
|
|
|
Scope *scope = findScope(line, column,
|
|
|
|
|
_functionScope->owner(),
|
|
|
|
|
translationUnit());
|
|
|
|
|
|
|
|
|
|
while (scope) {
|
|
|
|
|
if (scope->isFunctionScope()) {
|
|
|
|
|
Function *fun = scope->owner()->asFunction();
|
|
|
|
|
if (findMember(fun->members(), ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
else if (findMember(fun->arguments(), ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
} else if (scope->isBlockScope()) {
|
|
|
|
|
if (findMember(scope, ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scope = scope->enclosingScope();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-26 09:48:40 +02:00
|
|
|
virtual bool visit(TemplateIdAST *ast)
|
2009-06-25 11:02:02 +02:00
|
|
|
{
|
2009-10-06 11:53:01 +02:00
|
|
|
for (TemplateArgumentListAST *arg = ast->template_arguments; arg; arg = arg->next)
|
|
|
|
|
accept(arg->template_argument);
|
|
|
|
|
|
2009-06-26 09:48:40 +02:00
|
|
|
unsigned line, column;
|
|
|
|
|
getTokenStartPosition(ast->firstToken(), &line, &column);
|
|
|
|
|
|
|
|
|
|
Scope *scope = findScope(line, column,
|
|
|
|
|
_functionScope->owner(),
|
|
|
|
|
translationUnit());
|
|
|
|
|
|
|
|
|
|
while (scope) {
|
|
|
|
|
if (scope->isFunctionScope()) {
|
|
|
|
|
Function *fun = scope->owner()->asFunction();
|
|
|
|
|
if (findMember(fun->members(), ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
else if (findMember(fun->arguments(), ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
} else if (scope->isBlockScope()) {
|
|
|
|
|
if (findMember(scope, ast, line, column))
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scope = scope->enclosingScope();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(QualifiedNameAST *ast)
|
|
|
|
|
{
|
2009-10-06 11:53:01 +02:00
|
|
|
for (NestedNameSpecifierAST *it = ast->nested_name_specifier; it; it = it->next)
|
|
|
|
|
searchUsesInTemplateArguments(it->class_or_namespace_name);
|
2009-06-26 09:48:40 +02:00
|
|
|
|
2009-10-06 11:53:01 +02:00
|
|
|
searchUsesInTemplateArguments(ast->unqualified_name);
|
2009-06-25 11:02:02 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(PostfixExpressionAST *ast)
|
|
|
|
|
{
|
|
|
|
|
accept(ast->base_expression);
|
|
|
|
|
for (PostfixAST *it = ast->postfix_expressions; it; it = it->next) {
|
|
|
|
|
if (it->asMemberAccess() != 0)
|
|
|
|
|
continue; // skip members
|
|
|
|
|
accept(it);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ElaboratedTypeSpecifierAST *)
|
|
|
|
|
{
|
|
|
|
|
// ### template args
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ClassSpecifierAST *)
|
|
|
|
|
{
|
|
|
|
|
// ### template args
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(EnumSpecifierAST *)
|
|
|
|
|
{
|
|
|
|
|
// ### template args
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(UsingDirectiveAST *)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(UsingAST *ast)
|
|
|
|
|
{
|
|
|
|
|
accept(ast->name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
class FunctionDefinitionUnderCursor: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
unsigned _line;
|
|
|
|
|
unsigned _column;
|
|
|
|
|
FunctionDefinitionAST *_functionDefinition;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FunctionDefinitionUnderCursor(Control *control)
|
2009-07-09 12:14:00 +02:00
|
|
|
: ASTVisitor(control),
|
|
|
|
|
_line(0), _column(0)
|
2009-06-24 16:40:30 +02:00
|
|
|
{ }
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
FunctionDefinitionAST *operator()(AST *ast, unsigned line, unsigned column)
|
2009-06-24 16:40:30 +02:00
|
|
|
{
|
|
|
|
|
_functionDefinition = 0;
|
2009-07-09 12:14:00 +02:00
|
|
|
_line = line;
|
|
|
|
|
_column = column;
|
2009-06-24 16:40:30 +02:00
|
|
|
accept(ast);
|
|
|
|
|
return _functionDefinition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual bool preVisit(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
if (_functionDefinition)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
else if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
|
|
|
|
|
unsigned startLine, startColumn;
|
|
|
|
|
unsigned endLine, endColumn;
|
|
|
|
|
getTokenStartPosition(def->firstToken(), &startLine, &startColumn);
|
|
|
|
|
getTokenEndPosition(def->lastToken() - 1, &endLine, &endColumn);
|
|
|
|
|
|
|
|
|
|
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
|
|
|
|
|
if (_line < endLine || (_line == endLine && _column < endColumn)) {
|
|
|
|
|
_functionDefinition = def;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ProcessDeclarators: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
QList<DeclaratorIdAST *> _declarators;
|
|
|
|
|
bool _visitFunctionDeclarator;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
ProcessDeclarators(Control *control)
|
|
|
|
|
: ASTVisitor(control), _visitFunctionDeclarator(true)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
QList<DeclaratorIdAST *> operator()(FunctionDefinitionAST *ast)
|
|
|
|
|
{
|
|
|
|
|
_declarators.clear();
|
|
|
|
|
|
|
|
|
|
if (ast) {
|
|
|
|
|
if (ast->declarator) {
|
|
|
|
|
_visitFunctionDeclarator = true;
|
|
|
|
|
accept(ast->declarator->postfix_declarators);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_visitFunctionDeclarator = false;
|
|
|
|
|
accept(ast->function_body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _declarators;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using ASTVisitor::visit;
|
|
|
|
|
|
|
|
|
|
virtual bool visit(FunctionDeclaratorAST *)
|
|
|
|
|
{ return _visitFunctionDeclarator; }
|
|
|
|
|
|
|
|
|
|
virtual bool visit(DeclaratorIdAST *ast)
|
|
|
|
|
{
|
|
|
|
|
_declarators.append(ast);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2009-01-28 23:46:43 +01:00
|
|
|
class FindFunctionDefinitions: protected SymbolVisitor
|
|
|
|
|
{
|
|
|
|
|
Name *_declarationName;
|
|
|
|
|
QList<Function *> *_functions;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
FindFunctionDefinitions()
|
|
|
|
|
: _declarationName(0),
|
|
|
|
|
_functions(0)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
void operator()(Name *declarationName, Scope *globals,
|
|
|
|
|
QList<Function *> *functions)
|
|
|
|
|
{
|
|
|
|
|
_declarationName = declarationName;
|
|
|
|
|
_functions = functions;
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < globals->symbolCount(); ++i) {
|
|
|
|
|
accept(globals->symbolAt(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using SymbolVisitor::visit;
|
|
|
|
|
|
|
|
|
|
virtual bool visit(Function *function)
|
|
|
|
|
{
|
|
|
|
|
Name *name = function->name();
|
|
|
|
|
if (QualifiedNameId *q = name->asQualifiedNameId())
|
|
|
|
|
name = q->unqualifiedNameId();
|
|
|
|
|
|
|
|
|
|
if (_declarationName->isEqualTo(name))
|
|
|
|
|
_functions->append(function);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
2009-03-04 15:45:38 +01:00
|
|
|
static QualifiedNameId *qualifiedNameIdForSymbol(Symbol *s, const LookupContext &context)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
Name *symbolName = s->name();
|
|
|
|
|
if (! symbolName)
|
|
|
|
|
return 0; // nothing to do.
|
|
|
|
|
|
|
|
|
|
QVector<Name *> names;
|
|
|
|
|
|
|
|
|
|
for (Scope *scope = s->scope(); scope; scope = scope->enclosingScope()) {
|
|
|
|
|
if (scope->isClassScope() || scope->isNamespaceScope()) {
|
|
|
|
|
if (scope->owner() && scope->owner()->name()) {
|
|
|
|
|
Name *ownerName = scope->owner()->name();
|
|
|
|
|
if (QualifiedNameId *q = ownerName->asQualifiedNameId()) {
|
|
|
|
|
for (unsigned i = 0; i < q->nameCount(); ++i) {
|
|
|
|
|
names.prepend(q->nameAt(i));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
names.prepend(ownerName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (QualifiedNameId *q = symbolName->asQualifiedNameId()) {
|
|
|
|
|
for (unsigned i = 0; i < q->nameCount(); ++i) {
|
|
|
|
|
names.append(q->nameAt(i));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
names.append(symbolName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context.control()->qualifiedNameId(names.constData(), names.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
|
2008-12-02 15:08:31 +01:00
|
|
|
: BaseTextEditorEditable(editor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-21 15:52:34 +01:00
|
|
|
Core::UniqueIDManager *uidm = Core::UniqueIDManager::instance();
|
|
|
|
|
m_context << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
|
|
|
|
|
m_context << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
|
|
|
|
|
m_context << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
CPPEditor::CPPEditor(QWidget *parent)
|
|
|
|
|
: TextEditor::BaseTextEditor(parent)
|
2009-07-01 17:57:00 +02:00
|
|
|
, m_currentRenameSelection(-1)
|
2009-07-01 18:41:04 +02:00
|
|
|
, m_inRename(false)
|
2009-09-17 12:36:40 +02:00
|
|
|
, m_allowSkippingOfBlockEnd(false)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
qRegisterMetaType<SemanticInfo>("SemanticInfo");
|
|
|
|
|
|
|
|
|
|
m_semanticHighlighter = new SemanticHighlighter(this);
|
|
|
|
|
m_semanticHighlighter->start();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
|
setMarksVisible(true);
|
2009-03-16 17:23:50 +01:00
|
|
|
setCodeFoldingSupported(true);
|
2008-12-02 12:01:29 +01:00
|
|
|
setCodeFoldingVisible(true);
|
2008-12-16 13:19:11 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#ifdef WITH_TOKEN_MOVE_POSITION
|
|
|
|
|
new QShortcut(QKeySequence::MoveToPreviousWord, this, SLOT(moveToPreviousToken()),
|
|
|
|
|
/*ambiguousMember=*/ 0, Qt::WidgetShortcut);
|
|
|
|
|
|
|
|
|
|
new QShortcut(QKeySequence::MoveToNextWord, this, SLOT(moveToNextToken()),
|
|
|
|
|
/*ambiguousMember=*/ 0, Qt::WidgetShortcut);
|
|
|
|
|
|
|
|
|
|
new QShortcut(QKeySequence::DeleteStartOfWord, this, SLOT(deleteStartOfToken()),
|
|
|
|
|
/*ambiguousMember=*/ 0, Qt::WidgetShortcut);
|
|
|
|
|
|
|
|
|
|
new QShortcut(QKeySequence::DeleteEndOfWord, this, SLOT(deleteEndOfToken()),
|
|
|
|
|
/*ambiguousMember=*/ 0, Qt::WidgetShortcut);
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
m_modelManager = ExtensionSystem::PluginManager::instance()
|
|
|
|
|
->getObject<CppTools::CppModelManagerInterface>();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (m_modelManager) {
|
|
|
|
|
connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
|
|
|
|
|
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPEditor::~CPPEditor()
|
|
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
m_semanticHighlighter->abort();
|
|
|
|
|
m_semanticHighlighter->wait();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface()
|
|
|
|
|
{
|
|
|
|
|
CPPEditorEditable *editable = new CPPEditorEditable(this);
|
|
|
|
|
createToolBar(editable);
|
|
|
|
|
return editable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::createToolBar(CPPEditorEditable *editable)
|
|
|
|
|
{
|
|
|
|
|
m_methodCombo = new QComboBox;
|
|
|
|
|
m_methodCombo->setMinimumContentsLength(22);
|
2008-12-17 11:54:47 +01:00
|
|
|
|
|
|
|
|
// Make the combo box prefer to expand
|
|
|
|
|
QSizePolicy policy = m_methodCombo->sizePolicy();
|
|
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
|
|
|
|
m_methodCombo->setSizePolicy(policy);
|
|
|
|
|
|
2009-03-24 16:36:22 +01:00
|
|
|
QTreeView *methodView = new OverviewTreeView;
|
2008-12-02 12:01:29 +01:00
|
|
|
methodView->header()->hide();
|
|
|
|
|
methodView->setItemsExpandable(false);
|
|
|
|
|
m_methodCombo->setView(methodView);
|
|
|
|
|
m_methodCombo->setMaxVisibleItems(20);
|
|
|
|
|
|
|
|
|
|
m_overviewModel = new OverviewModel(this);
|
2009-04-27 17:22:49 +02:00
|
|
|
m_proxyModel = new QSortFilterProxyModel(this);
|
|
|
|
|
m_proxyModel->setSourceModel(m_overviewModel);
|
|
|
|
|
if (CppPlugin::instance()->sortedMethodOverview())
|
|
|
|
|
m_proxyModel->sort(0, Qt::AscendingOrder);
|
|
|
|
|
else
|
|
|
|
|
m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedMethodOverview()
|
|
|
|
|
m_proxyModel->setDynamicSortFilter(true);
|
|
|
|
|
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
|
m_methodCombo->setModel(m_proxyModel);
|
|
|
|
|
|
|
|
|
|
m_methodCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
|
|
|
m_sortAction = new QAction(tr("Sort alphabetically"), m_methodCombo);
|
|
|
|
|
m_sortAction->setCheckable(true);
|
|
|
|
|
m_sortAction->setChecked(sortedMethodOverview());
|
|
|
|
|
connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedMethodOverview(bool)));
|
|
|
|
|
m_methodCombo->addAction(m_sortAction);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
m_updateMethodBoxTimer = new QTimer(this);
|
|
|
|
|
m_updateMethodBoxTimer->setSingleShot(true);
|
|
|
|
|
m_updateMethodBoxTimer->setInterval(UPDATE_METHOD_BOX_INTERVAL);
|
|
|
|
|
connect(m_updateMethodBoxTimer, SIGNAL(timeout()), this, SLOT(updateMethodBoxIndexNow()));
|
|
|
|
|
|
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()));
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
|
|
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
|
2008-12-04 17:06:44 +01:00
|
|
|
connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
|
2009-07-01 18:41:04 +02:00
|
|
|
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
|
|
|
|
|
|
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()));
|
|
|
|
|
|
|
|
|
|
connect(m_semanticHighlighter, SIGNAL(changed(SemanticInfo)),
|
|
|
|
|
this, SLOT(updateSemanticInfo(SemanticInfo)));
|
|
|
|
|
|
2009-07-15 16:23:07 +02:00
|
|
|
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<QAction*> actions = toolBar->actions();
|
2009-06-16 13:42:33 +02:00
|
|
|
QWidget *w = toolBar->widgetForAction(actions.first());
|
|
|
|
|
static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_methodCombo, 1);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
void CPPEditor::inAllRenameSelections(EditOperation operation,
|
|
|
|
|
const QTextEdit::ExtraSelection ¤tRenameSelection,
|
|
|
|
|
QTextCursor cursor,
|
|
|
|
|
const QString &text)
|
|
|
|
|
{
|
|
|
|
|
m_inRename = true;
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
const int offset = cursor.position() - currentRenameSelection.cursor.anchor();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_renameSelections.size(); ++i) {
|
|
|
|
|
QTextEdit::ExtraSelection &s = m_renameSelections[i];
|
|
|
|
|
int pos = s.cursor.anchor();
|
|
|
|
|
int endPos = s.cursor.position();
|
|
|
|
|
s.cursor.setPosition(s.cursor.anchor() + offset);
|
|
|
|
|
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case DeletePreviousChar:
|
|
|
|
|
s.cursor.deletePreviousChar();
|
|
|
|
|
--endPos;
|
|
|
|
|
break;
|
|
|
|
|
case DeleteChar:
|
|
|
|
|
s.cursor.deleteChar();
|
|
|
|
|
--endPos;
|
|
|
|
|
break;
|
|
|
|
|
case InsertText:
|
|
|
|
|
s.cursor.insertText(text);
|
|
|
|
|
endPos += text.length();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.cursor.setPosition(pos);
|
|
|
|
|
s.cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
m_inRename = false;
|
|
|
|
|
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 18:41:04 +02:00
|
|
|
void CPPEditor::abortRename()
|
|
|
|
|
{
|
|
|
|
|
m_currentRenameSelection = -1;
|
|
|
|
|
m_renameSelections.clear();
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, m_renameSelections);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
int CPPEditor::previousBlockState(QTextBlock block) const
|
|
|
|
|
{
|
|
|
|
|
block = block.previous();
|
|
|
|
|
if (block.isValid()) {
|
|
|
|
|
int state = block.userState();
|
|
|
|
|
|
|
|
|
|
if (state != -1)
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor CPPEditor::moveToPreviousToken(QTextCursor::MoveMode mode) const
|
|
|
|
|
{
|
|
|
|
|
SimpleLexer tokenize;
|
|
|
|
|
QTextCursor c(textCursor());
|
|
|
|
|
QTextBlock block = c.block();
|
|
|
|
|
int column = c.columnNumber();
|
|
|
|
|
|
|
|
|
|
for (; block.isValid(); block = block.previous()) {
|
|
|
|
|
const QString textBlock = block.text();
|
|
|
|
|
QList<SimpleToken> tokens = tokenize(textBlock, previousBlockState(block));
|
|
|
|
|
|
|
|
|
|
if (! tokens.isEmpty()) {
|
|
|
|
|
tokens.prepend(SimpleToken());
|
|
|
|
|
|
|
|
|
|
for (int index = tokens.size() - 1; index != -1; --index) {
|
|
|
|
|
const SimpleToken &tk = tokens.at(index);
|
|
|
|
|
if (tk.position() < column) {
|
|
|
|
|
c.setPosition(block.position() + tk.position(), mode);
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
column = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.movePosition(QTextCursor::Start, mode);
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor CPPEditor::moveToNextToken(QTextCursor::MoveMode mode) const
|
|
|
|
|
{
|
|
|
|
|
SimpleLexer tokenize;
|
|
|
|
|
QTextCursor c(textCursor());
|
|
|
|
|
QTextBlock block = c.block();
|
|
|
|
|
int column = c.columnNumber();
|
|
|
|
|
|
|
|
|
|
for (; block.isValid(); block = block.next()) {
|
|
|
|
|
const QString textBlock = block.text();
|
|
|
|
|
QList<SimpleToken> tokens = tokenize(textBlock, previousBlockState(block));
|
|
|
|
|
|
|
|
|
|
if (! tokens.isEmpty()) {
|
|
|
|
|
for (int index = 0; index < tokens.size(); ++index) {
|
|
|
|
|
const SimpleToken &tk = tokens.at(index);
|
|
|
|
|
if (tk.position() > column) {
|
|
|
|
|
c.setPosition(block.position() + tk.position(), mode);
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
column = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.movePosition(QTextCursor::End, mode);
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::moveToPreviousToken()
|
|
|
|
|
{
|
|
|
|
|
setTextCursor(moveToPreviousToken(QTextCursor::MoveAnchor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::moveToNextToken()
|
|
|
|
|
{
|
|
|
|
|
setTextCursor(moveToNextToken(QTextCursor::MoveAnchor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::deleteStartOfToken()
|
|
|
|
|
{
|
|
|
|
|
QTextCursor c = moveToPreviousToken(QTextCursor::KeepAnchor);
|
|
|
|
|
c.removeSelectedText();
|
|
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::deleteEndOfToken()
|
|
|
|
|
{
|
|
|
|
|
QTextCursor c = moveToNextToken(QTextCursor::KeepAnchor);
|
|
|
|
|
c.removeSelectedText();
|
|
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::onDocumentUpdated(Document::Ptr doc)
|
|
|
|
|
{
|
|
|
|
|
if (doc->fileName() != file()->fileName())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_overviewModel->rebuild(doc);
|
2008-12-08 17:47:54 +01:00
|
|
|
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_methodCombo->view());
|
|
|
|
|
treeView->sync();
|
2009-06-24 16:40:30 +02:00
|
|
|
updateMethodBoxIndexNow();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-05-11 17:00:29 +02:00
|
|
|
void CPPEditor::reformatDocument()
|
|
|
|
|
{
|
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
|
|
|
|
|
QByteArray source = toPlainText().toUtf8();
|
|
|
|
|
|
|
|
|
|
Control control;
|
2009-06-15 15:38:20 +02:00
|
|
|
StringLiteral *fileId = control.findOrInsertStringLiteral("<file>");
|
2009-05-11 17:00:29 +02:00
|
|
|
TranslationUnit unit(&control, fileId);
|
|
|
|
|
unit.setQtMocRunEnabled(true);
|
|
|
|
|
unit.setSource(source.constData(), source.length());
|
|
|
|
|
unit.parse();
|
|
|
|
|
if (! unit.ast())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::ostringstream s;
|
|
|
|
|
|
|
|
|
|
TranslationUnitAST *ast = unit.ast()->asTranslationUnit();
|
|
|
|
|
PrettyPrinter pp(&control, s);
|
|
|
|
|
pp(ast, source);
|
|
|
|
|
|
|
|
|
|
const std::string str = s.str();
|
|
|
|
|
QTextCursor c = textCursor();
|
|
|
|
|
c.setPosition(0);
|
|
|
|
|
c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
|
|
|
|
c.insertText(QString::fromUtf8(str.c_str(), str.length()));
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-30 13:25:40 +02:00
|
|
|
CPlusPlus::Symbol *CPPEditor::findCanonicalSymbol(const QTextCursor &cursor,
|
|
|
|
|
Document::Ptr doc,
|
|
|
|
|
const Snapshot &snapshot) const
|
2009-08-07 13:02:36 +02:00
|
|
|
{
|
2009-09-30 13:25:40 +02:00
|
|
|
QTextCursor tc = cursor;
|
2009-08-07 13:02:36 +02:00
|
|
|
int line, col;
|
|
|
|
|
convertPosition(tc.position(), &line, &col);
|
|
|
|
|
++col;
|
|
|
|
|
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord);
|
|
|
|
|
|
|
|
|
|
ExpressionUnderCursor expressionUnderCursor;
|
|
|
|
|
const QString code = expressionUnderCursor(tc);
|
2009-09-30 13:25:40 +02:00
|
|
|
// qDebug() << "code:" << code;
|
2009-08-07 13:02:36 +02:00
|
|
|
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
|
|
|
|
typeOfExpression.setSnapshot(snapshot);
|
|
|
|
|
|
|
|
|
|
Symbol *lastVisibleSymbol = doc->findSymbolAt(line, col);
|
|
|
|
|
|
|
|
|
|
const QList<TypeOfExpression::Result> results = typeOfExpression(code, doc,
|
|
|
|
|
lastVisibleSymbol,
|
|
|
|
|
TypeOfExpression::Preprocess);
|
|
|
|
|
|
2009-10-07 16:11:42 +02:00
|
|
|
NamespaceBindingPtr glo = bind(doc, snapshot);
|
|
|
|
|
Symbol *canonicalSymbol = LookupContext::canonicalSymbol(results, glo.data());
|
2009-09-30 13:25:40 +02:00
|
|
|
return canonicalSymbol;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-05 15:17:25 +02:00
|
|
|
|
|
|
|
|
void CPPEditor::findUsages()
|
|
|
|
|
{
|
|
|
|
|
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
|
|
|
|
|
|
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
|
|
|
|
|
if (! info.doc)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (Symbol *canonicalSymbol = findCanonicalSymbol(textCursor(), info.doc, info.snapshot))
|
|
|
|
|
m_modelManager->findUsages(canonicalSymbol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::renameUsages()
|
2009-09-30 13:25:40 +02:00
|
|
|
{
|
2009-10-06 16:00:32 +02:00
|
|
|
Core::EditorManager::instance()->showEditorInfoBar(QLatin1String("CppEditor.Rename"),
|
|
|
|
|
tr("This change cannot be undone."),
|
|
|
|
|
tr("Yes, I know what I am doing."),
|
|
|
|
|
this, SLOT(renameUsagesNow()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::renameUsagesNow()
|
|
|
|
|
{
|
|
|
|
|
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
|
|
|
|
|
|
2009-09-30 13:25:40 +02:00
|
|
|
m_currentRenameSelection = -1;
|
|
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
|
|
|
|
|
SemanticInfo info = m_lastSemanticInfo;
|
|
|
|
|
|
|
|
|
|
if (info.doc) {
|
|
|
|
|
if (Symbol *canonicalSymbol = findCanonicalSymbol(textCursor(), info.doc, info.snapshot)) {
|
|
|
|
|
TranslationUnit *unit = info.doc->translationUnit();
|
|
|
|
|
|
|
|
|
|
const QList<int> references = m_modelManager->references(canonicalSymbol, info.doc, info.snapshot);
|
|
|
|
|
foreach (int index, references) {
|
|
|
|
|
unsigned line, column;
|
|
|
|
|
unit->getTokenPosition(index, &line, &column);
|
|
|
|
|
|
|
|
|
|
if (column)
|
|
|
|
|
--column; // adjust the column position.
|
|
|
|
|
|
|
|
|
|
const int len = unit->tokenAt(index).f.length;
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor(document()->findBlockByNumber(line - 1));
|
|
|
|
|
cursor.setPosition(cursor.position() + column);
|
|
|
|
|
cursor.setPosition(cursor.position() + len, QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.format = m_occurrencesFormat;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
|
|
|
|
|
2009-10-05 15:17:25 +02:00
|
|
|
m_modelManager->renameUsages(canonicalSymbol);
|
2009-09-30 13:25:40 +02:00
|
|
|
}
|
2009-08-07 13:02:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-04 13:44:54 +02:00
|
|
|
void CPPEditor::renameSymbolUnderCursor()
|
2009-07-01 17:57:00 +02:00
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
QTextCursor c = textCursor();
|
|
|
|
|
m_currentRenameSelection = -1;
|
|
|
|
|
|
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
|
2009-07-01 18:41:04 +02:00
|
|
|
void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded)
|
|
|
|
|
{
|
2009-07-02 15:09:02 +02:00
|
|
|
Q_UNUSED(position)
|
|
|
|
|
Q_UNUSED(charsAdded)
|
|
|
|
|
|
2009-07-02 18:38:21 +02:00
|
|
|
if (m_currentRenameSelection == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-07-01 18:41:04 +02:00
|
|
|
if (!m_inRename)
|
|
|
|
|
abortRename();
|
|
|
|
|
|
|
|
|
|
if (charsRemoved > 0)
|
|
|
|
|
updateUses();
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::updateFileName()
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
void CPPEditor::jumpToMethod(int)
|
|
|
|
|
{
|
2009-04-27 17:22:49 +02:00
|
|
|
QModelIndex index = m_proxyModel->mapToSource(m_methodCombo->view()->currentIndex());
|
2008-12-02 12:01:29 +01:00
|
|
|
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
|
|
|
|
|
if (! symbol)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
openCppEditorAt(linkToSymbol(symbol));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-27 17:22:49 +02:00
|
|
|
void CPPEditor::setSortedMethodOverview(bool sort)
|
|
|
|
|
{
|
|
|
|
|
if (sort != sortedMethodOverview()) {
|
|
|
|
|
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);
|
2009-06-24 16:40:30 +02:00
|
|
|
updateMethodBoxIndexNow();
|
2009-04-27 17:22:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPPEditor::sortedMethodOverview() const
|
|
|
|
|
{
|
|
|
|
|
return (m_proxyModel->sortColumn() == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::updateMethodBoxIndex()
|
|
|
|
|
{
|
2009-06-24 16:40:30 +02:00
|
|
|
m_updateMethodBoxTimer->start(UPDATE_METHOD_BOX_INTERVAL);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 10:05:33 +02:00
|
|
|
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
|
|
|
|
|
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) {
|
2009-07-09 17:32:39 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2009-06-25 16:11:28 +02:00
|
|
|
|
2009-09-23 18:06:59 +02:00
|
|
|
if (isUnused)
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-24 16:40:30 +02:00
|
|
|
void CPPEditor::updateMethodBoxIndexNow()
|
|
|
|
|
{
|
|
|
|
|
m_updateMethodBoxTimer->stop();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
|
|
|
|
QModelIndex lastIndex;
|
|
|
|
|
|
2008-12-05 15:06:11 +01:00
|
|
|
const int rc = m_overviewModel->rowCount();
|
2008-12-02 12:01:29 +01:00
|
|
|
for (int row = 0; row < rc; ++row) {
|
|
|
|
|
const QModelIndex index = m_overviewModel->index(row, 0, QModelIndex());
|
|
|
|
|
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
|
2008-12-08 17:47:54 +01:00
|
|
|
if (symbol && symbol->line() > unsigned(line))
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
lastIndex = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastIndex.isValid()) {
|
|
|
|
|
bool blocked = m_methodCombo->blockSignals(true);
|
2009-04-27 17:22:49 +02:00
|
|
|
m_methodCombo->setCurrentIndex(m_proxyModel->mapFromSource(lastIndex).row());
|
2008-12-04 17:06:44 +01:00
|
|
|
updateMethodBoxToolTip();
|
2008-12-02 12:01:29 +01:00
|
|
|
(void) m_methodCombo->blockSignals(blocked);
|
|
|
|
|
}
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateMethodBoxToolTip()
|
|
|
|
|
{
|
|
|
|
|
m_methodCombo->setToolTip(m_methodCombo->currentText());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateUses()
|
|
|
|
|
{
|
|
|
|
|
m_updateUsesTimer->start(UPDATE_USES_INTERVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateUsesNow()
|
|
|
|
|
{
|
|
|
|
|
m_updateUsesTimer->stop();
|
|
|
|
|
|
|
|
|
|
if (m_currentRenameSelection != -1)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
semanticRehighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isCompatible(Name *name, Name *otherName)
|
|
|
|
|
{
|
|
|
|
|
if (NameId *nameId = name->asNameId()) {
|
|
|
|
|
if (TemplateNameId *otherTemplId = otherName->asTemplateNameId())
|
|
|
|
|
return nameId->identifier()->isEqualTo(otherTemplId->identifier());
|
|
|
|
|
} else if (TemplateNameId *templId = name->asTemplateNameId()) {
|
|
|
|
|
if (NameId *otherNameId = otherName->asNameId())
|
|
|
|
|
return templId->identifier()->isEqualTo(otherNameId->identifier());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name->isEqualTo(otherName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isCompatible(Function *definition, Symbol *declaration, QualifiedNameId *declarationName)
|
|
|
|
|
{
|
2009-02-09 17:44:06 +01:00
|
|
|
Function *declTy = declaration->type()->asFunctionType();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (! declTy)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Name *definitionName = definition->name();
|
|
|
|
|
if (QualifiedNameId *q = definitionName->asQualifiedNameId()) {
|
|
|
|
|
if (! isCompatible(q->unqualifiedNameId(), declaration->name()))
|
|
|
|
|
return false;
|
|
|
|
|
else if (q->nameCount() > declarationName->nameCount())
|
|
|
|
|
return false;
|
|
|
|
|
else if (declTy->argumentCount() != definition->argumentCount())
|
|
|
|
|
return false;
|
|
|
|
|
else if (declTy->isConst() != definition->isConst())
|
|
|
|
|
return false;
|
|
|
|
|
else if (declTy->isVolatile() != definition->isVolatile())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < definition->argumentCount(); ++i) {
|
|
|
|
|
Symbol *arg = definition->argumentAt(i);
|
|
|
|
|
Symbol *otherArg = declTy->argumentAt(i);
|
|
|
|
|
if (! arg->type().isEqualTo(otherArg->type()))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i != q->nameCount(); ++i) {
|
|
|
|
|
Name *n = q->nameAt(q->nameCount() - i - 1);
|
|
|
|
|
Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1);
|
|
|
|
|
if (! isCompatible(n, m))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
// ### TODO: implement isCompatible for unqualified name ids.
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::switchDeclarationDefinition()
|
|
|
|
|
{
|
|
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
|
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2008-12-12 10:07:58 +01:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
|
|
|
|
|
Document::Ptr doc = snapshot.value(file()->fileName());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!doc)
|
|
|
|
|
return;
|
|
|
|
|
Symbol *lastSymbol = doc->findSymbolAt(line, column);
|
|
|
|
|
if (!lastSymbol || !lastSymbol->scope())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Function *f = lastSymbol->asFunction();
|
2009-01-23 13:03:36 +01:00
|
|
|
if (!f) {
|
2008-12-02 12:01:29 +01:00
|
|
|
Scope *fs = lastSymbol->scope();
|
2009-01-23 13:03:36 +01:00
|
|
|
if (!fs->isFunctionScope())
|
2008-12-02 12:01:29 +01:00
|
|
|
fs = fs->enclosingFunctionScope();
|
|
|
|
|
if (fs)
|
|
|
|
|
f = fs->owner()->asFunction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
2008-12-12 10:07:58 +01:00
|
|
|
typeOfExpression.setSnapshot(m_modelManager->snapshot());
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<TypeOfExpression::Result> resolvedSymbols = typeOfExpression(QString(), doc, lastSymbol);
|
|
|
|
|
const LookupContext &context = typeOfExpression.lookupContext();
|
|
|
|
|
|
|
|
|
|
QualifiedNameId *q = qualifiedNameIdForSymbol(f, context);
|
|
|
|
|
QList<Symbol *> symbols = context.resolve(q);
|
|
|
|
|
|
|
|
|
|
Symbol *declaration = 0;
|
|
|
|
|
foreach (declaration, symbols) {
|
|
|
|
|
if (isCompatible(f, declaration, q))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! declaration && ! symbols.isEmpty())
|
|
|
|
|
declaration = symbols.first();
|
|
|
|
|
|
|
|
|
|
if (declaration)
|
2009-03-24 11:13:37 +01:00
|
|
|
openCppEditorAt(linkToSymbol(declaration));
|
2009-02-09 17:44:06 +01:00
|
|
|
} else if (lastSymbol->type()->isFunctionType()) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (Symbol *def = findDefinition(lastSymbol))
|
2009-03-24 11:13:37 +01:00
|
|
|
openCppEditorAt(linkToSymbol(def));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 17:03:26 +01:00
|
|
|
CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
|
2009-09-08 13:03:24 +02:00
|
|
|
bool resolveTarget)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-03-24 11:13:37 +01:00
|
|
|
Link link;
|
2009-03-23 17:20:19 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!m_modelManager)
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2008-12-12 10:07:58 +01:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
2008-12-02 12:01:29 +01:00
|
|
|
int line = 0, column = 0;
|
2009-03-23 17:20:19 +01:00
|
|
|
convertPosition(cursor.position(), &line, &column);
|
2008-12-12 10:07:58 +01:00
|
|
|
Document::Ptr doc = snapshot.value(file()->fileName());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!doc)
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2008-12-10 17:21:01 +01:00
|
|
|
|
2009-03-23 17:20:19 +01:00
|
|
|
QTextCursor tc = cursor;
|
2009-03-24 11:13:37 +01:00
|
|
|
|
2009-04-21 15:32:50 +02:00
|
|
|
// Make sure we're not at the start of a word
|
|
|
|
|
{
|
|
|
|
|
const QChar c = characterAt(tc.position());
|
|
|
|
|
if (c.isLetter() || c == QLatin1Char('_'))
|
|
|
|
|
tc.movePosition(QTextCursor::Right);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 14:52:20 +01:00
|
|
|
static TokenUnderCursor tokenUnderCursor;
|
2009-03-30 15:25:06 +02:00
|
|
|
|
|
|
|
|
QTextBlock block;
|
|
|
|
|
const SimpleToken tk = tokenUnderCursor(tc, &block);
|
|
|
|
|
|
2009-04-07 10:37:37 +02:00
|
|
|
// Handle include directives
|
|
|
|
|
if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
|
|
|
|
|
const unsigned lineno = cursor.blockNumber() + 1;
|
|
|
|
|
foreach (const Document::Include &incl, doc->includes()) {
|
|
|
|
|
if (incl.line() == lineno && incl.resolved()) {
|
|
|
|
|
link.fileName = incl.fileName();
|
|
|
|
|
link.pos = cursor.block().position() + tk.position() + 1;
|
|
|
|
|
link.length = tk.length() - 2;
|
|
|
|
|
return link;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 15:25:06 +02:00
|
|
|
if (tk.isNot(T_IDENTIFIER))
|
|
|
|
|
return link;
|
|
|
|
|
|
2009-04-07 10:37:37 +02:00
|
|
|
// Find the last symbol up to the cursor position
|
|
|
|
|
Symbol *lastSymbol = doc->findSymbolAt(line, column);
|
|
|
|
|
if (!lastSymbol)
|
|
|
|
|
return link;
|
|
|
|
|
|
2009-03-30 15:25:06 +02:00
|
|
|
const int nameStart = tk.position();
|
|
|
|
|
const int nameLength = tk.length();
|
2009-04-07 16:31:09 +02:00
|
|
|
const int endOfName = block.position() + nameStart + nameLength;
|
2009-03-30 15:25:06 +02:00
|
|
|
|
|
|
|
|
const QString name = block.text().mid(nameStart, nameLength);
|
2009-04-07 16:31:09 +02:00
|
|
|
tc.setPosition(endOfName);
|
2009-03-24 11:13:37 +01:00
|
|
|
|
|
|
|
|
// Evaluate the type of the expression under the cursor
|
2008-12-02 12:01:29 +01:00
|
|
|
ExpressionUnderCursor expressionUnderCursor;
|
|
|
|
|
const QString expression = expressionUnderCursor(tc);
|
|
|
|
|
TypeOfExpression typeOfExpression;
|
2009-03-23 17:20:19 +01:00
|
|
|
typeOfExpression.setSnapshot(snapshot);
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<TypeOfExpression::Result> resolvedSymbols =
|
|
|
|
|
typeOfExpression(expression, doc, lastSymbol);
|
|
|
|
|
|
|
|
|
|
if (!resolvedSymbols.isEmpty()) {
|
2009-03-25 14:47:34 +01:00
|
|
|
TypeOfExpression::Result result = resolvedSymbols.first();
|
|
|
|
|
|
|
|
|
|
if (result.first->isForwardClassDeclarationType()) {
|
|
|
|
|
while (! resolvedSymbols.isEmpty()) {
|
|
|
|
|
TypeOfExpression::Result r = resolvedSymbols.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.first->isForwardClassDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-28 11:46:00 +02:00
|
|
|
if (result.first->isObjCForwardClassDeclarationType()) {
|
|
|
|
|
while (! resolvedSymbols.isEmpty()) {
|
|
|
|
|
TypeOfExpression::Result r = resolvedSymbols.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.first->isObjCForwardClassDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.first->isObjCForwardProtocolDeclarationType()) {
|
|
|
|
|
while (! resolvedSymbols.isEmpty()) {
|
|
|
|
|
TypeOfExpression::Result r = resolvedSymbols.takeFirst();
|
|
|
|
|
|
|
|
|
|
if (! r.first->isObjCForwardProtocolDeclarationType()) {
|
|
|
|
|
result = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-25 14:47:34 +01:00
|
|
|
if (Symbol *symbol = result.second) {
|
2008-12-02 12:01:29 +01:00
|
|
|
Symbol *def = 0;
|
2009-09-08 13:03:24 +02:00
|
|
|
if (resolveTarget && !lastSymbol->isFunction())
|
2008-12-02 12:01:29 +01:00
|
|
|
def = findDefinition(symbol);
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
link = linkToSymbol(def ? def : symbol);
|
2009-04-06 15:28:25 +02:00
|
|
|
link.pos = block.position() + nameStart;
|
2009-03-24 11:13:37 +01:00
|
|
|
link.length = nameLength;
|
|
|
|
|
return link;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// This would jump to the type of a name
|
|
|
|
|
#if 0
|
|
|
|
|
} else if (NamedType *namedType = firstType->asNamedType()) {
|
|
|
|
|
QList<Symbol *> candidates = context.resolve(namedType->name());
|
|
|
|
|
if (!candidates.isEmpty()) {
|
|
|
|
|
Symbol *s = candidates.takeFirst();
|
2008-12-11 10:28:39 +01:00
|
|
|
openCppEditorAt(s->fileName(), s->line(), s->column());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-03-23 17:20:19 +01:00
|
|
|
// Handle macro uses
|
2008-12-09 16:39:52 +01:00
|
|
|
foreach (const Document::MacroUse use, doc->macroUses()) {
|
|
|
|
|
if (use.contains(endOfName - 1)) {
|
|
|
|
|
const Macro ¯o = use.macro();
|
2009-05-27 15:34:13 +02:00
|
|
|
link.fileName = macro.fileName();
|
2009-03-24 11:13:37 +01:00
|
|
|
link.line = macro.line();
|
|
|
|
|
link.pos = use.begin();
|
|
|
|
|
link.length = use.end() - use.begin();
|
|
|
|
|
return link;
|
2008-12-09 16:39:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-03-23 17:20:19 +01:00
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return link;
|
2009-03-23 17:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::jumpToDefinition()
|
|
|
|
|
{
|
2009-09-08 13:03:24 +02:00
|
|
|
openLink(findLinkAt(textCursor()));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-28 23:46:43 +01:00
|
|
|
Symbol *CPPEditor::findDefinition(Symbol *symbol)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-28 23:46:43 +01:00
|
|
|
if (symbol->isFunction())
|
|
|
|
|
return 0; // symbol is a function definition.
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-09 17:44:06 +01:00
|
|
|
Function *funTy = symbol->type()->asFunctionType();
|
2009-01-28 23:46:43 +01:00
|
|
|
if (! funTy)
|
|
|
|
|
return 0; // symbol does not have function type.
|
|
|
|
|
|
|
|
|
|
Name *name = symbol->name();
|
|
|
|
|
if (! name)
|
|
|
|
|
return 0; // skip anonymous functions!
|
|
|
|
|
|
|
|
|
|
if (QualifiedNameId *q = name->asQualifiedNameId())
|
|
|
|
|
name = q->unqualifiedNameId();
|
|
|
|
|
|
|
|
|
|
// map from file names to function definitions.
|
|
|
|
|
QMap<QString, QList<Function *> > functionDefinitions;
|
|
|
|
|
|
|
|
|
|
// find function definitions.
|
|
|
|
|
FindFunctionDefinitions findFunctionDefinitions;
|
|
|
|
|
|
|
|
|
|
// save the current snapshot
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
|
|
|
|
|
foreach (Document::Ptr doc, snapshot) {
|
|
|
|
|
if (Scope *globals = doc->globalSymbols()) {
|
|
|
|
|
QList<Function *> *localFunctionDefinitions =
|
|
|
|
|
&functionDefinitions[doc->fileName()];
|
|
|
|
|
|
|
|
|
|
findFunctionDefinitions(name, globals,
|
|
|
|
|
localFunctionDefinitions);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-28 23:46:43 +01:00
|
|
|
// a dummy document.
|
|
|
|
|
Document::Ptr expressionDocument = Document::create("<empty>");
|
|
|
|
|
|
|
|
|
|
QMapIterator<QString, QList<Function *> > it(functionDefinitions);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
|
|
|
|
|
// get the instance of the document.
|
|
|
|
|
Document::Ptr thisDocument = snapshot.value(it.key());
|
|
|
|
|
|
|
|
|
|
foreach (Function *f, it.value()) {
|
|
|
|
|
// create a lookup context
|
|
|
|
|
const LookupContext context(f, expressionDocument,
|
|
|
|
|
thisDocument, snapshot);
|
|
|
|
|
|
|
|
|
|
// search the matching definition for the function declaration `symbol'.
|
|
|
|
|
foreach (Symbol *s, context.resolve(f->name())) {
|
|
|
|
|
if (s == symbol)
|
|
|
|
|
return f;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:09:26 +02:00
|
|
|
SemanticInfo CPPEditor::semanticInfo() const
|
|
|
|
|
{
|
|
|
|
|
return m_lastSemanticInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
bool CPPEditor::isElectricCharacter(const QChar &ch) const
|
|
|
|
|
{
|
|
|
|
|
if (ch == QLatin1Char('{') ||
|
|
|
|
|
ch == QLatin1Char('}') ||
|
2009-09-21 15:11:18 +02:00
|
|
|
ch == QLatin1Char(':') ||
|
2009-05-28 12:23:32 +02:00
|
|
|
ch == QLatin1Char('#')) {
|
2008-12-02 12:01:29 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-25 13:50:45 +02:00
|
|
|
|
|
|
|
|
static void countBracket(QChar open, QChar close, QChar c, int *errors, int *stillopen)
|
|
|
|
|
{
|
|
|
|
|
if (c == open)
|
|
|
|
|
++*stillopen;
|
|
|
|
|
else if (c == close)
|
|
|
|
|
--*stillopen;
|
|
|
|
|
|
|
|
|
|
if (*stillopen < 0) {
|
|
|
|
|
*errors += -1 * (*stillopen);
|
|
|
|
|
*stillopen = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void countBrackets(QTextCursor cursor, int from, int end, QChar open, QChar close, int *errors, int *stillopen)
|
|
|
|
|
{
|
|
|
|
|
cursor.setPosition(from);
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
while (block.isValid() && block.position() < end) {
|
|
|
|
|
TextEditor::Parentheses parenList = TextEditor::TextEditDocumentLayout::parentheses(block);
|
|
|
|
|
if (!parenList.isEmpty() && !TextEditor::TextEditDocumentLayout::ifdefedOut(block)) {
|
|
|
|
|
for (int i = 0; i < parenList.count(); ++i) {
|
|
|
|
|
TextEditor::Parenthesis paren = parenList.at(i);
|
|
|
|
|
int position = block.position() + paren.pos;
|
|
|
|
|
if (position < from || position >= end)
|
|
|
|
|
continue;
|
|
|
|
|
countBracket(open, close, paren.chr, errors, stillopen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-17 18:54:12 +02:00
|
|
|
QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &textToInsert) const
|
2009-09-17 17:57:17 +02:00
|
|
|
{
|
2009-09-21 18:19:09 +02:00
|
|
|
const bool checkBlockEnd = m_allowSkippingOfBlockEnd;
|
|
|
|
|
m_allowSkippingOfBlockEnd = false; // consume blockEnd.
|
2009-09-17 18:54:12 +02:00
|
|
|
|
2009-09-22 11:25:29 +02:00
|
|
|
if (!contextAllowsAutoParentheses(cursor, textToInsert))
|
2009-09-17 17:57:17 +02:00
|
|
|
return QString();
|
|
|
|
|
|
2009-09-17 18:54:12 +02:00
|
|
|
QString text = textToInsert;
|
|
|
|
|
const QChar lookAhead = characterAt(cursor.selectionEnd());
|
2009-09-17 18:39:10 +02:00
|
|
|
|
2009-09-25 13:50:45 +02:00
|
|
|
QChar character = textToInsert.at(0);
|
|
|
|
|
QString parentheses = QLatin1String("()");
|
|
|
|
|
QString brackets = QLatin1String("[]");
|
|
|
|
|
if (parentheses.contains(character) || brackets.contains(character)) {
|
|
|
|
|
QTextCursor tmp= cursor;
|
|
|
|
|
TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
|
|
|
|
|
int blockStart = tmp.isNull() ? 0 : tmp.position();
|
|
|
|
|
tmp = cursor;
|
|
|
|
|
TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
|
|
|
|
|
int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
|
|
|
|
|
QChar openChar = parentheses.contains(character) ? QLatin1Char('(') : QLatin1Char('[');
|
|
|
|
|
QChar closeChar = parentheses.contains(character) ? QLatin1Char(')') : QLatin1Char(']');
|
|
|
|
|
|
|
|
|
|
int errors = 0;
|
|
|
|
|
int stillopen = 0;
|
|
|
|
|
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
int errorsBeforeInsertion = errors + stillopen;
|
|
|
|
|
errors = 0;
|
|
|
|
|
stillopen = 0;
|
|
|
|
|
countBrackets(cursor, blockStart, cursor.position(), openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
countBracket(openChar, closeChar, character, &errors, &stillopen);
|
|
|
|
|
countBrackets(cursor, cursor.position(), blockEnd, openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
int errorsAfterInsertion = errors + stillopen;
|
|
|
|
|
if (errorsAfterInsertion < errorsBeforeInsertion)
|
|
|
|
|
return QString(); // insertion fixes parentheses or bracket errors, do not auto complete
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-21 18:19:09 +02:00
|
|
|
MatchingText matchingText;
|
2009-09-17 17:57:17 +02:00
|
|
|
int skippedChars = 0;
|
2009-09-21 18:19:09 +02:00
|
|
|
const QString autoText = matchingText.insertMatchingBrace(cursor, text, lookAhead, &skippedChars);
|
2009-09-17 17:57:17 +02:00
|
|
|
|
2009-09-21 18:19:09 +02:00
|
|
|
if (checkBlockEnd && textToInsert.at(0) == QLatin1Char('}')) {
|
|
|
|
|
if (textToInsert.length() > 1)
|
|
|
|
|
qWarning() << "*** handle event compression";
|
2009-09-17 18:54:12 +02:00
|
|
|
|
2009-09-21 18:19:09 +02:00
|
|
|
int startPos = cursor.selectionEnd(), pos = startPos;
|
|
|
|
|
while (characterAt(pos).isSpace())
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (characterAt(pos) == QLatin1Char('}'))
|
|
|
|
|
skippedChars += (pos - startPos) + 1;
|
|
|
|
|
}
|
2009-09-17 17:57:17 +02:00
|
|
|
|
|
|
|
|
if (skippedChars) {
|
|
|
|
|
const int pos = cursor.position();
|
|
|
|
|
cursor.setPosition(pos + skippedChars);
|
|
|
|
|
cursor.setPosition(pos, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return autoText;
|
|
|
|
|
}
|
2009-09-17 12:36:40 +02:00
|
|
|
|
|
|
|
|
bool CPPEditor::autoBackspace(QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
m_allowSkippingOfBlockEnd = false;
|
|
|
|
|
|
|
|
|
|
int pos = cursor.position();
|
|
|
|
|
QTextCursor c = cursor;
|
|
|
|
|
c.setPosition(pos - 1);
|
|
|
|
|
|
|
|
|
|
QChar lookAhead = characterAt(pos);
|
|
|
|
|
QChar lookBehind = characterAt(pos-1);
|
|
|
|
|
QChar lookFurtherBehind = characterAt(pos-2);
|
2009-09-25 13:50:45 +02:00
|
|
|
|
|
|
|
|
QChar character = lookBehind;
|
|
|
|
|
if (character == QLatin1Char('(') || character == QLatin1Char('[')) {
|
|
|
|
|
QTextCursor tmp = cursor;
|
|
|
|
|
TextEditor::TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
|
|
|
|
|
int blockStart = tmp.isNull() ? 0 : tmp.position();
|
|
|
|
|
tmp = cursor;
|
|
|
|
|
TextEditor::TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
|
|
|
|
|
int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
|
|
|
|
|
QChar openChar = character;
|
|
|
|
|
QChar closeChar = (character == QLatin1Char('(')) ? QLatin1Char(')') : QLatin1Char(']');
|
|
|
|
|
|
|
|
|
|
int errors = 0;
|
|
|
|
|
int stillopen = 0;
|
|
|
|
|
countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
int errorsBeforeDeletion = errors + stillopen;
|
|
|
|
|
errors = 0;
|
|
|
|
|
stillopen = 0;
|
|
|
|
|
countBrackets(cursor, blockStart, pos - 1, openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
countBrackets(cursor, pos, blockEnd, openChar, closeChar, &errors, &stillopen);
|
|
|
|
|
int errorsAfterDeletion = errors + stillopen;
|
|
|
|
|
|
|
|
|
|
if (errorsAfterDeletion < errorsBeforeDeletion)
|
|
|
|
|
return false; // insertion fixes parentheses or bracket errors, do not auto complete
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-22 18:06:41 +02:00
|
|
|
if ((lookBehind == QLatin1Char('(') && lookAhead == QLatin1Char(')'))
|
2009-09-17 12:36:40 +02:00
|
|
|
|| (lookBehind == QLatin1Char('[') && lookAhead == QLatin1Char(']'))
|
|
|
|
|
|| (lookBehind == QLatin1Char('"') && lookAhead == QLatin1Char('"')
|
|
|
|
|
&& lookFurtherBehind != QLatin1Char('\\'))
|
|
|
|
|
|| (lookBehind == QLatin1Char('\'') && lookAhead == QLatin1Char('\'')
|
|
|
|
|
&& lookFurtherBehind != QLatin1Char('\\'))) {
|
2009-09-22 18:06:41 +02:00
|
|
|
if (! isInComment(c)) {
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.deleteChar();
|
|
|
|
|
cursor.deletePreviousChar();
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-17 12:36:40 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-17 12:47:16 +02:00
|
|
|
int CPPEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
|
2009-09-17 12:36:40 +02:00
|
|
|
{
|
|
|
|
|
if (characterAt(cursor.position()-1) != QLatin1Char('{'))
|
2009-09-17 12:47:16 +02:00
|
|
|
return 0;
|
2009-09-17 12:36:40 +02:00
|
|
|
|
|
|
|
|
if (!contextAllowsAutoParentheses(cursor))
|
2009-09-17 12:47:16 +02:00
|
|
|
return 0;
|
2009-09-17 12:36:40 +02:00
|
|
|
|
|
|
|
|
// verify that we indeed do have an extra opening brace in the document
|
|
|
|
|
int braceDepth = document()->lastBlock().userState();
|
|
|
|
|
if (braceDepth >= 0)
|
|
|
|
|
braceDepth >>= 8;
|
|
|
|
|
else
|
|
|
|
|
braceDepth= 0;
|
|
|
|
|
|
2009-09-30 17:09:46 +02:00
|
|
|
if (braceDepth <= 0)
|
|
|
|
|
return 0; // braces are all balanced or worse, no need to do anything
|
|
|
|
|
|
|
|
|
|
// we have an extra brace , let's see if we should close it
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* verify that the next block is not further intended compared to the current block.
|
|
|
|
|
This covers the following case:
|
|
|
|
|
|
|
|
|
|
if (condition) {|
|
|
|
|
|
statement;
|
|
|
|
|
*/
|
|
|
|
|
const TabSettings &ts = tabSettings();
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
int indentation = ts.indentationColumn(block.text());
|
|
|
|
|
if (block.next().isValid()
|
|
|
|
|
&& ts.indentationColumn(block.next().text()) > indentation)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
int pos = cursor.position();
|
|
|
|
|
|
|
|
|
|
MatchingText matchingText;
|
|
|
|
|
const QString textToInsert = matchingText.insertParagraphSeparator(cursor);
|
|
|
|
|
|
|
|
|
|
cursor.insertText(textToInsert);
|
|
|
|
|
cursor.setPosition(pos);
|
|
|
|
|
if (ts.m_autoIndent) {
|
|
|
|
|
cursor.insertBlock();
|
|
|
|
|
indent(document(), cursor, QChar::Null);
|
|
|
|
|
} else {
|
|
|
|
|
QString previousBlockText = cursor.block().text();
|
|
|
|
|
cursor.insertBlock();
|
|
|
|
|
cursor.insertText(ts.indentationString(previousBlockText));
|
2009-09-17 12:36:40 +02:00
|
|
|
}
|
2009-09-30 17:09:46 +02:00
|
|
|
cursor.setPosition(pos);
|
|
|
|
|
m_allowSkippingOfBlockEnd = true;
|
|
|
|
|
return 1;
|
2009-09-17 12:36:40 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-22 11:25:29 +02:00
|
|
|
bool CPPEditor::contextAllowsAutoParentheses(const QTextCursor &cursor,
|
|
|
|
|
const QString &textToInsert) const
|
2009-09-16 13:56:34 +02:00
|
|
|
{
|
2009-09-22 11:25:29 +02:00
|
|
|
QChar ch;
|
2009-09-22 18:06:41 +02:00
|
|
|
|
2009-09-22 11:25:29 +02:00
|
|
|
if (! textToInsert.isEmpty())
|
|
|
|
|
ch = textToInsert.at(0);
|
|
|
|
|
|
|
|
|
|
if (! (MatchingText::shouldInsertMatchingText(cursor) || ch == QLatin1Char('\'') || ch == QLatin1Char('"')))
|
2009-09-21 18:19:09 +02:00
|
|
|
return false;
|
2009-09-22 18:06:41 +02:00
|
|
|
else if (isInComment(cursor))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-21 18:19:09 +02:00
|
|
|
|
2009-09-22 18:06:41 +02:00
|
|
|
bool CPPEditor::isInComment(const QTextCursor &cursor) const
|
|
|
|
|
{
|
2009-09-16 13:56:34 +02:00
|
|
|
CPlusPlus::TokenUnderCursor tokenUnderCursor;
|
|
|
|
|
const SimpleToken tk = tokenUnderCursor(cursor);
|
2009-09-17 17:57:17 +02:00
|
|
|
|
2009-09-18 12:27:04 +02:00
|
|
|
if (tk.isComment()) {
|
2009-09-29 11:34:11 +02:00
|
|
|
const int pos = cursor.selectionEnd() - cursor.block().position();
|
|
|
|
|
|
|
|
|
|
if (pos == tk.end()) {
|
|
|
|
|
if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
const int state = cursor.block().userState() & 0xFF;
|
|
|
|
|
if (state > 0)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-18 12:27:04 +02:00
|
|
|
|
|
|
|
|
if (pos < tk.end())
|
2009-09-22 18:06:41 +02:00
|
|
|
return true;
|
2009-09-18 12:27:04 +02:00
|
|
|
}
|
2009-09-17 17:57:17 +02:00
|
|
|
|
2009-09-22 18:06:41 +02:00
|
|
|
return false;
|
2009-09-16 13:56:34 +02:00
|
|
|
}
|
|
|
|
|
|
2009-06-05 14:30:18 +02:00
|
|
|
void CPPEditor::indentInsertedText(const QTextCursor &tc)
|
|
|
|
|
{
|
|
|
|
|
indent(tc.document(), tc, QChar::Null);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
// Indent a code line based on previous
|
|
|
|
|
template <class Iterator>
|
|
|
|
|
static void indentCPPBlock(const CPPEditor::TabSettings &ts,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
const Iterator &programBegin,
|
|
|
|
|
const Iterator &programEnd,
|
|
|
|
|
QChar typedChar)
|
|
|
|
|
{
|
|
|
|
|
typedef typename SharedTools::Indenter<Iterator> Indenter;
|
|
|
|
|
Indenter &indenter = Indenter::instance();
|
|
|
|
|
indenter.setIndentSize(ts.m_indentSize);
|
|
|
|
|
indenter.setTabSize(ts.m_tabSize);
|
|
|
|
|
|
|
|
|
|
const TextEditor::TextBlockIterator current(block);
|
|
|
|
|
const int indent = indenter.indentForBottomLine(current, programBegin, programEnd, typedChar);
|
|
|
|
|
ts.indentLine(block, indent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
|
|
|
|
|
{
|
2009-09-21 15:11:18 +02:00
|
|
|
QTextCursor tc(block);
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
|
|
|
|
|
BackwardsScanner tk(tc, QString(), 400);
|
|
|
|
|
const int tokenCount = tk.startToken();
|
|
|
|
|
const int indentSize = tabSettings().m_indentSize;
|
|
|
|
|
|
|
|
|
|
if (tokenCount != 0) {
|
|
|
|
|
const SimpleToken firstToken = tk[0];
|
|
|
|
|
|
|
|
|
|
if (firstToken.is(T_COLON)) {
|
|
|
|
|
const int indent = tk.indentation(-1) + // indentation of the previous newline
|
|
|
|
|
indentSize;
|
|
|
|
|
tabSettings().indentLine(block, indent);
|
|
|
|
|
return;
|
|
|
|
|
} else if ((firstToken.is(T_PUBLIC) || firstToken.is(T_PROTECTED) || firstToken.is(T_PRIVATE) ||
|
|
|
|
|
firstToken.is(T_Q_SIGNALS) || firstToken.is(T_Q_SLOTS)) && tk[1].is(T_COLON)) {
|
|
|
|
|
const int startOfBlock = tk.startOfBlock(0);
|
|
|
|
|
if (startOfBlock != 0) {
|
|
|
|
|
const int indent = tk.indentation(startOfBlock);
|
|
|
|
|
tabSettings().indentLine(block, indent);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (firstToken.is(T_CASE) || firstToken.is(T_DEFAULT)) {
|
|
|
|
|
const int startOfBlock = tk.startOfBlock(0);
|
|
|
|
|
if (startOfBlock != 0) {
|
|
|
|
|
const int indent = tk.indentation(startOfBlock);
|
|
|
|
|
tabSettings().indentLine(block, indent);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-21 16:09:01 +02:00
|
|
|
if ((tokenCount == 0 || tk[0].isNot(T_POUND)) && typedChar.isNull() && (tk[-1].is(T_IDENTIFIER) || tk[-1].is(T_RPAREN))) {
|
|
|
|
|
int tokenIndex = -1;
|
|
|
|
|
if (tk[-1].is(T_RPAREN)) {
|
|
|
|
|
const int matchingBrace = tk.startOfMatchingBrace(0);
|
|
|
|
|
if (matchingBrace != 0 && tk[matchingBrace - 1].is(T_IDENTIFIER)) {
|
|
|
|
|
tokenIndex = matchingBrace - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString spell = tk.text(tokenIndex);
|
|
|
|
|
if (tk[tokenIndex].followsNewline() && (spell.startsWith(QLatin1String("QT_")) ||
|
|
|
|
|
spell.startsWith(QLatin1String("Q_")))) {
|
|
|
|
|
const int indent = tk.indentation(tokenIndex);
|
|
|
|
|
tabSettings().indentLine(block, indent);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TextEditor::TextBlockIterator begin(doc->begin());
|
|
|
|
|
const TextEditor::TextBlockIterator end(block.next());
|
2008-12-02 12:01:29 +01:00
|
|
|
indentCPPBlock(tabSettings(), block, begin, end, typedChar);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
bool CPPEditor::event(QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
switch (e->type()) {
|
|
|
|
|
case QEvent::ShortcutOverride:
|
2009-07-01 18:41:04 +02:00
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != -1) {
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseTextEditor::event(e);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
|
|
|
|
|
{
|
2009-07-09 12:14:00 +02:00
|
|
|
// ### enable
|
|
|
|
|
// updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource()));
|
|
|
|
|
|
2009-09-24 14:50:22 +02:00
|
|
|
QMenu *menu = new QMenu();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-09-24 14:50:22 +02:00
|
|
|
|
|
|
|
|
// QMenu *menu = createStandardContextMenu();
|
|
|
|
|
//
|
|
|
|
|
// // Remove insert unicode control character
|
|
|
|
|
// QAction *lastAction = menu->actions().last();
|
|
|
|
|
// if (lastAction->menu() && QLatin1String(lastAction->menu()->metaObject()->className()) == QLatin1String("QUnicodeControlCharacterMenu"))
|
|
|
|
|
// menu->removeAction(lastAction);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-08-04 13:44:54 +02:00
|
|
|
Core::ActionManager *am = Core::ICore::instance()->actionManager();
|
|
|
|
|
Core::ActionContainer *mcontext = am->actionContainer(CppEditor::Constants::M_CONTEXT);
|
2008-12-02 12:01:29 +01:00
|
|
|
QMenu *contextMenu = mcontext->menu();
|
|
|
|
|
|
|
|
|
|
foreach (QAction *action, contextMenu->actions())
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
const QList<QTextEdit::ExtraSelection> selections =
|
|
|
|
|
extraSelections(BaseTextEditor::CodeSemanticsSelection);
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
menu->exec(e->globalPos());
|
|
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 17:57:00 +02:00
|
|
|
void CPPEditor::keyPressEvent(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (m_currentRenameSelection == -1) {
|
|
|
|
|
TextEditor::BaseTextEditor::keyPressEvent(e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
const QTextEdit::ExtraSelection ¤tRenameSelection = m_renameSelections.at(m_currentRenameSelection);
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
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
|
|
|
|
|
if (cursor.position() > currentRenameSelection.cursor.anchor()
|
|
|
|
|
&& cursor.position() <= currentRenameSelection.cursor.position()) {
|
|
|
|
|
cursor.setPosition(currentRenameSelection.cursor.anchor(), moveMode);
|
|
|
|
|
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
|
|
|
|
|
if (cursor.position() >= currentRenameSelection.cursor.anchor()
|
|
|
|
|
&& cursor.position() < currentRenameSelection.cursor.position()) {
|
|
|
|
|
cursor.setPosition(currentRenameSelection.cursor.position(), moveMode);
|
|
|
|
|
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: {
|
2009-07-02 15:09:02 +02:00
|
|
|
if (cursor.position() == currentRenameSelection.cursor.anchor()) {
|
|
|
|
|
// Eat backspace at start of name
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-07-02 15:09:02 +02:00
|
|
|
} else if (cursor.position() > currentRenameSelection.cursor.anchor()
|
|
|
|
|
&& cursor.position() <= currentRenameSelection.cursor.position()) {
|
2009-07-01 17:57:00 +02:00
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
inAllRenameSelections(DeletePreviousChar, currentRenameSelection, cursor);
|
2009-07-01 17:57:00 +02:00
|
|
|
e->accept();
|
2009-07-01 18:41:04 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Qt::Key_Delete: {
|
2009-07-02 15:09:02 +02:00
|
|
|
if (cursor.position() == currentRenameSelection.cursor.position()) {
|
|
|
|
|
// Eat delete at end of name
|
2009-07-01 18:41:04 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-07-02 15:09:02 +02:00
|
|
|
} else if (cursor.position() >= currentRenameSelection.cursor.anchor()
|
|
|
|
|
&& cursor.position() < currentRenameSelection.cursor.position()) {
|
2009-07-01 18:41:04 +02:00
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
inAllRenameSelections(DeleteChar, currentRenameSelection, cursor);
|
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
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
2009-07-01 18:41:04 +02:00
|
|
|
QString text = e->text();
|
2009-07-01 17:57:00 +02:00
|
|
|
if (! text.isEmpty() && text.at(0).isPrint()) {
|
2009-07-02 15:09:02 +02:00
|
|
|
if (cursor.position() >= currentRenameSelection.cursor.anchor()
|
|
|
|
|
&& cursor.position() <= currentRenameSelection.cursor.position()) {
|
2009-07-01 17:57:00 +02:00
|
|
|
|
2009-07-02 15:09:02 +02:00
|
|
|
inAllRenameSelections(InsertText, currentRenameSelection, cursor, text);
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-01 18:41:04 +02:00
|
|
|
|
|
|
|
|
TextEditor::BaseTextEditor::keyPressEvent(e);
|
2009-07-01 17:57:00 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<int> CPPEditorEditable::context() const
|
|
|
|
|
{
|
|
|
|
|
return m_context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::IEditor *CPPEditorEditable::duplicate(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
CPPEditor *newEditor = new CPPEditor(parent);
|
|
|
|
|
newEditor->duplicateFrom(editor());
|
|
|
|
|
CppPlugin::instance()->initializeEditor(newEditor);
|
|
|
|
|
return newEditor->editableInterface();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *CPPEditorEditable::kind() const
|
|
|
|
|
{
|
|
|
|
|
return CppEditor::Constants::CPPEDITOR_KIND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
|
|
|
|
{
|
|
|
|
|
TextEditor::BaseTextEditor::setFontSettings(fs);
|
2008-12-16 13:19:11 +01:00
|
|
|
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!highlighter)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
static QVector<QString> categories;
|
|
|
|
|
if (categories.isEmpty()) {
|
|
|
|
|
categories << QLatin1String(TextEditor::Constants::C_NUMBER)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_STRING)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_TYPE)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_KEYWORD)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_OPERATOR)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_PREPROCESSOR)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_LABEL)
|
2009-02-20 12:08:34 +01:00
|
|
|
<< QLatin1String(TextEditor::Constants::C_COMMENT)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_COMMENT)
|
2009-10-01 13:34:02 +02:00
|
|
|
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_TAG)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories);
|
|
|
|
|
highlighter->setFormats(formats.constBegin(), formats.constEnd());
|
|
|
|
|
highlighter->rehighlight();
|
2009-03-24 16:36:22 +01:00
|
|
|
|
2009-07-14 10:34:17 +02:00
|
|
|
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
|
2009-09-23 18:06:59 +02:00
|
|
|
m_occurrencesUnusedFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_UNUSED));
|
2009-07-14 10:34:17 +02:00
|
|
|
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
|
2009-09-24 15:19:45 +02:00
|
|
|
|
|
|
|
|
// only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
|
|
|
|
|
m_occurrencesFormat.clearForeground();
|
|
|
|
|
m_occurrenceRenameFormat.clearForeground();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::unCommentSelection()
|
|
|
|
|
{
|
2009-10-05 11:06:05 +02:00
|
|
|
Utils::unCommentSelection(this);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
CPPEditor::Link CPPEditor::linkToSymbol(CPlusPlus::Symbol *symbol)
|
2008-12-11 10:28:39 +01:00
|
|
|
{
|
2009-03-23 17:20:19 +01:00
|
|
|
const QString fileName = QString::fromUtf8(symbol->fileName(),
|
|
|
|
|
symbol->fileNameLength());
|
|
|
|
|
unsigned line = symbol->line();
|
|
|
|
|
unsigned column = symbol->column();
|
2009-03-04 15:47:26 +01:00
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
if (column)
|
|
|
|
|
--column;
|
2009-03-04 15:47:26 +01:00
|
|
|
|
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
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return Link(fileName, line, column);
|
2009-03-23 17:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
bool CPPEditor::openCppEditorAt(const Link &link)
|
2009-03-23 17:20:19 +01:00
|
|
|
{
|
2009-03-24 11:13:37 +01:00
|
|
|
if (link.fileName.isEmpty())
|
2009-03-23 17:20:19 +01:00
|
|
|
return false;
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
if (baseTextDocument()->fileName() == link.fileName) {
|
2009-03-18 19:20:28 +01:00
|
|
|
Core::EditorManager *editorManager = Core::EditorManager::instance();
|
|
|
|
|
editorManager->addCurrentPositionToNavigationHistory();
|
2009-03-24 11:13:37 +01:00
|
|
|
gotoLine(link.line, link.column);
|
2009-03-18 19:20:28 +01:00
|
|
|
setFocus();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:13:37 +01:00
|
|
|
return TextEditor::BaseTextEditor::openEditorAt(link.fileName,
|
|
|
|
|
link.line,
|
|
|
|
|
link.column,
|
2009-03-23 17:20:19 +01:00
|
|
|
Constants::C_CPPEDITOR);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
void CPPEditor::semanticRehighlight()
|
|
|
|
|
{
|
|
|
|
|
m_semanticHighlighter->rehighlight(currentSource());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
|
|
|
|
{
|
2009-09-24 16:34:18 +02:00
|
|
|
if (semanticInfo.revision != document()->revision()) {
|
2009-07-10 12:09:26 +02:00
|
|
|
// got outdated semantic info
|
|
|
|
|
semanticRehighlight();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_lastSemanticInfo = semanticInfo;
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
2009-10-05 14:08:10 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> allSelections;
|
|
|
|
|
|
|
|
|
|
m_renameSelections.clear();
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-05 14:08:10 +02:00
|
|
|
if (uses.size() == 1) {
|
|
|
|
|
// it's an unused declaration
|
|
|
|
|
highlightUses(uses, &allSelections);
|
|
|
|
|
} else if (good) {
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
2009-09-24 10:05:33 +02:00
|
|
|
highlightUses(uses, &selections);
|
2009-10-05 14:08:10 +02:00
|
|
|
m_renameSelections += selections;
|
|
|
|
|
allSelections += selections;
|
|
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
2009-10-05 14:08:10 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, allSelections);
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SemanticHighlighter::Source CPPEditor::currentSource()
|
|
|
|
|
{
|
|
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
const QString fileName = file()->fileName();
|
2009-07-10 12:42:00 +02:00
|
|
|
|
|
|
|
|
QString code;
|
2009-09-24 16:34:18 +02:00
|
|
|
if (m_lastSemanticInfo.revision != document()->revision())
|
2009-07-10 12:42:00 +02:00
|
|
|
code = toPlainText(); // get the source code only when needed.
|
|
|
|
|
|
2009-09-24 16:34:18 +02:00
|
|
|
const int revision = document()->revision();
|
2009-07-09 12:14:00 +02:00
|
|
|
const SemanticHighlighter::Source source(snapshot, fileName, code,
|
|
|
|
|
line, column, revision);
|
|
|
|
|
return source;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SemanticHighlighter::SemanticHighlighter(QObject *parent)
|
|
|
|
|
: QThread(parent),
|
|
|
|
|
m_done(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SemanticHighlighter::~SemanticHighlighter()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticHighlighter::abort()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_done = true;
|
|
|
|
|
m_condition.wakeOne();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SemanticHighlighter::rehighlight(const Source &source)
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_source = source;
|
|
|
|
|
m_condition.wakeOne();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
bool SemanticHighlighter::isOutdated()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
const bool outdated = ! m_source.fileName.isEmpty() || m_done;
|
|
|
|
|
return outdated;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-09 12:14:00 +02:00
|
|
|
void SemanticHighlighter::run()
|
|
|
|
|
{
|
|
|
|
|
setPriority(QThread::IdlePriority);
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
m_mutex.lock();
|
|
|
|
|
|
2009-07-10 12:17:57 +02:00
|
|
|
while (! (m_done || ! m_source.fileName.isEmpty()))
|
2009-07-09 12:14:00 +02:00
|
|
|
m_condition.wait(&m_mutex);
|
|
|
|
|
|
|
|
|
|
const bool done = m_done;
|
|
|
|
|
const Source source = m_source;
|
|
|
|
|
m_source.clear();
|
|
|
|
|
|
|
|
|
|
m_mutex.unlock();
|
|
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const SemanticInfo info = semanticInfo(source);
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
if (! isOutdated()) {
|
|
|
|
|
m_mutex.lock();
|
|
|
|
|
m_lastSemanticInfo = info;
|
|
|
|
|
m_mutex.unlock();
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
emit changed(info);
|
2009-07-10 12:42:00 +02:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 12:42:00 +02:00
|
|
|
SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
|
2009-07-09 12:14:00 +02:00
|
|
|
{
|
2009-07-10 12:42:00 +02:00
|
|
|
m_mutex.lock();
|
|
|
|
|
const int revision = m_lastSemanticInfo.revision;
|
|
|
|
|
m_mutex.unlock();
|
|
|
|
|
|
|
|
|
|
Snapshot snapshot;
|
|
|
|
|
Document::Ptr doc;
|
|
|
|
|
|
|
|
|
|
if (revision == source.revision) {
|
|
|
|
|
m_mutex.lock();
|
|
|
|
|
snapshot = m_lastSemanticInfo.snapshot;
|
|
|
|
|
doc = m_lastSemanticInfo.doc;
|
|
|
|
|
m_mutex.unlock();
|
2009-09-24 12:19:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!doc) {
|
2009-07-10 12:42:00 +02:00
|
|
|
const QByteArray preprocessedCode = source.snapshot.preprocessedCode(source.code, source.fileName);
|
|
|
|
|
|
2009-09-30 17:15:31 +02:00
|
|
|
snapshot = source.snapshot;
|
2009-07-10 12:42:00 +02:00
|
|
|
doc = source.snapshot.documentFromSource(preprocessedCode, source.fileName);
|
2009-09-30 17:15:31 +02:00
|
|
|
doc->check();
|
2009-07-10 12:42:00 +02:00
|
|
|
}
|
2009-07-09 12:14:00 +02:00
|
|
|
|
|
|
|
|
Control *control = doc->control();
|
|
|
|
|
TranslationUnit *translationUnit = doc->translationUnit();
|
|
|
|
|
AST *ast = translationUnit->ast();
|
|
|
|
|
|
|
|
|
|
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(control);
|
|
|
|
|
FunctionDefinitionAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
|
|
|
|
|
|
|
|
|
|
FindUses useTable(control);
|
|
|
|
|
useTable(currentFunctionDefinition);
|
|
|
|
|
|
|
|
|
|
SemanticInfo semanticInfo;
|
|
|
|
|
semanticInfo.revision = source.revision;
|
2009-07-10 11:59:01 +02:00
|
|
|
semanticInfo.snapshot = snapshot;
|
2009-07-09 12:14:00 +02:00
|
|
|
semanticInfo.doc = doc;
|
|
|
|
|
semanticInfo.localUses = useTable.localUses;
|
|
|
|
|
|
|
|
|
|
return semanticInfo;
|
|
|
|
|
}
|