3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #define QT_NO_DEBUG_OUTPUT
23 #include "autocompleter.hpp"
26 #include "../util/popup_widget.hpp"
27 #include "../../core/sc_introspection.hpp"
28 #include "../../core/sc_process.hpp"
29 #include "../../core/main.hpp"
31 #include "yaml-cpp/node.h"
32 #include "yaml-cpp/parser.h"
37 #include <QStandardItemModel>
38 #include <QStandardItem>
39 #include <QHBoxLayout>
40 #include <QApplication>
48 static bool tokenMaybeName( Token::Type type
)
50 return (type
== Token::Name
|| type
== Token::Keyword
|| type
== Token::Builtin
);
53 static QString
incrementedString( const QString
& other
)
59 int pos
= str
.length()-1;
60 str
[pos
] = QChar( str
[pos
].unicode() + 1 );
64 class CompletionMenu
: public PopUpWidget
68 CompletionRole
= Qt::UserRole
,
72 CompletionMenu( QWidget
* parent
= 0 ):
74 mCompletionRole( Qt::DisplayRole
)
76 mModel
= new QStandardItemModel(this);
77 mFilterModel
= new QSortFilterProxyModel(this);
78 mFilterModel
->setSourceModel(mModel
);
80 mListView
= new QListView();
81 mListView
->setModel(mFilterModel
);
82 mListView
->setFrameShape(QFrame::NoFrame
);
84 QHBoxLayout
*layout
= new QHBoxLayout(this);
85 layout
->addWidget(mListView
);
86 layout
->setContentsMargins(1,1,1,1);
88 connect(mListView
, SIGNAL(clicked(QModelIndex
)), this, SLOT(accept()));
90 mListView
->setFocus(Qt::OtherFocusReason
);
94 parent
->installEventFilter(this);
97 void addItem( QStandardItem
* item
)
99 mModel
->appendRow( item
);
102 void setCompletionRole( int role
)
104 mFilterModel
->setFilterRole(role
);
105 mFilterModel
->setSortRole(role
);
106 mCompletionRole
= role
;
109 QString
currentText()
111 QStandardItem
*item
=
112 mModel
->itemFromIndex (
113 mFilterModel
->mapToSource (
114 mListView
->currentIndex()));
116 return item
->data(mCompletionRole
).toString();
121 const ScLanguage::Method
* currentMethod()
123 QStandardItem
*item
=
124 mModel
->itemFromIndex (
125 mFilterModel
->mapToSource (
126 mListView
->currentIndex()));
128 return item
? item
->data(MethodRole
).value
<const ScLanguage::Method
*>() : 0;
131 QString
exec( const QPoint
& pos
)
134 QPointer
<CompletionMenu
> self
= this;
135 if (PopUpWidget::exec(pos
)) {
137 result
= currentText();
142 QSortFilterProxyModel
*model() { return mFilterModel
; }
144 QListView
*view() { return mListView
; }
147 virtual bool eventFilter( QObject
* obj
, QEvent
* ev
)
149 if (isVisible() && obj
== parentWidget() && ev
->type() == QEvent::KeyPress
)
151 QKeyEvent
*kev
= static_cast<QKeyEvent
*>(ev
);
157 case Qt::Key_PageDown
:
158 QApplication::sendEvent( mListView
, ev
);
167 return PopUpWidget::eventFilter(obj
, ev
);
171 QListView
*mListView
;
172 QStandardItemModel
*mModel
;
173 QSortFilterProxyModel
*mFilterModel
;
177 class MethodCallWidget
: public QWidget
180 MethodCallWidget( QWidget
* parent
= 0 ):
181 QWidget( parent
, Qt::ToolTip
)
183 mLabel
= new QLabel();
184 mLabel
->setTextFormat( Qt::RichText
);
187 if (qobject_cast
<QGtkStyle
*>(style()) != 0) {
189 p
.setColor( QPalette::Window
, QColor(255, 255, 220) );
190 p
.setColor( QPalette::WindowText
, Qt::black
);
196 QPalette
p( palette() );
197 p
.setColor( QPalette::Window
, p
.color(QPalette::ToolTipBase
) );
199 mLabel
->setForegroundRole(QPalette::ToolTipText
);
202 QHBoxLayout
*box
= new QHBoxLayout
;
203 box
->setContentsMargins(5,2,5,2);
204 box
->addWidget(mLabel
);
208 void showMethod( const ScLanguage::Method
* method
, int argNum
)
211 int argc
= method
->arguments
.count();
212 for (int i
= 0; i
< argc
; ++i
)
214 const ScLanguage::Argument
& arg
= method
->arguments
[i
];
219 //"text-decoration: underline;"
226 QString val
= arg
.defaultValue
;
234 text
+= ", ";
236 mLabel
->setText(text
);
243 AutoCompleter::AutoCompleter( CodeEditor
*editor
):
247 mCompletion
.on
= false;
248 mEditor
->installEventFilter(this);
250 connect(editor
, SIGNAL(cursorPositionChanged()),
251 this, SLOT(onCursorChanged()));
254 void AutoCompleter::documentChanged( QTextDocument
* doc
)
256 connect(doc
, SIGNAL(contentsChange(int,int,int)),
257 this, SLOT(onContentsChange(int,int,int)));
260 inline QTextDocument
*AutoCompleter::document()
262 return static_cast<QPlainTextEdit
*>(mEditor
)->document();
265 void AutoCompleter::keyPress( QKeyEvent
*e
)
270 case Qt::Key_ParenLeft
:
272 triggerMethodCallAid(false);
274 case Qt::Key_Backspace
:
279 // Only trigger completion if event produces at least 1 printable character:
280 if (!mCompletion
.on
&& !e
->text().isEmpty() && e
->text()[0].isPrint() )
285 bool AutoCompleter::eventFilter( QObject
*object
, QEvent
*event
)
287 if (object
!= mEditor
)
290 switch(event
->type()) {
291 case QEvent::FocusOut
:
292 if (mCompletion
.menu
)
293 mCompletion
.menu
->reject();
294 if (mMethodCall
.menu
)
295 mMethodCall
.menu
->reject();
296 if (mMethodCall
.widget
)
297 mMethodCall
.widget
->hide();
299 case QEvent::ShortcutOverride
: {
300 QKeyEvent
* kevent
= static_cast<QKeyEvent
*>(event
);
301 if (kevent
->key() == Qt::Key_Escape
) {
302 if (mCompletion
.menu
&& mCompletion
.menu
->isVisible())
303 mCompletion
.menu
->reject();
304 else if (mMethodCall
.menu
&& mMethodCall
.menu
->isVisible())
305 mMethodCall
.menu
->reject();
306 else if (mMethodCall
.widget
&& mMethodCall
.widget
->isVisible()) {
307 // disable method call aid for current call:
308 Q_ASSERT(!mMethodCall
.stack
.isEmpty());
309 mMethodCall
.stack
.top().method
= 0;
320 return QObject::eventFilter(object
, event
);
323 void AutoCompleter::onContentsChange( int pos
, int removed
, int added
)
325 qDebug(">>> contentsChange");
327 while (!mMethodCall
.stack
.isEmpty())
329 MethodCall
& call
= mMethodCall
.stack
.top();
330 if (pos
> call
.position
)
333 qDebug("Method call: change before method call. popping.");
334 mMethodCall
.stack
.pop();
340 if(pos
< mCompletion
.contextPos
)
342 quitCompletion("context changed");
344 else if(pos
<= mCompletion
.pos
+ mCompletion
.len
)
346 QTextBlock
block( document()->findBlock(mCompletion
.pos
) );
347 TokenIterator
it( block
, mCompletion
.pos
- block
.position() );
348 Token::Type type
= it
.type();
349 if (type
== Token::Class
|| tokenMaybeName(type
)) {
350 mCompletion
.len
= it
->length
;
351 mCompletion
.text
= tokenText(it
);
355 mCompletion
.text
.clear();
357 if (!mCompletion
.menu
.isNull())
358 updateCompletionMenu(false);
363 void AutoCompleter::onCursorChanged()
365 qDebug(">>> cursorChanged");
366 int cursorPos
= mEditor
->textCursor().position();
369 if (mCompletion
.on
) {
370 if (cursorPos
< mCompletion
.pos
||
371 cursorPos
> mCompletion
.pos
+ mCompletion
.len
)
373 quitCompletion("out of bounds");
377 if (!mMethodCall
.menu
.isNull()) {
378 qDebug("Method call: quitting menu");
379 delete mMethodCall
.menu
;
382 updateMethodCall(cursorPos
);
385 void AutoCompleter::triggerCompletion(bool forceShow
)
387 if (mCompletion
.on
) {
388 qDebug("AutoCompleter::triggerCompletion(): completion already started.");
389 updateCompletionMenu(forceShow
);
393 QTextCursor
cursor( mEditor
->textCursor() );
394 const int cursorPos
= cursor
.positionInBlock();
395 QTextBlock
block( cursor
.block() );
396 TokenIterator
it( block
, cursorPos
- 1 );
401 const Token
& triggeringToken
= *it
;
403 if (triggeringToken
.type
== Token::Class
)
405 if (triggeringToken
.length
< 3)
407 mCompletion
.type
= ClassCompletion
;
408 mCompletion
.pos
= it
.position();
409 mCompletion
.len
= it
->length
;
410 mCompletion
.text
= tokenText(it
);
411 mCompletion
.contextPos
= mCompletion
.pos
+ 3;
412 mCompletion
.base
= mCompletion
.text
;
413 mCompletion
.base
.truncate(3);
416 TokenIterator objectIt
, dotIt
, nameIt
;
418 Token::Type objectTokenType
= Token::Unknown
;
420 if (tokenMaybeName(it
.type())) {
425 if (it
.isValid() && it
.character() == '.') {
430 // don't trigger on method names without preceding dot (for now)
433 if (dotIt
.isValid()) {
434 objectTokenType
= it
.type();
435 switch (objectTokenType
) {
442 case Token::RadixFloat
:
451 if (!objectIt
.isValid() && (!nameIt
.isValid() || nameIt
->length
< 3))
454 if (nameIt
.isValid()) {
455 mCompletion
.pos
= nameIt
.position();
456 mCompletion
.len
= nameIt
->length
;
457 mCompletion
.text
= tokenText(nameIt
);
459 mCompletion
.pos
= dotIt
.position() + 1;
461 mCompletion
.text
.clear();
464 if (objectIt
.isValid()) {
465 mCompletion
.contextPos
= mCompletion
.pos
;
466 mCompletion
.base
= tokenText(objectIt
);
467 mCompletion
.tokenType
= objectTokenType
;
468 mCompletion
.type
= ClassMethodCompletion
;
471 mCompletion
.contextPos
= mCompletion
.pos
+ 3;
472 mCompletion
.base
= tokenText(nameIt
);
473 mCompletion
.type
= MethodCompletion
;
477 mCompletion
.on
= true;
479 qDebug() << QString("Completion: ON <%1>").arg(mCompletion
.base
);
481 showCompletionMenu(forceShow
);
483 if (mCompletion
.menu
.isNull())
484 mCompletion
.on
= false;
487 void AutoCompleter::quitCompletion( const QString
& reason
)
489 Q_ASSERT(mCompletion
.on
);
491 qDebug() << QString("Completion: OFF (%1)").arg(reason
);
493 if (mCompletion
.menu
) {
494 mCompletion
.menu
->hide();
495 mCompletion
.menu
->deleteLater();
496 mCompletion
.menu
= 0;
499 mCompletion
.on
= false;
502 void AutoCompleter::showCompletionMenu(bool forceShow
)
504 qDebug(">>> showCompletionMenu");
506 using namespace ScLanguage
;
507 using ScLanguage::Method
;
509 Q_ASSERT(mCompletion
.on
);
510 Q_ASSERT(mCompletion
.menu
.isNull());
512 QPointer
<CompletionMenu
> menu
;
514 switch (mCompletion
.type
) {
515 case ClassCompletion
:
516 menu
= menuForClassCompletion(mCompletion
, mEditor
);
519 case ClassMethodCompletion
:
520 menu
= menuForClassMethodCompletion(mCompletion
, mEditor
);
523 case MethodCompletion
:
524 menu
= menuForMethodCompletion(mCompletion
, mEditor
);
529 if (menu
== NULL
) return;
531 mCompletion
.menu
= menu
;
533 connect(menu
, SIGNAL(finished(int)), this, SLOT(onCompletionMenuFinished(int)));
535 QTextCursor
cursor(document());
536 cursor
.setPosition(mCompletion
.pos
);
537 QPoint pos
= mEditor
->viewport()->mapToGlobal( mEditor
->cursorRect(cursor
).bottomLeft() )
542 updateCompletionMenu(forceShow
);
545 CompletionMenu
* AutoCompleter::menuForClassCompletion(CompletionDescription
const & completion
,
548 using namespace ScLanguage
;
549 const Introspection
& introspection
= Main::scProcess()->introspection();
551 const ClassMap
& classes
= introspection
.classMap();
553 QString min
= completion
.base
;
554 QString max
= incrementedString(min
);
556 ClassMap::const_iterator matchStart
, matchEnd
;
557 matchStart
= classes
.lower_bound(min
);
558 matchEnd
= classes
.lower_bound(max
);
559 if (matchStart
== matchEnd
) {
560 qDebug() << "Completion: no class matches:" << completion
.base
;
564 CompletionMenu
* menu
= new CompletionMenu(editor
);
566 for (ClassMap::const_iterator it
= matchStart
; it
!= matchEnd
; ++it
) {
567 Class
*klass
= it
->second
.data();
568 menu
->addItem( new QStandardItem(klass
->name
) );
574 CompletionMenu
* AutoCompleter::menuForClassMethodCompletion(CompletionDescription
const & completion
,
577 using namespace ScLanguage
;
578 const Introspection
& introspection
= Main::scProcess()->introspection();
580 const Class
*klass
= NULL
;
582 if (completion
.tokenType
== Token::Class
) {
583 const ClassMap
& classes
= introspection
.classMap();
584 ClassMap::const_iterator it
= classes
.find(completion
.base
);
585 if (it
!= classes
.end())
586 klass
= it
->second
->metaClass
;
589 klass
= classForCompletionDescription(completion
);
593 qDebug() << "Autocompletion not implemented for" << completion
.base
;
597 QMap
<QString
, const Method
*> relevantMethods
;
599 foreach (const Method
* method
, klass
->methods
)
601 QString methodName
= method
->name
.get();
603 // Operators are also methods, but are not valid in
604 // a method call syntax, so filter them out.
605 Q_ASSERT(!methodName
.isEmpty());
606 if (!methodName
[0].isLetter())
609 if (relevantMethods
.value(methodName
) != 0)
612 relevantMethods
.insert(methodName
, method
);
614 klass
= klass
->superClass
;
617 CompletionMenu
* menu
= new CompletionMenu(editor
);
618 menu
->setCompletionRole(CompletionMenu::CompletionRole
);
620 foreach(const Method
*method
, relevantMethods
) {
621 QString methodName
= method
->name
.get();
622 QString
detail(" [ %1 ]");
624 QStandardItem
*item
= new QStandardItem();
625 item
->setText( methodName
+ detail
.arg(method
->ownerClass
->name
) );
626 item
->setData( QVariant::fromValue(method
), CompletionMenu::MethodRole
);
627 item
->setData( methodName
, CompletionMenu::CompletionRole
);
634 CompletionMenu
* AutoCompleter::menuForMethodCompletion(CompletionDescription
const & completion
,
637 using namespace ScLanguage
;
638 const Introspection
& introspection
= Main::scProcess()->introspection();
640 const MethodMap
& methods
= introspection
.methodMap();
642 QString min
= completion
.base
;
643 QString max
= incrementedString(min
);
645 MethodMap::const_iterator matchStart
, matchEnd
;
646 matchStart
= methods
.lower_bound(min
);
647 matchEnd
= methods
.lower_bound(max
);
648 if (matchStart
== matchEnd
) {
649 qDebug() << "Completion: no method matches:" << completion
.base
;
653 CompletionMenu
*menu
= new CompletionMenu(editor
);
654 menu
->setCompletionRole(CompletionMenu::CompletionRole
);
656 for (MethodMap::const_iterator it
= matchStart
; it
!= matchEnd
; ) {
657 const Method
*method
= it
->second
.data();
659 std::pair
<MethodMap::const_iterator
, MethodMap::const_iterator
> range
660 = methods
.equal_range(it
->first
);
662 int count
= std::distance(range
.first
, range
.second
);
664 QStandardItem
*item
= new QStandardItem();
666 QString methodName
= method
->name
.get();
667 QString
detail(" [ %1 ]");
669 item
->setText( methodName
+ detail
.arg(method
->ownerClass
->name
) );
670 item
->setData( QVariant::fromValue(method
), CompletionMenu::MethodRole
);
672 item
->setText(methodName
+ detail
.arg(count
));
674 item
->setData(methodName
, CompletionMenu::CompletionRole
);
683 const ScLanguage::Class
* AutoCompleter::classForCompletionDescription(CompletionDescription
const & completion
)
685 using namespace ScLanguage
;
686 const Introspection
& introspection
= Main::scProcess()->introspection();
688 switch (completion
.tokenType
) {
690 case Token::RadixFloat
:
692 if (completion
.base
.contains(".")) // else it is an int
693 return introspection
.findClass("Float");
694 else if (!completion
.text
.isEmpty())
695 return introspection
.findClass("Integer");
700 return introspection
.findClass("Char");
703 return introspection
.findClass("String");
706 return introspection
.findClass("Symbol");
712 QString
const & objectString
= completion
.base
;
714 if (objectString
== QString("true"))
715 return introspection
.findClass("True");
717 if (objectString
== QString("false"))
718 return introspection
.findClass("False");
720 if (objectString
== QString("nil"))
721 return introspection
.findClass("Nil");
723 if (objectString
== QString("thisProcess"))
724 return introspection
.findClass("Main");
726 if (objectString
== QString("thisFunction"))
727 return introspection
.findClass("Function");
729 if (objectString
== QString("thisMethod"))
730 return introspection
.findClass("Method");
732 if (objectString
== QString("thisFunctionDef"))
733 return introspection
.findClass("FunctionDef");
735 if (objectString
== QString("thisThread"))
736 return introspection
.findClass("Thread");
738 if (objectString
== QString("currentEnvironment"))
739 return introspection
.findClass("Environment");
741 if (objectString
== QString("topEnvironment"))
742 return introspection
.findClass("Environment");
744 if (objectString
== QString("inf"))
745 return introspection
.findClass("Float");
750 void AutoCompleter::updateCompletionMenu(bool forceShow
)
752 Q_ASSERT(mCompletion
.on
&& !mCompletion
.menu
.isNull());
754 CompletionMenu
*menu
= mCompletion
.menu
;
756 if (!mCompletion
.text
.isEmpty()) {
757 QString pattern
= mCompletion
.text
;
758 pattern
.prepend("^");
759 menu
->model()->setFilterRegExp(pattern
);
761 menu
->model()->setFilterRegExp(QString());
763 if (menu
->model()->hasChildren()) {
764 menu
->model()->sort(0);
765 menu
->view()->setCurrentIndex( menu
->model()->index(0,0) );
766 if (forceShow
|| menu
->currentText() != mCompletion
.text
)
774 void AutoCompleter::onCompletionMenuFinished( int result
)
776 qDebug("Completion: menu finished");
782 QString text
= mCompletion
.menu
->currentText();
784 if (!text
.isEmpty()) {
785 quitCompletion("done");
787 QTextCursor
cursor( mEditor
->textCursor() );
788 cursor
.setPosition( mCompletion
.pos
);
789 cursor
.setPosition( mCompletion
.pos
+ mCompletion
.len
, QTextCursor::KeepAnchor
);
790 cursor
.insertText(text
);
796 // Do not cancel completion whenever menu hidden.
797 // It could be hidden because of current filter yielding 0 results.
799 //quitCompletion("cancelled");
802 void AutoCompleter::triggerMethodCallAid( bool force
)
804 // go find the bracket that I'm currently in,
805 // and count relevant commas along the way
807 QTextDocument
*doc
= document();
808 QTextCursor
cursor( mEditor
->textCursor() );
810 int pos
= cursor
.position();
812 QTextBlock
block( doc
->findBlock(pos
) );
813 if (!block
.isValid())
815 pos
-= block
.position();
817 TokenIterator
it( TokenIterator::leftOf( block
, pos
) );
824 char chr
= it
->character
;
825 Token::Type type
= it
->type
;
830 else if (type
== Token::ClosingBracket
)
832 else if (type
== Token::OpeningBracket
)
849 pos
= bracketPos
= it
.position();
851 QString className
, methodName
;
857 if (type
== Token::Class
) {
858 className
= tokenText(it
);
861 else if (type
== Token::Name
) {
862 methodName
= tokenText(it
);
864 if (it
.isValid() && it
->character
== '.') {
866 if (it
.isValid() && it
->type
== Token::Class
)
867 className
= tokenText(it
);
872 if (methodName
.isEmpty())
875 qDebug("Method call: found call: %s.%s(%i)",
876 className
.toStdString().c_str(),
877 methodName
.toStdString().c_str(),
880 if ( !mMethodCall
.stack
.isEmpty() && mMethodCall
.stack
.last().position
== bracketPos
)
882 qDebug("Method call: call already on stack");
883 // method call popup should have been updated by updateMethodCall();
886 qDebug("Method call: forced re-trigger, popping current call.");
887 mMethodCall
.stack
.pop();
894 qDebug("Method call: new call");
896 call
.position
= bracketPos
;
897 pushMethodCall(call
);
899 using namespace ScLanguage
;
902 const Introspection
& introspection
= Main::scProcess()->introspection();
904 const Method
*method
= 0;
906 if (!className
.isEmpty())
908 const ClassMap
& classes
= introspection
.classMap();
909 ClassMap::const_iterator it
= classes
.find(className
);
910 if (it
== classes
.end()) {
911 qDebug() << "MethodCall: class not found:" << className
;
915 Class
*metaClass
= it
->second
->metaClass
;
917 foreach (const Method
* m
, metaClass
->methods
)
919 if (m
->name
== methodName
) {
925 metaClass
= metaClass
->superClass
;
929 const MethodMap
& methods
= introspection
.methodMap();
931 pair
<MethodMap::const_iterator
, MethodMap::const_iterator
> match
=
932 methods
.equal_range(methodName
);
934 if (match
.first
== match
.second
) {
935 qDebug() << "MethodCall: no method matches:" << methodName
;
937 } else if (std::distance(match
.first
, match
.second
) == 1)
938 method
= match
.first
->second
.data();
940 Q_ASSERT(mMethodCall
.menu
.isNull());
941 QPointer
<CompletionMenu
> menu
= new CompletionMenu(mEditor
);
942 mMethodCall
.menu
= menu
;
944 for (MethodMap::const_iterator it
= match
.first
; it
!= match
.second
; ++it
)
946 const Method
*method
= it
->second
.data();
947 QStandardItem
*item
= new QStandardItem();
948 item
->setText(method
->name
+ " (" + method
->ownerClass
->name
+ ')');
949 item
->setData( QVariant::fromValue(method
), CompletionMenu::MethodRole
);
953 QTextCursor
cursor(document());
954 cursor
.setPosition(bracketPos
);
956 mEditor
->viewport()->mapToGlobal( mEditor
->cursorRect(cursor
).bottomLeft() )
959 if ( ! static_cast<PopUpWidget
*>(menu
)->exec(pos
) ) {
964 method
= menu
->currentMethod();
970 Q_ASSERT(!mMethodCall
.stack
.isEmpty());
971 mMethodCall
.stack
.top().method
= method
;
972 updateMethodCall( mEditor
->textCursor().position() );
976 void AutoCompleter::updateMethodCall( int cursorPos
)
978 int i
= mMethodCall
.stack
.count();
981 MethodCall
& call
= mMethodCall
.stack
[i
];
982 if (call
.position
>= cursorPos
) {
983 qDebug("Method call: call right of cursor. popping.");
984 mMethodCall
.stack
.pop();
988 QTextBlock
block( document()->findBlock( call
.position
) );
989 TokenIterator token
= TokenIterator::rightOf(block
, call
.position
- block
.position());
990 if (!token
.isValid()) {
991 qWarning("Method call: call stack out of sync!");
992 mMethodCall
.stack
.clear();
999 TokenIterator argNameToken
;
1000 while( level
> 0 && token
.isValid() && token
.position() < cursorPos
)
1002 char chr
= token
.character();
1003 Token::Type type
= token
->type
;
1005 if (type
== Token::SymbolArg
) {
1006 argNameToken
= token
;
1009 else if (chr
== ',') {
1010 argNameToken
= TokenIterator();
1016 if (type
== Token::OpeningBracket
)
1018 else if (type
== Token::ClosingBracket
)
1025 Q_ASSERT(i
== mMethodCall
.stack
.count() - 1);
1026 qDebug("Method call: call left of cursor. popping.");
1027 mMethodCall
.stack
.pop();
1031 if (!call
.method
|| !call
.method
->arguments
.count()) {
1032 qDebug("Method call: no info to show. skipping.");
1036 if (argNameToken
.isValid()) {
1038 QString argName
= tokenText(argNameToken
);
1040 for (int idx
= 0; idx
< call
.method
->arguments
.count(); ++idx
) {
1041 if (call
.method
->arguments
[idx
].name
== argName
) {
1047 qDebug("Method call: found current call: %s(%i)",
1048 call
.method
->name
.get().toStdString().c_str(), arg
);
1049 showMethodCall(call
, arg
);
1056 void AutoCompleter::pushMethodCall( const MethodCall
& call
)
1058 qDebug("Method Call: pushing on stack.");
1059 Q_ASSERT( mMethodCall
.stack
.isEmpty()
1060 || mMethodCall
.stack
.last().position
< call
.position
);
1062 mMethodCall
.stack
.push(call
);
1065 void AutoCompleter::showMethodCall( const MethodCall
& call
, int arg
)
1067 QTextCursor
cursor(document());
1068 cursor
.setPosition(call
.position
);
1070 mEditor
->viewport()->mapToGlobal( mEditor
->cursorRect(cursor
).topLeft() );
1071 pos
+= QPoint(0, -20);
1073 if (mMethodCall
.widget
.isNull())
1074 mMethodCall
.widget
= new MethodCallWidget(mEditor
);
1076 MethodCallWidget
*w
= mMethodCall
.widget
;
1078 w
->showMethod( call
.method
, arg
);
1079 w
->resize(w
->sizeHint());
1084 void AutoCompleter::hideMethodCall()
1086 delete mMethodCall
.widget
;
1089 QString
AutoCompleter::tokenText( TokenIterator
& it
)
1094 int pos
= it
.position();
1095 QTextCursor
cursor(document());
1096 cursor
.setPosition(pos
);
1097 cursor
.setPosition(pos
+ it
->length
, QTextCursor::KeepAnchor
);
1098 return cursor
.selectedText();
1101 } // namespace ScIDE
1103 #undef QT_NO_DEBUG_OUTPUT