cppcodecompletion.cpp 54.6 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
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
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
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"
33
#include "cpptoolsconstants.h"
Roberto Raggi's avatar
Roberto Raggi committed
34
#include "cpptoolseditorsupport.h"
con's avatar
con committed
35
36
37
38
39
40
41
42
43
44
45
46

#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
47

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

#include <coreplugin/icore.h>
57
#include <coreplugin/mimedatabase.h>
con's avatar
con committed
58
59
60
61
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/itexteditor.h>
#include <texteditor/itexteditable.h>
#include <texteditor/basetexteditor.h>
62
63
#include <projectexplorer/projectexplorer.h>
#include <utils/qtcassert.h>
con's avatar
con committed
64
65
66
67
68

#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QFile>
#include <QtGui/QAction>
69
#include <QtGui/QApplication>
70
#include <QtGui/QDesktopWidget>
con's avatar
con committed
71
72
#include <QtGui/QKeyEvent>
#include <QtGui/QLabel>
73
74
75
#include <QtGui/QStyleOption>
#include <QtGui/QStylePainter>
#include <QtGui/QTextDocument> // Qt::escape()
76
#include <QtGui/QToolButton>
con's avatar
con committed
77
78
79
80
81
82
83
#include <QtGui/QVBoxLayout>

using namespace CPlusPlus;

namespace CppTools {
namespace Internal {

84
85
86
87
88
89
90
91
class FakeToolTipFrame : public QWidget
{
public:
    FakeToolTipFrame(QWidget *parent = 0) :
        QWidget(parent, Qt::ToolTip | Qt::WindowStaysOnTopHint)
    {
        setFocusPolicy(Qt::NoFocus);
        setAttribute(Qt::WA_DeleteOnClose);
92
93
94
95
96
97
98
99

        // Set the window and button text to the tooltip text color, since this
        // widget draws the background as a tooltip.
        QPalette p = palette();
        const QColor toolTipTextColor = p.color(QPalette::Inactive, QPalette::ToolTipText);
        p.setColor(QPalette::Inactive, QPalette::WindowText, toolTipTextColor);
        p.setColor(QPalette::Inactive, QPalette::ButtonText, toolTipTextColor);
        setPalette(p);
100
101
102
103
104
105
106
    }

protected:
    void paintEvent(QPaintEvent *e);
    void resizeEvent(QResizeEvent *e);
};

107
108
class FunctionArgumentWidget : public QLabel
{
109
110
    Q_OBJECT

con's avatar
con committed
111
public:
112
    FunctionArgumentWidget();
113
114
115
    void showFunctionHint(QList<Function *> functionSymbols,
                          const LookupContext &context,
                          int startPosition);
con's avatar
con committed
116
117
118
119

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

120
121
122
123
private slots:
    void nextPage();
    void previousPage();

con's avatar
con committed
124
private:
125
    void updateArgumentHighlight();
con's avatar
con committed
126
127
    void updateHintText();

128
129
130
    Function *currentFunction() const
    { return m_items.at(m_current); }

con's avatar
con committed
131
132
    int m_startpos;
    int m_currentarg;
133
    int m_current;
134
    bool m_escapePressed;
con's avatar
con committed
135
136
137

    TextEditor::ITextEditor *m_editor;

138
    QWidget *m_pager;
139
    QLabel *m_numberLabel;
140
    FakeToolTipFrame *m_popupFrame;
141
    QList<Function *> m_items;
142
    LookupContext m_context;
con's avatar
con committed
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
};

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)
175
            _item.data = QVariant::fromValue(symbol);
con's avatar
con committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
        (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;
    }

Roberto Raggi's avatar
Roberto Raggi committed
195
    TextEditor::CompletionItem newCompletionItem(const Name *name)
con's avatar
con committed
196
197
    {
        TextEditor::CompletionItem item(_collector);
198
199
        item.text = overview.prettyName(name);
        item.icon = _collector->iconForSymbol(_symbol);
con's avatar
con committed
200
201
202
        return item;
    }

Roberto Raggi's avatar
Roberto Raggi committed
203
    virtual void visit(const NameId *name)
con's avatar
con committed
204
205
    { _item = newCompletionItem(name); }

Roberto Raggi's avatar
Roberto Raggi committed
206
    virtual void visit(const TemplateNameId *name)
con's avatar
con committed
207
208
    {
        _item = newCompletionItem(name);
209
        _item.text = QLatin1String(name->identifier()->chars());
con's avatar
con committed
210
211
    }

Roberto Raggi's avatar
Roberto Raggi committed
212
    virtual void visit(const DestructorNameId *name)
con's avatar
con committed
213
214
    { _item = newCompletionItem(name); }

Roberto Raggi's avatar
Roberto Raggi committed
215
    virtual void visit(const OperatorNameId *name)
con's avatar
con committed
216
217
    { _item = newCompletionItem(name); }

Roberto Raggi's avatar
Roberto Raggi committed
218
    virtual void visit(const ConversionNameId *name)
con's avatar
con committed
219
220
    { _item = newCompletionItem(name); }

Roberto Raggi's avatar
Roberto Raggi committed
221
    virtual void visit(const QualifiedNameId *name)
con's avatar
con committed
222
223
224
    { _item = newCompletionItem(name->unqualifiedNameId()); }
};

225
226
227
228
229
230
231
232
struct CompleteFunctionDeclaration
{
    explicit CompleteFunctionDeclaration(Function *f = 0)
        : function(f)
    {}

    Function *function;
};
con's avatar
con committed
233
234
235
236
237
238

} // namespace Internal
} // namespace CppTools

using namespace CppTools::Internal;

239
Q_DECLARE_METATYPE(CompleteFunctionDeclaration)
240

241
void FakeToolTipFrame::paintEvent(QPaintEvent *)
242
243
244
245
246
247
248
249
{
    QStylePainter p(this);
    QStyleOptionFrame opt;
    opt.init(this);
    p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
    p.end();
}

250
void FakeToolTipFrame::resizeEvent(QResizeEvent *)
251
252
253
254
255
256
257
258
259
{
    QStyleHintReturnMask frameMask;
    QStyleOption option;
    option.init(this);
    if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
        setMask(frameMask.region);
}


260
FunctionArgumentWidget::FunctionArgumentWidget():
261
    m_startpos(-1),
262
263
    m_current(0),
    m_escapePressed(false)
con's avatar
con committed
264
{
265
    QObject *editorObject = Core::EditorManager::instance()->currentEditor();
con's avatar
con committed
266
267
    m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject);

268
    m_popupFrame = new FakeToolTipFrame(m_editor->widget());
con's avatar
con committed
269

270
271
272
273
    QToolButton *downArrow = new QToolButton;
    downArrow->setArrowType(Qt::DownArrow);
    downArrow->setFixedSize(16, 16);
    downArrow->setAutoRaise(true);
274

275
276
277
278
    QToolButton *upArrow = new QToolButton;
    upArrow->setArrowType(Qt::UpArrow);
    upArrow->setFixedSize(16, 16);
    upArrow->setAutoRaise(true);
279

con's avatar
con committed
280
281
282
    setParent(m_popupFrame);
    setFocusPolicy(Qt::NoFocus);

283
284
285
286
    m_pager = new QWidget;
    QHBoxLayout *hbox = new QHBoxLayout(m_pager);
    hbox->setMargin(0);
    hbox->setSpacing(0);
287
    hbox->addWidget(upArrow);
288
289
    m_numberLabel = new QLabel;
    hbox->addWidget(m_numberLabel);
290
    hbox->addWidget(downArrow);
291

292
    QHBoxLayout *layout = new QHBoxLayout;
con's avatar
con committed
293
    layout->setMargin(0);
294
295
296
    layout->setSpacing(0);
    layout->addWidget(m_pager);
    layout->addWidget(this);
con's avatar
con committed
297
298
    m_popupFrame->setLayout(layout);

299
300
    connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
    connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
301

con's avatar
con committed
302
303
    setTextFormat(Qt::RichText);
    setMargin(1);
304
305

    qApp->installEventFilter(this);
con's avatar
con committed
306
307
}

308
void FunctionArgumentWidget::showFunctionHint(QList<Function *> functionSymbols,
309
310
                                              const LookupContext &context,
                                              int startPosition)
con's avatar
con committed
311
{
312
313
    Q_ASSERT(!functionSymbols.isEmpty());

314
315
316
    if (m_startpos == startPosition)
        return;

317
    m_pager->setVisible(functionSymbols.size() > 1);
318

319
    m_items = functionSymbols;
320
    m_context = context;
321
    m_startpos = startPosition;
322
    m_current = 0;
323
    m_escapePressed = false;
con's avatar
con committed
324
325
326

    // update the text
    m_currentarg = -1;
327
    updateArgumentHighlight();
328

con's avatar
con committed
329
330
331
    m_popupFrame->show();
}

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
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();
}

348
void FunctionArgumentWidget::updateArgumentHighlight()
con's avatar
con committed
349
350
351
{
    int curpos = m_editor->position();
    if (curpos < m_startpos) {
352
        m_popupFrame->close();
con's avatar
con committed
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
        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)
377
        m_popupFrame->close();
con's avatar
con committed
378
379
380
381
382
}

bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e)
{
    switch (e->type()) {
383
384
385
386
387
    case QEvent::ShortcutOverride:
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
            m_escapePressed = true;
        }
        break;
388
    case QEvent::KeyPress:
389
390
391
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
            m_escapePressed = true;
        }
392
393
394
395
396
397
398
399
        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
400
            }
401
402
403
404
            return false;
        }
        break;
    case QEvent::KeyRelease:
405
        if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_escapePressed) {
406
407
            m_popupFrame->close();
            return false;
con's avatar
con committed
408
        }
409
410
        updateArgumentHighlight();
        break;
con's avatar
con committed
411
412
    case QEvent::WindowDeactivate:
    case QEvent::FocusOut:
413
414
415
        if (obj != m_editor->widget())
            break;
        m_popupFrame->close();
416
        break;
con's avatar
con committed
417
418
419
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
420
421
422
    case QEvent::Wheel: {
            QWidget *widget = qobject_cast<QWidget *>(obj);
            if (! (widget == this || m_popupFrame->isAncestorOf(widget))) {
423
                m_popupFrame->close();
424
425
            }
        }
con's avatar
con committed
426
427
428
429
430
431
432
433
434
435
436
437
        break;
    default:
        break;
    }
    return false;
}

void FunctionArgumentWidget::updateHintText()
{
    Overview overview;
    overview.setShowReturnTypes(true);
    overview.setShowArgumentNames(true);
438
    overview.setMarkedArgument(m_currentarg + 1);
439
    Function *f = currentFunction();
440

441
442
443
444
445
446
447
448
449
450
451
452
    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);

453
454
455
    m_numberLabel->setText(tr("%1 of %2").arg(m_current + 1).arg(m_items.size()));

    m_popupFrame->setFixedWidth(m_popupFrame->minimumSizeHint().width());
456
457

    const QDesktopWidget *desktop = QApplication::desktop();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
458
#ifdef Q_WS_MAC
459
    const QRect screen = desktop->availableGeometry(desktop->screenNumber(m_editor->widget()));
460
#else
461
    const QRect screen = desktop->screenGeometry(desktop->screenNumber(m_editor->widget()));
462
463
464
465
466
467
468
469
470
471
#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
472
473
}

474
CppCodeCompletion::CppCodeCompletion(CppModelManager *manager)
con's avatar
con committed
475
476
    : ICompletionCollector(manager),
      m_manager(manager),
477
478
      m_editor(0),
      m_startPosition(-1),
479
      m_caseSensitivity(FirstLetterCaseSensitive),
Roberto Raggi's avatar
Roberto Raggi committed
480
      m_autoInsertBrackets(true),
481
      m_partialCompletionEnabled(true),
con's avatar
con committed
482
      m_forcedCompletion(false),
483
484
      m_completionOperator(T_EOF_SYMBOL),
      m_objcEnabled(true)
485
486
{
}
con's avatar
con committed
487
488

QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const
489
490
491
492
{
    return m_icons.iconForSymbol(symbol);
}

493
CppCodeCompletion::CaseSensitivity CppCodeCompletion::caseSensitivity() const
494
495
496
497
{
    return m_caseSensitivity;
}

498
void CppCodeCompletion::setCaseSensitivity(CaseSensitivity caseSensitivity)
499
500
501
502
{
    m_caseSensitivity = caseSensitivity;
}

503
bool CppCodeCompletion::autoInsertBrackets() const
504
{
Roberto Raggi's avatar
Roberto Raggi committed
505
    return m_autoInsertBrackets;
506
507
}

508
void CppCodeCompletion::setAutoInsertBrackets(bool autoInsertBrackets)
509
{
Roberto Raggi's avatar
Roberto Raggi committed
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
  Searches backwards for an access operator.
con's avatar
con committed
525
526
527
528
529
530
531
532
533
534
*/
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 completionKind = T_EOF_SYMBOL;
con's avatar
con committed
536

537
538
539
    switch (ch.toLatin1()) {
    case '.':
        if (ch2 != QLatin1Char('.')) {
540
            completionKind = T_DOT;
541
542
543
544
            --start;
        }
        break;
    case ',':
545
        completionKind = T_COMMA;
546
        --start;
547
548
549
        break;
    case '(':
        if (wantFunctionCall) {
550
            completionKind = T_LPAREN;
551
552
553
554
555
            --start;
        }
        break;
    case ':':
        if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
556
            completionKind = T_COLON_COLON;
557
558
559
560
561
            start -= 2;
        }
        break;
    case '>':
        if (ch2 == QLatin1Char('-')) {
562
            completionKind = T_ARROW;
563
564
565
566
567
            start -= 2;
        }
        break;
    case '*':
        if (ch2 == QLatin1Char('.')) {
568
            completionKind = T_DOT_STAR;
569
570
            start -= 2;
        } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) {
571
            completionKind = T_ARROW_STAR;
572
573
574
575
576
577
            start -= 3;
        }
        break;
    case '\\':
    case '@':
        if (ch2.isNull() || ch2.isSpace()) {
578
            completionKind = T_DOXY_COMMENT;
579
580
581
582
            --start;
        }
        break;
    case '<':
583
        completionKind = T_ANGLE_STRING_LITERAL;
584
        --start;
585
586
        break;
    case '"':
587
        completionKind = T_STRING_LITERAL;
588
        --start;
589
590
        break;
    case '/':
591
        completionKind = T_SLASH;
592
        --start;
593
        break;
con's avatar
con committed
594
595
    }

596
597
598
599
600
601
602
    if (start == pos)
        return start;

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

603
    // Include completion: make sure the quote character is the first one on the line
604
    if (completionKind == T_STRING_LITERAL) {
605
606
607
608
        QTextCursor s = tc;
        s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
        QString sel = s.selectedText();
        if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) {
609
            completionKind = T_EOF_SYMBOL;
610
611
612
613
            start = pos;
        }
    }

614
    if (completionKind == T_COMMA) {
615
616
        ExpressionUnderCursor expressionUnderCursor;
        if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
617
            completionKind = T_EOF_SYMBOL;
618
619
620
621
            start = pos;
        }
    }

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

625
626
    if (completionKind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
        completionKind = T_EOF_SYMBOL;
627
        start = pos;
con's avatar
con committed
628
    }
629
    // Don't complete in comments or strings, but still check for include completion
630
631
632
633
634
    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;
635
636
637
        start = pos;
    }
    // Include completion: can be triggered by slash, but only in a string
638
639
    else if (completionKind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
        completionKind = T_EOF_SYMBOL;
640
641
        start = pos;
    }
642
    else if (completionKind == T_LPAREN) {
643
644
645
646
647
648
649
650
        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);
651
652
                if (previousToken.is(T_IDENTIFIER) || previousToken.is(T_GREATER)
                    || previousToken.is(T_SIGNAL) || previousToken.is(T_SLOT))
653
654
655
656
657
                    break;
            }
        }

        if (i == tokens.size()) {
658
            completionKind = T_EOF_SYMBOL;
659
660
661
            start = pos;
        }
    }
662
    // Check for include preprocessor directive
663
    else if (completionKind == T_STRING_LITERAL || completionKind == T_ANGLE_STRING_LITERAL || completionKind == T_SLASH) {
664
        bool include = false;
665
666
667
668
669
670
671
672
673
        const QList<SimpleToken> &tokens = tokenUnderCursor.tokens();
        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))) {
                QStringRef directive = tokens.at(1).text();
                if (directive == QLatin1String("include") ||
                    directive == QLatin1String("include_next") ||
                    directive == QLatin1String("import")) {
                    include = true;
674
675
676
677
678
                }
            }
        }

        if (!include) {
679
            completionKind = T_EOF_SYMBOL;
680
681
682
683
            start = pos;
        }
    }

684
    if (kind)
685
        *kind = completionKind;
686

con's avatar
con committed
687
688
689
    return start;
}

690
bool CppCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
691
692
{ return m_manager->isCppEditor(editor); }

693
694
695
696
697
698
TextEditor::ITextEditable *CppCodeCompletion::editor() const
{ return m_editor; }

int CppCodeCompletion::startPosition() const
{ return m_startPosition; }

con's avatar
con committed
699
700
701
bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
{
    const int pos = editor->position();
702
    if (startOfOperator(editor, pos, /*token =*/ 0, /*want function call=*/ true) != pos) {
con's avatar
con committed
703
        return true;
704
    }
con's avatar
con committed
705
706
707
708
709
710
711
712
713
714
715

    return false;
}

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

    m_editor = editor;
716

717
718
    const int startOfName = findStartOfName();
    m_startPosition = startOfName;
con's avatar
con committed
719
720
    m_completionOperator = T_EOF_SYMBOL;

721
    int endOfOperator = m_startPosition;
con's avatar
con committed
722
723

    // Skip whitespace preceding this position
724
725
    while (editor->characterAt(endOfOperator - 1).isSpace())
        --endOfOperator;
con's avatar
con committed
726

727
728
729
    int endOfExpression = startOfOperator(editor, endOfOperator,
                                          &m_completionOperator,
                                          /*want function call =*/ true);
con's avatar
con committed
730
731
732
733
734
735
736
737

    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;

738
739
740
    if (m_completionOperator == T_DOXY_COMMENT) {
        for (int i = 1; i < T_DOXY_LAST_TAG; ++i) {
            TextEditor::CompletionItem item(this);
741
742
            item.text.append(QString::fromLatin1(doxygenTagSpell(i)));
            item.icon = m_icons.keywordIcon();
743
744
745
746
747
748
            m_completions.append(item);
        }

        return m_startPosition;
    }

749
750
751
752
753
754
755
756
757
758
759
760
    // 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;
    }

761
762
    ExpressionUnderCursor expressionUnderCursor;
    QTextCursor tc(edit->document());
763

764
    if (m_completionOperator == T_COMMA) {
con's avatar
con committed
765
        tc.setPosition(endOfExpression);
766
        const int start = expressionUnderCursor.startOfFunctionCall(tc);
767
768
769
        if (start == -1) {
            m_completionOperator = T_EOF_SYMBOL;
            return -1;
770
        }
771
772
773
774

        endOfExpression = start;
        m_startPosition = start + 1;
        m_completionOperator = T_LPAREN;
775
776
777
778
    }

    QString expression;
    tc.setPosition(endOfExpression);
779

780
    if (m_completionOperator) {
con's avatar
con committed
781
        expression = expressionUnderCursor(tc);
782

con's avatar
con committed
783
784
785
        if (m_completionOperator == T_LPAREN) {
            if (expression.endsWith(QLatin1String("SIGNAL")))
                m_completionOperator = T_SIGNAL;
786

con's avatar
con committed
787
788
            else if (expression.endsWith(QLatin1String("SLOT")))
                m_completionOperator = T_SLOT;
789

790
791
792
793
            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;
794
                m_startPosition = startOfName;
795
            }
con's avatar
con committed
796
797
798
        }
    }

799
    //qDebug() << "***** expression:" << expression;
800
801
    return startCompletionInternal(edit, fileName, line, column, expression, endOfExpression);
}
con's avatar
con committed
802

803
804
805
806
807
808
809
int CppCodeCompletion::startCompletionInternal(TextEditor::BaseTextEditor *edit,
                                               const QString fileName,
                                               unsigned line, unsigned column,
                                               const QString &expr,
                                               int endOfExpression)
{
    QString expression = expr.trimmed();
810
811
    const Snapshot snapshot = m_manager->snapshot();

812
813
814
    Document::Ptr thisDocument = snapshot.document(fileName);
    if (! thisDocument)
        return -1;
con's avatar
con committed
815

816
    typeOfExpression.setSnapshot(snapshot);
817
    Symbol *lastVisibleSymbol = thisDocument->findSymbolAt(line, column);
con's avatar
con committed
818

819
820
821
    if (expression.isEmpty()) {
        if (m_completionOperator == T_EOF_SYMBOL || m_completionOperator == T_COLON_COLON)
            return globalCompletion(lastVisibleSymbol, thisDocument, snapshot);
con's avatar
con committed
822

823
        else if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
con's avatar
con committed
824
825
826
            // Apply signal/slot completion on 'this'
            expression = QLatin1String("this");
        }
827
    }
con's avatar
con committed
828

Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
829

Erik Verbruggen's avatar
Erik Verbruggen committed
830
    QList<LookupItem> results = typeOfExpression(expression, thisDocument, lastVisibleSymbol, TypeOfExpression::Preprocess);
831
    LookupContext context = typeOfExpression.lookupContext();
Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
832

833
834
835
836
837
838
    if (results.isEmpty()) {
        if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
            if (! (expression.isEmpty() || expression == QLatin1String("this"))) {
                expression = QLatin1String("this");
                results = typeOfExpression(expression, thisDocument, lastVisibleSymbol);
            }
Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
839

840
841
            if (results.isEmpty())
                return -1;
Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
842

843
            context = typeOfExpression.lookupContext();
844

845
        } else if (m_completionOperator == T_LPAREN) {
846
847
848
849
850
851
852
853
            // 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);
854

855
            ExpressionUnderCursor expressionUnderCursor;
856
            const QString baseExpression = expressionUnderCursor(tc);
857
858

            // Resolve the type of this expression
859
            const QList<LookupItem> results =
860
861
862
                    typeOfExpression(baseExpression, thisDocument,
                                     lastVisibleSymbol,
                                     TypeOfExpression::Preprocess);
863
864

            // If it's a class, add completions for the constructors
865
866
            foreach (const LookupItem &result, results) {
                if (result.type()->isClassType()) {
867
                    if (completeConstructorOrFunction(results, context, endOfExpression, true))
868
                        return m_startPosition;
869

870
871
872
                    break;
                }
            }
873
874
875
876
877
878
            return -1;

        } else {
            // nothing to do.
            return -1;

879
        }
con's avatar
con committed
880
881
    }

882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
    switch (m_completionOperator) {
    case T_LPAREN:
        if (completeConstructorOrFunction(results, context, endOfExpression, false))
            return m_startPosition;
        break;

    case T_DOT:
    case T_ARROW:
        if (completeMember(results, context))
            return m_startPosition;
        break;

    case T_COLON_COLON:
        if (completeScope(results, context))
            return m_startPosition;
        break;

    case T_SIGNAL:
        if (completeSignal(results, context))
            return m_startPosition;
        break;

    case T_SLOT:
        if (completeSlot(results, context))
            return m_startPosition;
        break;

    default:
        break;
    } // end of switch

con's avatar
con committed
913
914
915
916
    // nothing to do.
    return -1;
}

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
int CppCodeCompletion::globalCompletion(Symbol *lastVisibleSymbol,
                                        Document::Ptr thisDocument,
                                        const Snapshot &snapshot)
{
    if (m_completionOperator == T_EOF_SYMBOL) {
        addKeywords();
        addMacros(thisDocument->fileName(), snapshot);
    }

    Document::Ptr exprDoc = Document::create(QLatin1String("<expression>"));
    const LookupContext context(lastVisibleSymbol, exprDoc, thisDocument, snapshot);
    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;
}

939
bool CppCodeCompletion::completeConstructorOrFunction(const QList<LookupItem> &results,
940
                                                      const LookupContext &context,
941
                                                      int endOfExpression, bool toolTipOnly)
con's avatar
con committed
942
{
943
944
    QList<Function *> functions;

945
946
    foreach (const LookupItem &result, results) {
        FullySpecifiedType exprTy = result.type().simplified();
Roberto Raggi's avatar
Roberto Raggi committed
947
948

        if (Class *klass = exprTy->asClassType()) {
Roberto Raggi's avatar
Roberto Raggi committed
949
            const Name *className = klass->name();
950
951
952
            if (! className)
                continue; // nothing to do for anonymoous classes.

Roberto Raggi's avatar
Roberto Raggi committed
953
954
            for (unsigned i = 0; i < klass->memberCount(); ++i) {
                Symbol *member = klass->memberAt(i);
Roberto Raggi's avatar
Roberto Raggi committed
955
                const Name *memberName = member->name();
956
957
958
959
960
961
962
963
964
965
966
967

                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
968
                }
969
            }
Roberto Raggi's avatar
Roberto Raggi committed
970
971

            break;
972
        }
Roberto Raggi's avatar
Roberto Raggi committed
973
974
    }

975
    if (functions.isEmpty()) {
976
977
        foreach (const LookupItem &result, results) {
            FullySpecifiedType ty = result.type().simplified();
Roberto Raggi's avatar
Roberto Raggi committed
978

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

981
982
983
984
985
986
987
988
989
990
991
992
                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;
                    }
con's avatar
con committed
993
                }
994
995
996

                if (newOverload)
                    functions.append(fun);
con's avatar
con committed
997
998
            }
        }
999
1000
1001
1002
1003
    }

    if (functions.isEmpty()) {
        ResolveExpression resolveExpression(context);
        ResolveClass resolveClass;
Roberto Raggi's avatar
Roberto Raggi committed
1004
        const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
1005

1006
1007
        foreach (const LookupItem &result, results) {
            FullySpecifiedType ty = result.type().simplified();
1008
1009
1010

            if (NamedType *namedTy = ty->asNamedType()) {
                const QList<Symbol *> classObjectCandidates = resolveClass(namedTy->name(), result, context);
1011

1012
1013
                foreach (Symbol *classObjectCandidate, classObjectCandidates) {
                    if (Class *klass = classObjectCandidate->asClass()) {
1014
                        const QList<LookupItem> overloads =
1015
1016
1017
                                resolveExpression.resolveMember(functionCallOp, klass,
                                                                namedTy->name());

1018
1019
                        foreach (const LookupItem &overloadResult, overloads) {
                            FullySpecifiedType overloadTy = overloadResult.type().simplified();
1020
1021
1022
1023
1024
1025
1026
1027

                            if (Function *funTy = overloadTy->asFunctionType())
                                functions.append(funTy);
                        }
                    }
                }
            }
        }
con's avatar
con committed
1028
1029
    }

1030
1031
1032
1033
1034
    // 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.
1035

1036
1037
1038
1039
1040
    // 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.
1041
1042
1043
1044
1045
1046
1047
1048
1049

        // 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
1050
1051
1052
1053
1054
1055
1056
        Scope *sc = 0;
        if (context.symbol())
            sc = context.symbol()->scope();
        else if (context.thisDocument())
            sc = context.thisDocument()->globalSymbols();

        while (sc && sc->enclosingScope()) {
1057
1058
1059
1060
1061
            unsigned startLine, startColumn;
            context.thisDocument()->translationUnit()->getPosition(sc->owner()->startOffset(), &startLine, &startColumn);
            unsigned endLine, endColumn;
            context.thisDocument()->translationUnit()->getPosition(sc->owner()->endOffset(), &endLine, &endColumn);

1062
            if (startLine <= line && line <= endLine) {
1063
1064
1065
                if ((startLine != line || startColumn <= column)
                    && (endLine != line || column <= endColumn))
                    break;
1066
            }
1067
1068
1069
1070

            sc = sc->enclosingScope();
        }

1071
        if (sc && (sc->isClassScope() || sc->isNamespaceScope())) {
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
            // 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);
            BackwardsScanner bs(tc);
            QString possibleDecl = bs.mid(bs.startOfLine(bs.startToken())).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()) {
1086
1087
                    if (sd->declarator_list &&
                        sd->declarator_list && sd->declarator_list->value->postfix_declarator_list
Roberto Raggi's avatar
Roberto Raggi committed
1088
                        && sd->declarator_list->value->postfix_declarator_list->value->asFunctionDeclarator()) {
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
                        autocompleteSignature = true;
                    }
                }
            }

            if (autocompleteSignature) {
                // set up signature autocompletion
                foreach (Function *f, functions) {
                    Overview overview;
                    overview.setShowArgumentNames(true);
1099
                    overview.setShowDefaultArguments(false);
1100

1101
1102
1103
1104
1105
1106
1107
                    // 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);
1108
1109
1110
1111
                }
                return true;
            }
        }
1112
    }
1113

1114
    if (! functions.empty()) {
1115
        // set up function call tooltip
1116

1117
1118
1119
        // Recreate if necessary
        if (!m_functionArgumentWidget)
            m_functionArgumentWidget = new FunctionArgumentWidget;
1120

1121
1122
1123
        m_functionArgumentWidget->showFunctionHint(functions,
                                                   typeOfExpression.lookupContext(),
                                                   m_startPosition);
1124
1125
1126
    }

    return false;
con's avatar
con committed
1127
1128
}

1129
bool CppCodeCompletion::completeMember(const QList<LookupItem> &baseResults,