cppcodecompletion.cpp 46.2 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6 7 8
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
9
** Commercial Usage
10
**
11 12 13 14
** 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.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** 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.
24
**
25 26
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30 31
#include "cppcodecompletion.h"
#include "cppmodelmanager.h"
32
#include "cppdoxygen.h"
Roberto Raggi's avatar
Roberto Raggi committed
33
#include "cpptoolseditorsupport.h"
con's avatar
con committed
34 35 36 37 38 39 40 41 42 43 44 45

#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>
hjk's avatar
hjk committed
46

con's avatar
con committed
47 48 49 50 51 52 53 54 55 56
#include <cplusplus/ResolveExpression.h>
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TokenUnderCursor.h>

#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/itexteditor.h>
#include <texteditor/itexteditable.h>
hjk's avatar
hjk committed
57
#include <utils/qtcassert.h>
con's avatar
con committed
58 59 60 61 62 63
#include <texteditor/basetexteditor.h>

#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QFile>
#include <QtGui/QAction>
64
#include <QtGui/QApplication>
65
#include <QtGui/QDesktopWidget>
con's avatar
con committed
66 67
#include <QtGui/QKeyEvent>
#include <QtGui/QLabel>
68
#include <QtGui/QToolButton>
con's avatar
con committed
69
#include <QtGui/QVBoxLayout>
70
#include <QtGui/QTextDocument> // Qt::escape()
con's avatar
con committed
71 72 73 74 75 76

using namespace CPlusPlus;

namespace CppTools {
namespace Internal {

77 78
class FunctionArgumentWidget : public QLabel
{
79 80
    Q_OBJECT

con's avatar
con committed
81
public:
82
    FunctionArgumentWidget();
83 84 85
    void showFunctionHint(QList<Function *> functionSymbols,
                          const LookupContext &context,
                          int startPosition);
con's avatar
con committed
86 87 88 89

protected:
    bool eventFilter(QObject *obj, QEvent *e);

90 91 92 93
private slots:
    void nextPage();
    void previousPage();

con's avatar
con committed
94
private:
95
    void updateArgumentHighlight();
con's avatar
con committed
96 97
    void updateHintText();

98 99 100
    Function *currentFunction() const
    { return m_items.at(m_current); }

con's avatar
con committed
101 102
    int m_startpos;
    int m_currentarg;
103
    int m_current;
104
    bool m_escapePressed;
con's avatar
con committed
105 106 107

    TextEditor::ITextEditor *m_editor;

108
    QWidget *m_pager;
109
    QLabel *m_numberLabel;
con's avatar
con committed
110
    QFrame *m_popupFrame;
111
    QList<Function *> m_items;
112
    LookupContext m_context;
con's avatar
con committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
};

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.m_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(Name *name)
    {
        TextEditor::CompletionItem item(_collector);
        item.m_text = overview.prettyName(name);
        item.m_icon = _collector->iconForSymbol(_symbol);
        return item;
    }

    virtual void visit(NameId *name)
    { _item = newCompletionItem(name); }

    virtual void visit(TemplateNameId *name)
    {
        _item = newCompletionItem(name);
        _item.m_text = QLatin1String(name->identifier()->chars());
    }

    virtual void visit(DestructorNameId *name)
    { _item = newCompletionItem(name); }

    virtual void visit(OperatorNameId *name)
    { _item = newCompletionItem(name); }

    virtual void visit(ConversionNameId *name)
    { _item = newCompletionItem(name); }

    virtual void visit(QualifiedNameId *name)
    { _item = newCompletionItem(name->unqualifiedNameId()); }
};


} // namespace Internal
} // namespace CppTools

using namespace CppTools::Internal;

201
FunctionArgumentWidget::FunctionArgumentWidget():
202
    m_startpos(-1),
203 204
    m_current(0),
    m_escapePressed(false)
con's avatar
con committed
205
{
206
    QObject *editorObject = Core::EditorManager::instance()->currentEditor();
con's avatar
con committed
207 208
    m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject);

209
    m_popupFrame = new QFrame(m_editor->widget(), Qt::ToolTip | Qt::WindowStaysOnTopHint);
con's avatar
con committed
210 211 212
    m_popupFrame->setFocusPolicy(Qt::NoFocus);
    m_popupFrame->setAttribute(Qt::WA_DeleteOnClose);

213 214 215 216
    QToolButton *downArrow = new QToolButton;
    downArrow->setArrowType(Qt::DownArrow);
    downArrow->setFixedSize(16, 16);
    downArrow->setAutoRaise(true);
217

218 219 220 221
    QToolButton *upArrow = new QToolButton;
    upArrow->setArrowType(Qt::UpArrow);
    upArrow->setFixedSize(16, 16);
    upArrow->setAutoRaise(true);
222 223 224

    m_popupFrame->setFrameStyle(QFrame::Box);
    m_popupFrame->setFrameShadow(QFrame::Plain);
con's avatar
con committed
225 226 227 228

    setParent(m_popupFrame);
    setFocusPolicy(Qt::NoFocus);

229 230 231 232
    m_pager = new QWidget;
    QHBoxLayout *hbox = new QHBoxLayout(m_pager);
    hbox->setMargin(0);
    hbox->setSpacing(0);
233
    hbox->addWidget(upArrow);
234 235
    m_numberLabel = new QLabel;
    hbox->addWidget(m_numberLabel);
236
    hbox->addWidget(downArrow);
237

238
    QHBoxLayout *layout = new QHBoxLayout;
con's avatar
con committed
239
    layout->setMargin(0);
240 241 242
    layout->setSpacing(0);
    layout->addWidget(m_pager);
    layout->addWidget(this);
con's avatar
con committed
243 244
    m_popupFrame->setLayout(layout);

245 246
    connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
    connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
247 248

    QPalette pal = m_popupFrame->palette();
con's avatar
con committed
249 250
    setAutoFillBackground(true);
    pal.setColor(QPalette::Background, QColor(255, 255, 220));
251
    m_popupFrame->setPalette(pal);
con's avatar
con committed
252 253 254

    setTextFormat(Qt::RichText);
    setMargin(1);
255 256

    qApp->installEventFilter(this);
con's avatar
con committed
257 258
}

259
void FunctionArgumentWidget::showFunctionHint(QList<Function *> functionSymbols,
260 261
                                              const LookupContext &context,
                                              int startPosition)
con's avatar
con committed
262
{
263 264
    Q_ASSERT(!functionSymbols.isEmpty());

265 266 267
    if (m_startpos == startPosition)
        return;

268
    m_pager->setVisible(functionSymbols.size() > 1);
269

270
    m_items = functionSymbols;
271
    m_context = context;
272
    m_startpos = startPosition;
273
    m_current = 0;
274
    m_escapePressed = false;
con's avatar
con committed
275 276 277

    // update the text
    m_currentarg = -1;
278
    updateArgumentHighlight();
279

con's avatar
con committed
280 281 282
    m_popupFrame->show();
}

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
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();
}

299
void FunctionArgumentWidget::updateArgumentHighlight()
con's avatar
con committed
300 301 302
{
    int curpos = m_editor->position();
    if (curpos < m_startpos) {
303
        m_popupFrame->close();
con's avatar
con committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
        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)
328
        m_popupFrame->close();
con's avatar
con committed
329 330 331 332 333
}

bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e)
{
    switch (e->type()) {
334 335 336 337 338
    case QEvent::ShortcutOverride:
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
            m_escapePressed = true;
        }
        break;
339
    case QEvent::KeyPress:
340 341 342
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
            m_escapePressed = true;
        }
343 344 345 346 347 348 349 350
        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;
con's avatar
con committed
351
            }
352 353 354 355
            return false;
        }
        break;
    case QEvent::KeyRelease:
356
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_escapePressed) {
357 358
            m_popupFrame->close();
            return false;
con's avatar
con committed
359
        }
360 361
        updateArgumentHighlight();
        break;
con's avatar
con committed
362 363
    case QEvent::WindowDeactivate:
    case QEvent::FocusOut:
364 365 366
        if (obj != m_editor->widget())
            break;
        m_popupFrame->close();
367
        break;
con's avatar
con committed
368 369 370
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
371 372 373
    case QEvent::Wheel: {
            QWidget *widget = qobject_cast<QWidget *>(obj);
            if (! (widget == this || m_popupFrame->isAncestorOf(widget))) {
374
                m_popupFrame->close();
375 376
            }
        }
con's avatar
con committed
377 378 379 380 381 382 383 384 385 386 387 388
        break;
    default:
        break;
    }
    return false;
}

void FunctionArgumentWidget::updateHintText()
{
    Overview overview;
    overview.setShowReturnTypes(true);
    overview.setShowArgumentNames(true);
389
    overview.setMarkedArgument(m_currentarg + 1);
390
    Function *f = currentFunction();
391

392 393 394 395 396 397 398 399 400 401 402 403
    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);

404 405 406
    m_numberLabel->setText(tr("%1 of %2").arg(m_current + 1).arg(m_items.size()));

    m_popupFrame->setFixedWidth(m_popupFrame->minimumSizeHint().width());
407 408

    const QDesktopWidget *desktop = QApplication::desktop();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
409
#ifdef Q_WS_MAC
410
    const QRect screen = desktop->availableGeometry(desktop->screenNumber(m_editor->widget()));
411
#else
412
    const QRect screen = desktop->screenGeometry(desktop->screenNumber(m_editor->widget()));
413 414 415 416 417 418 419 420 421 422
#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);
con's avatar
con committed
423 424
}

Roberto Raggi's avatar
Roberto Raggi committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
CppQuickFixCollector::CppQuickFixCollector(CppModelManager *modelManager)
    : _modelManager(modelManager), _editor(0)
{ }

CppQuickFixCollector::~CppQuickFixCollector()
{ }

bool CppQuickFixCollector::supportsEditor(TextEditor::ITextEditable *editor)
{ return _modelManager->isCppEditor(editor); }

bool CppQuickFixCollector::triggersCompletion(TextEditor::ITextEditable *)
{ return false; }

int CppQuickFixCollector::startCompletion(TextEditor::ITextEditable *editor)
{
    _editor = editor;

    if (CppEditorSupport *extra = _modelManager->editorSupport(editor)) {
        const QList<QuickFixOperationPtr> quickFixes = extra->quickFixes();
        if (! quickFixes.isEmpty()) {
            int i = 0;
            foreach (QuickFixOperationPtr op, quickFixes) {
                TextEditor::CompletionItem item(this);
                item.m_text = op->description();
                item.m_data = QVariant::fromValue(i);
                _completions.append(item);
                ++i;
            }
            return editor->position();
        }
    }
    return -1;
}

void CppQuickFixCollector::completions(QList<TextEditor::CompletionItem> *completions)
{
    completions->append(_completions);
}

void CppQuickFixCollector::complete(const TextEditor::CompletionItem &item)
{
    CppEditorSupport *extra = _modelManager->editorSupport(_editor);
    const QList<QuickFixOperationPtr> quickFixes = extra->quickFixes();
    QuickFixOperationPtr quickFix = quickFixes.at(item.m_data.toInt());
    TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(_editor->widget());
    quickFix->apply(ed->textCursor());
}

void CppQuickFixCollector::cleanup()
{
    _completions.clear();
}

478
CppCodeCompletion::CppCodeCompletion(CppModelManager *manager)
con's avatar
con committed
479 480
    : ICompletionCollector(manager),
      m_manager(manager),
481
      m_caseSensitivity(Qt::CaseSensitive),
482
      m_autoInsertBrackets(true),
con's avatar
con committed
483 484
      m_forcedCompletion(false),
      m_completionOperator(T_EOF_SYMBOL)
485 486
{
}
con's avatar
con committed
487 488

QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const
489 490 491 492 493 494 495 496 497 498 499 500 501 502
{
    return m_icons.iconForSymbol(symbol);
}

Qt::CaseSensitivity CppCodeCompletion::caseSensitivity() const
{
    return m_caseSensitivity;
}

void CppCodeCompletion::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
{
    m_caseSensitivity = caseSensitivity;
}

503
bool CppCodeCompletion::autoInsertBrackets() const
504
{
505
    return m_autoInsertBrackets;
506 507
}

508
void CppCodeCompletion::setAutoInsertBrackets(bool autoInsertBrackets)
509
{
510
    m_autoInsertBrackets = autoInsertBrackets;
511
}
con's avatar
con committed
512

513 514 515 516 517 518 519 520 521 522
bool CppCodeCompletion::isPartialCompletionEnabled() const
{
    return m_partialCompletionEnabled;
}

void CppCodeCompletion::setPartialCompletionEnabled(bool partialCompletionEnabled)
{
    m_partialCompletionEnabled = partialCompletionEnabled;
}

con's avatar
con committed
523 524 525 526 527 528 529 530 531 532 533 534
/*
  Searches beckward for an access operator.
*/
static int startOfOperator(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;
535
    int k = T_EOF_SYMBOL;
con's avatar
con committed
536 537

    if        (ch2 != QLatin1Char('.') && ch == QLatin1Char('.')) {
538
        k = T_DOT;
con's avatar
con committed
539
        --start;
540 541 542
    } else if (ch == QLatin1Char(',')) {
        k = T_COMMA;
        --start;
con's avatar
con committed
543
    } else if (wantFunctionCall        && ch == QLatin1Char('(')) {
544
        k = T_LPAREN;
con's avatar
con committed
545
        --start;
546
    } else if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':') && ch == QLatin1Char(':')) {
547
        k = T_COLON_COLON;
con's avatar
con committed
548 549
        start -= 2;
    } else if (ch2 == QLatin1Char('-') && ch == QLatin1Char('>')) {
550
        k = T_ARROW;
con's avatar
con committed
551 552
        start -= 2;
    } else if (ch2 == QLatin1Char('.') && ch == QLatin1Char('*')) {
553
        k = T_DOT_STAR;
con's avatar
con committed
554 555
        start -= 2;
    } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>') && ch == QLatin1Char('*')) {
556
        k = T_ARROW_STAR;
con's avatar
con committed
557
        start -= 3;
558
    } else if ((ch2.isNull() || ch2.isSpace()) && (ch == QLatin1Char('@') || ch == QLatin1Char('\\'))) {
559 560
        k = T_DOXY_COMMENT;
        --start;
con's avatar
con committed
561 562
    }

563 564 565 566 567 568 569 570 571 572 573 574 575
    if (start == pos)
        return start;

    TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
    QTextCursor tc(edit->textCursor());
    tc.setPosition(pos);

    static CPlusPlus::TokenUnderCursor tokenUnderCursor;
    const SimpleToken tk = tokenUnderCursor(tc);

    if (k == T_DOXY_COMMENT && tk.isNot(T_DOXY_COMMENT)) {
        k = T_EOF_SYMBOL;
        start = pos;
con's avatar
con committed
576
    }
577 578 579 580 581
    else if (tk.is(T_COMMENT) || tk.isLiteral()) {
        k = T_EOF_SYMBOL;
        start = pos;
    }

582 583 584 585 586 587 588 589 590
    if (k == T_LPAREN) {
        const QList<SimpleToken> &tokens = tokenUnderCursor.tokens();
        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);
591 592
                if (previousToken.is(T_IDENTIFIER) || previousToken.is(T_GREATER)
                    || previousToken.is(T_SIGNAL) || previousToken.is(T_SLOT))
593 594 595 596 597 598 599 600 601 602
                    break;
            }
        }

        if (i == tokens.size()) {
            k = T_EOF_SYMBOL;
            start = pos;
        }
    }

603 604 605
    if (kind)
        *kind = k;

con's avatar
con committed
606 607 608
    return start;
}

609
bool CppCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
610 611
{ return m_manager->isCppEditor(editor); }

con's avatar
con committed
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
{
    const int pos = editor->position();
    if (startOfOperator(editor, pos, /*token =*/ 0,
                        /*want function call=*/ true) != pos)
        return true;

    return false;
}

int CppCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
{
    TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
    if (! edit)
        return -1;

    m_editor = editor;
629 630
    const int startOfName = findStartOfName();
    m_startPosition = startOfName;
con's avatar
con committed
631 632
    m_completionOperator = T_EOF_SYMBOL;

633
    int endOfOperator = m_startPosition;
con's avatar
con committed
634 635

    // Skip whitespace preceding this position
636 637
    while (editor->characterAt(endOfOperator - 1).isSpace())
        --endOfOperator;
con's avatar
con committed
638

639 640 641
    int endOfExpression = startOfOperator(editor, endOfOperator,
                                          &m_completionOperator,
                                          /*want function call =*/ true);
con's avatar
con committed
642 643 644 645 646 647 648 649

    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;

650 651 652 653
    if (m_completionOperator == T_DOXY_COMMENT) {
        for (int i = 1; i < T_DOXY_LAST_TAG; ++i) {
            TextEditor::CompletionItem item(this);
            item.m_text.append(QString::fromLatin1(doxygenTagSpell(i)));
654
            item.m_icon = m_icons.keywordIcon();
655 656 657 658 659 660
            m_completions.append(item);
        }

        return m_startPosition;
    }

661 662
    ExpressionUnderCursor expressionUnderCursor;
    QTextCursor tc(edit->document());
663

664
    if (m_completionOperator == T_COMMA) {
con's avatar
con committed
665
        tc.setPosition(endOfExpression);
666 667 668 669 670 671 672 673 674 675
        const int start = expressionUnderCursor.startOfFunctionCall(tc);
        if (start != -1) {
            endOfExpression = start;
            m_startPosition = start + 1;
            m_completionOperator = T_LPAREN;
        }
    }

    QString expression;
    tc.setPosition(endOfExpression);
676

677
    if (m_completionOperator) {
con's avatar
con committed
678
        expression = expressionUnderCursor(tc);
679

con's avatar
con committed
680 681 682
        if (m_completionOperator == T_LPAREN) {
            if (expression.endsWith(QLatin1String("SIGNAL")))
                m_completionOperator = T_SIGNAL;
683

con's avatar
con committed
684 685
            else if (expression.endsWith(QLatin1String("SLOT")))
                m_completionOperator = T_SLOT;
686

687 688 689 690
            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;
691
                m_startPosition = startOfName;
692
            }
con's avatar
con committed
693 694 695
        }
    }

696
    //qDebug() << "***** expression:" << expression;
con's avatar
con committed
697

698 699 700
    const Snapshot snapshot = m_manager->snapshot();

    if (Document::Ptr thisDocument = snapshot.value(fileName)) {
con's avatar
con committed
701 702
        Symbol *symbol = thisDocument->findSymbolAt(line, column);

703
        typeOfExpression.setSnapshot(m_manager->snapshot());
con's avatar
con committed
704

705 706
        QList<TypeOfExpression::Result> resolvedTypes = typeOfExpression(expression, thisDocument, symbol,
                                                                         TypeOfExpression::Preprocess);
con's avatar
con committed
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
        LookupContext context = typeOfExpression.lookupContext();

        if (!typeOfExpression.expressionAST() && (! m_completionOperator ||
                                                    m_completionOperator == T_COLON_COLON)) {
            if (!m_completionOperator) {
                addKeywords();
                addMacros(context);
            }

            const QList<Scope *> scopes = context.expand(context.visibleScopes());
            foreach (Scope *scope, scopes) {
                for (unsigned i = 0; i < scope->symbolCount(); ++i) {
                    addCompletionItem(scope->symbolAt(i));
                }
            }
            return m_startPosition;
        }

        // qDebug() << "found" << resolvedTypes.count() << "symbols for expression:" << expression;

        if (resolvedTypes.isEmpty() && (m_completionOperator == T_SIGNAL ||
                                        m_completionOperator == T_SLOT)) {
            // Apply signal/slot completion on 'this'
            expression = QLatin1String("this");
            resolvedTypes = typeOfExpression(expression, thisDocument, symbol);
            context = typeOfExpression.lookupContext();
        }

Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
735 736
        if (! resolvedTypes.isEmpty()) {
            if (m_completionOperator == T_LPAREN && completeConstructorOrFunction(resolvedTypes)) {
con's avatar
con committed
737
                return m_startPosition;
Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
738

739
            } else if ((m_completionOperator == T_DOT || m_completionOperator == T_ARROW) &&
740
                      completeMember(resolvedTypes, context)) {
con's avatar
con committed
741
                return m_startPosition;
Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
742

743
            } else if (m_completionOperator == T_COLON_COLON && completeScope(resolvedTypes, context)) {
con's avatar
con committed
744
                return m_startPosition;
Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
745 746

            } else if (m_completionOperator == T_SIGNAL      && completeSignal(resolvedTypes, context)) {
con's avatar
con committed
747
                return m_startPosition;
Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
748 749

            } else if (m_completionOperator == T_SLOT        && completeSlot(resolvedTypes, context)) {
con's avatar
con committed
750 751 752
                return m_startPosition;
            }
        }
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770

        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);
            QString baseExpression = expressionUnderCursor(tc);

            // Resolve the type of this expression
            QList<TypeOfExpression::Result> results =
                    typeOfExpression(baseExpression, thisDocument, symbol, TypeOfExpression::Preprocess);

            // If it's a class, add completions for the constructors
            foreach (const TypeOfExpression::Result &result, results) {
771
                if (result.first->isClassType()) {
Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
772
                    if (completeConstructorOrFunction(results))
773 774 775 776 777
                        return m_startPosition;
                    break;
                }
            }
        }
con's avatar
con committed
778 779 780 781 782 783
    }

    // nothing to do.
    return -1;
}

Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
784
bool CppCodeCompletion::completeConstructorOrFunction(const QList<TypeOfExpression::Result> &results)
con's avatar
con committed
785
{
786 787
    QList<Function *> functions;

Roberto Raggi's avatar
Roberto Raggi committed
788 789 790 791
    foreach (const TypeOfExpression::Result &result, results) {
        FullySpecifiedType exprTy = result.first;

        if (Class *klass = exprTy->asClassType()) {
792 793 794 795
            Name *className = klass->name();
            if (! className)
                continue; // nothing to do for anonymoous classes.

Roberto Raggi's avatar
Roberto Raggi committed
796 797
            for (unsigned i = 0; i < klass->memberCount(); ++i) {
                Symbol *member = klass->memberAt(i);
798 799 800 801 802 803 804 805 806 807 808 809 810
                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);
                    }
Roberto Raggi's avatar
Roberto Raggi committed
811
                }
812
            }
Roberto Raggi's avatar
Roberto Raggi committed
813 814

            break;
815
        }
Roberto Raggi's avatar
Roberto Raggi committed
816 817
    }

818
    if (functions.isEmpty()) {
Roberto Raggi's avatar
Roberto Raggi committed
819
        foreach (const TypeOfExpression::Result &p, results) {
con's avatar
con committed
820
            FullySpecifiedType ty = p.first;
Roberto Raggi's avatar
Roberto Raggi committed
821

822
            if (Function *fun = ty->asFunctionType()) {
con's avatar
con committed
823

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
                if (! fun->name())
                    continue;
                else if (! functions.isEmpty() && functions.first()->scope() != fun->scope())
                    continue; // skip fun, it's an hidden declaration.

                Name *name = fun->name();
                if (QualifiedNameId *q = fun->name()->asQualifiedNameId())
                    name = q->unqualifiedNameId();

                bool newOverload = true;

                foreach (Function *f, functions) {
                    if (fun->isEqualTo(f)) {
                        newOverload = false;
                        break;
                    }
con's avatar
con committed
840
                }
841 842 843

                if (newOverload)
                    functions.append(fun);
con's avatar
con committed
844 845
            }
        }
846

con's avatar
con committed
847 848
    }

849
    if (! functions.isEmpty()) {
850

851 852 853
        // Recreate if necessary
        if (!m_functionArgumentWidget)
            m_functionArgumentWidget = new FunctionArgumentWidget;
854

855 856 857
        m_functionArgumentWidget->showFunctionHint(functions,
                                                   typeOfExpression.lookupContext(),
                                                   m_startPosition);
858 859 860
    }

    return false;
con's avatar
con committed
861 862
}

863
bool CppCodeCompletion::completeMember(const QList<TypeOfExpression::Result> &results,
con's avatar
con committed
864 865
                                       const LookupContext &context)
{
866
    if (results.isEmpty())
Roberto Raggi's avatar
Roberto Raggi committed
867
        return false;
con's avatar
con committed
868

869
    TypeOfExpression::Result result = results.first();
con's avatar
con committed
870 871 872
    QList<Symbol *> classObjectCandidates;

    if (m_completionOperator == T_ARROW)  {
873
        FullySpecifiedType ty = result.first;
con's avatar
con committed
874 875 876 877

        if (ReferenceType *refTy = ty->asReferenceType())
            ty = refTy->elementType();

878
        if (Class *classTy = ty->asClassType()) {
879 880 881
            Symbol *symbol = result.second;
            if (symbol && ! symbol->isClass())
                classObjectCandidates.append(classTy);
882
        } else if (NamedType *namedTy = ty->asNamedType()) {
883 884 885 886 887 888 889 890 891 892 893 894
            // ### This code is pretty slow.
            const QList<Symbol *> candidates = context.resolve(namedTy->name());
            foreach (Symbol *candidate, candidates) {
                if (candidate->isTypedef()) {
                    ty = candidate->type();
                    const ResolveExpression::Result r(ty, candidate);
                    result = r;
                    break;
                }
            }
        }

con's avatar
con committed
895 896
        if (NamedType *namedTy = ty->asNamedType()) {
            ResolveExpression resolveExpression(context);
897
            ResolveClass resolveClass;
con's avatar
con committed
898

899
            const QList<Symbol *> candidates = resolveClass(result, context);
con's avatar
con committed
900 901
            foreach (Symbol *classObject, candidates) {
                const QList<TypeOfExpression::Result> overloads =
902
                        resolveExpression.resolveArrowOperator(result, namedTy,
con's avatar
con committed
903 904 905 906
                                                               classObject->asClass());

                foreach (TypeOfExpression::Result r, overloads) {
                    FullySpecifiedType ty = r.first;
907
                    Function *funTy = ty->asFunctionType();
con's avatar
con committed
908 909 910 911 912 913 914 915 916 917 918
                    if (! funTy)
                        continue;

                    ty = funTy->returnType();

                    if (ReferenceType *refTy = ty->asReferenceType())
                        ty = refTy->elementType();

                    if (PointerType *ptrTy = ty->asPointerType()) {
                        if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
                            const QList<Symbol *> classes =
919
                                    resolveClass(namedTy, result, context);
con's avatar
con committed
920 921 922 923 924 925 926 927 928 929 930

                            foreach (Symbol *c, classes) {
                                if (! classObjectCandidates.contains(c))
                                    classObjectCandidates.append(c);
                            }
                        }
                    }
                }
            }
        } else if (PointerType *ptrTy = ty->asPointerType()) {
            if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
931
                ResolveClass resolveClass;
932

933 934
                const QList<Symbol *> classes = resolveClass(namedTy, result,
                                                             context);
con's avatar
con committed
935 936 937 938 939

                foreach (Symbol *c, classes) {
                    if (! classObjectCandidates.contains(c))
                        classObjectCandidates.append(c);
                }
940
            } else if (Class *classTy = ptrTy->elementType()->asClassType()) {
941 942 943 944
                // typedef struct { int x } *Ptr;
                // Ptr p;
                // p->
                classObjectCandidates.append(classTy);
con's avatar
con committed
945 946 947
            }
        }
    } else if (m_completionOperator == T_DOT) {
948
        FullySpecifiedType ty = result.first;
con's avatar
con committed
949 950 951 952 953

        if (ReferenceType *refTy = ty->asReferenceType())
            ty = refTy->elementType();

        NamedType *namedTy = 0;
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971

        if (ArrayType *arrayTy = ty->asArrayType()) {
            // Replace . with [0]. when `ty' is an array type.
            FullySpecifiedType elementTy = arrayTy->elementType();

            if (ReferenceType *refTy = elementTy->asReferenceType())
                elementTy = refTy->elementType();

            if (elementTy->isNamedType() || elementTy->isPointerType()) {
                ty = elementTy;

                const int length = m_editor->position() - m_startPosition + 1;
                m_editor->setCurPos(m_startPosition - 1);
                m_editor->replace(length, QLatin1String("[0]."));
                m_startPosition += 3;
            }
        }

con's avatar
con committed
972
        if (PointerType *ptrTy = ty->asPointerType()) {
973 974 975 976 977 978 979 980
            if (ptrTy->elementType()->isNamedType()) {
                // Replace . with ->
                int length = m_editor->position() - m_startPosition + 1;
                m_editor->setCurPos(m_startPosition - 1);
                m_editor->replace(length, QLatin1String("->"));
                ++m_startPosition;
                namedTy = ptrTy->elementType()->asNamedType();
            }
981
        } else if (Class *classTy = ty->asClassType()) {
982 983 984
            Symbol *symbol = result.second;
            if (symbol && ! symbol->isClass())
                classObjectCandidates.append(classTy);
con's avatar
con committed
985 986 987
        } else {
            namedTy = ty->asNamedType();
            if (! namedTy) {
988
                Function *fun = ty->asFunctionType();
con's avatar
con committed
989 990 991 992 993 994
                if (fun && (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope()))
                    namedTy = fun->returnType()->asNamedType();
            }
        }

        if (namedTy) {
995
            ResolveClass resolveClass;
996 997
            const QList<Symbol *> symbols = resolveClass(namedTy, result,
                                                         context);
Roberto Raggi's avatar
Roberto Raggi committed
998 999 1000 1001 1002
            foreach (Symbol *symbol, symbols) {
                if (classObjectCandidates.contains(symbol))
                    continue;
                if (Class *klass = symbol->asClass())
                    classObjectCandidates.append(klass);
con's avatar
con committed
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
            }
        }
    }

    completeClass(classObjectCandidates, context, /*static lookup = */ false);
    if (! m_completions.isEmpty())
        return true;

    return false;
}

1014
bool CppCodeCompletion::completeScope(const QList<TypeOfExpression::Result> &results,
con's avatar
con committed
1015 1016
                                      const LookupContext &context)
{
1017
    QList<Symbol *> classes, namespaces;
1018

1019
    foreach (TypeOfExpression::Result result, results) {
1020 1021
        FullySpecifiedType ty = result.first;

1022 1023
        if (Class *classTy = ty->asClassType())
            classes.append(classTy);
con's avatar
con committed
1024

1025 1026
        else if (Namespace *namespaceTy = ty->asNamespaceType())
            namespaces.append(namespaceTy);
con's avatar
con committed
1027 1028
    }

1029 1030 1031 1032 1033 1034
    if (! classes.isEmpty())
        completeClass(classes, context);

    else if (! namespaces.isEmpty() && m_completions.isEmpty())
        completeNamespace(namespaces, context);

con's avatar
con committed
1035 1036 1037 1038 1039 1040
    return ! m_completions.isEmpty();
}

void CppCodeCompletion::addKeywords()
{
    // keyword completion items.
1041
    for (int i = T_FIRST_KEYWORD; i < T_FIRST_OBJC_AT_KEYWORD; ++i) {
con's avatar
con committed
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
        TextEditor::CompletionItem item(this);
        item.m_text = QLatin1String(Token::name(i));
        item.m_icon = m_icons.keywordIcon();
        m_completions.append(item);
    }
}

void CppCodeCompletion::addMacros(const LookupContext &context)
{
    QSet<QString> processed;
1052 1053 1054 1055
    QSet<QString> definedMacros;

    addMacros_helper(context, context.thisDocument()->fileName(),
                     &processed, &definedMacros);
con's avatar
con committed
1056

1057
    foreach (const QString &macroName, definedMacros) {
con's avatar
con committed
1058
        TextEditor::CompletionItem item(this);
1059
        item.m_text = macroName;
con's avatar
con committed
1060 1061 1062 1063 1064
        item.m_icon = m_icons.macroIcon();
        m_completions.append(item);
    }
}

1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
void CppCodeCompletion::addMacros_helper(const LookupContext &context,
                                         const QString &fileName,
                                         QSet<QString> *processed,
                                         QSet<QString> *definedMacros)
{
    Document::Ptr doc = context.document(fileName);

    if (! doc || processed->contains(doc->fileName()))
        return;

    processed->insert(doc->fileName());

    foreach (const Document::Include &i, doc->includes()) {
        addMacros_helper(context, 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);
    }
}

con's avatar
con committed
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114