Skip to content

Commit c7ac9f9

Browse files
authored
Merge pull request #55 from mathworks/dklilley/release/1.3.0
MATLAB language server - v1.3.0
2 parents bb6f79e + a9b0cf5 commit c7ac9f9

37 files changed

+1577
-145
lines changed

.eslintrc

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
"@typescript-eslint/semi": "off",
2222
"@typescript-eslint/no-inferrable-types": "off",
2323
"@typescript-eslint/promise-function-async": "off",
24+
"@typescript-eslint/no-misused-promises": ["error", {
25+
"checksVoidReturn": false
26+
}],
27+
"@typescript-eslint/no-empty-function": "off",
28+
"@typescript-eslint/consistent-type-assertions": "off",
2429
"@typescript-eslint/indent": ["error", 4, {
2530
"SwitchCase": 1,
2631
"VariableDeclarator": 1,

README.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
MATLAB® language server implements the Microsoft® [Language Server Protocol](https://github.com/Microsoft/language-server-protocol) for the MATLAB language.
55

6-
MATLAB language server requires MATLAB version R2021a or later.
7-
8-
**Note:** This language server will no longer support MATLAB R2021a in a future release. To use advanced features or run MATLAB code, you will need to have MATLAB R2021b or later installed.
6+
MATLAB language server requires MATLAB version R2021b or later.
97

108
## Features Implemented
119
MATLAB language server implements several Language Server Protocol features and their related services:
@@ -28,6 +26,20 @@ MATLAB language server supports these editors by installing the corresponding ex
2826

2927
### Unreleased
3028

29+
### 1.3.0
30+
Release date: 2024-12-18
31+
32+
Notice:
33+
* The MATLAB language server no longer supports MATLAB R2021a. To make use of the advanced features of the extension or run and debug MATLAB code, you must have MATLAB R2021b or later installed.
34+
35+
Added:
36+
* Debugging support
37+
* Include snippets defined within MATLAB (requires MATLAB R2025a or later)
38+
39+
Fixed:
40+
* Use default values when settings are missing from configuration
41+
* Patches CVE-2024-52798
42+
3143
### 1.2.7
3244
Release date: 2024-11-07
3345

matlab/initmatlabls.m

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ function initmatlabls (outFile)
2222
disp('Error while attempting to add shadow directory to path')
2323
disp(ME.message)
2424
end
25+
26+
try
27+
s = settings;
28+
s.matlab.editor.OpenFileAtBreakpoint.TemporaryValue = false;
29+
end
2530

2631
% Create matlabls helper for calculating language server operations
2732
persistent matlablsHelper %#ok<PUSE>

package-lock.json

+35-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "matlab-language-server",
3-
"version": "1.2.7",
3+
"version": "1.3.0",
44
"description": "Language Server for MATLAB code",
55
"main": "./src/index.ts",
66
"bin": "./out/index.js",
@@ -59,6 +59,7 @@
5959
"node-fetch": "^3.3.2",
6060
"vscode-languageserver": "^8.0.2",
6161
"vscode-languageserver-textdocument": "^1.0.7",
62+
"@vscode/debugadapter": "^1.56.0",
6263
"vscode-uri": "^3.0.6",
6364
"which": "^2.0.2",
6465
"xml2js": "^0.6.2",

src/ClientConnection.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { _Connection, createConnection, ProposedFeatures } from 'vscode-language
33

44
export type Connection = _Connection
55

6+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
67
export default class ClientConnection {
78
private static connection: Connection | undefined
89

src/debug/DebugServices.ts

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
3+
import { IMVM } from '../mvm/impl/MVM'
4+
import EventEmitter from 'events';
5+
6+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7+
type MatlabData = any;
8+
9+
export class BreakpointInfo {
10+
filePath: string;
11+
lineNumber: number;
12+
anonymousFunctionIndex: number;
13+
condition?: string;
14+
enabled: boolean;
15+
16+
constructor (filePath: string, lineNumber: number, condition: string | undefined, anonymousFunctionIndex: number) {
17+
this.filePath = filePath;
18+
this.lineNumber = lineNumber;
19+
this.condition = condition === '' ? undefined : condition;
20+
this.anonymousFunctionIndex = 0;
21+
this.enabled = condition === undefined || !(condition === 'false' || /$false && \(.*\)^/.test(condition));
22+
}
23+
24+
equals (other: BreakpointInfo, ignoreCondition: boolean): boolean {
25+
const result = other.filePath === this.filePath && other.lineNumber === this.lineNumber && other.anonymousFunctionIndex === this.anonymousFunctionIndex;
26+
if (!result || ignoreCondition) {
27+
return result;
28+
} else {
29+
return this.condition === other.condition;
30+
}
31+
}
32+
}
33+
34+
export enum GlobalBreakpointType {
35+
ERROR = 'ERROR',
36+
CAUGHT_ERROR = 'CAUGHT_ERROR',
37+
WARNING = 'WARNING',
38+
NAN_INF = 'NAN_INF'
39+
}
40+
41+
export class GlobalBreakpointInfo {
42+
static TypeMap: {[index: string | number]: GlobalBreakpointType} = {
43+
0: GlobalBreakpointType.ERROR,
44+
1: GlobalBreakpointType.CAUGHT_ERROR,
45+
2: GlobalBreakpointType.WARNING,
46+
3: GlobalBreakpointType.NAN_INF,
47+
error: GlobalBreakpointType.ERROR,
48+
'caught error': GlobalBreakpointType.CAUGHT_ERROR,
49+
warning: GlobalBreakpointType.WARNING,
50+
naninf: GlobalBreakpointType.NAN_INF,
51+
[GlobalBreakpointType.ERROR]: GlobalBreakpointType.ERROR,
52+
[GlobalBreakpointType.CAUGHT_ERROR]: GlobalBreakpointType.CAUGHT_ERROR,
53+
[GlobalBreakpointType.WARNING]: GlobalBreakpointType.WARNING,
54+
[GlobalBreakpointType.NAN_INF]: GlobalBreakpointType.NAN_INF
55+
}
56+
57+
type: GlobalBreakpointType;
58+
identifiers: string[];
59+
60+
constructor (type: GlobalBreakpointType, identifiers: string[]) {
61+
this.type = type;
62+
this.identifiers = identifiers;
63+
}
64+
}
65+
66+
enum Events {
67+
DBEnter = 'DBEnter',
68+
DBStop = 'DBStop',
69+
DBExit = 'DBExit',
70+
DBCont = 'DBCont',
71+
DBWorkspaceChanged = 'DBWorkspaceChanged',
72+
BreakpointAdded = 'BreakpointAdded',
73+
BreakpointRemoved = 'BreakpointRemoved',
74+
BreakpointsCleared = 'BreakpointsCleared',
75+
GlobalBreakpointAdded = 'GlobalBreakpointAdded',
76+
GlobalBreakpointRemoved = 'GlobalBreakpointRemoved'
77+
}
78+
79+
export class DebugServices extends EventEmitter {
80+
static Events = Events;
81+
private _mvm: IMVM;
82+
83+
constructor (mvm: IMVM) {
84+
super();
85+
this._mvm = mvm;
86+
this._setupListeners();
87+
}
88+
89+
private _setupListeners (): void {
90+
this._mvm.on('EnterDebuggerEvent', this._handleEnterEvent.bind(this));
91+
this._mvm.on('EnterDebuggerWithWarningEvent', this._handleEnterEvent.bind(this));
92+
this._mvm.on('ExitDebuggerEvent', this._handleExitEvent.bind(this));
93+
this._mvm.on('ContinueExecutionEvent', (data: MatlabData) => {
94+
this.emit(DebugServices.Events.DBCont);
95+
});
96+
this._mvm.on('ChangeCurrentWorkspace', (data: MatlabData) => {
97+
this.emit(DebugServices.Events.DBWorkspaceChanged);
98+
});
99+
100+
this._mvm.on('AddLineNumberBreakpointEvent', (data: MatlabData) => {
101+
this.emit(DebugServices.Events.BreakpointAdded, new BreakpointInfo(data.Filespec, data.LineNumber, data.Condition, data.whichAnonymousFunctionOnCurrentLine));
102+
});
103+
this._mvm.on('DeleteLineNumberBreakpointEvent', (data: MatlabData) => {
104+
this.emit(DebugServices.Events.BreakpointRemoved, new BreakpointInfo(data.Filespec, data.LineNumber, data.Condition, data.whichAnonymousFunctionOnCurrentLine));
105+
});
106+
this._mvm.on('DeleteAllBreakpointsEvent', () => {
107+
this.emit(DebugServices.Events.BreakpointsCleared);
108+
});
109+
this._mvm.on('AddProgramWideBreakpointEvent', (data: MatlabData) => {
110+
this.emit(DebugServices.Events.GlobalBreakpointAdded, new GlobalBreakpointInfo(GlobalBreakpointInfo.TypeMap[data.programWideTag], data.messageIdentifier === 'all' ? [] : [data.messageIdentifier]));
111+
});
112+
this._mvm.on('DeleteProgramWideBreakpointEvent', (data: MatlabData) => {
113+
this.emit(DebugServices.Events.GlobalBreakpointRemoved, new GlobalBreakpointInfo(GlobalBreakpointInfo.TypeMap[data.programWideTag], data.messageIdentifier === 'all' ? [] : [data.messageIdentifier]));
114+
});
115+
}
116+
117+
private _isSessionLevelEvent (eventData: MatlabData): boolean {
118+
if (this._mvm.getMatlabRelease() === 'R2021b') {
119+
return eventData.DebugNestLevel >= 3;
120+
} else {
121+
return eventData.DebugNestLevel === 2;
122+
}
123+
}
124+
125+
private _handleEnterEvent (data: MatlabData): void {
126+
if (this._isSessionLevelEvent(data)) {
127+
this.emit(DebugServices.Events.DBEnter);
128+
} else {
129+
const filepath = data.Filespec;
130+
const lineNumber = (data.IsAtEndOfFunction as boolean) ? -data.LineNumber : data.LineNumber;
131+
this.emit(DebugServices.Events.DBStop, filepath, lineNumber, data.Stack ?? []);
132+
}
133+
}
134+
135+
private _handleExitEvent (data: MatlabData): void {
136+
if (this._isSessionLevelEvent(data)) {
137+
this.emit(DebugServices.Events.DBExit, data.IsDebuggerActive);
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)