Skip to content

MATLAB language server - v1.3.0 #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
"@typescript-eslint/semi": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/no-misused-promises": ["error", {
"checksVoidReturn": false
}],
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"@typescript-eslint/indent": ["error", 4, {
"SwitchCase": 1,
"VariableDeclarator": 1,
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

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

MATLAB language server requires MATLAB version R2021a or later.

**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.
MATLAB language server requires MATLAB version R2021b or later.

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

### Unreleased

### 1.3.0
Release date: 2024-12-18

Notice:
* 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.

Added:
* Debugging support
* Include snippets defined within MATLAB (requires MATLAB R2025a or later)

Fixed:
* Use default values when settings are missing from configuration
* Patches CVE-2024-52798

### 1.2.7
Release date: 2024-11-07

Expand Down
5 changes: 5 additions & 0 deletions matlab/initmatlabls.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ function initmatlabls (outFile)
disp('Error while attempting to add shadow directory to path')
disp(ME.message)
end

try
s = settings;
s.matlab.editor.OpenFileAtBreakpoint.TemporaryValue = false;
end

% Create matlabls helper for calculating language server operations
persistent matlablsHelper %#ok<PUSE>
Expand Down
47 changes: 35 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "matlab-language-server",
"version": "1.2.7",
"version": "1.3.0",
"description": "Language Server for MATLAB code",
"main": "./src/index.ts",
"bin": "./out/index.js",
Expand Down Expand Up @@ -59,6 +59,7 @@
"node-fetch": "^3.3.2",
"vscode-languageserver": "^8.0.2",
"vscode-languageserver-textdocument": "^1.0.7",
"@vscode/debugadapter": "^1.56.0",
"vscode-uri": "^3.0.6",
"which": "^2.0.2",
"xml2js": "^0.6.2",
Expand Down
1 change: 1 addition & 0 deletions src/ClientConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { _Connection, createConnection, ProposedFeatures } from 'vscode-language

export type Connection = _Connection

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

Expand Down
140 changes: 140 additions & 0 deletions src/debug/DebugServices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2024 The MathWorks, Inc.

import { IMVM } from '../mvm/impl/MVM'
import EventEmitter from 'events';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type MatlabData = any;

export class BreakpointInfo {
filePath: string;
lineNumber: number;
anonymousFunctionIndex: number;
condition?: string;
enabled: boolean;

constructor (filePath: string, lineNumber: number, condition: string | undefined, anonymousFunctionIndex: number) {
this.filePath = filePath;
this.lineNumber = lineNumber;
this.condition = condition === '' ? undefined : condition;
this.anonymousFunctionIndex = 0;
this.enabled = condition === undefined || !(condition === 'false' || /$false && \(.*\)^/.test(condition));
}

equals (other: BreakpointInfo, ignoreCondition: boolean): boolean {
const result = other.filePath === this.filePath && other.lineNumber === this.lineNumber && other.anonymousFunctionIndex === this.anonymousFunctionIndex;
if (!result || ignoreCondition) {
return result;
} else {
return this.condition === other.condition;
}
}
}

export enum GlobalBreakpointType {
ERROR = 'ERROR',
CAUGHT_ERROR = 'CAUGHT_ERROR',
WARNING = 'WARNING',
NAN_INF = 'NAN_INF'
}

export class GlobalBreakpointInfo {
static TypeMap: {[index: string | number]: GlobalBreakpointType} = {
0: GlobalBreakpointType.ERROR,
1: GlobalBreakpointType.CAUGHT_ERROR,
2: GlobalBreakpointType.WARNING,
3: GlobalBreakpointType.NAN_INF,
error: GlobalBreakpointType.ERROR,
'caught error': GlobalBreakpointType.CAUGHT_ERROR,
warning: GlobalBreakpointType.WARNING,
naninf: GlobalBreakpointType.NAN_INF,
[GlobalBreakpointType.ERROR]: GlobalBreakpointType.ERROR,
[GlobalBreakpointType.CAUGHT_ERROR]: GlobalBreakpointType.CAUGHT_ERROR,
[GlobalBreakpointType.WARNING]: GlobalBreakpointType.WARNING,
[GlobalBreakpointType.NAN_INF]: GlobalBreakpointType.NAN_INF
}

type: GlobalBreakpointType;
identifiers: string[];

constructor (type: GlobalBreakpointType, identifiers: string[]) {
this.type = type;
this.identifiers = identifiers;
}
}

enum Events {
DBEnter = 'DBEnter',
DBStop = 'DBStop',
DBExit = 'DBExit',
DBCont = 'DBCont',
DBWorkspaceChanged = 'DBWorkspaceChanged',
BreakpointAdded = 'BreakpointAdded',
BreakpointRemoved = 'BreakpointRemoved',
BreakpointsCleared = 'BreakpointsCleared',
GlobalBreakpointAdded = 'GlobalBreakpointAdded',
GlobalBreakpointRemoved = 'GlobalBreakpointRemoved'
}

export class DebugServices extends EventEmitter {
static Events = Events;
private _mvm: IMVM;

constructor (mvm: IMVM) {
super();
this._mvm = mvm;
this._setupListeners();
}

private _setupListeners (): void {
this._mvm.on('EnterDebuggerEvent', this._handleEnterEvent.bind(this));
this._mvm.on('EnterDebuggerWithWarningEvent', this._handleEnterEvent.bind(this));
this._mvm.on('ExitDebuggerEvent', this._handleExitEvent.bind(this));
this._mvm.on('ContinueExecutionEvent', (data: MatlabData) => {
this.emit(DebugServices.Events.DBCont);
});
this._mvm.on('ChangeCurrentWorkspace', (data: MatlabData) => {
this.emit(DebugServices.Events.DBWorkspaceChanged);
});

this._mvm.on('AddLineNumberBreakpointEvent', (data: MatlabData) => {
this.emit(DebugServices.Events.BreakpointAdded, new BreakpointInfo(data.Filespec, data.LineNumber, data.Condition, data.whichAnonymousFunctionOnCurrentLine));
});
this._mvm.on('DeleteLineNumberBreakpointEvent', (data: MatlabData) => {
this.emit(DebugServices.Events.BreakpointRemoved, new BreakpointInfo(data.Filespec, data.LineNumber, data.Condition, data.whichAnonymousFunctionOnCurrentLine));
});
this._mvm.on('DeleteAllBreakpointsEvent', () => {
this.emit(DebugServices.Events.BreakpointsCleared);
});
this._mvm.on('AddProgramWideBreakpointEvent', (data: MatlabData) => {
this.emit(DebugServices.Events.GlobalBreakpointAdded, new GlobalBreakpointInfo(GlobalBreakpointInfo.TypeMap[data.programWideTag], data.messageIdentifier === 'all' ? [] : [data.messageIdentifier]));
});
this._mvm.on('DeleteProgramWideBreakpointEvent', (data: MatlabData) => {
this.emit(DebugServices.Events.GlobalBreakpointRemoved, new GlobalBreakpointInfo(GlobalBreakpointInfo.TypeMap[data.programWideTag], data.messageIdentifier === 'all' ? [] : [data.messageIdentifier]));
});
}

private _isSessionLevelEvent (eventData: MatlabData): boolean {
if (this._mvm.getMatlabRelease() === 'R2021b') {
return eventData.DebugNestLevel >= 3;
} else {
return eventData.DebugNestLevel === 2;
}
}

private _handleEnterEvent (data: MatlabData): void {
if (this._isSessionLevelEvent(data)) {
this.emit(DebugServices.Events.DBEnter);
} else {
const filepath = data.Filespec;
const lineNumber = (data.IsAtEndOfFunction as boolean) ? -data.LineNumber : data.LineNumber;
this.emit(DebugServices.Events.DBStop, filepath, lineNumber, data.Stack ?? []);
}
}

private _handleExitEvent (data: MatlabData): void {
if (this._isSessionLevelEvent(data)) {
this.emit(DebugServices.Events.DBExit, data.IsDebuggerActive);
}
}
}
Loading
Loading