• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 33401816133

31 Aug 2026 02:17PM UTC coverage: 23.563%. Remained the same
33401816133

Pull #2247

github

web-flow
Merge 45d897033 into 0a4f8c967
Pull Request #2247: js: make the scripting API and --script able to drive a .mjs module

41 of 173 new or added lines in 6 files covered. (23.7%)

8 existing lines in 5 files now uncovered.

53230 of 225905 relevant lines covered (23.56%)

62980.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.85
/src/plugins/score-plugin-js/JS/ConsolePanel.cpp
1
#include "ConsolePanel.hpp"
2

3
#include <JS/ApplicationPlugin.hpp>
4

5
#include <score/widgets/HelpInteraction.hpp>
6
#include <score/widgets/PromptLineEdit.hpp>
7

8
#include <QCompleter>
9
#include <QDebug>
10
#include <QDir>
11
#include <QFileInfo>
12
#include <QJSCompleter>
13
#include <QJSValueIterator>
14
#include <QQmlEngine>
15
#include <QStandardPaths>
16

17
namespace JS
18
{
19

20
PanelDelegate::PanelDelegate(const score::GUIApplicationContext& ctx)
104✔
21
    : score::PanelDelegate{ctx}
104✔
22
    , m_engine{ctx.guiApplicationPlugin<JS::ApplicationPlugin>().m_consoleEngine}
104✔
23
    , m_widget{new QWidget}
104✔
24
{
104✔
25
  m_engine.installExtensions(QJSEngine::ConsoleExtension);
104✔
26
  m_engine.globalObject().setProperty(
208✔
27
      "ActionContext", m_engine.newQObject(new ActionContext));
104✔
28

29
  auto lay = new QVBoxLayout;
104✔
30
  m_widget->setLayout(lay);
104✔
31
  score::setHelp(m_widget, 
104✔
32
      QObject::tr("This panel prompts the scripting console \n"
104✔
33
                  "still in early development"));
34

35
  m_edit = new QPlainTextEdit{m_widget};
104✔
36
  m_edit->setTextInteractionFlags(Qt::TextEditorInteraction);
104✔
37

38
  lay->addWidget(m_edit, 1);
104✔
39
  m_lineEdit = new score::PromptLineEdit{m_widget};
104✔
40
  static const QString history_save_path
131✔
41
      = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)
54✔
42
        + QDir::separator() + "ossia" + QDir::separator() + "console_history.txt";
27✔
43
  m_lineEdit->historyLoad(history_save_path);
104✔
44

45
  lay->addWidget(m_lineEdit, 0);
104✔
46

47
  m_edit->appendPlainText(
104✔
48
      "Welcome to the ossia score scripting console!\n"
104✔
49
      "The console provides a JavaScript ES7 environment.\n"
50
      "Read more about the avaiable functions here:\n"
51
      "-> https://ossia.io/score-docs/panels/console.html\n\n");
52

53
  auto c = new QJSCompleter{m_lineEdit};
104✔
54
  auto& plug = this->context().guiApplicationPlugin<JS::ApplicationPlugin>();
104✔
55
  auto console_engine_object = plug.m_consoleEngine.globalObject();
104✔
56
  c->registerObject("Score", console_engine_object.property("Score").toQObject());
104✔
57
  c->registerObject("Util", console_engine_object.property("Util").toQObject());
104✔
58
  c->registerObject("Device", console_engine_object.property("Device").toQObject());
104✔
59
  c->setCompletionMode(QCompleter::CompletionMode::PopupCompletion);
104✔
60
  c->setWidget(m_lineEdit);
104✔
61
  m_lineEdit->setCompleter(c);
104✔
62

63
  connect(
104✔
64
      m_lineEdit, &score::PromptLineEdit::lineEntered, this,
104✔
65
      [this](const QString& line) {
104✔
66
    if(!line.isEmpty())
×
67
    {
68
      m_lineEdit->historySave(history_save_path);
×
69

70
      evaluate(line);
×
71
      m_lineEdit->clear();
×
72
      m_edit->verticalScrollBar()->setValue(m_edit->verticalScrollBar()->maximum());
×
73
    }
×
74
  });
×
75
}
×
76

77
QJSEngine& PanelDelegate::engine() noexcept
×
78
{
79
  return m_engine;
×
80
}
81

82
void PanelDelegate::evaluate(const QString& txt)
×
83
{
84
  m_edit->appendPlainText("> " + txt);
×
85
  auto res = m_engine.evaluate(txt);
×
86
  if(res.isError())
×
87
  {
88
    m_edit->appendPlainText("ERROR: " + res.toString() + "\n");
×
89
  }
×
90
  else if(!res.isUndefined())
×
91
  {
92
    m_edit->appendPlainText(res.toString() + "\n");
×
93
  }
×
94
}
×
95

96
void PanelDelegate::compute(const QString& txt, std::function<void(QVariant)> r)
×
97
{
98
  m_edit->appendPlainText("> " + txt);
×
99
  auto res = m_engine.evaluate(txt);
×
100
  if(res.isError())
×
101
  {
102
    m_edit->appendPlainText("ERROR: " + res.toString() + "\n");
×
103
  }
×
104
  else if(!res.isUndefined())
×
105
  {
106
    if(r)
×
107
      r(res.toVariant());
×
108
  }
×
109
  else
110
  {
111
    qDebug() << res.toString();
×
112
  }
113
}
×
114

115
QMenu* PanelDelegate::addMenu(QMenu* cur, QStringList names)
×
116
{
117
  QMenu* newmenu{};
×
118

119
  for(int i = 0; i < names.size() - 1; i++)
×
120
  {
121
    for(auto act : cur->findChildren<QMenu*>(QString{}, Qt::FindDirectChildrenOnly))
×
122
    {
123
      if(act->title() == names[i])
×
124
      {
125
        cur = act;
×
126
        goto ok;
×
127
      }
128
    }
129

130
    newmenu = new QMenu{names[i], cur};
×
131
    cur->addMenu(newmenu);
×
132
    cur = newmenu;
×
133

134
  ok:
135
    continue;
×
136
  }
137
  return cur;
×
138
}
×
139

140
void PanelDelegate::importModule(const QString& path)
×
141
{
142
  // Shared with --script so that both agree on what loading a module means:
143
  // import, run initialize(), publish under the base name.
NEW
144
  QJSValue mod = ApplicationPlugin::importModule(m_engine, path);
×
UNCOV
145
  if(auto init = mod.property("actions"); init.isArray())
×
146
  {
147
    QJSValueIterator it(init);
×
148
    while(it.hasNext())
×
149
    {
150
      if(it.next())
×
151
      {
152
        auto obj = it.value();
×
153
        if(obj.isObject())
×
154
        {
155
          auto name = obj.property("name");
×
156
          auto context = obj.property("context");
×
157
          auto action = obj.property("action");
×
158
          auto shortcut = obj.property("shortcut");
×
159
          if(!name.isString())
×
160
            return;
×
161
          if(context.toString() != "Menu")
×
162
            return;
×
163
          if(!action.isCallable())
×
164
            return;
×
165

166
          auto names = name.toString().split('/');
×
167
          if(names.empty())
×
168
            return;
×
169

170
          score::Menu& script = this->context().menus.get().at(score::Menus::Scripts());
×
171
          if(auto act = script.menu()->actions()[0];
×
172
             act->objectName() == "DefaultScriptMenuAction")
×
173
            script.menu()->removeAction(act);
×
174

175
          auto menu = addMenu(script.menu(), names);
×
176

177
          auto act = new QAction{names.back(), this};
×
178
          if(auto str = shortcut.toString(); !str.isEmpty())
×
179
          {
180
            act->setShortcut(QKeySequence::fromString(str));
×
181
          }
×
182
          connect(
×
183
              act, &QAction::triggered, this, [action = std::move(action)]() mutable {
×
184
                auto res = action.call();
×
185
                if(res.isError())
×
186
                {
187
                  qDebug() << "Script error: " << res.errorType() << res.toString();
×
188
                }
×
189
              });
×
190
          menu->addAction(act);
×
191
        }
×
192
      }
×
193
    }
194
  }
×
195

196
  auto obj = m_engine.globalObject();
×
197
  obj.setProperty(QFileInfo{path}.baseName(), mod);
×
198
  if(mod.isString() || mod.isError())
×
199
    m_edit->appendPlainText(mod.toString());
×
200
}
×
201

202
QWidget* PanelDelegate::widget()
104✔
203
{
204
  return m_widget;
104✔
205
}
206

207
const score::PanelStatus& PanelDelegate::defaultPanelStatus() const
624✔
208
{
209
  static const score::PanelStatus status{
651✔
210
      false,
211
      false,
212
      Qt::BottomDockWidgetArea,
213
      0,
214
      QObject::tr("Console"),
27✔
215
      "console",
27✔
216
      QObject::tr("Ctrl+Shift+C")};
27✔
217

218
  return status;
624✔
219
}
×
220
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc