Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c01ae18
ST6RI-766 Updated syntax highlight to work with jupyter 4.x
TheKorpos Jul 29, 2025
11b27b4
ST6RI-766 Updated target ecmascript script version
TheKorpos Jul 29, 2025
a02da22
ST6RI-766 Updated code template and removed comments
TheKorpos Jul 29, 2025
0ff4116
ST6RI-766 Removed dependency from package.json
TheKorpos Jul 29, 2025
05bfa6e
Merge branch 'master' into ST6RI-766
TheKorpos Jul 31, 2025
d3033f9
ST6RI-766 Removed yarn.lock
TheKorpos Jul 31, 2025
9805673
ST6RI-766 Added yarn.lock to .gitignore
TheKorpos Jul 31, 2025
f381b98
ST6RI-766 Removed comments from mode.ts
TheKorpos Jul 31, 2025
23e83e8
Merge branch 'master' into ST6RI-766
seidewitz Sep 30, 2025
9da911b
ST6RI-766 Updated install scripts in jupyter installer
TheKorpos Oct 2, 2025
5996c6c
Merge branch 'master' into ST6RI-766-1
himi Oct 24, 2025
70f765b
ST6RI-766 Added foldService extension.
himi Oct 25, 2025
6fcb7c2
ST6RI-766 Changed the copyright holder name.
himi Oct 25, 2025
0361a49
ST6RI-766 fold.ts (isInStringCommentOrVariable): Skip variableName
himi Oct 25, 2025
08488f6
ST6RI-766 Updated copyright notices in plugin.ts.
seidewitz Oct 25, 2025
31f1b54
ST6RI-766 Fixed infinite packaging
TheKorpos Oct 27, 2025
e0038e7
ST6RI-766 Updated mode_template.js for xtext_grammar_converter.
seidewitz Oct 27, 2025
0f0a726
Merge branch 'master' into ST6RI-766
seidewitz Oct 27, 2025
7e0eaa0
* mode.ts: name the language as 'sysml' instead of 'clike'
himi Oct 28, 2025
2cfc3e7
Merge branch 'ST6RI-766' of github.com:Systems-Modeling/SysML-v2-Pilo…
himi Oct 28, 2025
e4400de
Changed the mode name to 'sysml' from 'clike'
himi Oct 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion org.omg.sysml.jupyter.installer/install.bat
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ call java -version || goto :error

echo --- Step 3: Installing Jupyter SysML kernel and dependencies ---
call jupyter kernelspec remove sysml -f >nul 2>&1
call conda install "jupyter-sysml-kernel=%SYSML_VERSION%" python=3.* jupyterlab=3.* graphviz=2.* nodejs="<17" -c conda-forge -y || goto:error
call conda install "jupyter-sysml-kernel=%SYSML_VERSION%" python=3.* jupyterlab=4.* graphviz=2.* nodejs -c conda-forge -y || goto:error

echo --- Step 4: Installing JupyterLab SysML extension ---
call jupyter labextension uninstall @systems-modeling/jupyterlab-sysml
Expand Down
2 changes: 1 addition & 1 deletion org.omg.sysml.jupyter.installer/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ java -version

echo "--- Step 3: Installing Jupyter SysML kernel and dependencies ---"
jupyter kernelspec remove sysml -f > /dev/null 2>&1 || true
conda install "jupyter-sysml-kernel=$SYSML_VERSION" python=3.* jupyterlab=3.* graphviz=2.* nodejs="<17" -c conda-forge -y
conda install "jupyter-sysml-kernel=$SYSML_VERSION" python=3.* jupyterlab=4.* graphviz=2.* nodejs -c conda-forge -y

echo "--- Step 4: Installing JupyterLab SysML extension ---"
jupyter labextension uninstall @systems-modeling/jupyterlab-sysml > /dev/null 2>&1 || true
Expand Down
3 changes: 2 additions & 1 deletion org.omg.sysml.jupyter.jupyterlab/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ build
data
log
scrap
.gradle
.gradle
yarn.lock
21 changes: 6 additions & 15 deletions org.omg.sysml.jupyter.jupyterlab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,23 @@
"repository": "github:Systems-Modeling/SysML-v2-Pilot-Implementation",
"author": "SysML v2 Submission Team",
"license": "LGPL-3.0-or-later",
"main": "build/plugin.js",
"keywords": [
"jupyter",
"jupyterlab",
"jupyterlab-extension"
],
"dependencies": {
"@jupyterlab/application": "3.x"
"@jupyterlab/application": "4.x"
},
"devDependencies": {
"@types/codemirror": "^0.0.98",
"@codemirror/legacy-modes": "^6.3.2",
"@jupyterlab/codemirror": ">=4.0",
"@types/json-schema": "*",
"typescript": "<4.4.0"
"typescript": "~5.8.3"
},
"resolutions": {
"@lumino/coreutils": "^1.11.0",
"@lumino/widgets": "^1.37.2",
"lib0": "0.2.108"
},
"peerDependencies": {
"codemirror": "^5.58.1"
},
"files": [
"build/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}"
],
"jupyterlab": {
"extension": "build/plugin.js"
"extension": true
},
"scripts": {
"build": "tsc",
Expand Down
112 changes: 112 additions & 0 deletions org.omg.sysml.jupyter.jupyterlab/src/main/fold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* SysML 2 Pilot Implementation
* Copyright (c) 2020-2025 Mgnite Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* @license LGPL-3.0-or-later <http://spdx.org/licenses/LGPL-3.0-or-later>
*/

import { IEditorExtensionFactory } from "@jupyterlab/codemirror";
import { EditorState, Extension } from '@codemirror/state';
import { foldService, syntaxTree } from '@codemirror/language';
import { SyntaxNode } from '@lezer/common';

function isInStringCommentOrVariable(state: EditorState, pos: number): boolean {
const tree = syntaxTree(state);
let node: SyntaxNode | null = tree.resolveInner(pos, 1);

while (node) {
const nodeType = node.type.name;
// Check if we're in a string, comment, or other non-code context
// Note that 'variableName' correesponds to a quoted name
if (nodeType === 'String' ||
nodeType === 'Comment' ||
nodeType === 'BlockComment' ||
nodeType === 'LineComment' ||
nodeType === 'variableName' ||
nodeType.toLowerCase().includes('string') ||
nodeType.toLowerCase().includes('comment')) {
return true;
}
node = node.parent;
}
return false;
}

function findMatchingCloseBrace(state: EditorState, openPos: number): number | null {
const docLength = state.doc.length;
let nest = 1;
let pos = openPos + 1;

while (pos < docLength && nest > 0) {
const char = state.sliceDoc(pos, pos + 1);
// Skip if we're in a string, comment, or variable
if (!isInStringCommentOrVariable(state, pos)) {
if (char === '{') {
nest++;
} else if (char === '}') {
nest--;
if (nest === 0) {
return pos;
}
}
}
pos++;
}

return null;
}

function computeFoldRange(state: EditorState, lineStart: number, lineEnd: number) {
const lineText = state.sliceDoc(lineStart, lineEnd);

for (let i = 0; i < lineText.length; i++) {
const char = lineText[i];
if (char === '{') {
const absolutePos = lineStart + i;

// Check if this brace is in a string, comment, or variable
if (isInStringCommentOrVariable(state, absolutePos)) {
continue;
}

// Find the matching closing brace
const closePos = findMatchingCloseBrace(state, absolutePos);
if (closePos === null) {
return null;
}

// Check if the fold spans multiple lines
const openLine = state.doc.lineAt(absolutePos);
const closeLine = state.doc.lineAt(closePos);

if (openLine.number === closeLine.number) {
/* Do not fold the same line */
return null;
}

return { from: absolutePos, to: closePos };
}
}

return null;
}

export function sysmlFoldServiceSelection(options: IEditorExtensionFactory.IOptions): Extension {
if (options.model.mimeType === 'text/x-sysml') {
return [ foldService.of(computeFoldRange) ];
} else {
return [];
}
}
141 changes: 61 additions & 80 deletions org.omg.sysml.jupyter.jupyterlab/src/main/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,89 +24,70 @@
*/

// tslint:disable-next-line
import 'codemirror/addon/mode/simple';
//import 'codemirror/addon/mode/simple';

import * as CodeMirror from 'codemirror';

const SI_MODE = 'sysml';
const P_MIME = 'text/x-sysml'
import {StringStream} from "@codemirror/language"
import { clike } from '@codemirror/legacy-modes/mode/clike';

const f_wordify = (h: any, s: string) => ({...h, [s]: true});

export function defineSysMLv2Mode(): void {
CodeMirror.defineMode(SI_MODE, (gc_mode, gc_parser) => {
return CodeMirror.getMode(gc_mode, {
name: 'clike',
keywords: [
"about", "abstract", "accept", "action", "actor", "after", "alias", "all", "allocate", "allocation",
"analysis", "and", "as", "assert", "assign", "assume", "at", "attribute", "bind", "binding", "by",
"calc", "case", "comment", "concern", "connect", "connection", "constant", "constraint", "crosses",
"decide", "def", "default", "defined", "dependency", "derived", "do", "doc", "else", "end", "entry",
"enum", "event", "exhibit", "exit", "expose", "filter", "first", "flow", "for", "fork", "frame", "from",
"hastype", "if", "implies", "import", "in", "include", "individual", "inout", "interface", "istype",
"item", "join", "language", "library", "locale", "loop", "merge", "message", "meta", "metadata", "new",
"nonunique", "not", "objective", "occurrence", "of", "or", "ordered", "out", "package", "parallel",
"part", "perform", "port", "private", "protected", "public", "redefines", "ref", "references", "render",
"rendering", "rep", "require", "requirement", "return", "satisfy", "send", "snapshot", "specializes",
"stakeholder", "standard", "state", "subject", "subsets", "succession", "terminate", "then",
"timeslice", "to", "transition", "until", "use", "variant", "variation", "verification", "verify",
"via", "view", "viewpoint", "when", "while", "xor"
].reduce(f_wordify, {}),
defKeywords: [
"action", "allocation", "analysis", "attribute", "binding", "calc", "case", "comment", "concern",
"connection", "constraint", "def", "doc", "enum", "flow", "interface", "item", "metadata", "objective",
"occurrence", "package", "part", "port", "ref", "rendering", "rep", "requirement", "snapshot", "state",
"subject", "succession", "timeslice", "transition", "verification", "view", "viewpoint"
].reduce(f_wordify, {}),
typeFirstDefinitions: true,
atoms: ['true', 'false', 'null'].reduce(f_wordify),
number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
modeProps: {
fold: ['brace'],
export const sysmlparser = clike({
name: 'clike',
keywords: [
"about", "abstract", "accept", "action", "actor", "after", "alias", "all", "allocate", "allocation",
"analysis", "and", "as", "assert", "assign", "assume", "at", "attribute", "bind", "binding", "by",
"calc", "case", "comment", "concern", "connect", "connection", "constant", "constraint", "crosses",
"decide", "def", "default", "defined", "dependency", "derived", "do", "doc", "else", "end", "entry",
"enum", "event", "exhibit", "exit", "expose", "filter", "first", "flow", "for", "fork", "frame", "from",
"hastype", "if", "implies", "import", "in", "include", "individual", "inout", "interface", "istype",
"item", "join", "language", "library", "locale", "loop", "merge", "message", "meta", "metadata", "new",
"nonunique", "not", "objective", "occurrence", "of", "or", "ordered", "out", "package", "parallel",
"part", "perform", "port", "private", "protected", "public", "redefines", "ref", "references", "render",
"rendering", "rep", "require", "requirement", "return", "satisfy", "send", "snapshot", "specializes",
"stakeholder", "standard", "state", "subject", "subsets", "succession", "terminate", "then",
"timeslice", "to", "transition", "until", "use", "variant", "variation", "verification", "verify",
"via", "view", "viewpoint", "when", "while", "xor"
].reduce(f_wordify, {}),
types: [
"action", "allocation", "analysis", "attribute", "binding", "calc", "case", "comment", "concern",
"connection", "constraint", "def", "doc", "enum", "flow", "interface", "item", "metadata", "objective",
"occurrence", "package", "part", "port", "ref", "rendering", "rep", "requirement", "snapshot", "state",
"subject", "succession", "timeslice", "transition", "verification", "view", "viewpoint"
].reduce(f_wordify, {}),
atoms: ['true', 'false', 'null'].reduce(f_wordify, {}),
number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
hooks: {
"'": function(stream: StringStream) {
let b_escaped = false;
let s_next;
while(s_next = stream.next()) {
if(s_next === "'" && !b_escaped) break;
b_escaped = !b_escaped && s_next === '\\';
}
return 'variable';
},
'/': function(stream: StringStream) {
if(stream.match('/*', false)) stream.next();
return false;
},
hooks: {
"'": function(stream: CodeMirror.StringStream) {
let b_escaped = false;
let s_next;
while(s_next = stream.next()) {
if(s_next === "'" && !b_escaped) break;
b_escaped = !b_escaped && s_next === '\\';
"#": function(stream: StringStream) {
let b_first = true;
do {
if (stream.match("'", true)) {
let b_escaped = false;
let s_next;
while(s_next = stream.next()) {
if(s_next === "'" && !b_escaped) break;
b_escaped = !b_escaped && s_next === '\\';
}
} else if (stream.match(/\w/, true)) {
stream.eatWhile(/\w/);
} else if (b_first) {
return 'operator';
}
return 'variable';
},
'/': function(stream: CodeMirror.StringStream) {
if(stream.match('/*', false)) stream.next();
return false;
},
"#": function(stream: CodeMirror.StringStream) {
let b_first = true;
do {
if (stream.match("'", true)) {
let b_escaped = false;
let s_next;
while(s_next = stream.next()) {
if(s_next === "'" && !b_escaped) break;
b_escaped = !b_escaped && s_next === '\\';
}
} else if (stream.match(/\w/, true)) {
stream.eatWhile(/\w/);
} else if (b_first) {
return 'operator';
}
b_first = false;
} while (stream.match('::', true))
return 'keyword';
},
b_first = false;
} while (stream.match('::', true))
return 'keyword';
},
});
});

CodeMirror.defineMIME(P_MIME, SI_MODE);

(CodeMirror as any).modeInfo.push({
ext: ['sysml'],
mime: P_MIME,
mode: SI_MODE,
name: 'sysml',
});
}
},
}
);
53 changes: 34 additions & 19 deletions org.omg.sysml.jupyter.jupyterlab/src/main/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/*
* SysML 2 Pilot Implementation
* Copyright (C) 2020 California Institute of Technology ("Caltech")
* Copyright (C) 2025 Model Driven Solutions, Inc.
* Copyright (C) 2025 Mgnite Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
Expand All @@ -17,26 +19,39 @@
* @license LGPL-3.0-or-later <http://spdx.org/licenses/LGPL-3.0-or-later>
*/

import {
JupyterLab,
JupyterFrontEndPlugin,
} from '@jupyterlab/application';
import { JupyterFrontEndPlugin, JupyterFrontEnd} from '@jupyterlab/application';
import { IEditorLanguageRegistry, EditorLanguageRegistry,
IEditorExtensionRegistry, EditorExtensionRegistry,
IEditorExtensionFactory } from "@jupyterlab/codemirror";

import {
defineSysMLv2Mode,
} from './mode';
import { sysmlparser } from './mode';
import { sysmlFoldServiceSelection } from './fold';

function activate(app: JupyterLab) {
defineSysMLv2Mode();
}
const plugin: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-sysml:plugin',
description: 'A JupyterLab extension adding a syntax highlight for SysMLv2 language.',
autoStart: true,
requires: [IEditorLanguageRegistry, IEditorExtensionRegistry],
activate: (app: JupyterFrontEnd, languages: IEditorLanguageRegistry, ext: IEditorExtensionRegistry) => {

/**
* Initialization data for extension
*/
const extension: JupyterFrontEndPlugin<void> = {
activate,
autoStart: true,
id: 'jupyterlab-sysml:plugin',
};
languages.addLanguage({
name: 'sysml',
displayName: 'sysml',
mime: 'text/x-sysml',
extensions: ['sysml'],
load: async () => {
return EditorLanguageRegistry.legacy(sysmlparser)
}
})

const sysmlFoldExtension = Object.freeze({
name: 'sysml-fold-extension',
default: true,
factory: (options: IEditorExtensionFactory.IOptions) =>
EditorExtensionRegistry.createConditionalExtension(sysmlFoldServiceSelection(options))

});
ext.addExtension(sysmlFoldExtension);
}}

export default extension;
export default plugin;
Loading