Skip to content

Commit 635ee45

Browse files
committed
ST6RI-823 Allow external magic command defininitions in Jupyter
This commit adds a service registration based mechanism that can be used to register additional magic commands to the system. Furthermore, to allow these commands to add new items to the built-in help system, and appropriate extension method was added to SysMLInteractiveHelp
1 parent 633e761 commit 635ee45

6 files changed

Lines changed: 101 additions & 56 deletions

File tree

org.omg.sysml.interactive/src/org/omg/sysml/interactive/SysMLInteractiveHelp.java

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,33 @@
3030

3131
import java.util.HashMap;
3232
import java.util.Map;
33+
import java.util.TreeMap;
3334

3435
import org.omg.sysml.plantuml.SysML2PlantUMLStyle;
3536

37+
import com.google.common.base.Preconditions;
38+
3639
public class SysMLInteractiveHelp {
3740

38-
private static final String GENERAL_HELP_STRING =
41+
42+
private static final String GENERAL_HELP_STRING_PREFIX =
3943
"The following SysML v2 magic commands are available.\n"
4044
+ "For help on a specific command, use \"%help <COMMAND>\" or \"%<cmd> -h\".\n\n"
41-
+ "%eval\t\tEvaluate a given expression.\n"
42-
+ "%export\t\tSave a file of the JSON representation of the abstract syntax tree rooted in the named element.\n"
43-
+ "%help\t\tGet a list of available commands or help on a specific command\n"
44-
+ "%list\t\tList loaded library packages or the results of a given query\n"
45-
+ "%load\tLoads a model from the repository and adds it to the Xtext index\n"
46-
+ "%show\t\tPrint the abstract syntax tree rooted in a named element\n"
47-
+ "%projects\tList projects in the repository\n"
48-
+ "%publish\tPublish to the repository the modele elements rooted in a named element\n"
49-
+ "%view\t\tRender the view specified by the named view usage\n"
50-
+ "%viz\t\tVisualize the name model elements\n"
5145
;
5246

47+
private static final String HELP_HELP_SHORT_STRING = "%help\t\tGet a list of available commands or help on a specific command";
5348
private static final String HELP_HELP_STRING =
5449
"Usage: %help [<COMMAND>]\n\n"
5550
+ "Print help information on the named SysML v2 magic <COMMAND>.\n"
5651
+ "If no <COMMAND> is given, then list the available commands.\n";
5752

53+
private static final String EVAL_HELP_SHORT_STRING = "%eval\t\tEvaluate a given expression.";
5854
private static final String EVAL_HELP_STRING =
5955
"Usage: %eval [--target=<NAME>] <EXPR>\n\n"
6056
+ "Print the results of evaluating <EXPR> on the target given by <NAME>, which must be fully qualified.\n"
6157
+ "If a target is not given, then evaluate <EXPR> in global scope.\n";
6258

59+
private static final String LIST_HELP_SHORT_STRING = "%list\t\tList loaded library packages or the results of a given query";
6360
private static final String LIST_HELP_STRING =
6461
"Usage: %list [<QUERY>]\n\n"
6562
+ "If <QUERY> is not given, then list all loaded library packages.\n"
@@ -71,17 +68,24 @@ public class SysMLInteractiveHelp {
7168
+ " <NAME>::**\t\tall members of the namespace <NAME> and, recursively, members of owned namespaces.\n"
7269
+ "The last two forms may be optionally followed by a filter expression in square brackets.\n";
7370

71+
private static final String SHOW_HELP_SHORT_STRING =
72+
"%show\t\tPrint the abstract syntax tree rooted in a named element";
7473
private static final String SHOW_HELP_STRING =
7574
"Usage: %show [--style=<STYLE>] <NAME>\n\n"
7675
+ "Print the abstract syntax tree rooted in <NAME>. <NAME> must be fully qualified.\n\n"
7776
+ "<STYLE> is also case insensitive. The possible style names are:\n"
7877
+ " TREE\t\tHierarchically indented representation with only identifying information\n"
7978
+ " JSON\t\tComplete JSON representation of the tree\n";
8079

80+
private static final String PUBLISH_HELP_SHORT_STRING =
81+
"%publish\tPublish to the repository the modele elements rooted in a named element";
8182
private static final String PUBLISH_HELP_STRING =
8283
"Usage: %publish <NAME>\n\n"
8384
+ "Publish the model elements rooted in <NAME> to the repository. <NAME> must be fully qualified.\n";
8485

86+
87+
private static final String VIZ_HELP_SHORT_STRING =
88+
"%viz\t\tVisualize the name model elements";
8589
private static final String VIZ_HELP_STRING =
8690
"Usage: %viz [--view=<VIEW>] [--style=<STYLE>...] <NAME> [<NAME>...]\n\n"
8791
+ "Visualize model elements of <NAME>(s). <NAME>s must be fully qualified.\n\n"
@@ -99,6 +103,8 @@ public class SysMLInteractiveHelp {
99103
+ "\t%viz --view Tree --style LR --style ortholine Pkg1::PartDef Pkg1::Pkg2::partUsage\n"
100104
+ "should visualize Pkg1::PartDef and Pkg1::Pkg2::partUsage with a tree view ordered in the left-to-right direction with orthogonal lines.\n";
101105

106+
private static final String VIEW_HELP_SHORT_STRING =
107+
"%view\t\tRender the view specified by the named view usage";
102108
private static final String VIEW_HELP_STRING =
103109
"Usage: %view [--render=<RENDERING>] [--style=<STYLE>...] <NAME>\n\n"
104110
+ "Render the view specified by the view usage <NAME>. <NAME> must be fully qualified.\n"
@@ -114,23 +120,38 @@ public class SysMLInteractiveHelp {
114120
+ SysML2PlantUMLStyle.getStyleHelp();
115121

116122

123+
private static final String EXPORT_HELP_SHORT_STRING =
124+
"%export\t\tSave a file of the JSON representation of the abstract syntax tree rooted in the named element.";
117125
private static final String EXPORT_HELP_STRING =
118126
"Usage: %export <NAME>\n\n"
119127
+ "Save a file containing the complete JSON representation of the abstract syntax tree rooted in <NAME>.\n"
120128
+ "<NAME> must be fully qualified.\n";
121129

130+
private static final String LOAD_HELP_SHORT_STRING = "%load\t\tLoads a model from the repository and adds it to the Xtext index";
122131
private static final String LOAD_HELP_STRING =
123132
"Usage: %load [--id=<PROJECT ID] [--name=<NAME>] [<NAME>]\n\n"
124-
+ "Downloads previously published models from the repository. <NAME> must be the full name of the project.\n"
133+
+ "Downloads previously published models from the repository. <NAME> must be the full name of the project."
125134
+ "Use --id=<PROJECT ID> to load projects by id.\n"
126135
+ "Use %projects to view repository contents.\n";
127136

137+
private static final String PROJECTS_HELP_SHORT_STRING = "%load\t\tLoads a model from the repository and adds it to the Xtext index";
128138
private static final String PROJECTS_HELP_STRING =
129139
"Usage: %projects\n\n"
130140
+ "Returns the name and identifier from the repository for all publications\n";
131141

142+
private static String generalHelpCached;
143+
132144
public static String getGeneralHelp() {
133-
return GENERAL_HELP_STRING;
145+
if (generalHelpCached == null) {
146+
StringBuilder sb = new StringBuilder();
147+
sb.append(GENERAL_HELP_STRING_PREFIX);
148+
for (var entry: commandShortHelpMap.entrySet()) {
149+
sb.append(entry.getValue());
150+
sb.append(System.lineSeparator());
151+
}
152+
generalHelpCached = sb.toString();
153+
}
154+
return generalHelpCached;
134155
}
135156

136157
public static String getHelpHelp() {
@@ -173,22 +194,37 @@ public static String getLoadHelp() {
173194
return LOAD_HELP_STRING;
174195
}
175196

176-
private static Map<String, String> commandHelpMap = createCommandHelpMap();
197+
private static Map<String, String> commandShortHelpMap = new TreeMap<>();
198+
private static Map<String, String> commandHelpMap = new HashMap<>();
199+
static {
200+
registerHelpString("%help", HELP_HELP_SHORT_STRING, HELP_HELP_STRING);
201+
registerHelpString("%eval", EVAL_HELP_SHORT_STRING, EVAL_HELP_STRING);
202+
registerHelpString("%list", LIST_HELP_SHORT_STRING, LIST_HELP_STRING);
203+
registerHelpString("%show", SHOW_HELP_SHORT_STRING, SHOW_HELP_STRING);
204+
registerHelpString("%publish", PUBLISH_HELP_SHORT_STRING, PUBLISH_HELP_STRING);
205+
registerHelpString("%viz", VIZ_HELP_SHORT_STRING, VIZ_HELP_STRING);
206+
registerHelpString("%view", VIEW_HELP_SHORT_STRING, VIEW_HELP_STRING);
207+
registerHelpString("%export", EXPORT_HELP_SHORT_STRING, EXPORT_HELP_STRING);
208+
registerHelpString("%load", LOAD_HELP_SHORT_STRING, LOAD_HELP_STRING);
209+
registerHelpString("%projects", PROJECTS_HELP_SHORT_STRING, PROJECTS_HELP_STRING);
210+
}
177211

178-
private static Map<String, String> createCommandHelpMap() {
179-
Map<String, String> map = new HashMap<>();
180-
map.put("%help", HELP_HELP_STRING);
181-
map.put("%eval", EVAL_HELP_STRING);
182-
map.put("%list", LIST_HELP_STRING);
183-
map.put("%show", SHOW_HELP_STRING);
184-
map.put("%publish", PUBLISH_HELP_STRING);
185-
map.put("%viz", VIZ_HELP_STRING);
186-
map.put("%view", VIEW_HELP_STRING);
187-
map.put("%export", EXPORT_HELP_STRING);
188-
map.put("%load", LOAD_HELP_STRING);
189-
map.put("%projects", PROJECTS_HELP_STRING);
190-
191-
return map;
212+
/**
213+
* Adds command helps to the centralized store.
214+
* @param command The name of the magic command, including the '%' prefix
215+
* @param shortDescription
216+
* A one-line description of a command, including the command name with the '%' prefix
217+
* one or two tabs to make the output of the %help command nicely formatted.
218+
* @param detailedHelp
219+
* A multi-line help for a given command, expected to also detail the parameter formatting
220+
*
221+
* @throws IllegalStateException when help for a command is being re-registered
222+
*/
223+
public static void registerHelpString(String command, String shortDescription, String detailedHelp) {
224+
Preconditions.checkState(!commandHelpMap.containsKey(command), "Help is already registered for command %s", command);
225+
commandShortHelpMap.put(command, shortDescription);
226+
commandHelpMap.put(command, detailedHelp);
227+
generalHelpCached = null;
192228
}
193229

194230
public static String getHelpString(String command) {

org.omg.sysml.jupyter.kernel/.classpath

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
</classpathentry>
2222
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
2323
<attributes>
24-
<attribute name="module" value="true"/>
2524
<attribute name="maven.pomderived" value="true"/>
2625
</attributes>
2726
</classpathentry>
@@ -37,12 +36,6 @@
3736
<attribute name="maven.pomderived" value="true"/>
3837
</attributes>
3938
</classpathentry>
40-
<classpathentry kind="src" output="target/classes" path="target/kernel">
41-
<attributes>
42-
<attribute name="optional" value="true"/>
43-
<attribute name="maven.pomderived" value="true"/>
44-
</attributes>
45-
</classpathentry>
4639
<classpathentry combineaccessrules="false" kind="src" path="/org.omg.sysml.interactive"/>
4740
<classpathentry combineaccessrules="false" kind="src" path="/org.omg.sysml"/>
4841
<classpathentry kind="output" path="target/classes"/>

org.omg.sysml.jupyter.kernel/pom.xml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -200,25 +200,6 @@
200200
</execution>
201201
</executions>
202202
</plugin>
203-
204-
<!-- Needed for updating the version number in the kernel.json. This is used to copy the resources to the unzipped kernel.-->
205-
<plugin>
206-
<groupId>org.codehaus.mojo</groupId>
207-
<artifactId>templating-maven-plugin</artifactId>
208-
<version>3.0.0</version>
209-
<executions>
210-
<execution>
211-
<phase>prepare-package</phase>
212-
<goals>
213-
<goal>filter-sources</goal>
214-
</goals>
215-
</execution>
216-
</executions>
217-
<configuration>
218-
<sourceDirectory>src/main/resources</sourceDirectory>
219-
<outputDirectory>target/kernel</outputDirectory>
220-
</configuration>
221-
</plugin>
222203

223204
<plugin>
224205
<groupId>org.apache.maven.plugins</groupId>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*****************************************************************************
2+
* SysML 2 Pilot Implementation
3+
* Copyright (c) 2025 Model Driven Solutions, Inc.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of theGNU Lesser General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
* @license LGPL-3.0-or-later <http://spdx.org/licenses/LGPL-3.0-or-later>
19+
*
20+
* Contributors:
21+
* Zoltan Ujhelyi, MDS
22+
*
23+
*****************************************************************************/
24+
package org.omg.sysml.jupyter.kernel;
25+
26+
import io.github.spencerpark.jupyter.kernel.magic.registry.Magics;
27+
28+
public interface IMagicCommandRegistrator {
29+
30+
void registerMagicCommand(Magics magics);
31+
}

org.omg.sysml.jupyter.kernel/src/main/java/org/omg/sysml/jupyter/kernel/SysMLKernel.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Arrays;
3737
import java.util.List;
3838
import java.util.Optional;
39+
import java.util.ServiceLoader;
3940

4041
public class SysMLKernel extends BaseKernel {
4142

@@ -82,6 +83,8 @@ public SysMLKernel() {
8283
this.magics.registerMagics(Export.class);
8384
this.magics.registerMagics(Projects.class);
8485
this.magics.registerMagics(Load.class);
86+
87+
ServiceLoader.load(IMagicCommandRegistrator.class).forEach(reg -> reg.registerMagicCommand(this.magics));
8588

8689
this.magicParser = new MyMagicParser();
8790
}

org.omg.sysml.jupyter.kernel/src/main/resources/sysml/kernel.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"argv": [
33
"java",
4-
"-jar",
5-
"@KERNEL_INSTALL_DIRECTORY@/jupyter-sysml-kernel-${project.version}-all.jar",
4+
"-cp",
5+
"@KERNEL_INSTALL_DIRECTORY@/*",
6+
"org.omg.sysml.jupyter.kernel.ISysML",
67
"{connection_file}"
78
],
89
"display_name": "SysML",

0 commit comments

Comments
 (0)