src/scripttools/debugging/qscriptcompletiontask.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSCriptTools module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    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.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    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"
       
    47 #include "qscriptdebuggercommandschedulerjob_p.h"
       
    48 #include "qscriptdebuggercommandschedulerfrontend_p.h"
       
    49 #include "qscriptdebuggerjobschedulerinterface_p.h"
       
    50 #include "qscriptdebuggerresponse_p.h"
       
    51 
       
    52 #include "private/qobject_p.h"
       
    53 
       
    54 #include <QtCore/qset.h>
       
    55 #include <QtCore/qdebug.h>
       
    56 
       
    57 QT_BEGIN_NAMESPACE
       
    58 
       
    59 class QScriptCompletionTaskPrivate
       
    60     : public QScriptCompletionTaskInterfacePrivate
       
    61 {
       
    62     Q_DECLARE_PUBLIC(QScriptCompletionTask)
       
    63 public:
       
    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 
       
    78 QScriptCompletionTaskPrivate::QScriptCompletionTaskPrivate()
       
    79     : cursorPosition(0), frameIndex(0), commandScheduler(0),
       
    80       jobScheduler(0), console(0)
       
    81 {
       
    82 }
       
    83 
       
    84 QScriptCompletionTaskPrivate::~QScriptCompletionTaskPrivate()
       
    85 {
       
    86 }
       
    87 
       
    88 class QScriptCompleteExpressionJob : public QScriptDebuggerCommandSchedulerJob
       
    89 {
       
    90 public:
       
    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(m_frameIndex, 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 
       
   110 private:
       
   111     int m_frameIndex;
       
   112     QStringList m_path;
       
   113     QScriptCompletionTaskPrivate *m_task;
       
   114 };
       
   115 
       
   116 namespace {
       
   117 
       
   118 static bool isIdentChar(const QChar &ch)
       
   119 {
       
   120     static QChar underscore = QLatin1Char('_');
       
   121     return ch.isLetter() || (ch == underscore);
       
   122 }
       
   123 
       
   124 static bool isPrefixOf(const QString &prefix, const QString &what)
       
   125 {
       
   126     return ((what.length() > prefix.length())
       
   127             && what.startsWith(prefix));
       
   128 }
       
   129 
       
   130 } // namespace
       
   131 
       
   132 class QScriptCompleteScriptsJob : public QScriptDebuggerCommandSchedulerJob
       
   133 {
       
   134 public:
       
   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(m_prefix, fileName))
       
   153                 m_task->results.append(fileName);
       
   154         }
       
   155         m_task->emitFinished();
       
   156         finish();
       
   157     }
       
   158 private:
       
   159     QString m_prefix;
       
   160     QScriptCompletionTaskPrivate *m_task;
       
   161 };
       
   162 
       
   163 void QScriptCompletionTaskPrivate::completeScriptExpression()
       
   164 {
       
   165     int pos = cursorPosition;
       
   166     if ((pos > 0) && contents.at(pos-1).isNumber()) {
       
   167         // completion of numbers is pointless
       
   168         emitFinished();
       
   169         return;
       
   170     }
       
   171 
       
   172     while ((pos > 0) && isIdentChar(contents.at(pos-1)))
       
   173         --pos;
       
   174     int pos2 = cursorPosition - 1;
       
   175     while ((pos2 < contents.size()-1) && isIdentChar(contents.at(pos2+1)))
       
   176         ++pos2;
       
   177     QString ident = contents.mid(pos, pos2 - pos + 1);
       
   178     position = pos;
       
   179 
       
   180     QStringList path;
       
   181     path.append(ident);
       
   182     while ((pos > 0) && (contents.at(pos-1) == QLatin1Char('.'))) {
       
   183         --pos;
       
   184         pos2 = pos;
       
   185         while ((pos > 0) && isIdentChar(contents.at(pos-1)))
       
   186             --pos;
       
   187         path.prepend(contents.mid(pos, 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 
       
   197 void QScriptCompletionTaskPrivate::emitFinished()
       
   198 {
       
   199     emit q_func()->finished();
       
   200 }
       
   201 
       
   202 QScriptCompletionTask::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 
       
   223 QScriptCompletionTask::~QScriptCompletionTask()
       
   224 {
       
   225 }
       
   226 
       
   227 void 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("^\\s*\\.([a-zA-Z]*)"));
       
   234     int cmdIndex = cmdRx.indexIn(d->contents);
       
   235     if ((cmdIndex != -1) && d->console) {
       
   236         int len = cmdRx.matchedLength();
       
   237         QString prefix = cmdRx.capturedTexts().at(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(1);
       
   242             d->length = prefix.length();
       
   243             d->type = CommandNameCompletion;
       
   244             d->appendix = QString::fromLatin1(" ");
       
   245             emit finished();
       
   246         } else {
       
   247             QScriptDebuggerConsoleCommand *cmd = d->console->commandManager()->findCommand(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(pos).isSpace())
       
   258                     ++pos;
       
   259                 if (pos < d->contents.size()) {
       
   260                     int pos2 = pos + 1;
       
   261                     while ((pos2 < d->contents.size()) && !d->contents.at(pos2).isSpace())
       
   262                         ++pos2;
       
   263                     if ((d->cursorPosition >= pos) && (d->cursorPosition <= pos2)) {
       
   264                         arg = d->contents.mid(pos, pos2 - pos);
       
   265                         break;
       
   266                     }
       
   267                     pos = pos2;
       
   268                     ++argNum;
       
   269                 }
       
   270             }
       
   271             QString argType = cmd->argumentTypes().value(argNum);
       
   272             if (!argType.isEmpty()) {
       
   273                 if (argType == QLatin1String("command-or-group-name")) {
       
   274                     d->results = d->console->commandManager()->completions(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(arg, name))
       
   285                             d->results.append(name);
       
   286                     }
       
   287                     qStableSort(d->results);
       
   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 
       
   307 QT_END_NAMESPACE