1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSCriptTools module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qscriptcompletiontask_p.h"
43 #include "qscriptcompletiontaskinterface_p_p.h"
44 #include "qscriptdebuggerconsole_p.h"
45 #include "qscriptdebuggerconsolecommand_p.h"
46 #include "qscriptdebuggerconsolecommandmanager_p.h"
48 #include "qscriptenginedebuggerfrontend_p.h" // ### kill
49 #include "qscriptdebuggerbackend_p.h" // ### kill
50 #include <QtScript/qscriptcontext.h>
51 #include <QtScript/qscriptvalue.h>
52 #include <QtScript/qscriptvalueiterator.h>
54 #include "private/qobject_p.h"
56 #include <QtCore/qset.h>
57 #include <QtCore/qdebug.h>
61 class QScriptCompletionTaskPrivate
62 : public QScriptCompletionTaskInterfacePrivate
64 Q_DECLARE_PUBLIC(QScriptCompletionTask
)
66 QScriptCompletionTaskPrivate();
67 ~QScriptCompletionTaskPrivate();
69 void completeScriptExpression();
74 QScriptDebuggerFrontend
*frontend
;
75 QScriptDebuggerConsole
*console
;
78 QScriptCompletionTaskPrivate::QScriptCompletionTaskPrivate()
79 : cursorPosition(0), frameIndex(0), frontend(0), console(0)
83 QScriptCompletionTaskPrivate::~QScriptCompletionTaskPrivate()
87 QScriptCompletionTask::QScriptCompletionTask(
88 const QString
&contents
, int cursorPosition
,
89 int frameIndex
, QScriptDebuggerFrontend
*frontend
,
90 QScriptDebuggerConsole
*console
,
92 : QScriptCompletionTaskInterface(
93 *new QScriptCompletionTaskPrivate
, parent
)
95 Q_D(QScriptCompletionTask
);
96 d
->contents
= contents
;
97 d
->cursorPosition
= cursorPosition
;
98 if ((frameIndex
== -1) && console
)
99 d
->frameIndex
= console
->currentFrameIndex();
101 d
->frameIndex
= frameIndex
;
102 d
->frontend
= frontend
;
103 d
->console
= console
;
106 QScriptCompletionTask::~QScriptCompletionTask()
112 static bool isIdentChar(const QChar
&ch
)
114 static QChar underscore
= QLatin1Char('_');
115 return ch
.isLetter() || (ch
== underscore
);
118 static bool isPrefixOf(const QString
&prefix
, const QString
&what
)
120 return ((what
.length() > prefix
.length())
121 && what
.startsWith(prefix
));
126 void QScriptCompletionTaskPrivate::completeScriptExpression()
128 int pos
= cursorPosition
;
129 if ((pos
> 0) && contents
.at(pos
-1).isNumber()) {
130 // completion of numbers is pointless
134 while ((pos
> 0) && isIdentChar(contents
.at(pos
-1)))
136 int pos2
= cursorPosition
;
137 while ((pos2
< contents
.size()-1) && isIdentChar(contents
.at(pos2
+1)))
139 QString ident
= contents
.mid(pos
, pos2
- pos
+ 1);
144 while ((pos
> 0) && (contents
.at(pos
-1) == QLatin1Char('.'))) {
147 while ((pos
> 0) && isIdentChar(contents
.at(pos
-1)))
149 path
.prepend(contents
.mid(pos
, pos2
- pos
));
152 // ### super-cheating for now; have to use the async API
153 QScriptEngineDebuggerFrontend
*edf
= static_cast<QScriptEngineDebuggerFrontend
*>(frontend
);
154 QScriptDebuggerBackend
*backend
= edf
->backend();
155 QScriptContext
*ctx
= backend
->context(frameIndex
);
156 QScriptValueList objects
;
157 QString prefix
= path
.last();
158 QSet
<QString
> matches
;
159 if (path
.size() > 1) {
160 const QString
&topLevelIdent
= path
.at(0);
162 if (topLevelIdent
== QString::fromLatin1("this")) {
163 obj
= ctx
->thisObject();
165 QScriptValueList scopeChain
;
166 scopeChain
= ctx
->scopeChain();
167 for (int i
= 0; i
< scopeChain
.size(); ++i
) {
168 QScriptValue oo
= scopeChain
.at(i
).property(topLevelIdent
);
175 for (int i
= 1; obj
.isObject() && (i
< path
.size()-1); ++i
)
176 obj
= obj
.property(path
.at(i
));
180 objects
<< ctx
->scopeChain();
181 QStringList keywords
;
182 keywords
.append(QString::fromLatin1("this"));
183 keywords
.append(QString::fromLatin1("true"));
184 keywords
.append(QString::fromLatin1("false"));
185 keywords
.append(QString::fromLatin1("null"));
186 for (int i
= 0; i
< keywords
.size(); ++i
) {
187 const QString
&kwd
= keywords
.at(i
);
188 if (isPrefixOf(prefix
, kwd
))
193 for (int i
= 0; i
< objects
.size(); ++i
) {
194 QScriptValue obj
= objects
.at(i
);
195 while (obj
.isObject()) {
196 QScriptValueIterator
it(obj
);
197 while (it
.hasNext()) {
199 QString propertyName
= it
.name();
200 if (isPrefixOf(prefix
, propertyName
))
201 matches
.insert(propertyName
);
203 obj
= obj
.prototype();
206 results
= matches
.toList();
207 qStableSort(results
);
209 length
= prefix
.length();
210 type
= QScriptCompletionTask::ScriptIdentifierCompletion
;
213 void QScriptCompletionTask::start()
215 Q_D(QScriptCompletionTask
);
216 d
->type
= NoCompletion
;
217 // see if we're typing a command
218 // ### don't hardcode the command prefix
219 QRegExp
cmdRx(QString::fromLatin1("^\\s*\\.([a-zA-Z]*)"));
220 int cmdIndex
= cmdRx
.indexIn(d
->contents
);
221 if ((cmdIndex
!= -1) && d
->console
) {
222 int len
= cmdRx
.matchedLength();
223 QString prefix
= cmdRx
.capturedTexts().at(1);
224 if ((d
->cursorPosition
>= cmdIndex
) && (d
->cursorPosition
<= (cmdIndex
+len
))) {
225 // editing command --> get command completions
226 d
->results
= d
->console
->commandManager()->completions(prefix
);
227 qStableSort(d
->results
);
228 d
->position
= cmdRx
.pos(1);
229 d
->length
= prefix
.length();
230 d
->type
= CommandNameCompletion
;
231 d
->appendix
= QString::fromLatin1(" ");
234 QScriptDebuggerConsoleCommand
*cmd
= d
->console
->commandManager()->findCommand(prefix
);
239 // editing an argument
242 int pos
= cmdIndex
+ len
;
243 while (pos
< d
->contents
.size()) {
244 while ((pos
< d
->contents
.size()) && d
->contents
.at(pos
).isSpace())
246 if (pos
< d
->contents
.size()) {
248 while ((pos2
< d
->contents
.size()) && !d
->contents
.at(pos2
).isSpace())
250 if ((d
->cursorPosition
>= pos
) && (d
->cursorPosition
<= pos2
)) {
251 arg
= d
->contents
.mid(pos
, pos2
- pos
);
258 QString argType
= cmd
->argumentTypes().value(argNum
);
259 if (!argType
.isEmpty()) {
260 if (argType
== QString::fromLatin1("command-or-group-name")) {
261 d
->results
= d
->console
->commandManager()->completions(arg
);
262 } else if (argType
== QString::fromLatin1("script-filename")) {
263 // ### super-cheating for now; have to use the async API
264 QScriptEngineDebuggerFrontend
*edf
= static_cast<QScriptEngineDebuggerFrontend
*>(d
->frontend
);
265 QScriptDebuggerBackend
*backend
= edf
->backend();
266 QScriptScriptMap scripts
= backend
->scripts();
267 QScriptScriptMap::const_iterator it
;
268 for (it
= scripts
.constBegin(); it
!= scripts
.constEnd(); ++it
) {
269 QString fileName
= it
.value().fileName();
270 if (isPrefixOf(arg
, fileName
))
271 d
->results
.append(fileName
);
273 } else if (argType
== QString::fromLatin1("subcommand-name")) {
274 for (int i
= 0; i
< cmd
->subCommands().size(); ++i
) {
275 QString name
= cmd
->subCommands().at(i
);
276 if (isPrefixOf(arg
, name
))
277 d
->results
.append(name
);
279 } else if (argType
== QString::fromLatin1("script")) {
280 d
->completeScriptExpression();
282 if ((d
->type
== NoCompletion
) && !d
->results
.isEmpty()) {
283 qStableSort(d
->results
);
285 d
->length
= arg
.length();
286 d
->type
= CommandArgumentCompletion
;
292 // assume it's an eval expression
293 d
->completeScriptExpression();