import { monaco } from 'minwebide'; import { numblBuiltinNames } from './builtinNames'; // MATLAB-syntax language support for .m files, adapted from numbl.org's own // Monaco language definition (numbl's src/numblLanguage.ts, which is not // published on npm). Registered after the built-in languages so its claim on // the .m extension takes precedence over VS Code's Objective-C mapping. const languageConfig: monaco.languages.LanguageConfiguration = { comments: { lineComment: '%', blockComment: ['%{', '%}'], }, brackets: [ ['{', '}'], ['[', ']'], ['(', ')'], ], autoClosingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: '"', close: '"' }, ], surroundingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: '"', close: '"' }, ], }; const builtinConstants = ['pi', 'e', 'eps', 'Inf', 'inf', 'NaN', 'nan', 'i', 'j']; // numbl's "special" builtins (I/O, plotting, path/fs commands) are dispatched // outside its regular builtin registry, so `numbl list-builtins` (the source // of builtinNames.ts) doesn't include them — copied from numbl 0.4.8's // SPECIAL_BUILTIN_NAMES plus the plot-dispatch names. const specialBuiltinNames = [ 'help', 'disp', 'fprintf', 'arrayfun', 'cellfun', 'structfun', 'feval', 'bsxfun', 'subsref', 'subsasgn', 'builtin', 'fopen', 'fclose', 'fgetl', 'fgets', 'fileread', 'feof', 'ferror', 'fread', 'fwrite', 'frewind', 'fseek', 'ftell', 'fileparts', 'fullfile', 'assignin', 'evalin', 'set', 'get', 'drawnow', 'pause', 'plot', 'plot3', 'line', 'patch', 'trimesh', 'fill', 'surf', 'surface', 'scatter', 'imagesc', 'pcolor', 'contour', 'contourf', 'mesh', 'waterfall', 'isosurface', 'bar', 'barh', 'bar3', 'bar3h', 'stairs', 'errorbar', 'semilogx', 'semilogy', 'loglog', 'area', 'fplot', 'fplot3', 'scatter3', 'histogram', 'histogram2', 'boxchart', 'swarmchart', 'swarmchart3', 'piechart', 'donutchart', 'heatmap', 'quiver', 'quiver3', 'streamline', 'stream2', 'ishold', 'figure', 'uihtml', 'uigridlayout', 'subplot', 'tiledlayout', 'nexttile', 'title', 'xlabel', 'ylabel', 'zlabel', 'hold', 'grid', 'box', 'legend', 'close', 'sgtitle', 'shading', 'clf', 'cla', 'colormap', 'view', 'colorbar', 'axis', 'caxis', 'clim', 'gcf', 'gca', 'mfilename', 'addpath', 'rmpath', 'savepath', 'path', 'mkdir', 'websave', 'webread', 'delete', 'rmdir', 'movefile', 'copyfile', 'fileattrib', 'unzip', 'dir', 'warning', 'input', 'tempdir', 'tempname', 'userpath', 'getenv', 'setenv', 'pwd', 'cd', 'ode45', 'ode23', 'deval', 'tic', 'toc', 'quadgk', 'gmres', 'eigs', 'onCleanup', ]; function createTokensProvider(): monaco.languages.IMonarchLanguage { return { defaultToken: '', keywords: [ 'function', 'if', 'else', 'elseif', 'for', 'while', 'break', 'continue', 'return', 'end', 'classdef', 'properties', 'methods', 'events', 'enumeration', 'arguments', 'import', 'switch', 'case', 'otherwise', 'try', 'catch', 'global', 'persistent', 'true', 'false', ], builtinFunctions: [...new Set([...numblBuiltinNames, ...specialBuiltinNames])], builtinConstants, tokenizer: { root: [ // section markers (must be at line start) [/^%%.*$/, 'comment.doc'], // block comments [/%\{/, 'comment', '@blockComment'], // line comments [/%.*$/, 'comment'], // identifiers and keywords — push afterValue since they produce values [ /[a-zA-Z_]\w*/, { cases: { '@keywords': { token: 'keyword', next: '@afterValue' }, '@builtinFunctions': { token: 'predefined', next: '@afterValue' }, '@builtinConstants': { token: 'constant.language', next: '@afterValue' }, '@default': { token: 'identifier', next: '@afterValue' }, }, }, ], // numbers — push afterValue since they produce values [/\d+\.?\d*([eE][+-]?\d+)?/, { token: 'number.float', next: '@afterValue' }], [/\.\d+([eE][+-]?\d+)?/, { token: 'number.float', next: '@afterValue' }], // strings (single and double quoted) [/"([^"\\]|\\.)*$/, 'string.invalid'], [/'([^'\\]|\\.)*$/, 'string.invalid'], [/"/, 'string', '@doubleQuotedString'], [/'/, 'string', '@singleQuotedString'], // closing brackets produce values — push afterValue [/[)\]]/, { token: '@brackets', next: '@afterValue' }], [/\}/, { token: '@brackets', next: '@afterValue' }], // opening brackets [/[{([]/, '@brackets'], // delimiters and operators (no ' — handled in afterValue as transpose) [/[;,.]/, 'delimiter'], [/==|~=|<=|>=|&&|\|\||\.\.\.|\.\*|\.\/|\.\\|\.\^|[=<>~+\-*/\\^&|!@?:]/, 'operator'], { include: '@whitespace' }, ], // state after a value-producing token (identifier, number, closing // bracket, or string) — here ' is transpose, not a string delimiter afterValue: [ [/\.'/, 'operator'], [/'/, 'operator'], [/$/, { token: '', next: '@pop' }], [/(?=[\s\S])/, { token: '', next: '@pop' }], ], blockComment: [ [/%\}/, 'comment', '@pop'], [/./, 'comment'], ], doubleQuotedString: [ [/[^\\"]+/, 'string'], [/\\./, 'string.escape'], [/"/, { token: 'string', switchTo: '@afterValue' }], ], singleQuotedString: [ [/[^\\']+/, 'string'], [/''/, 'string.escape'], [/'/, { token: 'string', switchTo: '@afterValue' }], ], whitespace: [[/[ \t\r\n]+/, 'white']], }, }; } let registered = false; /** Registers MATLAB language support for .m files. Call once, after registerBuiltinLanguages. */ export function registerMatlabLanguage(): void { if (registered) { return; } registered = true; monaco.languages.register({ id: 'matlab', extensions: ['.m'], aliases: ['MATLAB', 'matlab'], }); monaco.languages.setLanguageConfiguration('matlab', languageConfig); monaco.languages.setMonarchTokensProvider('matlab', createTokensProvider()); }