Files
qt-creator/src/plugins/cpptools/cppcodecompletion.cpp
Thorbjørn Lindeijer a1a565b9c5 Two more columnNumber() calls that should be positionInBlock()
columnNumber() relies on text wrapping, and we're only interested in the
position of the cursor in the block.
2010-06-16 18:03:43 +02:00

1771 lines
57 KiB
C++

/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "cppcodecompletion.h"
#include "cppmodelmanager.h"
#include "cppdoxygen.h"
#include "cpptoolsconstants.h"
#include "cpptoolseditorsupport.h"
#include <Control.h>
#include <AST.h>
#include <ASTVisitor.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <Names.h>
#include <NameVisitor.h>
#include <Symbols.h>
#include <SymbolVisitor.h>
#include <Scope.h>
#include <TranslationUnit.h>
#include <cplusplus/ResolveExpression.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/Overview.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/LookupContext.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/completionsettings.h>
#include <texteditor/itexteditor.h>
#include <texteditor/itexteditable.h>
#include <texteditor/basetexteditor.h>
#include <projectexplorer/projectexplorer.h>
#include <utils/faketooltip.h>
#include <utils/qtcassert.h>
#include <QtCore/QMap>
#include <QtCore/QFile>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtGui/QKeyEvent>
#include <QtGui/QLabel>
#include <QtGui/QStyle>
#include <QtGui/QTextDocument> // Qt::escape()
#include <QtGui/QToolButton>
#include <QtGui/QVBoxLayout>
namespace {
const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty();
}
using namespace CPlusPlus;
namespace CppTools {
namespace Internal {
class FunctionArgumentWidget : public QLabel
{
Q_OBJECT
public:
FunctionArgumentWidget();
void showFunctionHint(QList<Function *> functionSymbols,
const LookupContext &context,
int startPosition);
protected:
bool eventFilter(QObject *obj, QEvent *e);
private slots:
void nextPage();
void previousPage();
private:
void updateArgumentHighlight();
void updateHintText();
Function *currentFunction() const
{ return m_items.at(m_current); }
int m_startpos;
int m_currentarg;
int m_current;
bool m_escapePressed;
TextEditor::ITextEditor *m_editor;
QWidget *m_pager;
QLabel *m_numberLabel;
Utils::FakeToolTip *m_popupFrame;
QList<Function *> m_items;
LookupContext m_context;
};
class ConvertToCompletionItem: protected NameVisitor
{
// The completion collector.
CppCodeCompletion *_collector;
// The completion item.
TextEditor::CompletionItem _item;
// The current symbol.
Symbol *_symbol;
// The pretty printer.
Overview overview;
public:
ConvertToCompletionItem(CppCodeCompletion *collector)
: _collector(collector),
_item(0),
_symbol(0)
{ }
TextEditor::CompletionItem operator()(Symbol *symbol)
{
if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId())
return 0;
TextEditor::CompletionItem previousItem = switchCompletionItem(0);
Symbol *previousSymbol = switchSymbol(symbol);
accept(symbol->identity());
if (_item)
_item.data = QVariant::fromValue(symbol);
(void) switchSymbol(previousSymbol);
return switchCompletionItem(previousItem);
}
protected:
Symbol *switchSymbol(Symbol *symbol)
{
Symbol *previousSymbol = _symbol;
_symbol = symbol;
return previousSymbol;
}
TextEditor::CompletionItem switchCompletionItem(TextEditor::CompletionItem item)
{
TextEditor::CompletionItem previousItem = _item;
_item = item;
return previousItem;
}
TextEditor::CompletionItem newCompletionItem(const Name *name)
{
TextEditor::CompletionItem item(_collector);
item.text = overview.prettyName(name);
item.icon = _collector->iconForSymbol(_symbol);
return item;
}
virtual void visit(const NameId *name)
{ _item = newCompletionItem(name); }
virtual void visit(const TemplateNameId *name)
{
_item = newCompletionItem(name);
_item.text = QLatin1String(name->identifier()->chars());
}
virtual void visit(const DestructorNameId *name)
{ _item = newCompletionItem(name); }
virtual void visit(const OperatorNameId *name)
{ _item = newCompletionItem(name); }
virtual void visit(const ConversionNameId *name)
{ _item = newCompletionItem(name); }
virtual void visit(const QualifiedNameId *name)
{ _item = newCompletionItem(name->unqualifiedNameId()); }
};
struct CompleteFunctionDeclaration
{
explicit CompleteFunctionDeclaration(Function *f = 0)
: function(f)
{}
Function *function;
};
} // namespace Internal
} // namespace CppTools
using namespace CppTools::Internal;
Q_DECLARE_METATYPE(CompleteFunctionDeclaration)
FunctionArgumentWidget::FunctionArgumentWidget():
m_startpos(-1),
m_current(0),
m_escapePressed(false)
{
QObject *editorObject = Core::EditorManager::instance()->currentEditor();
m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject);
m_popupFrame = new Utils::FakeToolTip(m_editor->widget());
QToolButton *downArrow = new QToolButton;
downArrow->setArrowType(Qt::DownArrow);
downArrow->setFixedSize(16, 16);
downArrow->setAutoRaise(true);
QToolButton *upArrow = new QToolButton;
upArrow->setArrowType(Qt::UpArrow);
upArrow->setFixedSize(16, 16);
upArrow->setAutoRaise(true);
setParent(m_popupFrame);
setFocusPolicy(Qt::NoFocus);
m_pager = new QWidget;
QHBoxLayout *hbox = new QHBoxLayout(m_pager);
hbox->setMargin(0);
hbox->setSpacing(0);
hbox->addWidget(upArrow);
m_numberLabel = new QLabel;
hbox->addWidget(m_numberLabel);
hbox->addWidget(downArrow);
QHBoxLayout *layout = new QHBoxLayout;
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(m_pager);
layout->addWidget(this);
m_popupFrame->setLayout(layout);
connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
setTextFormat(Qt::RichText);
qApp->installEventFilter(this);
}
void FunctionArgumentWidget::showFunctionHint(QList<Function *> functionSymbols,
const LookupContext &context,
int startPosition)
{
Q_ASSERT(!functionSymbols.isEmpty());
if (m_startpos == startPosition)
return;
m_pager->setVisible(functionSymbols.size() > 1);
m_items = functionSymbols;
m_context = context;
m_startpos = startPosition;
m_current = 0;
m_escapePressed = false;
// update the text
m_currentarg = -1;
updateArgumentHighlight();
m_popupFrame->show();
}
void FunctionArgumentWidget::nextPage()
{
m_current = (m_current + 1) % m_items.size();
updateHintText();
}
void FunctionArgumentWidget::previousPage()
{
if (m_current == 0)
m_current = m_items.size() - 1;
else
--m_current;
updateHintText();
}
void FunctionArgumentWidget::updateArgumentHighlight()
{
int curpos = m_editor->position();
if (curpos < m_startpos) {
m_popupFrame->close();
return;
}
QString str = m_editor->textAt(m_startpos, curpos - m_startpos);
int argnr = 0;
int parcount = 0;
SimpleLexer tokenize;
QList<SimpleToken> tokens = tokenize(str);
for (int i = 0; i < tokens.count(); ++i) {
const SimpleToken &tk = tokens.at(i);
if (tk.is(T_LPAREN))
++parcount;
else if (tk.is(T_RPAREN))
--parcount;
else if (! parcount && tk.is(T_COMMA))
++argnr;
}
if (m_currentarg != argnr) {
m_currentarg = argnr;
updateHintText();
}
if (parcount < 0)
m_popupFrame->close();
}
bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e)
{
switch (e->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
m_escapePressed = true;
}
break;
case QEvent::KeyPress:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
m_escapePressed = true;
}
if (m_items.size() > 1) {
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
if (ke->key() == Qt::Key_Up) {
previousPage();
return true;
} else if (ke->key() == Qt::Key_Down) {
nextPage();
return true;
}
return false;
}
break;
case QEvent::KeyRelease:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_escapePressed) {
m_popupFrame->close();
return false;
}
updateArgumentHighlight();
break;
case QEvent::WindowDeactivate:
case QEvent::FocusOut:
if (obj != m_editor->widget())
break;
m_popupFrame->close();
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::Wheel: {
QWidget *widget = qobject_cast<QWidget *>(obj);
if (! (widget == this || m_popupFrame->isAncestorOf(widget))) {
m_popupFrame->close();
}
}
break;
default:
break;
}
return false;
}
void FunctionArgumentWidget::updateHintText()
{
Overview overview;
overview.setShowReturnTypes(true);
overview.setShowArgumentNames(true);
overview.setMarkedArgument(m_currentarg + 1);
Function *f = currentFunction();
const QString prettyMethod = overview(f->type(), f->name());
const int begin = overview.markedArgumentBegin();
const int end = overview.markedArgumentEnd();
QString hintText;
hintText += Qt::escape(prettyMethod.left(begin));
hintText += "<b>";
hintText += Qt::escape(prettyMethod.mid(begin, end - begin));
hintText += "</b>";
hintText += Qt::escape(prettyMethod.mid(end));
setText(hintText);
m_numberLabel->setText(tr("%1 of %2").arg(m_current + 1).arg(m_items.size()));
m_popupFrame->setFixedWidth(m_popupFrame->minimumSizeHint().width());
const QDesktopWidget *desktop = QApplication::desktop();
#ifdef Q_WS_MAC
const QRect screen = desktop->availableGeometry(desktop->screenNumber(m_editor->widget()));
#else
const QRect screen = desktop->screenGeometry(desktop->screenNumber(m_editor->widget()));
#endif
const QSize sz = m_popupFrame->sizeHint();
QPoint pos = m_editor->cursorRect(m_startpos).topLeft();
pos.setY(pos.y() - sz.height() - 1);
if (pos.x() + sz.width() > screen.right())
pos.setX(screen.right() - sz.width());
m_popupFrame->move(pos);
}
CppCodeCompletion::CppCodeCompletion(CppModelManager *manager)
: ICompletionCollector(manager),
m_manager(manager),
m_editor(0),
m_startPosition(-1),
m_forcedCompletion(false),
m_completionOperator(T_EOF_SYMBOL),
m_objcEnabled(true)
{
}
QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const
{
return m_icons.iconForSymbol(symbol);
}
/*
Searches backwards for an access operator.
*/
static int startOfOperator(TokenCache *tokenCache,
TextEditor::ITextEditable *editor,
int pos, unsigned *kind,
bool wantFunctionCall)
{
const QChar ch = pos > -1 ? editor->characterAt(pos - 1) : QChar();
const QChar ch2 = pos > 0 ? editor->characterAt(pos - 2) : QChar();
const QChar ch3 = pos > 1 ? editor->characterAt(pos - 3) : QChar();
int start = pos;
int completionKind = T_EOF_SYMBOL;
switch (ch.toLatin1()) {
case '.':
if (ch2 != QLatin1Char('.')) {
completionKind = T_DOT;
--start;
}
break;
case ',':
completionKind = T_COMMA;
--start;
break;
case '(':
if (wantFunctionCall) {
completionKind = T_LPAREN;
--start;
}
break;
case ':':
if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
completionKind = T_COLON_COLON;
start -= 2;
}
break;
case '>':
if (ch2 == QLatin1Char('-')) {
completionKind = T_ARROW;
start -= 2;
}
break;
case '*':
if (ch2 == QLatin1Char('.')) {
completionKind = T_DOT_STAR;
start -= 2;
} else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) {
completionKind = T_ARROW_STAR;
start -= 3;
}
break;
case '\\':
case '@':
if (ch2.isNull() || ch2.isSpace()) {
completionKind = T_DOXY_COMMENT;
--start;
}
break;
case '<':
completionKind = T_ANGLE_STRING_LITERAL;
--start;
break;
case '"':
completionKind = T_STRING_LITERAL;
--start;
break;
case '/':
completionKind = T_SLASH;
--start;
break;
case '#':
completionKind = T_POUND;
--start;
break;
}
if (start == pos)
return start;
TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
QTextCursor tc(edit->textCursor());
tc.setPosition(pos);
// Include completion: make sure the quote character is the first one on the line
if (completionKind == T_STRING_LITERAL) {
QTextCursor s = tc;
s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString sel = s.selectedText();
if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
}
if (completionKind == T_COMMA) {
ExpressionUnderCursor expressionUnderCursor(tokenCache);
if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
}
const SimpleToken tk = tokenCache->tokenUnderCursor(tc);
if (completionKind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
// Don't complete in comments or strings, but still check for include completion
else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT) ||
(tk.isLiteral() && (completionKind != T_STRING_LITERAL
&& completionKind != T_ANGLE_STRING_LITERAL
&& completionKind != T_SLASH))) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
// Include completion: can be triggered by slash, but only in a string
else if (completionKind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
else if (completionKind == T_LPAREN) {
const QList<SimpleToken> &tokens = tokenCache->tokensForBlock(tc.block());
int i = 0;
for (; i < tokens.size(); ++i) {
const SimpleToken &token = tokens.at(i);
if (token.position() == tk.position()) {
if (i == 0) // no token on the left, but might be on a previous line
break;
const SimpleToken &previousToken = tokens.at(i - 1);
if (previousToken.is(T_IDENTIFIER) || previousToken.is(T_GREATER)
|| previousToken.is(T_SIGNAL) || previousToken.is(T_SLOT))
break;
}
}
if (i == tokens.size()) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
}
// Check for include preprocessor directive
else if (completionKind == T_STRING_LITERAL || completionKind == T_ANGLE_STRING_LITERAL || completionKind == T_SLASH) {
bool include = false;
const QList<SimpleToken> &tokens = tokenCache->tokensForBlock(tc.block());
if (tokens.size() >= 3) {
if (tokens.at(0).is(T_POUND) && tokens.at(1).is(T_IDENTIFIER) && (tokens.at(2).is(T_STRING_LITERAL) ||
tokens.at(2).is(T_ANGLE_STRING_LITERAL))) {
QString directive = tokenCache->text(tc.block(), 1);
if (directive == QLatin1String("include") ||
directive == QLatin1String("include_next") ||
directive == QLatin1String("import")) {
include = true;
}
}
}
if (!include) {
completionKind = T_EOF_SYMBOL;
start = pos;
}
}
if (kind)
*kind = completionKind;
return start;
}
bool CppCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
{ return m_manager->isCppEditor(editor); }
TextEditor::ITextEditable *CppCodeCompletion::editor() const
{ return m_editor; }
int CppCodeCompletion::startPosition() const
{ return m_startPosition; }
bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
{
const int pos = editor->position();
TokenCache *tokenCache = m_manager->tokenCache(editor);
unsigned token = T_EOF_SYMBOL;
if (startOfOperator(tokenCache, editor, pos, &token, /*want function call=*/ true) != pos) {
if (token == T_POUND) {
if (TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget())) {
QTextCursor tc(edit->document());
tc.setPosition(pos);
return tc.positionInBlock() == 1;
}
return false;
}
return true;
}
return false;
}
int CppCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
{
int index = startCompletionHelper(editor);
if (index != -1) {
if (m_completionOperator != T_EOF_SYMBOL)
qSort(m_completions.begin(), m_completions.end(), completionItemLessThan);
// always remove duplicates
m_completions = removeDuplicates(m_completions);
}
return index;
}
int CppCodeCompletion::startCompletionHelper(TextEditor::ITextEditable *editor)
{
TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
if (! edit)
return -1;
m_editor = editor;
const int startOfName = findStartOfName();
m_startPosition = startOfName;
m_completionOperator = T_EOF_SYMBOL;
int endOfOperator = m_startPosition;
// Skip whitespace preceding this position
while (editor->characterAt(endOfOperator - 1).isSpace())
--endOfOperator;
TokenCache *tokenCache = m_manager->tokenCache(editor);
int endOfExpression = startOfOperator(tokenCache, editor, endOfOperator,
&m_completionOperator,
/*want function call =*/ true);
Core::IFile *file = editor->file();
QString fileName = file->fileName();
int line = 0, column = 0;
edit->convertPosition(editor->position(), &line, &column);
// qDebug() << "line:" << line << "column:" << column;
if (m_completionOperator == T_DOXY_COMMENT) {
for (int i = 1; i < T_DOXY_LAST_TAG; ++i) {
TextEditor::CompletionItem item(this);
item.text.append(QString::fromLatin1(doxygenTagSpell(i)));
item.icon = m_icons.keywordIcon();
m_completions.append(item);
}
return m_startPosition;
}
// Pre-processor completion
if (m_completionOperator == T_POUND) {
completePreprocessor();
m_startPosition = startOfName;
return m_startPosition;
}
// Include completion
if (m_completionOperator == T_STRING_LITERAL
|| m_completionOperator == T_ANGLE_STRING_LITERAL
|| m_completionOperator == T_SLASH) {
QTextCursor c = edit->textCursor();
c.setPosition(endOfExpression);
if (completeInclude(c))
m_startPosition = startOfName;
return m_startPosition;
}
ExpressionUnderCursor expressionUnderCursor(m_manager->tokenCache(editor));
QTextCursor tc(edit->document());
if (m_completionOperator == T_COMMA) {
tc.setPosition(endOfExpression);
const int start = expressionUnderCursor.startOfFunctionCall(tc);
if (start == -1) {
m_completionOperator = T_EOF_SYMBOL;
return -1;
}
endOfExpression = start;
m_startPosition = start + 1;
m_completionOperator = T_LPAREN;
}
QString expression;
tc.setPosition(endOfExpression);
if (m_completionOperator) {
expression = expressionUnderCursor(tc);
if (m_completionOperator == T_LPAREN) {
if (expression.endsWith(QLatin1String("SIGNAL")))
m_completionOperator = T_SIGNAL;
else if (expression.endsWith(QLatin1String("SLOT")))
m_completionOperator = T_SLOT;
else if (editor->position() != endOfOperator) {
// We don't want a function completion when the cursor isn't at the opening brace
expression.clear();
m_completionOperator = T_EOF_SYMBOL;
m_startPosition = startOfName;
}
}
}
//qDebug() << "***** expression:" << expression;
return startCompletionInternal(edit, fileName, line, column, expression, endOfExpression);
}
int CppCodeCompletion::startCompletionInternal(TextEditor::BaseTextEditor *edit,
const QString fileName,
unsigned line, unsigned column,
const QString &expr,
int endOfExpression)
{
QString expression = expr.trimmed();
const Snapshot snapshot = m_manager->snapshot();
Document::Ptr thisDocument = snapshot.document(fileName);
if (! thisDocument)
return -1;
typeOfExpression.init(thisDocument, snapshot);
Scope *scope = thisDocument->scopeAt(line, column);
Q_ASSERT(scope != 0);
if (expression.isEmpty()) {
if (m_completionOperator == T_EOF_SYMBOL || m_completionOperator == T_COLON_COLON) {
(void) typeOfExpression(expression, scope);
globalCompletion(scope);
if (m_completions.isEmpty())
return -1;
return m_startPosition;
}
else if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
// Apply signal/slot completion on 'this'
expression = QLatin1String("this");
}
}
QList<LookupItem> results = typeOfExpression(expression, scope, TypeOfExpression::Preprocess);
if (results.isEmpty()) {
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
if (! (expression.isEmpty() || expression == QLatin1String("this"))) {
expression = QLatin1String("this");
results = typeOfExpression(expression, scope);
}
if (results.isEmpty())
return -1;
} else if (m_completionOperator == T_LPAREN) {
// Find the expression that precedes the current name
int index = endOfExpression;
while (m_editor->characterAt(index - 1).isSpace())
--index;
index = findStartOfName(index);
QTextCursor tc(edit->document());
tc.setPosition(index);
TokenCache *tokenCache = m_manager->tokenCache(edit->editableInterface());
ExpressionUnderCursor expressionUnderCursor(tokenCache);
const QString baseExpression = expressionUnderCursor(tc);
// Resolve the type of this expression
const QList<LookupItem> results =
typeOfExpression(baseExpression, scope,
TypeOfExpression::Preprocess);
// If it's a class, add completions for the constructors
foreach (const LookupItem &result, results) {
if (result.type()->isClassType()) {
if (completeConstructorOrFunction(results, endOfExpression, true))
return m_startPosition;
break;
}
}
return -1;
} else {
// nothing to do.
return -1;
}
}
switch (m_completionOperator) {
case T_LPAREN:
if (completeConstructorOrFunction(results, endOfExpression, false))
return m_startPosition;
break;
case T_DOT:
case T_ARROW:
if (completeMember(results))
return m_startPosition;
break;
case T_COLON_COLON:
if (completeScope(results))
return m_startPosition;
break;
case T_SIGNAL:
if (completeSignal(results))
return m_startPosition;
break;
case T_SLOT:
if (completeSlot(results))
return m_startPosition;
break;
default:
break;
} // end of switch
// nothing to do.
return -1;
}
void CppCodeCompletion::globalCompletion(Scope *currentScope)
{
const LookupContext &context = typeOfExpression.context();
if (m_completionOperator == T_COLON_COLON) {
completeNamespace(context.globalNamespace());
return;
}
QList<ClassOrNamespace *> usingBindings;
ClassOrNamespace *currentBinding = 0;
for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
if (scope->isBlockScope()) {
if (ClassOrNamespace *binding = context.lookupType(scope->owner())) {
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
Symbol *member = scope->symbolAt(i);
if (! member->name())
continue;
else if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
if (ClassOrNamespace *b = binding->lookupType(u->name()))
usingBindings.append(b);
}
}
}
} else if (scope->isFunctionScope() || scope->isClassScope() || scope->isNamespaceScope()) {
currentBinding = context.lookupType(scope->owner());
break;
}
}
for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
if (scope->isBlockScope()) {
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
addCompletionItem(scope->symbolAt(i));
}
} else if (scope->isFunctionScope()) {
Scope *arguments = scope->owner()->asFunction()->arguments();
for (unsigned i = 0; i < arguments->symbolCount(); ++i) {
addCompletionItem(arguments->symbolAt(i));
}
break;
} else {
break;
}
}
for (; currentBinding; currentBinding = currentBinding->parent()) {
const QList<Symbol *> symbols = currentBinding->symbols();
if (! symbols.isEmpty()) {
if (symbols.first()->isNamespace())
completeNamespace(currentBinding);
else
completeClass(currentBinding);
}
}
foreach (ClassOrNamespace *b, usingBindings)
completeNamespace(b);
addKeywords();
addMacros(context.thisDocument()->fileName(), context.snapshot());
}
bool CppCodeCompletion::completeConstructorOrFunction(const QList<LookupItem> &results,
int endOfExpression, bool toolTipOnly)
{
const LookupContext &context = typeOfExpression.context();
QList<Function *> functions;
foreach (const LookupItem &result, results) {
FullySpecifiedType exprTy = result.type().simplified();
if (Class *klass = exprTy->asClassType()) {
const Name *className = klass->name();
if (! className)
continue; // nothing to do for anonymoous classes.
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
const Name *memberName = member->name();
if (! memberName)
continue; // skip anonymous member.
else if (memberName->isQualifiedNameId())
continue; // skip
if (Function *funTy = member->type()->asFunctionType()) {
if (memberName->isEqualTo(className)) {
// it's a ctor.
functions.append(funTy);
}
}
}
break;
}
}
if (functions.isEmpty()) {
foreach (const LookupItem &result, results) {
FullySpecifiedType ty = result.type().simplified();
if (Function *fun = ty->asFunctionType()) {
if (! fun->name())
continue;
else if (! functions.isEmpty() && functions.first()->scope() != fun->scope())
continue; // skip fun, it's an hidden declaration.
bool newOverload = true;
foreach (Function *f, functions) {
if (fun->isEqualTo(f)) {
newOverload = false;
break;
}
}
if (newOverload)
functions.append(fun);
}
}
}
if (functions.isEmpty()) {
const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
foreach (const LookupItem &result, results) {
FullySpecifiedType ty = result.type().simplified();
Scope *scope = result.scope();
if (NamedType *namedTy = ty->asNamedType()) {
if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
foreach (Symbol *overload, b->lookup(functionCallOp)) {
FullySpecifiedType overloadTy = overload->type().simplified();
if (Function *funTy = overloadTy->asFunctionType())
functions.append(funTy);
}
}
}
}
}
// There are two different kinds of completion we want to provide:
// 1. If this is a function call, we want to pop up a tooltip that shows the user
// the possible overloads with their argument types and names.
// 2. If this is a function definition, we want to offer autocompletion of
// the function signature.
// check if function signature autocompletion is appropriate
if (! functions.isEmpty() && ! toolTipOnly) {
// function definitions will only happen in class or namespace scope,
// so get the current location's enclosing scope.
// get current line and column
TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(m_editor->widget());
int lineSigned = 0, columnSigned = 0;
edit->convertPosition(m_editor->position(), &lineSigned, &columnSigned);
unsigned line = lineSigned, column = columnSigned;
// find a scope that encloses the current location, starting from the lastVisibileSymbol
// and moving outwards
Scope *sc = 0;
if (typeOfExpression.scope())
sc = typeOfExpression.scope();
else if (context.thisDocument())
sc = context.thisDocument()->globalSymbols();
while (sc && sc->enclosingScope()) {
unsigned startLine, startColumn;
context.thisDocument()->translationUnit()->getPosition(sc->owner()->startOffset(), &startLine, &startColumn);
unsigned endLine, endColumn;
context.thisDocument()->translationUnit()->getPosition(sc->owner()->endOffset(), &endLine, &endColumn);
if (startLine <= line && line <= endLine) {
if ((startLine != line || startColumn <= column)
&& (endLine != line || column <= endColumn))
break;
}
sc = sc->enclosingScope();
}
if (sc && (sc->isClassScope() || sc->isNamespaceScope())) {
// It may still be a function call. If the whole line parses as a function
// declaration, we should be certain that it isn't.
bool autocompleteSignature = false;
QTextCursor tc(edit->document());
tc.setPosition(endOfExpression);
TokenCache *tokenCache = m_manager->tokenCache(m_editor);
BackwardsScanner bs(tokenCache, tc);
const int startToken = bs.startToken();
const int lineStartToken = bs.startOfLine(startToken);
// make sure the required tokens are actually available
bs.LA(startToken - lineStartToken);
QString possibleDecl = bs.mid(lineStartToken).trimmed().append("();");
Document::Ptr doc = Document::create(QLatin1String("<completion>"));
doc->setSource(possibleDecl.toLatin1());
if (doc->parse(Document::ParseDeclaration)) {
doc->check();
if (SimpleDeclarationAST *sd = doc->translationUnit()->ast()->asSimpleDeclaration()) {
if (sd->declarator_list &&
sd->declarator_list && sd->declarator_list->value->postfix_declarator_list
&& sd->declarator_list->value->postfix_declarator_list->value->asFunctionDeclarator()) {
autocompleteSignature = true;
}
}
}
if (autocompleteSignature) {
// set up signature autocompletion
foreach (Function *f, functions) {
Overview overview;
overview.setShowArgumentNames(true);
overview.setShowDefaultArguments(false);
// gets: "parameter list) cv-spec",
QString completion = overview(f->type()).mid(1);
TextEditor::CompletionItem item(this);
item.text = completion;
item.data = QVariant::fromValue(CompleteFunctionDeclaration(f));
m_completions.append(item);
}
return true;
}
}
}
if (! functions.empty()) {
// set up function call tooltip
// Recreate if necessary
if (!m_functionArgumentWidget)
m_functionArgumentWidget = new FunctionArgumentWidget;
m_functionArgumentWidget->showFunctionHint(functions,
typeOfExpression.context(),
m_startPosition);
}
return false;
}
bool CppCodeCompletion::completeMember(const QList<LookupItem> &baseResults)
{
const LookupContext &context = typeOfExpression.context();
// if (debug)
// qDebug() << Q_FUNC_INFO << __LINE__;
if (baseResults.isEmpty())
return false;
ResolveExpression resolveExpression(context);
bool replacedDotOperator = false;
if (ClassOrNamespace *binding = resolveExpression.baseExpression(baseResults,
m_completionOperator,
&replacedDotOperator)) {
// if (debug)
// qDebug() << "cool we got a binding for the base expression";
if (replacedDotOperator && binding) {
// Replace . with ->
int length = m_editor->position() - m_startPosition + 1;
m_editor->setCurPos(m_startPosition - 1);
m_editor->replace(length, QLatin1String("->"));
++m_startPosition;
}
if (binding)
completeClass(binding, /*static lookup = */ false);
return ! m_completions.isEmpty();
}
// if (debug) {
// Overview oo;
// qDebug() << "hmm, got:" << oo(baseResults.first().type());
// }
return false;
}
bool CppCodeCompletion::completeScope(const QList<LookupItem> &results)
{
const LookupContext &context = typeOfExpression.context();
if (results.isEmpty())
return false;
foreach (const LookupItem &result, results) {
FullySpecifiedType ty = result.type();
Scope *scope = result.scope();
if (NamedType *namedTy = ty->asNamedType()) {
if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
completeClass(b);
break;
}
} else if (Class *classTy = ty->asClassType()) {
if (ClassOrNamespace *b = context.lookupType(classTy)) {
completeClass(b);
break;
}
} else if (Namespace *nsTy = ty->asNamespaceType()) {
if (ClassOrNamespace *b = context.lookupType(nsTy)) {
completeNamespace(b);
break;
}
}
}
return ! m_completions.isEmpty();
}
void CppCodeCompletion::addKeywords()
{
int keywordLimit = T_FIRST_OBJC_AT_KEYWORD;
if (objcKeywordsWanted())
keywordLimit = T_LAST_OBJC_AT_KEYWORD + 1;
// keyword completion items.
for (int i = T_FIRST_KEYWORD; i < keywordLimit; ++i) {
TextEditor::CompletionItem item(this);
item.text = QLatin1String(Token::name(i));
item.icon = m_icons.keywordIcon();
m_completions.append(item);
}
}
void CppCodeCompletion::addMacros(const QString &fileName, const Snapshot &snapshot)
{
QSet<QString> processed;
QSet<QString> definedMacros;
addMacros_helper(snapshot, fileName, &processed, &definedMacros);
foreach (const QString &macroName, definedMacros) {
TextEditor::CompletionItem item(this);
item.text = macroName;
item.icon = m_icons.macroIcon();
m_completions.append(item);
}
}
void CppCodeCompletion::addMacros_helper(const Snapshot &snapshot,
const QString &fileName,
QSet<QString> *processed,
QSet<QString> *definedMacros)
{
Document::Ptr doc = snapshot.document(fileName);
if (! doc || processed->contains(doc->fileName()))
return;
processed->insert(doc->fileName());
foreach (const Document::Include &i, doc->includes()) {
addMacros_helper(snapshot, i.fileName(), processed, definedMacros);
}
foreach (const Macro &macro, doc->definedMacros()) {
const QString macroName = QString::fromUtf8(macro.name().constData(), macro.name().length());
if (! macro.isHidden())
definedMacros->insert(macroName);
else
definedMacros->remove(macroName);
}
}
void CppCodeCompletion::addCompletionItem(Symbol *symbol)
{
ConvertToCompletionItem toCompletionItem(this);
if (TextEditor::CompletionItem item = toCompletionItem(symbol))
m_completions.append(item);
}
bool CppCodeCompletion::completeInclude(const QTextCursor &cursor)
{
QString directoryPrefix;
if (m_completionOperator == T_SLASH) {
QTextCursor c = cursor;
c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString sel = c.selectedText();
int startCharPos = sel.indexOf(QLatin1Char('"'));
if (startCharPos == -1) {
startCharPos = sel.indexOf(QLatin1Char('<'));
m_completionOperator = T_ANGLE_STRING_LITERAL;
} else {
m_completionOperator = T_STRING_LITERAL;
}
if (startCharPos != -1)
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
}
// Make completion for all relevant includes
if (ProjectExplorer::Project *project = ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject()) {
QStringList includePaths = m_manager->projectInfo(project).includePaths;
const QString currentFilePath = QFileInfo(m_editor->file()->fileName()).path();
if (!includePaths.contains(currentFilePath))
includePaths.append(currentFilePath);
foreach (const QString &includePath, includePaths) {
QString realPath = includePath;
if (!directoryPrefix.isEmpty()) {
realPath += QLatin1Char('/');
realPath += directoryPrefix;
}
foreach (const QString &itemText, m_manager->includesInPath(realPath)) {
TextEditor::CompletionItem item(this);
item.text += itemText;
// TODO: Icon for include files
item.icon = m_icons.keywordIcon();
m_completions.append(item);
}
}
QStringList frameworkPaths = m_manager->projectInfo(project).frameworkPaths;
foreach (const QString &frameworkPath, frameworkPaths) {
QString realPath = frameworkPath;
if (!directoryPrefix.isEmpty()) {
realPath += QLatin1Char('/');
realPath += directoryPrefix;
realPath += QLatin1String(".framework/Headers");
}
foreach (const QString &itemText, m_manager->includesInPath(realPath)) {
TextEditor::CompletionItem item(this);
item.text += itemText;
// TODO: Icon for include files
item.icon = m_icons.keywordIcon();
m_completions.append(item);
}
}
}
return !m_completions.isEmpty();
}
QStringList CppCodeCompletion::preprocessorCompletions
= QStringList()
<< QLatin1String("define")
<< QLatin1String("error")
<< QLatin1String("include")
<< QLatin1String("line")
<< QLatin1String("pragma")
<< QLatin1String("undef")
<< QLatin1String("if")
<< QLatin1String("ifdef")
<< QLatin1String("ifndef")
<< QLatin1String("elif")
<< QLatin1String("else")
<< QLatin1String("endif")
;
void CppCodeCompletion::completePreprocessor()
{
TextEditor::CompletionItem item(this);
foreach (const QString &preprocessorCompletion, preprocessorCompletions) {
item.text = preprocessorCompletion;
m_completions.append(item);
}
if (objcKeywordsWanted()) {
item.text = QLatin1String("import");
m_completions.append(item);
}
}
void CppCodeCompletion::completeNamespace(ClassOrNamespace *b)
{
QSet<ClassOrNamespace *> bindingsVisited;
QList<ClassOrNamespace *> bindingsToVisit;
bindingsToVisit.append(b);
while (! bindingsToVisit.isEmpty()) {
ClassOrNamespace *binding = bindingsToVisit.takeFirst();
if (! binding || bindingsVisited.contains(binding))
continue;
bindingsVisited.insert(binding);
bindingsToVisit += binding->usings();
QList<Scope *> scopesToVisit;
QSet<Scope *> scopesVisited;
foreach (Symbol *bb, binding->symbols()) {
if (Namespace *ns = bb->asNamespace())
scopesToVisit.append(ns->members());
}
foreach (Enum *e, binding->enums()) {
scopesToVisit.append(e->members());
}
while (! scopesToVisit.isEmpty()) {
Scope *scope = scopesToVisit.takeFirst();
if (! scope || scopesVisited.contains(scope))
continue;
scopesVisited.insert(scope);
for (Scope::iterator it = scope->firstSymbol(); it != scope->lastSymbol(); ++it) {
Symbol *member = *it;
addCompletionItem(member);
}
}
}
}
void CppCodeCompletion::completeClass(ClassOrNamespace *b, bool staticLookup)
{
QSet<ClassOrNamespace *> bindingsVisited;
QList<ClassOrNamespace *> bindingsToVisit;
bindingsToVisit.append(b);
while (! bindingsToVisit.isEmpty()) {
ClassOrNamespace *binding = bindingsToVisit.takeFirst();
if (! binding || bindingsVisited.contains(binding))
continue;
bindingsVisited.insert(binding);
bindingsToVisit += binding->usings();
QList<Scope *> scopesToVisit;
QSet<Scope *> scopesVisited;
foreach (Symbol *bb, binding->symbols()) {
if (Class *k = bb->asClass())
scopesToVisit.append(k->members());
}
foreach (Enum *e, binding->enums())
scopesToVisit.append(e->members());
while (! scopesToVisit.isEmpty()) {
Scope *scope = scopesToVisit.takeFirst();
if (! scope || scopesVisited.contains(scope))
continue;
scopesVisited.insert(scope);
addCompletionItem(scope->owner()); // add a completion item for the injected class name.
for (Scope::iterator it = scope->firstSymbol(); it != scope->lastSymbol(); ++it) {
Symbol *member = *it;
if (member->isFriend())
continue;
else if (! staticLookup && (member->isTypedef() ||
member->isEnum() ||
member->isClass()))
continue;
addCompletionItem(member);
}
}
}
}
bool CppCodeCompletion::completeQtMethod(const QList<LookupItem> &results,
bool wantSignals)
{
if (results.isEmpty())
return false;
const LookupContext &context = typeOfExpression.context();
ConvertToCompletionItem toCompletionItem(this);
Overview o;
o.setShowReturnTypes(false);
o.setShowArgumentNames(false);
o.setShowFunctionSignatures(true);
QSet<QString> signatures;
foreach (const LookupItem &p, results) {
FullySpecifiedType ty = p.type().simplified();
if (PointerType *ptrTy = ty->asPointerType())
ty = ptrTy->elementType().simplified();
else
continue; // not a pointer or a reference to a pointer.
NamedType *namedTy = ty->asNamedType();
if (! namedTy) // not a class name.
continue;
ClassOrNamespace *b = context.lookupType(namedTy->name(), p.scope());
if (! b)
continue;
QList<ClassOrNamespace *>todo;
QSet<ClassOrNamespace *> processed;
QList<Scope *> scopes;
todo.append(b);
while (!todo.isEmpty()) {
ClassOrNamespace *binding = todo.takeLast();
if (!processed.contains(binding)) {
processed.insert(binding);
foreach (Symbol *s, binding->symbols())
if (Class *clazz = s->asClass())
scopes.append(clazz->members());
todo.append(binding->usings());
}
}
foreach (Scope *scope, scopes) {
if (! scope->isClassScope())
continue;
for (unsigned i = 0; i < scope->symbolCount(); ++i) {
Symbol *member = scope->symbolAt(i);
Function *fun = member->type()->asFunctionType();
if (! fun)
continue;
if (wantSignals && ! fun->isSignal())
continue;
else if (! wantSignals && ! fun->isSlot())
continue;
if (TextEditor::CompletionItem item = toCompletionItem(fun)) {
unsigned count = fun->argumentCount();
while (true) {
TextEditor::CompletionItem ci = item;
QString signature;
signature += overview.prettyName(fun->name());
signature += QLatin1Char('(');
for (unsigned i = 0; i < count; ++i) {
Symbol *arg = fun->argumentAt(i);
if (i != 0)
signature += QLatin1Char(',');
signature += o.prettyType(arg->type());
}
signature += QLatin1Char(')');
const QByteArray normalized =
QMetaObject::normalizedSignature(signature.toLatin1());
signature = QString::fromLatin1(normalized, normalized.size());
if (! signatures.contains(signature)) {
signatures.insert(signature);
ci.text = signature; // fix the completion item.
m_completions.append(ci);
}
if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer())
--count;
else
break;
}
}
}
}
}
return ! m_completions.isEmpty();
}
void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
{
const int length = m_editor->position() - m_startPosition;
if (length < 0)
return;
const QString key = m_editor->textAt(m_startPosition, length);
if (length == 0)
*completions = m_completions;
else if (length > 0) {
/* Close on the trailing slash for include completion, to enable the slash to
* trigger a new completion list. */
if ((m_completionOperator == T_STRING_LITERAL ||
m_completionOperator == T_ANGLE_STRING_LITERAL) && key.endsWith(QLatin1Char('/')))
return;
if (m_completionOperator != T_LPAREN) {
filter(m_completions, completions, key);
} else if (m_completionOperator == T_LPAREN ||
m_completionOperator == T_SIGNAL ||
m_completionOperator == T_SLOT) {
foreach (const TextEditor::CompletionItem &item, m_completions) {
if (item.text.startsWith(key, Qt::CaseInsensitive)) {
completions->append(item);
}
}
}
}
}
QList<TextEditor::CompletionItem> CppCodeCompletion::removeDuplicates(const QList<TextEditor::CompletionItem> &items)
{
// Remove duplicates
QList<TextEditor::CompletionItem> uniquelist;
QSet<QString> processed;
foreach (const TextEditor::CompletionItem &item, items) {
if (! processed.contains(item.text)) {
processed.insert(item.text);
uniquelist.append(item);
if (Symbol *symbol = qvariant_cast<Symbol *>(item.data)) {
if (Function *funTy = symbol->type()->asFunctionType()) {
if (funTy->hasArguments())
++uniquelist.back().duplicateCount;
}
}
}
}
return uniquelist;
}
QList<TextEditor::CompletionItem> CppCodeCompletion::getCompletions()
{
QList<TextEditor::CompletionItem> completionItems;
completions(&completionItems);
return completionItems;
}
void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
{
Symbol *symbol = 0;
if (item.data.isValid())
symbol = item.data.value<Symbol *>();
QString toInsert;
QString extraChars;
int extraLength = 0;
int cursorOffset = 0;
bool autoParenthesesEnabled = true;
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
toInsert = item.text;
extraChars += QLatin1Char(')');
} else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
toInsert = item.text;
if (!toInsert.endsWith(QLatin1Char('/')))
extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
} else {
toInsert = item.text;
//qDebug() << "current symbol:" << overview.prettyName(symbol->name())
//<< overview.prettyType(symbol->type());
const bool autoInsertBrackets = completionSettings().m_autoInsertBrackets;
if (autoInsertBrackets && symbol && symbol->type()) {
if (Function *function = symbol->type()->asFunctionType()) {
// If the member is a function, automatically place the opening parenthesis,
// except when it might take template parameters.
if (! function->hasReturnType() && (function->identity() && !function->identity()->isDestructorNameId())) {
// Don't insert any magic, since the user might have just wanted to select the class
} else if (function->templateParameterCount() != 0) {
// If there are no arguments, then we need the template specification
if (function->argumentCount() == 0) {
extraChars += QLatin1Char('<');
}
} else if (! function->isAmbiguous()) {
if (completionSettings().m_spaceAfterFunctionName)
extraChars += QLatin1Char(' ');
extraChars += QLatin1Char('(');
// If the function doesn't return anything, automatically place the semicolon,
// unless we're doing a scope completion (then it might be function definition).
bool endWithSemicolon = function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON;
// If the function takes no arguments, automatically place the closing parenthesis
if (item.duplicateCount == 0 && ! function->hasArguments()) {
extraChars += QLatin1Char(')');
if (endWithSemicolon)
extraChars += QLatin1Char(';');
} else if (autoParenthesesEnabled) {
const QChar lookAhead = m_editor->characterAt(m_editor->position() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraChars += QLatin1Char(')');
--cursorOffset;
if (endWithSemicolon) {
extraChars += QLatin1Char(';');
--cursorOffset;
}
}
}
}
}
}
if (autoInsertBrackets && item.data.canConvert<CompleteFunctionDeclaration>()) {
// everything from the closing parenthesis on are extra chars, to
// make sure an auto-inserted ")" gets replaced by ") const" if necessary
int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
extraChars = toInsert.mid(closingParen);
toInsert.truncate(closingParen);
}
}
// Avoid inserting characters that are already there
for (int i = 0; i < extraChars.length(); ++i) {
const QChar a = extraChars.at(i);
const QChar b = m_editor->characterAt(m_editor->position() + i);
if (a == b)
++extraLength;
else
break;
}
toInsert += extraChars;
// Insert the remainder of the name
int length = m_editor->position() - m_startPosition + extraLength;
m_editor->setCurPos(m_startPosition);
m_editor->replace(length, toInsert);
if (cursorOffset)
m_editor->setCurPos(m_editor->position() + cursorOffset);
}
bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems)
{
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
return false;
} else if (completionItems.count() == 1) {
complete(completionItems.first());
return true;
} else if (m_completionOperator != T_LPAREN) {
return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
}
return false;
}
void CppCodeCompletion::cleanup()
{
m_completions.clear();
// Set empty map in order to avoid referencing old versions of the documents
// until the next completion
typeOfExpression.reset();
}
int CppCodeCompletion::findStartOfName(int pos) const
{
if (pos == -1)
pos = m_editor->position();
QChar chr;
// Skip to the start of a name
do {
chr = m_editor->characterAt(--pos);
} while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
return pos + 1;
}
bool CppCodeCompletion::objcKeywordsWanted() const
{
if (!m_objcEnabled)
return false;
Core::IFile *file = m_editor->file();
QString fileName = file->fileName();
const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase();
return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE;
}
#include "cppcodecompletion.moc"