Skip to content

Commit c216381

Browse files
philipc-mwkrisctl
authored andcommittedAug 6, 2024
Introduces MATLAB language syntax highlighting for JupyterLab 4.
1 parent 3d770c1 commit c216381

32 files changed

+9074
-6628
lines changed
 

‎.github/workflows/publish-jupyter-matlab-proxy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ jobs:
4444
run: python3 -m hatch build -t wheel
4545

4646
- name: Publish to PyPI.
47-
uses: pypa/gh-action-pypi-publish@release/v1
47+
uses: pypa/gh-action-pypi-publish@release/v1

‎.github/workflows/run-e2e-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
run: |
4949
python3 -m pip install --upgrade pip
5050
python3 -m pip install ".[dev]"
51-
python3 -m pip install "jupyterlab>3.1.0,<4.0.0"
51+
python3 -m pip install "jupyterlab>4.0.0,<5.0.0"
5252
5353
- name: Install playwright browsers
5454
run: npx playwright install --with-deps

‎.github/workflows/run-unit-tests.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,32 @@ jobs:
7878
path: ./coverage.xml
7979
retention-days: 5
8080

81+
lezer_unit_tests:
82+
strategy:
83+
fail-fast: false
84+
matrix:
85+
os: [ubuntu-latest, windows-latest, macos-latest]
86+
87+
runs-on: ${{ matrix.os }}
88+
defaults:
89+
run:
90+
working-directory: src/jupyter_matlab_labextension/src/lezer-matlab/test/
91+
env:
92+
NODE_VERSION: 18
93+
steps:
94+
- name: Checkout
95+
uses: actions/checkout@v4
96+
97+
- uses: actions/setup-node@v4
98+
with:
99+
node-version: ${{ env.NODE_VERSION }}
100+
101+
- name: Install node dependencies and build parser
102+
run: npm install
103+
104+
- name: Run Lezer tests
105+
run: npm test
106+
81107
upload_code_coverage:
82108
name: "Upload Code Coverage using codecov"
83109
needs: [python_unit_tests]

‎.github/workflows/test-jupyter-matlab-proxy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ jobs:
2626
call-e2e-tests:
2727
# Run the end-to-end tests
2828
uses: ./.github/workflows/run-e2e-tests.yml
29-
secrets: inherit
29+
secrets: inherit

‎.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ test-results
1717
jupyterlab.log
1818
jlab.pid
1919
licensing-logs
20+
.yarn
21+
src/jupyter_matlab_labextension/src/lezer-matlab/src/parser.js
22+
src/jupyter_matlab_labextension/src/lezer-matlab/src/parser.terms.js

‎README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,11 @@ Install Jupyter Notebook or JupyterLab:
107107
# For Jupyter Notebook
108108
python -m pip install notebook
109109

110-
# For JupyterLab 3
111-
python -m pip install 'jupyterlab>=3.0.0,<4.0.0a0'
110+
# For JupyterLab 4
111+
python -m pip install jupyterlab
112112
```
113113

114-
Note: the package allows you to execute MATLAB code in both JupyterLab 3 and JupyterLab 4, but syntax highlighting and auto indentation are currently only supported on JupyterLab 3.
115-
116-
> :warning: **Functionality being removed or changed.** </br>
117-
> Project Jupyter ended maintenance of JupyterLab 3 on May 15, 2024. For details, see [JupyterLab 3 End of Maintenance](https://blog.jupyter.org/jupyterlab-3-end-of-maintenance-879778927db2).
118-
> Starting from a future release of MATLAB Integration _for Jupyter_, editor features such as syntax highlighting and autoindentation will be removed from JupyterLab 3 and introduced for JupyterLab 4. Execution of MATLAB code will remain available with both JupyterLab 3 and JupyterLab 4.
114+
Note: you can use this package to execute MATLAB code in both JupyterLab 3 and 4, but syntax highlighting is only supported on JupyterLab 4.
119115

120116

121117
Open your Jupyter environment by starting Jupyter Notebook or JupyterLab.
@@ -186,7 +182,7 @@ You can also edit MATLAB `.m` files in JupyterLab. Click the `MATLAB File` butto
186182

187183
<p align="center"><img width="300" src="https://github.com/mathworks/jupyter-matlab-proxy/raw/main/img/matlabfile-icon.png"></p>
188184

189-
This opens an untitled `.m` file where you can write MATLAB code with syntax highlighting and auto indentation.
185+
This opens an untitled `.m` file where you can write MATLAB code with syntax highlighting.
190186

191187
<p align="center"><img width="600" src="https://github.com/mathworks/jupyter-matlab-proxy/raw/main/img/new-matlab-file.png"></p>
192188

‎pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2023-2024 The MathWorks, Inc.
22

33
[build-system]
4-
requires = ["jupyterlab>=3.1,<4.0.0", "hatchling"]
4+
requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0.3.2"]
55
build-backend = "hatchling.build"
66

77
[project]
@@ -63,6 +63,8 @@ matlab = "jupyter_matlab_proxy:setup_matlab"
6363
[project.entry-points.matlab_proxy_configs]
6464
Jupyter = "jupyter_matlab_proxy.jupyter_config:config"
6565

66+
[tool.hatch.version]
67+
source = "nodejs"
6668

6769
[tool.hatch.build.targets.wheel]
6870
packages = ["src/jupyter_matlab_kernel", "src/jupyter_matlab_proxy"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

‎src/jupyter_matlab_labextension/package.json

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "jupyter_matlab_labextension",
3-
"version": "0.1.0",
3+
"version": "1.0.0",
44
"description": "A JupyterLab extension.",
55
"keywords": [
66
"jupyter",
77
"jupyterlab",
88
"jupyterlab-extension"
99
],
10-
"license": "BSD-3-Clause",
10+
"license": "SEE LICENSE IN LICENSE.md",
1111
"author": {
1212
"name": "The MathWorks Inc.",
1313
"email": "jupyter-support@mathworks.com"
@@ -28,7 +28,8 @@
2828
"build:prod": "jlpm clean && jlpm build:lib && jlpm build:labextension",
2929
"build:labextension": "jupyter labextension build .",
3030
"build:labextension:dev": "jupyter labextension build --development True .",
31-
"build:lib": "tsc",
31+
"build:lib": "jlpm build:lezer && tsc",
32+
"build:lezer": "cd src/lezer-matlab && npm run build",
3233
"clean": "jlpm clean:lib",
3334
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
3435
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
@@ -50,30 +51,39 @@
5051
"audit:fix": "npx yarn-audit-fix"
5152
},
5253
"dependencies": {
53-
"@jupyterlab/application": "^3.1.0",
54-
"@jupyterlab/launcher-extension": "",
55-
"@jupyterlab/notebook": "",
56-
"@types/codemirror": ""
54+
"@codemirror/language": "^6.0.0",
55+
"@jupyterlab/application": "^4.0.0",
56+
"@jupyterlab/apputils": "^4.0.0",
57+
"@jupyterlab/codemirror": "^4.0.0",
58+
"@jupyterlab/coreutils": "^6.0.0",
59+
"@jupyterlab/docregistry": "^4.0.0",
60+
"@jupyterlab/launcher": "^4.0.0",
61+
"@jupyterlab/notebook": "^4.0.0",
62+
"@jupyterlab/ui-components": "^4.0.0",
63+
"@lumino/coreutils": "^2.0.0",
64+
"@lumino/disposable": "^2.0.0",
65+
"@rollup/plugin-node-resolve": "^9.0.0"
5766
},
5867
"devDependencies": {
59-
"@jupyterlab/builder": "^3.1.0",
60-
"@typescript-eslint/eslint-plugin": "^5.0.0",
61-
"@typescript-eslint/parser": "^5.30.7",
62-
"eslint": "^8.0.1",
68+
"@jupyterlab/builder": ">=4.0.0",
69+
"@typescript-eslint/eslint-plugin": "^5.62.0",
70+
"@typescript-eslint/parser": "^5.62.0",
71+
"eslint": "^8.57.0",
6372
"eslint-config-standard": "^17.0.0",
6473
"eslint-config-standard-with-typescript": "^22.0.0",
6574
"eslint-plugin-import": "^2.25.2",
6675
"eslint-plugin-n": "^15.0.0",
6776
"eslint-plugin-node": "^11.1.0",
6877
"eslint-plugin-promise": "^6.0.0",
6978
"npm-run-all": "^4.1.5",
70-
"prettier": "^2.1.1",
71-
"rimraf": "^3.0.2",
79+
"prettier": "^2.8.7",
80+
"rimraf": "^4.4.1",
81+
"rollup": "^2.52.2",
7282
"semver": ">=5.7.2",
73-
"typescript": "~4.1.3",
83+
"typescript": "~5.0.2",
84+
"ws": "^7.5.10",
7485
"yarn-audit-fix": "^10.0.1",
75-
"yarn-deduplicate": "^6.0.2",
76-
"ws": "^7.5.10"
86+
"yarn-deduplicate": "^6.0.2"
7787
},
7888
"sideEffects": [
7989
"style/*.css",
@@ -90,7 +100,7 @@
90100
"jupyter-releaser": {
91101
"hooks": {
92102
"before-build-npm": [
93-
"python -m pip install jupyterlab~=3.1",
103+
"python -m pip install jupyterlab~=4",
94104
"jlpm"
95105
],
96106
"before-build-python": [
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
3+
import { parser } from '../lezer-matlab/dist/index';
4+
import { LRLanguage, LanguageSupport } from '@codemirror/language';
5+
6+
// Define a CodeMirror language from the Lezer parser.
7+
// https://codemirror.net/docs/ref/#language.LRLanguage
8+
export const matlabLanguage = LRLanguage.define({
9+
name: 'matlab',
10+
parser,
11+
languageData: {
12+
commentTokens: { line: '%' }
13+
}
14+
});
15+
16+
// MATLAB language support
17+
export function matlab () {
18+
return new LanguageSupport(matlabLanguage);
19+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Copyright 2023 The MathWorks, Inc.
1+
// Copyright 2023-2024 The MathWorks, Inc.
22

33
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
44
import { matlabToolbarButtonPlugin } from './matlab_browser_button';
55
import { matlabMFilesPlugin } from './matlab_files';
6-
import { matlabCodeMirrorPlugin } from './matlab_cm_mode';
6+
import { matlabCodeMirror6Plugin } from './matlab_cm6_mode';
77

8-
const plugins: JupyterFrontEndPlugin<any>[] = [matlabToolbarButtonPlugin, matlabMFilesPlugin, matlabCodeMirrorPlugin];
8+
const plugins: JupyterFrontEndPlugin<any>[] = [matlabToolbarButtonPlugin, matlabMFilesPlugin, matlabCodeMirror6Plugin];
99
export default plugins;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
import {LRParser} from "@lezer/lr"
3+
4+
export const parser: LRParser
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
import { LRParser } from '@lezer/lr';
3+
4+
export const parser: LRParser;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
{
3+
"name": "@lezer/matlab",
4+
"version": "1.0.0",
5+
"description": "Lezer-based MATLAB grammar",
6+
"main": "dist/index.cjs",
7+
"type": "module",
8+
"exports": {
9+
"import": "./dist/index.js",
10+
"require": "./dist/index.cjs"
11+
},
12+
"module": "dist/index.js",
13+
"types": "dist/index.d.ts",
14+
"author": {
15+
"name": "The MathWorks Inc.",
16+
"email": "jupyter-support@mathworks.com"
17+
},
18+
"license": "SEE LICENSE IN LICENSE.md",
19+
"devDependencies": {
20+
"@lezer/generator": "^1.0.0",
21+
"mocha": "^10.2.0",
22+
"rollup": "^2.52.2",
23+
"@rollup/plugin-node-resolve": "^9.0.0"
24+
},
25+
"dependencies": {
26+
"@lezer/common": "^1.2.0",
27+
"@lezer/lr": "^1.0.0",
28+
"@lezer/highlight": "^1.0.0"
29+
},
30+
"scripts": {
31+
"build": "lezer-generator src/matlab.grammar -o src/parser && rollup -c && npm run copy-lezer-files-to-build-on-windows",
32+
"build-debug": "lezer-generator src/matlab.grammar --names -o src/parser && rollup -c && npm run copy-lezer-files-to-build-on-windows",
33+
"copy-lezer-files-to-build-on-windows": "cp src/*.js dist/",
34+
"prepare": "npm run build",
35+
"test": "mocha test/test-*.js"
36+
}
37+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
3+
import { nodeResolve } from '@rollup/plugin-node-resolve';
4+
5+
export default {
6+
input: "./src/parser.js",
7+
output: [{
8+
format: "cjs",
9+
file: "./dist/index.cjs"
10+
}, {
11+
format: "es",
12+
file: "./dist/index.js"
13+
}],
14+
external(id) { return !/^[\.\/]/.test(id) },
15+
plugins: [
16+
nodeResolve()
17+
]
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
3+
import { styleTags, tags as t } from '@lezer/highlight';
4+
5+
// Associate nodes in the Lezer tree with styles.
6+
// https://lezer.codemirror.net/docs/ref/#highlight.styleTags
7+
export const matlabHighlighting = styleTags({
8+
Keyword: t.keyword,
9+
VariableName: t.variableName,
10+
LineComment: t.comment,
11+
MultilineComment: t.comment,
12+
String: t.string,
13+
'( )': t.paren,
14+
'[ ]': t.squareBracket,
15+
'{ }': t.brace
16+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
3+
@top Script { expression* }
4+
5+
expression {
6+
VariableName |
7+
String |
8+
MultilineComment |
9+
keyword |
10+
Symbol |
11+
SystemCommand
12+
}
13+
14+
// See https://lezer.codemirror.net/docs/guide/ for documentation on syntax
15+
// specific to @tokens blocks, and how it differs from regular expression syntax.
16+
@tokens {
17+
VariableName { $[a-zA-Z0-9_]+ $[a-zA-Z0-9_']? }
18+
charVector { '"' (!["\n])* '"' }
19+
stringArray { "'" (!['\n])* "'" }
20+
SystemCommand { "!" (![\n])* }
21+
Symbol { "+" | "-" | "*" | "=" | ";" | ":" | "(" | ")" | "{" | "}" | "[" | "]" }
22+
space { @whitespace+ }
23+
@precedence { SystemCommand, VariableName }
24+
@precedence { SystemCommand, space }
25+
@precedence { VariableName, charVector }
26+
@precedence { VariableName, stringArray }
27+
}
28+
29+
String { charVector | stringArray }
30+
31+
// Once a string has been parsed and found to be a VariableName, it will then
32+
// be tested against its specialize table, to test if it is a keyword.
33+
// The keyword node name is "Keyword".
34+
// https://lezer.codemirror.net/docs/guide/#token-specialization
35+
keyword {
36+
@specialize[@name=Keyword]<VariableName, "break" | "case" | "classdef" | "continue" | "global" | "otherwise" | "persistent" | "return" | "spmd" | "arguments" | "enumeration" | "events" | "for" | "function" | "if" | "methods" | "parfor" | "properties" | "try" | "while" | "elseif" | "else" | "end" | "switch" | "catch">
37+
}
38+
39+
@skip { space | LineComment }
40+
41+
@external propSource matlabHighlighting from "./highlight"
42+
43+
// Call out to comment parser.
44+
@external tokens parseComments from "./parse_comments" { MultilineComment, LineComment }
45+
46+
@detectDelim

0 commit comments

Comments
 (0)
Please sign in to comment.