Skip to content
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ MATLAB language server implements several Language Server Protocol features and
* Document symbols — [documentSymbolProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol)
* Symbol rename - [renameProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rename)
* Code folding - [foldingRangeProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange)
* Document highlights - [highlightSymbolProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentHighlight)

## Clients
MATLAB language server supports these editors by installing the corresponding extension:
Expand All @@ -29,6 +30,15 @@ MATLAB language server supports these editors by installing the corresponding ex

### Unreleased

### 1.3.5
Release date: 2025-09-04

Fixed:
* Resolves issue where newly saved document contents are ignored during execution

Added:
* Support for highlighting all references to a selected function, variable, class, or class property

### 1.3.4
Release date: 2025-07-31

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
% Copyright 2025 The MathWorks, Inc.

% Clears any temporary changes made in matlab.editor settings associated with the matlab.defaultEditor config
function clearTemporaryEditorSettings()
s = settings;
if hasTemporaryValue(s.matlab.editor.UseMATLABEditor)
clearTemporaryValue(s.matlab.editor.UseMATLABEditor);
clearTemporaryValue(s.matlab.editor.OtherEditor);
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
% Copyright 2025 The MathWorks, Inc.

% Temporarily sets default editor to the provided executablePath when matlab.defaultEditor config is enabled using the matlab.editor settings
function setTemporaryEditorSettings(executablePath)
s = settings;
s.matlab.editor.UseMATLABEditor.TemporaryValue = 0;
s.matlab.editor.OtherEditor.TemporaryValue = executablePath;
end
11 changes: 11 additions & 0 deletions matlab/+matlabls/+utils/saveHelper.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function saveHelper(filePath)
% This function is triggered after documents are saved, and can be used to
% trigger behaviors when this occurs.

% Copyright 2025 The MathWorks, Inc.

% Ensure that changes to the file are registered by MATLAB to prevent cached
% file contents from being used during execution.
fschange(filePath);
clear(filePath);
end
45 changes: 45 additions & 0 deletions matlab/+matlabls/setupShadows.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
function setupShadows(languageServerFolder)
currentDirectory = pwd;
cleanup = onCleanup(@() cd(currentDirectory));

try
addRestoreDefaultPathShadow(languageServerFolder);
addEditShadow(languageServerFolder);
addClcShadow(languageServerFolder);
catch ME
disp('Error while attempting to add shadow directories to path')
disp(ME.message)
end
end

function addRestoreDefaultPathShadow(languageServerFolder)
cd(fullfile(matlabroot, 'toolbox', 'local'));
originalRestoreDefaultPath = @restoredefaultpath;
cd(matlabroot);
addpath(fullfile(languageServerFolder, 'shadows', 'restoredefaultpath'));

restoredefaultpath('__SET__', originalRestoreDefaultPath, @handleReset);

function handleReset()
addpath(languageServerFolder)
matlabls.setupShadows(languageServerFolder)
end
end

function addEditShadow(languageServerFolder)
cd(fullfile(matlabroot, 'toolbox', 'matlab', 'codetools'));
originalEdit = @edit;
cd(matlabroot);
addpath(fullfile(languageServerFolder, 'shadows', 'edit'));

% Need to pass the originalEdit function handle in within a cell array
% to avoid @function_handle/edit being used instead.
edit('__SET__', {originalEdit});
end

function addClcShadow(languageServerFolder)
% Only need to do this for <R2023a
if isMATLABReleaseOlderThan('R2023a')
addpath(fullfile(languageServerFolder, 'shadows', 'clc'));
end
end
28 changes: 2 additions & 26 deletions matlab/initmatlabls.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function initmatlabls (outFile)
% Ensure the language server code is on the path
folder = fileparts(mfilename('fullpath'));

updatePath(folder);
% Shadow necessary functions
matlabls.setupShadows(folder);

try
s = settings;
Expand Down Expand Up @@ -69,28 +70,3 @@ function logConnectionData (outFile)
end

end

function updatePath(languageServerFolder)
addpath(languageServerFolder)

try
addRestoreDefaultPathShadow(languageServerFolder);

if isMATLABReleaseOlderThan('R2023a')
addpath(fullfile(languageServerFolder, 'shadows', 'clc'));
end
catch ME
disp('Error while attempting to add shadow directory to path')
disp(ME.message)
end
end

function addRestoreDefaultPathShadow(languageServerFolder)
currentDirectory = pwd;
cd(fullfile(matlabroot, 'toolbox', 'local'));
originalRestoreDefaultPath = @restoredefaultpath;
cd(matlabroot);
addpath(fullfile(languageServerFolder, 'shadows', 'restoredefaultpath'));
restoredefaultpath('SET', originalRestoreDefaultPath, @() updatePath(languageServerFolder));
cd(currentDirectory);
end
23 changes: 23 additions & 0 deletions matlab/shadows/edit/edit.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
% Copyright 2025 The MathWorks, Inc.

function edit(varargin)
mlock
persistent originalEdit;

if nargin == 2 && ischar(varargin{1}) && isequal(varargin{1}, '__SET__')
originalEdit = varargin{2}{1};
return;
end

if (isempty(originalEdit))
error('MATLAB:edit:UninitializedShadow', ...
'MATLAB Language Server - Edit shadow is uninitialized.');
end

if (nargin == 0 && ~settings().matlab.editor.UseMATLABEditor.ActiveValue)
error('MATLAB:edit:NoArgsNotAllowed', ...
'Calling `edit` with no arguments is not supported when opening files outside of MATLAB.');
end

originalEdit(varargin{:});
end
6 changes: 4 additions & 2 deletions matlab/shadows/restoredefaultpath/restoredefaultpath.m
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
% Copyright 2025 The MathWorks, Inc.

function restoredefaultpath(varargin)
mlock
persistent originalRestoreDefaultPath;
persistent pathUpdateFunction;
if nargin == 3 && ischar(varargin{1}) && isequal(varargin{1}, 'SET')
if nargin == 3 && ischar(varargin{1}) && isequal(varargin{1}, '__SET__')
originalRestoreDefaultPath = varargin{2};
pathUpdateFunction = varargin{3};
return;
end

if isempty(originalRestoreDefaultPath)
error('Matlab Language Server - RestoreDefaultPath shadow is uninitialized.');
error('MATLAB Language Server - RestoreDefaultPath shadow is uninitialized.');
end

originalRestoreDefaultPath();
Expand Down
26 changes: 24 additions & 2 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.3.4",
"version": "1.3.5",
"description": "Language Server for MATLAB code",
"main": "./src/index.ts",
"bin": "./out/index.js",
Expand Down Expand Up @@ -42,6 +42,7 @@
"eslint-plugin-promise": "^6.0.1",
"mocha": "^10.4.0",
"node-loader": "^2.0.0",
"quibble": "^0.9.2",
"sinon": "^18.0.0",
"ts-loader": "^9.4.1",
"tsx": "^4.19.4",
Expand Down
28 changes: 21 additions & 7 deletions src/indexing/SymbolSearchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export enum RequestType {
References,
DocumentSymbol,
RenameSymbol,
DocumentHighlight
}

export function reportTelemetry (type: RequestType, errorCondition = ''): void {
Expand All @@ -33,6 +34,9 @@ export function reportTelemetry (type: RequestType, errorCondition = ''): void {
case RequestType.RenameSymbol:
action = Actions.RenameSymbol
break
case RequestType.DocumentHighlight:
action = Actions.HighlightSymbol
break
}
reportTelemetryAction(action, errorCondition)
}
Expand All @@ -56,7 +60,7 @@ class SymbolSearchService {
* @param position The position of the expression
* @param expression The expression for which we are looking for references
* @param documentManager The text document manager
* @param requestType The type of request (definition, references, or rename)
* @param requestType The type of request
* @returns The references' locations
*/
findReferences (uri: string, position: Position, expression: Expression, documentManager: TextDocuments<TextDocument>, requestType: RequestType): Location[] {
Expand Down Expand Up @@ -197,43 +201,53 @@ class SymbolSearchService {

/**
* Finds the definition(s) of an expression.
*
* For DocumentHighlight requests, only the file containing the expression will be
* searched. For all other request types, the entire workspace and the MATLAB path
* will be searched.
*
* @param uri The URI of the document containing the expression
* @param position The position of the expression
* @param expression The expression for which we are looking for the definition
* @param matlabConnection The connection to MATLAB®
* @param pathResolver The path resolver
* @param indexer The workspace indexer
* @param requestType The type of request - DocumentHighlight requests will only return
* definitions in the file referenced by `uri`
* @returns The definition location(s)
*/
async findDefinition (uri: string, position: Position, expression: Expression, pathResolver: PathResolver, indexer: Indexer): Promise<Location[]> {
async findDefinitions (uri: string, position: Position, expression: Expression, pathResolver: PathResolver,
indexer: Indexer, requestType: RequestType): Promise<Location[]> {

// Get code data for current file
const codeData = FileInfoIndex.codeDataCache.get(uri)

if (codeData == null) {
// File not indexed - unable to look for definition
reportTelemetry(RequestType.Definition, 'File not indexed')
reportTelemetry(requestType, 'File not indexed')
return []
}

reportTelemetry(requestType)

// First check within the current file's code data
const definitionInCodeData = this.findDefinitionInCodeData(uri, position, expression, codeData)

if (definitionInCodeData != null) {
reportTelemetry(RequestType.Definition)
return definitionInCodeData
}

if (requestType === RequestType.DocumentHighlight) {
return []
}

// Check the MATLAB path
const definitionOnPath = await this.findDefinitionOnPath(uri, position, expression, pathResolver, indexer)

if (definitionOnPath != null) {
reportTelemetry(RequestType.Definition)
return definitionOnPath
}

// If not on path, may be in user's workspace
reportTelemetry(RequestType.Definition)
return this.findDefinitionInWorkspace(uri, expression)
}

Expand Down
Loading
Loading