Skip to content

Commit 2c926f3

Browse files
authored
Merge pull request #279 from mathworks/dklilley/release/1.3.5
MATLAB extension for VS Code - v1.3.5
2 parents 45eeb4a + 0386417 commit 2c926f3

File tree

15 files changed

+254
-21
lines changed

15 files changed

+254
-21
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.3.5] - 2025-09-04
11+
12+
### Added
13+
- Support for prewarming graphics to improve the performance of first-time graphics rendering
14+
- Support for using Visual Studio Code as the default editor when using the MATLAB `edit` and `open` commands
15+
- Support for highlighting all references to a selected function, variable, class, or class property
16+
17+
### Fixed
18+
- Resolves issue where newly saved document contents are ignored during execution
19+
- Resolves issue where section breaks are not displayed and the `Run Section` command does not work until a file is modified for the first time.
20+
- Applied patch for CVE-2025-54798
21+
1022
## [1.3.4] - 2025-07-31
1123

1224
### Added

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ You also can use this extension along with the Jupyter Extension for Visual Stud
5353
## Configuration
5454
To configure the extension, go to the extension settings and select from the available options.
5555

56+
### MATLAB Default Editor Setting
57+
By default, the extension uses the editor specified in the MATLAB Editor/Debugger settings to open files with the MATLAB `edit` and `open` commands. To make Visual Studio Code the default editor for these commands, set the `MATLAB.defaultEditor` setting to `true`. To revert to using the editor specified in the MATLAB Editor/Debugger settings, set `MATLAB.defaultEditor` to `false`.
58+
**Note:** Certain file types always open in MATLAB by default — for example, live scripts saved in the binary Live Code file format (.mlx) and MATLAB app files (.mlapp).
59+
5660
### MATLAB Install Path Setting
5761
If you have MATLAB installed on your system, the extension automatically checks the system path for the location of the MATLAB executable. If the MATLAB executable is not on the system path, you may need to manually set the `MATLAB.installPath` setting to the full path of your MATLAB installation. For example, `C:\Program Files\MATLAB\R2022b` (Windows®), `/Applications/MATLAB_R2022b.app` (macOS), or `/usr/local/MATLAB/R2022b` (Linux®).
5862

@@ -98,6 +102,9 @@ You can help improve the extension by sending user experience information to Mat
98102

99103
For more information, see the [MathWorks Privacy Policy](https://www.mathworks.com/company/aboutus/policies_statements.html).
100104

105+
### MATLAB Prewarm Graphics Setting
106+
By default, MATLAB services are started early to improve the first-time performance of MATLAB figure rendering. To disable this behavior, set the `MATLAB.prewarmGraphics` setting to `false`.
107+
This setting is supported with MATLAB R2025a and later. For earlier releases, this setting is ignored.
101108

102109

103110
## Troubleshooting

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Edit MATLAB code with syntax highlighting, linting, navigation support, and more",
55
"icon": "public/L-Membrane_RGB_128x128.png",
66
"license": "MIT",
7-
"version": "1.3.4",
7+
"version": "1.3.5",
88
"engines": {
99
"vscode": "^1.67.0"
1010
},
@@ -269,6 +269,18 @@
269269
"default": 0,
270270
"markdownDescription": "The maximum number of characters a file can contain for features such as linting, code navigation, and symbol renaming to be enabled. Use `0` for no limit.",
271271
"scope": "window"
272+
},
273+
"MATLAB.prewarmGraphics": {
274+
"type": "boolean",
275+
"default": true,
276+
"description": "Prewarm graphics at MATLAB startup to improve performance of first-time graphics rendering.",
277+
"scope": "window"
278+
},
279+
"MATLAB.defaultEditor": {
280+
"type": "boolean",
281+
"default": false,
282+
"markdownDescription": "Use Visual Studio Code instead of the MATLAB Editor to open and edit files using the `open` and `edit` commands. Certain file types always open in MATLAB by default — for example, live scripts saved in the binary Live Code file format (.mlx) and MATLAB app files (.mlapp).",
283+
"scope": "window"
272284
}
273285
}
274286
},

src/DefaultEditorService.ts

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright 2025 The MathWorks, Inc.
2+
3+
import * as fs from 'fs';
4+
import * as os from 'os';
5+
import { exec } from 'child_process';
6+
import * as path from 'path'
7+
import * as vscode from 'vscode'
8+
import { LanguageClient } from 'vscode-languageclient/node';
9+
import { MatlabState, MVM } from './commandwindow/MVM'
10+
import Notification from './Notifications'
11+
12+
export default class DefaultEditorService {
13+
private initialized = false;
14+
15+
constructor (private readonly context: vscode.ExtensionContext, private readonly client: LanguageClient, private readonly mvm: MVM) {
16+
context.subscriptions.push(
17+
vscode.workspace.onDidChangeConfiguration(() => {
18+
void this.handleConfigChanged()
19+
})
20+
)
21+
22+
mvm.on(MVM.Events.stateChanged, (oldState: MatlabState, newState: MatlabState) => {
23+
if (oldState === newState) {
24+
return;
25+
}
26+
27+
if (newState === MatlabState.READY || newState === MatlabState.BUSY) {
28+
if (!this.initialized) {
29+
this.initialized = true
30+
void this.handleConfigChanged()
31+
}
32+
} else {
33+
this.initialized = false
34+
}
35+
});
36+
}
37+
38+
/** Helper function: checks if a specific path from array of path strings exists and returns true if a path string is found in file system else returns false
39+
* @param paths array of string paths to be checked in file system
40+
* @returns path string if it exists in file system else null
41+
*/
42+
private checkPath (paths: string[]): string | null {
43+
for (const p of paths) {
44+
if (fs.existsSync(p)) {
45+
return p;
46+
}
47+
}
48+
return null;
49+
}
50+
51+
private getFallbackExecutablePaths (): string[] {
52+
const appRoot = vscode.env.appRoot;
53+
const platform = process.platform;
54+
const fallbackPaths: string[] = [];
55+
56+
if (platform === 'win32') {
57+
// appRoot: C:\Program Files\Microsoft VS Code\resources\app
58+
// Executable: C:\Program Files\Microsoft VS Code\Code.exe
59+
fallbackPaths.push(
60+
path.join(path.dirname(path.dirname(appRoot)), 'Code.exe')
61+
);
62+
} else if (platform === 'darwin') {
63+
// appRoot: /Applications/Visual Studio Code.app/Contents/Resources/app
64+
// Executable: /Applications/Visual Studio Code.app/Contents/MacOS/Electron
65+
// or /Applications/Visual Studio Code.app/Contents/MacOS/Visual Studio Code
66+
// Try both
67+
const appDir = path.dirname(path.dirname(appRoot)); // .../Visual Studio Code.app/Contents
68+
fallbackPaths.push(
69+
path.join(appDir, 'MacOS', 'Visual Studio Code'),
70+
path.join(appDir, 'MacOS', 'Electron')
71+
);
72+
} else {
73+
// Linux
74+
// appRoot: /usr/share/code/resources/app
75+
// Executable: /usr/share/code/code
76+
fallbackPaths.push(
77+
path.join(path.dirname(path.dirname(appRoot)), 'code')
78+
);
79+
}
80+
return fallbackPaths
81+
}
82+
83+
/** Look into most probable installation paths based on OS to find VS Code executable path
84+
* @returns path string found for VS Code executable
85+
*/
86+
public getVSCodePath (): Promise<string> {
87+
return new Promise((resolve, reject) => {
88+
const fallbackPaths = this.getFallbackExecutablePaths()
89+
90+
const platform = os.platform();
91+
92+
const command = platform === 'win32' ? 'where code' : 'which code';
93+
94+
exec(command, (error, stdout, stderr) => {
95+
if ((error == null) && typeof stdout === 'string' && stdout !== '') {
96+
resolve(stdout.trim().split('\n').filter(Boolean)[0].trim());
97+
} else {
98+
const fallback = this.checkPath(fallbackPaths);
99+
if (typeof fallback === 'string' && fallback.length > 0) {
100+
resolve(fallback);
101+
} else {
102+
reject(new Error('MATLAB Default Editor: Error getting VS Code executable path'));
103+
}
104+
}
105+
});
106+
});
107+
}
108+
109+
/**
110+
* Handles the notification that MATLAB failed to set VS Code as default editor successfully. This most likely indicates that
111+
* either VS Code is not installed in a default location and 'code' is not added to PATH.
112+
* @param matlabConfig VsCode Workspace object for MATLAB extension configuration
113+
*/
114+
public async handleVsCodePathError (matlabConfig: vscode.WorkspaceConfiguration): Promise<void> {
115+
const message = 'Unable to set MATLAB default editor to Visual Studio Code. Check that VS Code is installed in a default location or add it to the system PATH.'
116+
const availableCmds = await vscode.commands.getCommands()
117+
const options = ['Add VS Code to PATH']
118+
119+
if (availableCmds.includes('workbench.action.installCommandLine')) {
120+
vscode.window.showErrorMessage(message, ...options).then(choice => {
121+
switch (choice) {
122+
case options[0]:
123+
void vscode.commands.executeCommand('workbench.action.installCommandLine')
124+
break
125+
}
126+
}, reject => console.error(reject))
127+
} else {
128+
vscode.window.showErrorMessage(message).then(choice => { /* empty */ }, reject => console.error(reject))
129+
}
130+
void matlabConfig.update('defaultEditor', false, true);
131+
}
132+
133+
/**
134+
* Handles config state management. Finds VS Code executable path when defaultEditor config is enabled and displays an error message if path not found.
135+
*/
136+
public handleConfigChanged (): void {
137+
const configuration = vscode.workspace.getConfiguration('MATLAB')
138+
if ((configuration.get<boolean>('defaultEditor') ?? true)) {
139+
this.getVSCodePath().then(validPath => {
140+
void this.sendExecutablePathNotification(validPath)
141+
}).catch(err => {
142+
console.error(err)
143+
void this.handleVsCodePathError(configuration)
144+
});
145+
}
146+
}
147+
148+
/**
149+
* Sends notification to language server to update default editor to VS Code.
150+
* @param executablePath The path to VS Code
151+
*/
152+
public sendExecutablePathNotification (executablePath: string): void {
153+
void this.client.sendNotification(Notification.EditorExecutablePath, executablePath)
154+
}
155+
}

src/Notifications.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ enum Notification {
5151
LicensingServerUrl = 'licensing/server/url',
5252
LicensingData = 'licensing/data',
5353
LicensingDelete = 'licensing/delete',
54-
LicensingError = 'licensing/error'
54+
LicensingError = 'licensing/error',
55+
56+
// Default Editor
57+
EditorExecutablePath = 'matlab/otherEditor'
5558
}
5659

5760
export default Notification

src/commandwindow/CommandWindow.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const ACTION_KEYS = {
6666

6767
MOVE_TO_POSITION_IN_LINE: (n: number) => ESC + '[' + n.toString() + 'G',
6868
CLEAR_AND_MOVE_TO_BEGINNING: ESC + '[0G' + ESC + '[0J',
69-
CLEAR_COMPLETELY: ESC + '[2J' + ESC + '[1;1H',
69+
CLEAR_COMPLETELY: ESC + '[2J' + ESC + '[3J' + ESC + '[1;1H',
7070

7171
QUERY_CURSOR: ESC + '[6n',
7272
SET_CURSOR_STYLE_TO_BAR: ESC + '[5 q'
@@ -707,7 +707,6 @@ export default class CommandWindow implements vscode.Pseudoterminal {
707707
*/
708708
clear (): void {
709709
this._writeEmitter.fire(ACTION_KEYS.CLEAR_COMPLETELY)
710-
void vscode.commands.executeCommand('workbench.action.terminal.clear');
711710
this._setToEmptyPrompt();
712711
}
713712

src/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import DeprecationPopupService from './DeprecationPopupService'
1717
import { SectionModel } from './model/SectionModel'
1818
import SectionStylingService from './styling/SectionStylingService'
1919
import MatlabDebugger from './debug/MatlabDebugger'
20+
import DefaultEditorService from './DefaultEditorService'
2021

2122
let client: LanguageClient
2223
const OPEN_SETTINGS_ACTION = 'workbench.action.openSettings'
@@ -37,6 +38,8 @@ let telemetryLogger: TelemetryLogger
3738

3839
let deprecationPopupService: DeprecationPopupService
3940

41+
let defaultEditorService: DefaultEditorService
42+
4043
let sectionModel: SectionModel;
4144
let sectionStylingService: SectionStylingService;
4245

@@ -144,6 +147,8 @@ export async function activate (context: vscode.ExtensionContext): Promise<void>
144147
deprecationPopupService = new DeprecationPopupService(context)
145148
deprecationPopupService.initialize(client)
146149

150+
defaultEditorService = new DefaultEditorService(context, client, mvm)
151+
147152
sectionModel = new SectionModel()
148153
sectionModel.initialize(client as Notifier);
149154
context.subscriptions.push(sectionModel)

src/test/tools/tester/TestSuite.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ export class TestSuite {
1616
private readonly releaseQuality: ReleaseQuality
1717

1818
public constructor () {
19-
this.storageFolder = path.join(__dirname, '..', '..', '..', '..', '.vscode-test', 'test-resources')
20-
if (os.platform() === 'darwin') {
21-
this.storageFolder = os.tmpdir()
22-
}
19+
this.storageFolder = path.join(__dirname, '..', '..', '..', '..', 's')
2320
this.mochaConfig = path.join(__dirname, '..', 'config', '.mocharc.js')
2421
const pjson = require(path.resolve('package.json')) // eslint-disable-line
2522
this.vsixPath = path.resolve(`${pjson.name}-${pjson.version}.vsix`) // eslint-disable-line

0 commit comments

Comments
 (0)