diff --git a/doomsday/libappfw/include/de/widgets/scriptcommandwidget.h b/doomsday/libappfw/include/de/widgets/scriptcommandwidget.h index b000da7ebc..abae162386 100644 --- a/doomsday/libappfw/include/de/widgets/scriptcommandwidget.h +++ b/doomsday/libappfw/include/de/widgets/scriptcommandwidget.h @@ -48,6 +48,7 @@ class LIBAPPFW_PUBLIC ScriptCommandWidget : public CommandWidget protected: bool isAcceptedAsCommand(String const &text); void executeCommand(String const &text); + void autoCompletionBegan(String const &prefix); private: DENG2_PRIVATE(d) diff --git a/doomsday/libappfw/src/widgets/scriptcommandwidget.cpp b/doomsday/libappfw/src/widgets/scriptcommandwidget.cpp index 0b8125ce3a..8fa9704db1 100644 --- a/doomsday/libappfw/src/widgets/scriptcommandwidget.cpp +++ b/doomsday/libappfw/src/widgets/scriptcommandwidget.cpp @@ -19,18 +19,88 @@ #include "de/ScriptCommandWidget" #include "de/PopupWidget" +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include namespace de { DENG2_PIMPL(ScriptCommandWidget) +, DENG2_OBSERVES(App, StartupComplete) +, DENG2_OBSERVES(App, GameChange) { Script script; Process process; Instance(Public *i) : Base(i) - {} + { + App::app().audienceForStartupComplete() += this; + App::app().audienceForGameChange() += this; + } + + ~Instance() + { + App::app().audienceForStartupComplete() -= this; + App::app().audienceForGameChange() -= this; + } + + void appStartupCompleted() + { + importNativeModules(); + updateLexicon(); + } + + void currentGameChanged(game::Game const &) + { + importNativeModules(); + updateLexicon(); + } + + void importNativeModules() + { + // Automatically import all native modules into the interactive process. + foreach(String const &name, App::scriptSystem().nativeModules()) + { + process.globals().add(new Variable(name, + new RecordValue(App::scriptSystem().nativeModule(name)))); + } + } + + void updateLexicon() + { + shell::Lexicon lexi; + lexi.setCaseSensitive(true); + lexi.setAdditionalWordChars("_"); + + // Add the variables in the global scope. + /// @todo Should be determined dynamically based on the scope at the cursor position. + DENG2_FOR_EACH_CONST(Record::Members, i, process.globals().members()) + { + lexi.addTerm(i.key()); + } + + // Add all built-in Doomsday Script functions. + foreach(String name, BuiltInExpression::identifiers()) + { + lexi.addTerm(name); + } + + // Add all Doomsday Script keywords. + foreach(String keyword, ScriptLex::keywords()) + { + lexi.addTerm(keyword); + } + + self.setLexicon(lexi); + } bool shouldShowAsPopup(Error const &) { @@ -92,14 +162,30 @@ void ScriptCommandWidget::executeCommand(String const &text) LOG_SCR_WARNING("Error in script:\n") << er.asText(); } - /* - // Print the result. - Value const &result = d->process.context().evaluator().result(); - if(!result.is()) + // Print the result (if possible). + try + { + Value const &result = d->process.context().evaluator().result(); + if(!result.is()) + { + String msg = DENG2_CHAR_RIGHT_DOUBLEARROW " " _E(>)_E(m) + result.asText(); + LOG_SCR_MSG(msg); + } + } + catch(Error const &) + {} +} + +void ScriptCommandWidget::autoCompletionBegan(String const &prefix) +{ + // Prepare a list of annotated completions to show in the popup. + QStringList const compls = suggestedCompletions(); + if(compls.size() > 1) { - LOG_INFO("%s") << result.asText(); + showAutocompletionPopup(tr("Completions for %1:\n") + .arg(_E(b) + prefix + _E(.)) + + _E(m) + compls.join("\n")); } - */ } } // namespace de