1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtSCriptTools module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qscriptcompletiontask_p.h"
41#include "qscriptcompletiontaskinterface_p_p.h"
42#include "qscriptdebuggerconsole_p.h"
43#include "qscriptdebuggerconsolecommand_p.h"
44#include "qscriptdebuggerconsolecommandmanager_p.h"
45#include "qscriptdebuggercommandschedulerjob_p.h"
46#include "qscriptdebuggercommandschedulerfrontend_p.h"
47#include "qscriptdebuggerjobschedulerinterface_p.h"
48#include "qscriptdebuggerresponse_p.h"
49
50#include "private/qobject_p.h"
51
52#include <QtCore/qset.h>
53#include <QtCore/qdebug.h>
54
55#include <algorithm>
56
57QT_BEGIN_NAMESPACE
58
59class QScriptCompletionTaskPrivate
60 : public QScriptCompletionTaskInterfacePrivate
61{
62 Q_DECLARE_PUBLIC(QScriptCompletionTask)
63public:
64 QScriptCompletionTaskPrivate();
65 ~QScriptCompletionTaskPrivate();
66
67 void completeScriptExpression();
68 void emitFinished();
69
70 QString contents;
71 int cursorPosition;
72 int frameIndex;
73 QScriptDebuggerCommandSchedulerInterface *commandScheduler;
74 QScriptDebuggerJobSchedulerInterface *jobScheduler;
75 QScriptDebuggerConsole *console;
76};
77
78QScriptCompletionTaskPrivate::QScriptCompletionTaskPrivate()
79 : cursorPosition(0), frameIndex(0), commandScheduler(0),
80 jobScheduler(0), console(0)
81{
82}
83
84QScriptCompletionTaskPrivate::~QScriptCompletionTaskPrivate()
85{
86}
87
88class QScriptCompleteExpressionJob : public QScriptDebuggerCommandSchedulerJob
89{
90public:
91 QScriptCompleteExpressionJob(int frameIndex, const QStringList &path,
92 QScriptCompletionTaskPrivate *task,
93 QScriptDebuggerCommandSchedulerInterface *scheduler)
94 : QScriptDebuggerCommandSchedulerJob(scheduler),
95 m_frameIndex(frameIndex), m_path(path), m_task(task)
96 {}
97
98 void start()
99 {
100 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
101 frontend.scheduleGetCompletions(contextIndex: m_frameIndex, path: m_path);
102 }
103 void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/)
104 {
105 m_task->results = response.result().toStringList();
106 m_task->emitFinished();
107 finish();
108 }
109
110private:
111 int m_frameIndex;
112 QStringList m_path;
113 QScriptCompletionTaskPrivate *m_task;
114};
115
116namespace {
117
118static bool isIdentChar(const QChar &ch)
119{
120 static QChar underscore = QLatin1Char('_');
121 return ch.isLetterOrNumber() || (ch == underscore);
122}
123
124static bool isPrefixOf(const QString &prefix, const QString &what)
125{
126 return ((what.length() > prefix.length())
127 && what.startsWith(s: prefix));
128}
129
130} // namespace
131
132class QScriptCompleteScriptsJob : public QScriptDebuggerCommandSchedulerJob
133{
134public:
135 QScriptCompleteScriptsJob(const QString &prefix, QScriptCompletionTaskPrivate *task,
136 QScriptDebuggerCommandSchedulerInterface *scheduler)
137 : QScriptDebuggerCommandSchedulerJob(scheduler),
138 m_prefix(prefix), m_task(task)
139 {}
140
141 void start()
142 {
143 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
144 frontend.scheduleGetScripts();
145 }
146 void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/)
147 {
148 QScriptScriptMap scripts = response.resultAsScripts();
149 QScriptScriptMap::const_iterator it;
150 for (it = scripts.constBegin(); it != scripts.constEnd(); ++it) {
151 QString fileName = it.value().fileName();
152 if (isPrefixOf(prefix: m_prefix, what: fileName))
153 m_task->results.append(t: fileName);
154 }
155 m_task->emitFinished();
156 finish();
157 }
158private:
159 QString m_prefix;
160 QScriptCompletionTaskPrivate *m_task;
161};
162
163void QScriptCompletionTaskPrivate::completeScriptExpression()
164{
165 int pos = cursorPosition;
166 if ((pos > 0) && contents.at(i: pos-1).isNumber()) {
167 // completion of numbers is pointless
168 emitFinished();
169 return;
170 }
171
172 while ((pos > 0) && isIdentChar(ch: contents.at(i: pos-1)))
173 --pos;
174 int pos2 = cursorPosition - 1;
175 while ((pos2+1 < contents.size()) && isIdentChar(ch: contents.at(i: pos2+1)))
176 ++pos2;
177 QString ident = contents.mid(position: pos, n: pos2 - pos + 1);
178 position = pos;
179
180 QStringList path;
181 path.append(t: ident);
182 while ((pos > 0) && (contents.at(i: pos-1) == QLatin1Char('.'))) {
183 --pos;
184 pos2 = pos;
185 while ((pos > 0) && isIdentChar(ch: contents.at(i: pos-1)))
186 --pos;
187 path.prepend(t: contents.mid(position: pos, n: pos2 - pos));
188 }
189
190 length = path.last().length();
191 type = QScriptCompletionTask::ScriptIdentifierCompletion;
192
193 QScriptDebuggerJob *job = new QScriptCompleteExpressionJob(frameIndex, path, this, commandScheduler);
194 jobScheduler->scheduleJob(job);
195}
196
197void QScriptCompletionTaskPrivate::emitFinished()
198{
199 emit q_func()->finished();
200}
201
202QScriptCompletionTask::QScriptCompletionTask(
203 const QString &contents, int cursorPosition, int frameIndex,
204 QScriptDebuggerCommandSchedulerInterface *commandScheduler,
205 QScriptDebuggerJobSchedulerInterface *jobScheduler,
206 QScriptDebuggerConsole *console,
207 QObject *parent)
208 : QScriptCompletionTaskInterface(
209 *new QScriptCompletionTaskPrivate, parent)
210{
211 Q_D(QScriptCompletionTask);
212 d->contents = contents;
213 d->cursorPosition = cursorPosition;
214 if ((frameIndex == -1) && console)
215 d->frameIndex = console->currentFrameIndex();
216 else
217 d->frameIndex = frameIndex;
218 d->commandScheduler = commandScheduler;
219 d->jobScheduler = jobScheduler;
220 d->console = console;
221}
222
223QScriptCompletionTask::~QScriptCompletionTask()
224{
225}
226
227void QScriptCompletionTask::start()
228{
229 Q_D(QScriptCompletionTask);
230 d->type = NoCompletion;
231 // see if we're typing a command
232 // ### don't hardcode the command prefix
233 QRegExp cmdRx(QString::fromLatin1(str: "^\\s*\\.([a-zA-Z]*)"));
234 int cmdIndex = cmdRx.indexIn(str: d->contents);
235 if ((cmdIndex != -1) && d->console) {
236 int len = cmdRx.matchedLength();
237 QString prefix = cmdRx.capturedTexts().at(i: 1);
238 if ((d->cursorPosition >= cmdIndex) && (d->cursorPosition <= (cmdIndex+len))) {
239 // editing command --> get command completions
240 d->results = d->console->commandManager()->completions(prefix);
241 d->position = cmdRx.pos(nth: 1);
242 d->length = prefix.length();
243 d->type = CommandNameCompletion;
244 d->appendix = QString::fromLatin1(str: " ");
245 emit finished();
246 } else {
247 QScriptDebuggerConsoleCommand *cmd = d->console->commandManager()->findCommand(name: prefix);
248 if (!cmd) {
249 emit finished();
250 return;
251 }
252 // editing an argument
253 int argNum = 0;
254 QString arg;
255 int pos = cmdIndex + len;
256 while (pos < d->contents.size()) {
257 while ((pos < d->contents.size()) && d->contents.at(i: pos).isSpace())
258 ++pos;
259 if (pos < d->contents.size()) {
260 int pos2 = pos + 1;
261 while ((pos2 < d->contents.size()) && !d->contents.at(i: pos2).isSpace())
262 ++pos2;
263 if ((d->cursorPosition >= pos) && (d->cursorPosition <= pos2)) {
264 arg = d->contents.mid(position: pos, n: pos2 - pos);
265 break;
266 }
267 pos = pos2;
268 ++argNum;
269 }
270 }
271 QString argType = cmd->argumentTypes().value(i: argNum);
272 if (!argType.isEmpty()) {
273 if (argType == QLatin1String("command-or-group-name")) {
274 d->results = d->console->commandManager()->completions(prefix: arg);
275 } else if (argType == QLatin1String("script-filename")) {
276 d->position = pos;
277 d->length = arg.length();
278 d->type = CommandArgumentCompletion;
279 QScriptDebuggerJob *job = new QScriptCompleteScriptsJob(arg, d, d->commandScheduler);
280 d->jobScheduler->scheduleJob(job);
281 } else if (argType == QLatin1String("subcommand-name")) {
282 for (int i = 0; i < cmd->subCommands().size(); ++i) {
283 QString name = cmd->subCommands().at(i);
284 if (isPrefixOf(prefix: arg, what: name))
285 d->results.append(t: name);
286 }
287 std::stable_sort(first: d->results.begin(), last: d->results.end());
288 } else if (argType == QLatin1String("script")) {
289 d->completeScriptExpression();
290 } else {
291 emit finished();
292 }
293 if ((d->type == NoCompletion) && !d->results.isEmpty()) {
294 d->position = pos;
295 d->length = arg.length();
296 d->type = CommandArgumentCompletion;
297 emit finished();
298 }
299 }
300 }
301 } else {
302 // assume it's an eval expression
303 d->completeScriptExpression();
304 }
305}
306
307QT_END_NAMESPACE
308

source code of qtscript/src/scripttools/debugging/qscriptcompletiontask.cpp