Skip to content

MATLAB language server - v1.3.4 #63

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
],
"internalConsoleOptions": "openOnSessionStart",
"name": "Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"program": "${workspaceFolder}/node_modules/mocha/bin/mocha",
"request": "launch",
"skipFiles": [
"<node_internals>/**"
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ MATLAB language server implements several Language Server Protocol features and
* Code diagnostics — [publishDiagnostics](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics)
* Quick fixes — [codeActionProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction)
* Document formatting — [documentFormattingProvider](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting)
* Document range formatting - [documentRangeFormattingProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rangeFormatting)
* Code completions — [completionProvider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion)
* Function signature help — [signatureHelpProvider](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp)
* Go to definition — [definitionProvider](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition)
* Go to references — [referencesProvider](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references)
* Document symbols — [documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol)
* 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)

## Clients
MATLAB language server supports these editors by installing the corresponding extension:
Expand All @@ -26,6 +28,18 @@ MATLAB language server supports these editors by installing the corresponding ex

### Unreleased

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

Added:
* Support for document range formatting
* Document symbol model now include methods, properties, and enumerations for improved navigation

Fixed:
* Resolves a crash that occurs when language server is used over stdin/stdout
* Resolves issue where language server stops working after calling `restoredefaultpath`
* Applied patches for CVE-2023-44270, CVE-2024-11831, CVE-2025-27789, CVE-2025-30359, CVE-2025-30360, CVE-2025-32996, and CVE-2025-5889

### 1.3.3
Release date: 2025-05-15

Expand Down
98 changes: 92 additions & 6 deletions matlab/+matlabls/+handlers/+formatting/formatCode.m
Original file line number Diff line number Diff line change
@@ -1,17 +1,103 @@
function formattedCode = formatCode (codeToFormat, options)
% FORMATCODE Formats the given MATLAB code according to the specified options.
function formattedCode = formatCode (code, startLine, endLine, options)
% FORMATCODE Formats the specifid line range of the given MATLAB
% code according to the provided options.
%
% Note: `startLine` and `endLine` should be provided as 0-based line numbers.

% Copyright 2025 The MathWorks, Inc.

s = settings;

% Update settings (temporarily) for formatting
s = settings;
cleanupObj1 = setTemporaryValue(s.matlab.editor.tab.InsertSpaces, options.insertSpaces); %#ok<NASGU>
cleanupObj2 = setTemporaryValue(s.matlab.editor.tab.TabSize, options.tabSize); %#ok<NASGU>
cleanupObj3 = setTemporaryValue(s.matlab.editor.tab.IndentSize, options.tabSize); %#ok<NASGU>

% Format code
formattedCode = indentcode(codeToFormat);
% Formatting logic expects 1-based line numbers
formattedCode = doFormatLines(code, startLine + 1, endLine + 1, options);
end

function formattedCode = doFormatLines (code, startLine, endLine, options)
% Standardize line endings to \n
code = regexprep(code , sprintf('(\r\n)|\r|\n'), newline);

lines = strsplit(code, newline, CollapseDelimiters = false);

% Determine the range of lines to format
if startLine == 1
% Case 1: start is the first code line
linesToFormat = lines(startLine:endLine);
else
% Case 2: start is not the first code line
% In this case, we include the preceding line when formatting to
% ensure the indentation of startLine is correct with respect to
% the previous line.
linesToFormat = lines(startLine-1:endLine);
end

% Format the lines using the indentcode function
formattedCode = indentcode(strjoin(linesToFormat, '\n'));
formattedLines = strsplit(formattedCode, '\n', CollapseDelimiters = false);

% Replace the respective lines in the original snippet
if startLine == 1
lines(startLine:endLine) = formattedLines;
else
% Calculate the difference in indentation
originalIndent = regexp(lines{startLine-1}, '^\s*', 'match', 'once');
newIndent = regexp(formattedLines{1}, '^\s*', 'match', 'once');
diff = getWhitespaceLength(originalIndent, options.tabSize) - getWhitespaceLength(newIndent, options.tabSize);

if diff ~= 0
% Adjust whitespace indent of formatted lines [startLine:endLine]
for i = 2:numel(formattedLines)
if options.insertSpaces
formattedLines{i} = append(getWhitespaceStringOfLength(diff, true, options.tabSize), formattedLines{i});
else
existingWhitespace = regexp(formattedLines{i}, '^\s*', 'match', 'once');
existingWhitespaceLength = getWhitespaceLength(existingWhitespace, options.tabSize);
targetWhitespaceLength = existingWhitespaceLength + diff;

newWhitespace = getWhitespaceStringOfLength(targetWhitespaceLength, false, options.tabSize);
formattedLines{i} = append(newWhitespace, strip(formattedLines{i}, 'left'));
end
end
end

% Replace the formatted lines
lines(startLine:endLine) = formattedLines(2:end);
end

% Join the lines back into a single string
formattedCode = strjoin(lines, '\n');
end

function lengthInSpaces = getWhitespaceLength (whitespaceStr, tabSize)
lengthInSpaces = 0;

% Iterate through each character in the string
for i = 1:length(whitespaceStr)
charAtPos = whitespaceStr(i);

if charAtPos == ' '
% Space character - add length of 1
lengthInSpaces = lengthInSpaces + 1;
else
% Assume tab character
% The length will now be the next multiple of `tabSize`
lengthInSpaces = floor(lengthInSpaces / tabSize + 1) * tabSize;
end
end
end

function whitespaceStr = getWhitespaceStringOfLength (length, insertSpaces, tabSize)
if insertSpaces
whitespaceStr = blanks(length);
else
% Calculate how many tab characters and additional spaces are needed
nTabs = floor(length / tabSize);
nSpaces = rem(length, tabSize);
whitespaceStr = append(repmat(char(9), 1, nTabs), blanks(nSpaces));
end
end

function cleanupObj = setTemporaryValue (setting, tempValue)
Expand Down
Binary file modified matlab/+matlabls/+internal/computeCodeData.p
Binary file not shown.
36 changes: 27 additions & 9 deletions matlab/initmatlabls.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,8 @@ function initmatlabls (outFile)

% Ensure the language server code is on the path
folder = fileparts(mfilename('fullpath'));
addpath(folder)

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

try
s = settings;
Expand Down Expand Up @@ -75,4 +67,30 @@ function logConnectionData (outFile)
if ~status
error('Failed to rename connection file.')
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
17 changes: 17 additions & 0 deletions matlab/shadows/restoredefaultpath/restoredefaultpath.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function restoredefaultpath(varargin)
mlock
persistent originalRestoreDefaultPath;
persistent pathUpdateFunction;
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.');
end

originalRestoreDefaultPath();
pathUpdateFunction();
end
Loading