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
|
|
|
**
|
|
|
|
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
|
|
|
|
**
|
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
|
|
|
|
|
** contact the sales department at qt-sales@nokia.com.
|
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"
|
|
|
|
|
|
|
|
|
|
#include <AST.h>
|
|
|
|
|
#include <Token.h>
|
|
|
|
|
#include <Scope.h>
|
|
|
|
|
#include <Symbols.h>
|
|
|
|
|
#include <Names.h>
|
|
|
|
|
#include <Control.h>
|
|
|
|
|
#include <CoreTypes.h>
|
|
|
|
|
#include <Literals.h>
|
|
|
|
|
#include <Semantic.h>
|
2009-01-28 23:46:43 +01:00
|
|
|
#include <SymbolVisitor.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
|
|
|
|
#include <cplusplus/LookupContext.h>
|
|
|
|
|
#include <cplusplus/Overview.h>
|
|
|
|
|
#include <cplusplus/OverviewModel.h>
|
|
|
|
|
#include <cplusplus/SimpleLexer.h>
|
|
|
|
|
#include <cplusplus/TypeOfExpression.h>
|
|
|
|
|
#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-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>
|
|
|
|
|
#include <QtGui/QAction>
|
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>
|
|
|
|
|
#include <QtGui/QTreeView>
|
|
|
|
|
|
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
using namespace CppEditor::Internal;
|
|
|
|
|
|
|
|
|
|
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-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)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
|
setMarksVisible(true);
|
|
|
|
|
setCodeFoldingVisible(true);
|
2008-12-16 13:19:11 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(new CppHighlighter);
|
2008-12-02 12:01:29 +01:00
|
|
|
// new QShortcut(QKeySequence("Ctrl+Alt+M"), this, SLOT(foo()), 0, Qt::WidgetShortcut);
|
|
|
|
|
|
|
|
|
|
#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()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
m_methodCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
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);
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QTreeView *methodView = new OverviewTreeView();
|
|
|
|
|
methodView->header()->hide();
|
|
|
|
|
methodView->setItemsExpandable(false);
|
|
|
|
|
m_methodCombo->setView(methodView);
|
|
|
|
|
m_methodCombo->setMaxVisibleItems(20);
|
|
|
|
|
|
|
|
|
|
m_overviewModel = new OverviewModel(this);
|
2008-12-08 17:47:54 +01:00
|
|
|
m_methodCombo->setModel(m_overviewModel);
|
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()));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
|
|
|
|
|
|
|
|
|
|
QToolBar *toolBar = editable->toolBar();
|
|
|
|
|
QList<QAction*> actions = toolBar->actions();
|
|
|
|
|
toolBar->insertWidget(actions.first(), m_methodCombo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
updateMethodBoxIndex();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateFileName()
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
void CPPEditor::jumpToMethod(int)
|
|
|
|
|
{
|
|
|
|
|
QModelIndex index = m_methodCombo->view()->currentIndex();
|
|
|
|
|
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
|
|
|
|
|
if (! symbol)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-01-21 15:52:34 +01:00
|
|
|
Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(true);
|
2009-03-04 14:00:56 +01:00
|
|
|
openEditorAt(symbol);
|
2009-01-21 15:52:34 +01:00
|
|
|
Core::EditorManager::instance()->addCurrentPositionToNavigationHistory();
|
2008-12-02 12:01:29 +01:00
|
|
|
setFocus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::updateMethodBoxIndex()
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (lastIndex.isValid()) {
|
|
|
|
|
bool blocked = m_methodCombo->blockSignals(true);
|
|
|
|
|
m_methodCombo->setCurrentIndex(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-03-03 13:46:37 +01:00
|
|
|
|
|
|
|
|
#ifdef QTCREATOR_WITH_ADVANCED_HIGHLIGHTER
|
|
|
|
|
Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
Document::Ptr thisDocument = snapshot.value(file()->fileName());
|
|
|
|
|
if (! thisDocument)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (Symbol *symbol = thisDocument->findSymbolAt(line, column)) {
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord);
|
|
|
|
|
|
|
|
|
|
ExpressionUnderCursor expressionUnderCursor;
|
|
|
|
|
|
|
|
|
|
const QString expression = expressionUnderCursor(tc);
|
|
|
|
|
//qDebug() << "expression:" << expression;
|
|
|
|
|
|
2009-03-03 17:55:12 +01:00
|
|
|
Snapshot snapshot;
|
|
|
|
|
snapshot.insert(thisDocument->fileName(), thisDocument);
|
|
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
TypeOfExpression typeOfExpression;
|
2009-03-03 17:55:12 +01:00
|
|
|
typeOfExpression.setSnapshot(snapshot);
|
2009-03-03 13:46:37 +01:00
|
|
|
|
|
|
|
|
const QList<TypeOfExpression::Result> results =
|
|
|
|
|
typeOfExpression(expression, thisDocument, symbol, TypeOfExpression::Preprocess);
|
|
|
|
|
|
|
|
|
|
LookupContext context = typeOfExpression.lookupContext();
|
|
|
|
|
|
|
|
|
|
foreach (const TypeOfExpression::Result &result, results) {
|
|
|
|
|
FullySpecifiedType ty = result.first;
|
|
|
|
|
Symbol *symbol = result.second;
|
|
|
|
|
|
|
|
|
|
if (file()->fileName() != symbol->fileName())
|
|
|
|
|
continue;
|
|
|
|
|
|
2009-03-04 15:45:38 +01:00
|
|
|
else if (symbol->isGenerated())
|
|
|
|
|
continue;
|
|
|
|
|
|
2009-03-04 15:50:35 +01:00
|
|
|
else if (symbol->isBlock())
|
|
|
|
|
continue;
|
|
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
if (symbol) {
|
|
|
|
|
int column = symbol->column();
|
|
|
|
|
|
|
|
|
|
if (column != 0)
|
|
|
|
|
--column;
|
|
|
|
|
|
2009-03-04 15:45:38 +01:00
|
|
|
if (symbol->isGenerated())
|
|
|
|
|
column = 0;
|
|
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
QTextCursor c(document()->findBlockByNumber(symbol->line() - 1));
|
|
|
|
|
c.setPosition(c.position() + column);
|
2009-03-03 17:55:12 +01:00
|
|
|
|
|
|
|
|
const QString text = c.block().text();
|
|
|
|
|
|
|
|
|
|
int i = column;
|
|
|
|
|
for (; i < text.length(); ++i) {
|
|
|
|
|
const QChar &ch = text.at(i);
|
|
|
|
|
|
|
|
|
|
if (ch == QLatin1Char('*') || ch == QLatin1Char('&'))
|
|
|
|
|
c.movePosition(QTextCursor::NextCharacter);
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 13:46:37 +01:00
|
|
|
c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.cursor = c;
|
|
|
|
|
sel.format.setBackground(Qt::darkYellow);
|
|
|
|
|
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
//break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
|
|
|
|
#endif // QTCREATOR_WITH_ADVANCED_HIGHLIGHTER
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-04 17:06:44 +01:00
|
|
|
void CPPEditor::updateMethodBoxToolTip()
|
|
|
|
|
{
|
|
|
|
|
m_methodCombo->setToolTip(m_methodCombo->currentText());
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
openEditorAt(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))
|
|
|
|
|
openEditorAt(def);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::jumpToDefinition()
|
|
|
|
|
{
|
|
|
|
|
if (!m_modelManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2008-12-12 10:07:58 +01:00
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
// Find the last symbol up to the cursor position
|
|
|
|
|
int line = 0, column = 0;
|
|
|
|
|
convertPosition(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)
|
|
|
|
|
return;
|
2008-12-10 17:21:01 +01:00
|
|
|
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
unsigned lineno = tc.blockNumber() + 1;
|
|
|
|
|
foreach (const Document::Include &incl, doc->includes()) {
|
|
|
|
|
if (incl.line() == lineno) {
|
2008-12-11 10:28:39 +01:00
|
|
|
if (openCppEditorAt(incl.fileName(), 0, 0))
|
2008-12-10 17:21:01 +01:00
|
|
|
return; // done
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
Symbol *lastSymbol = doc->findSymbolAt(line, column);
|
|
|
|
|
if (!lastSymbol)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Get the expression under the cursor
|
|
|
|
|
const int endOfName = endOfNameUnderCursor();
|
|
|
|
|
tc.setPosition(endOfName);
|
|
|
|
|
ExpressionUnderCursor expressionUnderCursor;
|
|
|
|
|
const QString expression = expressionUnderCursor(tc);
|
|
|
|
|
|
|
|
|
|
// Evaluate the type of the expression
|
|
|
|
|
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(expression, doc, lastSymbol);
|
|
|
|
|
|
|
|
|
|
if (!resolvedSymbols.isEmpty()) {
|
|
|
|
|
Symbol *symbol = resolvedSymbols.first().second;
|
|
|
|
|
if (symbol) {
|
|
|
|
|
Symbol *def = 0;
|
|
|
|
|
if (!lastSymbol->isFunction())
|
|
|
|
|
def = findDefinition(symbol);
|
|
|
|
|
|
|
|
|
|
if (def)
|
|
|
|
|
openEditorAt(def);
|
|
|
|
|
else
|
|
|
|
|
openEditorAt(symbol);
|
|
|
|
|
|
|
|
|
|
// 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 {
|
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();
|
2008-12-22 13:55:42 +01:00
|
|
|
const QString fileName = QString::fromUtf8(macro.fileName());
|
|
|
|
|
if (openCppEditorAt(fileName, macro.line(), 0))
|
2008-12-09 16:39:52 +01:00
|
|
|
return; // done
|
|
|
|
|
}
|
|
|
|
|
}
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPPEditor::isElectricCharacter(const QChar &ch) const
|
|
|
|
|
{
|
|
|
|
|
if (ch == QLatin1Char('{') ||
|
|
|
|
|
ch == QLatin1Char('}') ||
|
|
|
|
|
ch == QLatin1Char('#')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
{
|
|
|
|
|
const TextEditor::TextBlockIterator begin(doc->begin());
|
|
|
|
|
const TextEditor::TextBlockIterator end(block.next());
|
|
|
|
|
|
|
|
|
|
indentCPPBlock(tabSettings(), block, begin, end, typedChar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
2009-01-14 12:39:59 +01:00
|
|
|
Core::ActionContainer *mcontext =
|
2009-01-20 17:57:07 +01:00
|
|
|
Core::ICore::instance()->actionManager()->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);
|
|
|
|
|
|
|
|
|
|
menu->exec(e->globalPos());
|
|
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_DOXYGEN_TAG);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories);
|
|
|
|
|
highlighter->setFormats(formats.constBegin(), formats.constEnd());
|
|
|
|
|
highlighter->rehighlight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CPPEditor::unCommentSelection()
|
|
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
int pos = cursor.position();
|
|
|
|
|
int anchor = cursor.anchor();
|
|
|
|
|
int start = qMin(anchor, pos);
|
|
|
|
|
int end = qMax(anchor, pos);
|
|
|
|
|
bool anchorIsStart = (anchor == start);
|
|
|
|
|
|
|
|
|
|
QTextBlock startBlock = document()->findBlock(start);
|
|
|
|
|
QTextBlock endBlock = document()->findBlock(end);
|
|
|
|
|
|
|
|
|
|
if (end > start && endBlock.position() == end) {
|
|
|
|
|
--end;
|
|
|
|
|
endBlock = endBlock.previous();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool doCStyleUncomment = false;
|
|
|
|
|
bool doCStyleComment = false;
|
|
|
|
|
bool doCppStyleUncomment = false;
|
|
|
|
|
|
|
|
|
|
bool hasSelection = cursor.hasSelection();
|
|
|
|
|
|
|
|
|
|
if (hasSelection) {
|
|
|
|
|
QString startText = startBlock.text();
|
|
|
|
|
int startPos = start - startBlock.position();
|
|
|
|
|
bool hasLeadingCharacters = !startText.left(startPos).trimmed().isEmpty();
|
|
|
|
|
if ((startPos >= 2
|
|
|
|
|
&& startText.at(startPos-2) == QLatin1Char('/')
|
|
|
|
|
&& startText.at(startPos-1) == QLatin1Char('*'))) {
|
|
|
|
|
startPos -= 2;
|
|
|
|
|
start -= 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasSelStart = (startPos < startText.length() - 2
|
|
|
|
|
&& startText.at(startPos) == QLatin1Char('/')
|
|
|
|
|
&& startText.at(startPos+1) == QLatin1Char('*'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QString endText = endBlock.text();
|
|
|
|
|
int endPos = end - endBlock.position();
|
2008-12-11 13:35:58 +01:00
|
|
|
bool hasTrailingCharacters = !endText.left(endPos).remove(QLatin1String("//")).trimmed().isEmpty()
|
|
|
|
|
&& !endText.mid(endPos).trimmed().isEmpty();
|
2008-12-02 12:01:29 +01:00
|
|
|
if ((endPos <= endText.length() - 2
|
|
|
|
|
&& endText.at(endPos) == QLatin1Char('*')
|
|
|
|
|
&& endText.at(endPos+1) == QLatin1Char('/'))) {
|
|
|
|
|
endPos += 2;
|
|
|
|
|
end += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasSelEnd = (endPos >= 2
|
|
|
|
|
&& endText.at(endPos-2) == QLatin1Char('*')
|
|
|
|
|
&& endText.at(endPos-1) == QLatin1Char('/'));
|
|
|
|
|
|
|
|
|
|
doCStyleUncomment = hasSelStart && hasSelEnd;
|
|
|
|
|
doCStyleComment = !doCStyleUncomment && (hasLeadingCharacters || hasTrailingCharacters);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doCStyleUncomment) {
|
|
|
|
|
cursor.setPosition(end);
|
|
|
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2);
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
cursor.setPosition(start);
|
|
|
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
} else if (doCStyleComment) {
|
|
|
|
|
cursor.setPosition(end);
|
|
|
|
|
cursor.insertText(QLatin1String("*/"));
|
|
|
|
|
cursor.setPosition(start);
|
|
|
|
|
cursor.insertText(QLatin1String("/*"));
|
|
|
|
|
} else {
|
|
|
|
|
endBlock = endBlock.next();
|
|
|
|
|
doCppStyleUncomment = true;
|
|
|
|
|
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
|
|
|
|
QString text = block.text();
|
|
|
|
|
if (!text.trimmed().startsWith(QLatin1String("//"))) {
|
|
|
|
|
doCppStyleUncomment = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
|
|
|
|
if (doCppStyleUncomment) {
|
|
|
|
|
QString text = block.text();
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < text.size() - 1) {
|
|
|
|
|
if (text.at(i) == QLatin1Char('/')
|
|
|
|
|
&& text.at(i + 1) == QLatin1Char('/')) {
|
|
|
|
|
cursor.setPosition(block.position() + i);
|
|
|
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!text.at(i).isSpace())
|
|
|
|
|
break;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
cursor.insertText(QLatin1String("//"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// adjust selection when commenting out
|
|
|
|
|
if (hasSelection && !doCStyleUncomment && !doCppStyleUncomment) {
|
|
|
|
|
cursor = textCursor();
|
|
|
|
|
if (!doCStyleComment)
|
|
|
|
|
start = startBlock.position(); // move the double slashes into the selection
|
|
|
|
|
int lastSelPos = anchorIsStart ? cursor.position() : cursor.anchor();
|
|
|
|
|
if (anchorIsStart) {
|
|
|
|
|
cursor.setPosition(start);
|
|
|
|
|
cursor.setPosition(lastSelPos, QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
cursor.setPosition(lastSelPos);
|
|
|
|
|
cursor.setPosition(start, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CPPEditor::endOfNameUnderCursor()
|
|
|
|
|
{
|
|
|
|
|
int pos = position();
|
|
|
|
|
QChar chr = characterAt(pos);
|
|
|
|
|
|
|
|
|
|
// Skip to the start of a name
|
|
|
|
|
while (chr.isLetterOrNumber() || chr == QLatin1Char('_'))
|
|
|
|
|
chr = characterAt(++pos);
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-11 10:28:39 +01:00
|
|
|
TextEditor::ITextEditor *CPPEditor::openCppEditorAt(const QString &fileName,
|
|
|
|
|
int line, int column)
|
|
|
|
|
{
|
|
|
|
|
return TextEditor::BaseTextEditor::openEditorAt(fileName, line, column,
|
|
|
|
|
Constants::C_CPPEDITOR);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
bool CPPEditor::openEditorAt(Symbol *s)
|
|
|
|
|
{
|
|
|
|
|
const QString fileName = QString::fromUtf8(s->fileName(), s->fileNameLength());
|
2009-03-04 15:47:26 +01:00
|
|
|
unsigned line = s->line();
|
2009-03-03 13:46:37 +01:00
|
|
|
unsigned column = s->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-04 15:45:38 +01:00
|
|
|
if (s->isGenerated())
|
|
|
|
|
unsigned column = 0;
|
2009-03-03 13:46:37 +01:00
|
|
|
|
2009-03-04 15:47:26 +01:00
|
|
|
return openCppEditorAt(fileName, line, column);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|