From a25548dd9a6ad08961df56a97b6b5189d465f251 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 12 Jun 2009 17:05:27 +0200 Subject: make debugger console's auto-completion asynchronous Needed for remote debugging. --- .../debugging/qscriptcompletiontask.cpp | 204 +++++++++++---------- .../debugging/qscriptcompletiontask_p.h | 9 +- src/scripttools/debugging/qscriptdebugger.cpp | 2 +- .../debugging/qscriptdebuggercommand.cpp | 9 + .../debugging/qscriptdebuggercommand_p.h | 2 + .../debugging/qscriptdebuggercommandexecutor.cpp | 67 +++++++ .../qscriptdebuggercommandschedulerfrontend.cpp | 5 + .../qscriptdebuggercommandschedulerfrontend_p.h | 1 + 8 files changed, 196 insertions(+), 103 deletions(-) diff --git a/src/scripttools/debugging/qscriptcompletiontask.cpp b/src/scripttools/debugging/qscriptcompletiontask.cpp index 9554681..e2034f3 100644 --- a/src/scripttools/debugging/qscriptcompletiontask.cpp +++ b/src/scripttools/debugging/qscriptcompletiontask.cpp @@ -44,12 +44,10 @@ #include "qscriptdebuggerconsole_p.h" #include "qscriptdebuggerconsolecommand_p.h" #include "qscriptdebuggerconsolecommandmanager_p.h" - -#include "qscriptenginedebuggerfrontend_p.h" // ### kill -#include "qscriptdebuggerbackend_p.h" // ### kill -#include -#include -#include +#include "qscriptdebuggercommandschedulerjob_p.h" +#include "qscriptdebuggercommandschedulerfrontend_p.h" +#include "qscriptdebuggerjobschedulerinterface_p.h" +#include "qscriptdebuggerresponse_p.h" #include "private/qobject_p.h" @@ -67,11 +65,13 @@ public: ~QScriptCompletionTaskPrivate(); void completeScriptExpression(); + void emitFinished(); QString contents; int cursorPosition; int frameIndex; - QScriptDebuggerFrontend *frontend; + QScriptDebuggerCommandSchedulerInterface *commandScheduler; + QScriptDebuggerJobSchedulerInterface *jobScheduler; QScriptDebuggerConsole *console; }; @@ -83,28 +83,33 @@ QScriptCompletionTaskPrivate::~QScriptCompletionTaskPrivate() { } -QScriptCompletionTask::QScriptCompletionTask( - const QString &contents, int cursorPosition, - int frameIndex, QScriptDebuggerFrontend *frontend, - QScriptDebuggerConsole *console, - QObject *parent) - : QScriptCompletionTaskInterface( - *new QScriptCompletionTaskPrivate, parent) +class QScriptCompleteExpressionJob : public QScriptDebuggerCommandSchedulerJob { - Q_D(QScriptCompletionTask); - d->contents = contents; - d->cursorPosition = cursorPosition; - if ((frameIndex == -1) && console) - d->frameIndex = console->currentFrameIndex(); - else - d->frameIndex = frameIndex; - d->frontend = frontend; - d->console = console; -} +public: + QScriptCompleteExpressionJob(int frameIndex, const QStringList &path, + QScriptCompletionTaskPrivate *task, + QScriptDebuggerCommandSchedulerInterface *scheduler) + : QScriptDebuggerCommandSchedulerJob(scheduler), + m_frameIndex(frameIndex), m_path(path), m_task(task) + {} -QScriptCompletionTask::~QScriptCompletionTask() -{ -} + void start() + { + QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this); + frontend.scheduleGetCompletions(m_frameIndex, m_path); + } + void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/) + { + m_task->results = response.result().toStringList(); + m_task->emitFinished(); + finish(); + } + +private: + int m_frameIndex; + QStringList m_path; + QScriptCompletionTaskPrivate *m_task; +}; namespace { @@ -122,11 +127,43 @@ static bool isPrefixOf(const QString &prefix, const QString &what) } // namespace +class QScriptCompleteScriptsJob : public QScriptDebuggerCommandSchedulerJob +{ +public: + QScriptCompleteScriptsJob(const QString &prefix, QScriptCompletionTaskPrivate *task, + QScriptDebuggerCommandSchedulerInterface *scheduler) + : QScriptDebuggerCommandSchedulerJob(scheduler), + m_prefix(prefix), m_task(task) + {} + + void start() + { + QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this); + frontend.scheduleGetScripts(); + } + void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/) + { + QScriptScriptMap scripts = response.resultAsScripts(); + QScriptScriptMap::const_iterator it; + for (it = scripts.constBegin(); it != scripts.constEnd(); ++it) { + QString fileName = it.value().fileName(); + if (isPrefixOf(m_prefix, fileName)) + m_task->results.append(fileName); + } + m_task->emitFinished(); + finish(); + } +private: + QString m_prefix; + QScriptCompletionTaskPrivate *m_task; +}; + void QScriptCompletionTaskPrivate::completeScriptExpression() { int pos = cursorPosition; if ((pos > 0) && contents.at(pos-1).isNumber()) { // completion of numbers is pointless + emitFinished(); return; } @@ -148,65 +185,41 @@ void QScriptCompletionTaskPrivate::completeScriptExpression() path.prepend(contents.mid(pos, pos2 - pos)); } - // ### super-cheating for now; have to use the async API - QScriptEngineDebuggerFrontend *edf = static_cast(frontend); - QScriptDebuggerBackend *backend = edf->backend(); - QScriptContext *ctx = backend->context(frameIndex); - QScriptValueList objects; - QString prefix = path.last(); - QSet matches; - if (path.size() > 1) { - const QString &topLevelIdent = path.at(0); - QScriptValue obj; - if (topLevelIdent == QLatin1String("this")) { - obj = ctx->thisObject(); - } else { - QScriptValueList scopeChain; - scopeChain = ctx->scopeChain(); - for (int i = 0; i < scopeChain.size(); ++i) { - QScriptValue oo = scopeChain.at(i).property(topLevelIdent); - if (oo.isObject()) { - obj = oo; - break; - } - } - } - for (int i = 1; obj.isObject() && (i < path.size()-1); ++i) - obj = obj.property(path.at(i)); - if (obj.isValid()) - objects.append(obj); - } else { - objects << ctx->scopeChain(); - QStringList keywords; - keywords.append(QString::fromLatin1("this")); - keywords.append(QString::fromLatin1("true")); - keywords.append(QString::fromLatin1("false")); - keywords.append(QString::fromLatin1("null")); - for (int i = 0; i < keywords.size(); ++i) { - const QString &kwd = keywords.at(i); - if (isPrefixOf(prefix, kwd)) - matches.insert(kwd); - } - } + length = path.last().length(); + type = QScriptCompletionTask::ScriptIdentifierCompletion; - for (int i = 0; i < objects.size(); ++i) { - QScriptValue obj = objects.at(i); - while (obj.isObject()) { - QScriptValueIterator it(obj); - while (it.hasNext()) { - it.next(); - QString propertyName = it.name(); - if (isPrefixOf(prefix, propertyName)) - matches.insert(propertyName); - } - obj = obj.prototype(); - } - } - results = matches.toList(); - qStableSort(results); + QScriptDebuggerJob *job = new QScriptCompleteExpressionJob(frameIndex, path, this, commandScheduler); + jobScheduler->scheduleJob(job); +} - length = prefix.length(); - type = QScriptCompletionTask::ScriptIdentifierCompletion; +void QScriptCompletionTaskPrivate::emitFinished() +{ + emit q_func()->finished(); +} + +QScriptCompletionTask::QScriptCompletionTask( + const QString &contents, int cursorPosition, int frameIndex, + QScriptDebuggerCommandSchedulerInterface *commandScheduler, + QScriptDebuggerJobSchedulerInterface *jobScheduler, + QScriptDebuggerConsole *console, + QObject *parent) + : QScriptCompletionTaskInterface( + *new QScriptCompletionTaskPrivate, parent) +{ + Q_D(QScriptCompletionTask); + d->contents = contents; + d->cursorPosition = cursorPosition; + if ((frameIndex == -1) && console) + d->frameIndex = console->currentFrameIndex(); + else + d->frameIndex = frameIndex; + d->commandScheduler = commandScheduler; + d->jobScheduler = jobScheduler; + d->console = console; +} + +QScriptCompletionTask::~QScriptCompletionTask() +{ } void QScriptCompletionTask::start() @@ -223,7 +236,6 @@ void QScriptCompletionTask::start() if ((d->cursorPosition >= cmdIndex) && (d->cursorPosition <= (cmdIndex+len))) { // editing command --> get command completions d->results = d->console->commandManager()->completions(prefix); - qStableSort(d->results); d->position = cmdRx.pos(1); d->length = prefix.length(); d->type = CommandNameCompletion; @@ -259,38 +271,34 @@ void QScriptCompletionTask::start() if (argType == QLatin1String("command-or-group-name")) { d->results = d->console->commandManager()->completions(arg); } else if (argType == QLatin1String("script-filename")) { - // ### super-cheating for now; have to use the async API - QScriptEngineDebuggerFrontend *edf = static_cast(d->frontend); - QScriptDebuggerBackend *backend = edf->backend(); - QScriptScriptMap scripts = backend->scripts(); - QScriptScriptMap::const_iterator it; - for (it = scripts.constBegin(); it != scripts.constEnd(); ++it) { - QString fileName = it.value().fileName(); - if (isPrefixOf(arg, fileName)) - d->results.append(fileName); - } + d->position = pos; + d->length = arg.length(); + d->type = CommandArgumentCompletion; + QScriptDebuggerJob *job = new QScriptCompleteScriptsJob(arg, d, d->commandScheduler); + d->jobScheduler->scheduleJob(job); } else if (argType == QLatin1String("subcommand-name")) { for (int i = 0; i < cmd->subCommands().size(); ++i) { QString name = cmd->subCommands().at(i); if (isPrefixOf(arg, name)) d->results.append(name); } + qStableSort(d->results); } else if (argType == QLatin1String("script")) { d->completeScriptExpression(); + } else { + emit finished(); } if ((d->type == NoCompletion) && !d->results.isEmpty()) { - qStableSort(d->results); d->position = pos; d->length = arg.length(); d->type = CommandArgumentCompletion; + emit finished(); } } - emit finished(); } } else { // assume it's an eval expression d->completeScriptExpression(); - emit finished(); } } diff --git a/src/scripttools/debugging/qscriptcompletiontask_p.h b/src/scripttools/debugging/qscriptcompletiontask_p.h index dd82250..0055959 100644 --- a/src/scripttools/debugging/qscriptcompletiontask_p.h +++ b/src/scripttools/debugging/qscriptcompletiontask_p.h @@ -60,7 +60,8 @@ QT_BEGIN_NAMESPACE -class QScriptDebuggerFrontend; +class QScriptDebuggerCommandSchedulerInterface; +class QScriptDebuggerJobSchedulerInterface; class QScriptDebuggerConsole; class QScriptCompletionTaskPrivate; @@ -70,9 +71,9 @@ class Q_AUTOTEST_EXPORT QScriptCompletionTask Q_OBJECT public: QScriptCompletionTask( - const QString &contents, int cursorPosition, - int frameIndex, - QScriptDebuggerFrontend *frontend, + const QString &contents, int cursorPosition, int frameIndex, + QScriptDebuggerCommandSchedulerInterface *commandScheduler, + QScriptDebuggerJobSchedulerInterface *jobScheduler, QScriptDebuggerConsole *console, QObject *parent = 0); ~QScriptCompletionTask(); diff --git a/src/scripttools/debugging/qscriptdebugger.cpp b/src/scripttools/debugging/qscriptdebugger.cpp index d83a1d0..5626a4b 100644 --- a/src/scripttools/debugging/qscriptdebugger.cpp +++ b/src/scripttools/debugging/qscriptdebugger.cpp @@ -695,7 +695,7 @@ QScriptCompletionTaskInterface *QScriptDebuggerPrivate::createCompletionTask( const QString &contents, int cursorPosition, int frameIndex, int options) { return new QScriptCompletionTask( - contents, cursorPosition, frameIndex, frontend, + contents, cursorPosition, frameIndex, this, this, (options & QScriptCompletionProviderInterface::ConsoleCommandCompletion) ? console : 0); } diff --git a/src/scripttools/debugging/qscriptdebuggercommand.cpp b/src/scripttools/debugging/qscriptdebuggercommand.cpp index 3ceb0d5..fc9b96a 100644 --- a/src/scripttools/debugging/qscriptdebuggercommand.cpp +++ b/src/scripttools/debugging/qscriptdebuggercommand.cpp @@ -568,6 +568,15 @@ QScriptDebuggerCommand QScriptDebuggerCommand::getPropertyExpressionValue( return cmd; } +QScriptDebuggerCommand QScriptDebuggerCommand::getCompletions( + int contextIndex, const QStringList &path) +{ + QScriptDebuggerCommand cmd(GetCompletions); + cmd.setContextIndex(contextIndex); + cmd.setAttribute(UserAttribute, path); + return cmd; +} + QScriptDebuggerCommand QScriptDebuggerCommand::newScriptObjectSnapshotCommand() { QScriptDebuggerCommand cmd(NewScriptObjectSnapshot); diff --git a/src/scripttools/debugging/qscriptdebuggercommand_p.h b/src/scripttools/debugging/qscriptdebuggercommand_p.h index 6cfdec5..7ba4b6f 100644 --- a/src/scripttools/debugging/qscriptdebuggercommand_p.h +++ b/src/scripttools/debugging/qscriptdebuggercommand_p.h @@ -107,6 +107,7 @@ public: GetScopeChain, ContextsCheckpoint, GetPropertyExpressionValue, + GetCompletions, NewScriptObjectSnapshot, ScriptObjectSnapshotCapture, @@ -232,6 +233,7 @@ public: static QScriptDebuggerCommand contextsCheckpoint(); static QScriptDebuggerCommand getPropertyExpressionValue(int contextIndex, int lineNumber, const QStringList &path); + static QScriptDebuggerCommand getCompletions(int contextIndex, const QStringList &path); static QScriptDebuggerCommand newScriptObjectSnapshotCommand(); static QScriptDebuggerCommand scriptObjectSnapshotCaptureCommand(int id, const QScriptDebuggerValue &object); diff --git a/src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp b/src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp index ce9aa03..afac2e7 100644 --- a/src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp +++ b/src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp @@ -101,6 +101,12 @@ QScriptDebuggerCommandExecutor::~QScriptDebuggerCommandExecutor() delete d_ptr; } +static bool isPrefixOf(const QString &prefix, const QString &what) +{ + return ((what.length() > prefix.length()) + && what.startsWith(prefix)); +} + /*! Applies the given \a command to the given \a backend. */ @@ -337,6 +343,67 @@ QScriptDebuggerResponse QScriptDebuggerCommandExecutor::execute( } } break; + case QScriptDebuggerCommand::GetCompletions: { + QScriptContext *ctx = backend->context(command.contextIndex()); + QVariant attr = command.attribute(QScriptDebuggerCommand::UserAttribute); + QStringList path = attr.toStringList(); + if (!ctx || path.isEmpty()) + break; + QScriptValueList objects; + QString prefix = path.last(); + QSet matches; + if (path.size() > 1) { + const QString &topLevelIdent = path.at(0); + QScriptValue obj; + if (topLevelIdent == QLatin1String("this")) { + obj = ctx->thisObject(); + } else { + QScriptValueList scopeChain; + scopeChain = ctx->scopeChain(); + for (int i = 0; i < scopeChain.size(); ++i) { + QScriptValue oo = scopeChain.at(i).property(topLevelIdent); + if (oo.isObject()) { + obj = oo; + break; + } + } + } + for (int i = 1; obj.isObject() && (i < path.size()-1); ++i) + obj = obj.property(path.at(i)); + if (obj.isValid()) + objects.append(obj); + } else { + objects << ctx->scopeChain(); + QStringList keywords; + keywords.append(QString::fromLatin1("this")); + keywords.append(QString::fromLatin1("true")); + keywords.append(QString::fromLatin1("false")); + keywords.append(QString::fromLatin1("null")); + for (int i = 0; i < keywords.size(); ++i) { + const QString &kwd = keywords.at(i); + if (isPrefixOf(prefix, kwd)) + matches.insert(kwd); + } + } + + for (int i = 0; i < objects.size(); ++i) { + QScriptValue obj = objects.at(i); + while (obj.isObject()) { + QScriptValueIterator it(obj); + while (it.hasNext()) { + it.next(); + QString propertyName = it.name(); + if (isPrefixOf(prefix, propertyName)) + matches.insert(propertyName); + } + obj = obj.prototype(); + } + } + QStringList matchesList = matches.toList(); + qStableSort(matchesList); + response.setResult(matchesList); + } break; + case QScriptDebuggerCommand::NewScriptObjectSnapshot: { int id = backend->newScriptObjectSnapshot(); response.setResult(id); diff --git a/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend.cpp b/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend.cpp index a181711..83e634e 100644 --- a/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend.cpp +++ b/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend.cpp @@ -257,6 +257,11 @@ int QScriptDebuggerCommandSchedulerFrontend::scheduleGetPropertyExpressionValue( return scheduleCommand(QScriptDebuggerCommand::getPropertyExpressionValue(contextIndex, lineNumber, path)); } +int QScriptDebuggerCommandSchedulerFrontend::scheduleGetCompletions(int contextIndex, const QStringList &path) +{ + return scheduleCommand(QScriptDebuggerCommand::getCompletions(contextIndex, path)); +} + int QScriptDebuggerCommandSchedulerFrontend::scheduleEvaluate(int contextIndex, const QString &program, const QString &fileName, diff --git a/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend_p.h b/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend_p.h index ff9c570..9a1a36d 100644 --- a/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend_p.h +++ b/src/scripttools/debugging/qscriptdebuggercommandschedulerfrontend_p.h @@ -111,6 +111,7 @@ public: int scheduleContextsCheckpoint(); int scheduleGetPropertyExpressionValue(int contextIndex, int lineNumber, const QStringList &path); + int scheduleGetCompletions(int contextIndex, const QStringList &path); // iteration int scheduleNewScriptValueIterator(const QScriptDebuggerValue &object); -- cgit v0.12