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

824 lines
27 KiB
C++

/***************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/
#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>
#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>
#include <coreplugin/actionmanager/actionmanagerinterface.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
#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/QFileInfo>
#include <QtCore/QTextStream>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtGui/QAction>
#include <QtGui/QKeyEvent>
#include <QtGui/QLayout>
#include <QtGui/QMenu>
#include <QtGui/QShortcut>
#include <QtGui/QTextEdit>
#include <QtGui/QComboBox>
#include <QtGui/QTreeView>
#include <QtGui/QHeaderView>
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()));
}
};
} // end of anonymous namespace
QualifiedNameId *qualifiedNameIdForSymbol(Symbol *s, const LookupContext &context)
{
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)
: BaseTextEditorEditable(editor)
{
Core::ICore *core = CppPlugin::core();
m_context << core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
m_context << core->uniqueIDManager()->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
m_context << core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
}
CPPEditor::CPPEditor(QWidget *parent) :
TextEditor::BaseTextEditor(parent),
m_core(CppPlugin::core())
{
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setCodeFoldingVisible(true);
baseTextDocument()->setSyntaxHighlighter(new CPPHighlighter);
// 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
m_modelManager = m_core->pluginManager()->getObject<CppTools::CppModelManagerInterface>();
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);
QTreeView *methodView = new OverviewTreeView();
methodView->header()->hide();
methodView->setItemsExpandable(false);
m_methodCombo->setView(methodView);
m_methodCombo->setMaxVisibleItems(20);
m_overviewModel = new OverviewModel(this);
m_methodCombo->setModel(m_overviewModel);
connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
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);
OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_methodCombo->view());
treeView->sync();
updateMethodBoxIndex();
}
void CPPEditor::updateFileName()
{ }
void CPPEditor::jumpToMethod(int)
{
QModelIndex index = m_methodCombo->view()->currentIndex();
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
if (! symbol)
return;
m_core->editorManager()->addCurrentPositionToNavigationHistory(true);
int line = symbol->line();
gotoLine(line);
m_core->editorManager()->addCurrentPositionToNavigationHistory();
setFocus();
}
void CPPEditor::updateMethodBoxIndex()
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
QModelIndex lastIndex;
const int rc = m_overviewModel->rowCount(QModelIndex());
for (int row = 0; row < rc; ++row) {
const QModelIndex index = m_overviewModel->index(row, 0, QModelIndex());
Symbol *symbol = m_overviewModel->symbolFromIndex(index);
if (symbol->line() > unsigned(line))
break;
lastIndex = index;
}
if (lastIndex.isValid()) {
bool blocked = m_methodCombo->blockSignals(true);
m_methodCombo->setCurrentIndex(lastIndex.row());
updateMethodBoxToolTip();
(void) m_methodCombo->blockSignals(blocked);
}
}
void CPPEditor::updateMethodBoxToolTip()
{
m_methodCombo->setToolTip(m_methodCombo->currentText());
}
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)
{
Function *declTy = declaration->type()->asFunction();
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;
Document::Ptr doc = m_modelManager->document(file()->fileName());
if (!doc)
return;
Symbol *lastSymbol = doc->findSymbolAt(line, column);
if (!lastSymbol || !lastSymbol->scope())
return;
Function *f = lastSymbol->asFunction();
if (! f) {
Scope *fs = lastSymbol->scope();
if (! fs->isFunctionScope())
fs = fs->enclosingFunctionScope();
if (fs)
f = fs->owner()->asFunction();
}
if (f) {
TypeOfExpression typeOfExpression;
typeOfExpression.setDocuments(m_modelManager->documents());
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);
} else if (lastSymbol->type()->isFunction()) {
if (Symbol *def = findDefinition(lastSymbol))
openEditorAt(def);
}
}
void CPPEditor::jumpToDefinition()
{
if (!m_modelManager)
return;
// Find the last symbol up to the cursor position
int line = 0, column = 0;
convertPosition(position(), &line, &column);
Document::Ptr doc = m_modelManager->document(file()->fileName());
if (!doc)
return;
Symbol *lastSymbol = doc->findSymbolAt(line, column);
if (!lastSymbol)
return;
// Get the expression under the cursor
const int endOfName = endOfNameUnderCursor();
QTextCursor tc = textCursor();
tc.setPosition(endOfName);
ExpressionUnderCursor expressionUnderCursor;
const QString expression = expressionUnderCursor(tc);
// Evaluate the type of the expression
TypeOfExpression typeOfExpression;
typeOfExpression.setDocuments(m_modelManager->documents());
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();
openEditorAt(s->fileName(), s->line(), s->column());
}
#endif
}
} else {
qDebug() << "No results for expression:" << expression;
}
}
Symbol *CPPEditor::findDefinition(Symbol *lastSymbol)
{
// Currently only functions are supported
if (!lastSymbol->type()->isFunction())
return 0;
QVector<Name *> qualifiedName;
Scope *scope = lastSymbol->scope();
for (; scope; scope = scope->enclosingScope()) {
if (scope->isClassScope() || scope->isNamespaceScope()) {
if (scope->owner() && scope->owner()->name()) {
Name *scopeOwnerName = scope->owner()->name();
if (QualifiedNameId *q = scopeOwnerName->asQualifiedNameId()) {
for (unsigned i = 0; i < q->nameCount(); ++i) {
qualifiedName.prepend(q->nameAt(i));
}
} else {
qualifiedName.prepend(scopeOwnerName);
}
}
}
}
qualifiedName.append(lastSymbol->name());
Control control;
QualifiedNameId *q = control.qualifiedNameId(&qualifiedName[0], qualifiedName.size());
LookupContext context(&control);
const QMap<QString, Document::Ptr> documents = m_modelManager->documents();
foreach (Document::Ptr doc, documents) {
QList<Scope *> visibleScopes;
visibleScopes.append(doc->globalSymbols());
visibleScopes = context.expand(visibleScopes);
//qDebug() << "** doc:" << doc->fileName() << "visible scopes:" << visibleScopes.count();
foreach (Scope *visibleScope, visibleScopes) {
Symbol *symbol = 0;
if (NameId *nameId = q->unqualifiedNameId()->asNameId())
symbol = visibleScope->lookat(nameId->identifier());
else if (DestructorNameId *dtorId = q->unqualifiedNameId()->asDestructorNameId())
symbol = visibleScope->lookat(dtorId->identifier());
else if (TemplateNameId *templNameId = q->unqualifiedNameId()->asTemplateNameId())
symbol = visibleScope->lookat(templNameId->identifier());
else if (OperatorNameId *opId = q->unqualifiedNameId()->asOperatorNameId())
symbol = visibleScope->lookat(opId->kind());
// ### cast operators
for (; symbol; symbol = symbol->next()) {
if (! symbol->isFunction())
continue;
else if (! isCompatible(symbol->asFunction(), lastSymbol, q))
continue;
return symbol;
}
}
}
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);
Core::IActionContainer *mcontext =
m_core->actionManager()->actionContainer(CppEditor::Constants::M_CONTEXT);
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);
CPPHighlighter *highlighter = qobject_cast<CPPHighlighter*>(baseTextDocument()->syntaxHighlighter());
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)
<< QLatin1String(TextEditor::Constants::C_COMMENT);
}
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();
bool hasTrailingCharacters = !endText.mid(endPos).trimmed().isEmpty();
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;
}
bool CPPEditor::openEditorAt(Symbol *s)
{
const QString fileName = QString::fromUtf8(s->fileName(), s->fileNameLength());
return TextEditor::BaseTextEditor::openEditorAt(fileName, s->line(), s->column());
}