From 8068c5eff5554850fa1cc820b2013ee9cd1a3461 Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:30:02 -0400 Subject: [PATCH 01/93] Duplicate seti icon theme from VS Code --- icons/theme-seti/.vscodeignore | 4 + icons/theme-seti/CONTRIBUTING.md | 33 + icons/theme-seti/README.md | 32 + icons/theme-seti/ThirdPartyNotices.txt | 32 + icons/theme-seti/build/update-icon-theme.js | 473 ++++ icons/theme-seti/cgmanifest.json | 16 + icons/theme-seti/icons/preview.html | 104 + .../icons/seti-circular-128x128.png | Bin 0 -> 4814 bytes icons/theme-seti/icons/seti.woff | Bin 0 -> 37300 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 2406 +++++++++++++++++ icons/theme-seti/package.json | 30 + icons/theme-seti/package.nls.json | 5 + 12 files changed, 3135 insertions(+) create mode 100644 icons/theme-seti/.vscodeignore create mode 100644 icons/theme-seti/CONTRIBUTING.md create mode 100644 icons/theme-seti/README.md create mode 100644 icons/theme-seti/ThirdPartyNotices.txt create mode 100644 icons/theme-seti/build/update-icon-theme.js create mode 100644 icons/theme-seti/cgmanifest.json create mode 100644 icons/theme-seti/icons/preview.html create mode 100644 icons/theme-seti/icons/seti-circular-128x128.png create mode 100644 icons/theme-seti/icons/seti.woff create mode 100644 icons/theme-seti/icons/vs-seti-icon-theme.json create mode 100644 icons/theme-seti/package.json create mode 100644 icons/theme-seti/package.nls.json diff --git a/icons/theme-seti/.vscodeignore b/icons/theme-seti/.vscodeignore new file mode 100644 index 0000000..25699ef --- /dev/null +++ b/icons/theme-seti/.vscodeignore @@ -0,0 +1,4 @@ +build/** +cgmanifest.json +icons/preview.html +CONTRIBUTING.md diff --git a/icons/theme-seti/CONTRIBUTING.md b/icons/theme-seti/CONTRIBUTING.md new file mode 100644 index 0000000..df0501d --- /dev/null +++ b/icons/theme-seti/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# theme-seti + +This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). + +## Previewing icons + +There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. +To view this, it needs to be hosted by a web server. The easiest way is to open the file with the `Open with Live Server` command from the [Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer). + + +## Updating icons + +- Make a PR against https://github.com/jesseweed/seti-ui with your icon changes. +- Once accepted there, ping us or make a PR yourself that updates the theme and font here + +To adopt the latest changes from https://github.com/jesseweed/seti-ui: + +- have the main branches of `https://github.com/jesseweed/seti-ui` and `https://github.com/microsoft/vscode` cloned in the same parent folder +- in the `seti-ui` folder, run `npm install` and `npm run prepublishOnly`. This will generate updated icons and fonts. +- in the `vscode/extensions/theme-seti` folder run `npm run update`. This will launch the [icon theme update script](build/update-icon-theme.js) that updates the theme as well as the font based on content in `seti-ui`. +- to test the icon theme, look at the icon preview as described above. +- when done, create a PR with the changes in https://github.com/microsoft/vscode. +Add a screenshot of the preview page to accompany it. + + +### Languages not shipped with `vscode` + +Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. + +These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). + +Please try and keep this list in alphabetical order! Thank you. + diff --git a/icons/theme-seti/README.md b/icons/theme-seti/README.md new file mode 100644 index 0000000..dcd9ba9 --- /dev/null +++ b/icons/theme-seti/README.md @@ -0,0 +1,32 @@ +# theme-seti + +This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). + +## Updating icons + +There is script that can be used to update icons, [./build/update-icon-theme.js](build/update-icon-theme.js). + +To run this script, run `npm run update` from the `theme-seti` directory. + +This can be run in one of two ways: looking at a local copy of `seti-ui` for icons, or getting them straight from GitHub. + +If you want to run it from a local copy of `seti-ui`, first clone [`seti-ui`](https://github.com/jesseweed/seti-ui) to the folder next to your `vscode` repo (from the `theme-seti` directory, `../../`). +Then, inside the `set-ui` directory, run `npm install` followed by `npm run prepublishOnly`. This will generate updated icons. + +If you want to download the icons straight from GitHub, change the `FROM_DISK` variable to `false` inside of `update-icon-theme.js`. + +### Languages not shipped with `vscode` + +Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. + +These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). + +Please try and keep this list in alphabetical order! Thank you. + +## Previewing icons + +There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. +Note that to view this, it needs to be hosted by a web server. + +When updating icons, it is always a good idea to make sure that they work properly by looking at this page. +When submitting a PR that updates these icons, a screenshot of the preview page should accompany it. diff --git a/icons/theme-seti/ThirdPartyNotices.txt b/icons/theme-seti/ThirdPartyNotices.txt new file mode 100644 index 0000000..29cbcd4 --- /dev/null +++ b/icons/theme-seti/ThirdPartyNotices.txt @@ -0,0 +1,32 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-theme-seti + +This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for +the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +by implication, estoppel or otherwise.† + +1. Seti UI - A subtle dark colored UI theme for Atom. (https://github.com/jesseweed/seti-ui) + +Copyright (c) 2014 Jesse Weed + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/icons/theme-seti/build/update-icon-theme.js b/icons/theme-seti/build/update-icon-theme.js new file mode 100644 index 0000000..2b16374 --- /dev/null +++ b/icons/theme-seti/build/update-icon-theme.js @@ -0,0 +1,473 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const https = require('https'); +const url = require('url'); +const minimatch = require('minimatch'); + +// list of languagesId not shipped with VSCode. The information is used to associate an icon with a language association +// Please try and keep this list in alphabetical order! Thank you. +const nonBuiltInLanguages = { // { fileNames, extensions } + "argdown": { extensions: ['ad', 'adown', 'argdown', 'argdn'] }, + "bicep": { extensions: ['bicep'] }, + "elixir": { extensions: ['ex'] }, + "elm": { extensions: ['elm'] }, + "erb": { extensions: ['erb', 'rhtml', 'html.erb'] }, + "github-issues": { extensions: ['github-issues'] }, + "gradle": { extensions: ['gradle'] }, + "godot": { extensions: ['gd', 'godot', 'tres', 'tscn'] }, + "haml": { extensions: ['haml'] }, + "haskell": { extensions: ['hs'] }, + "haxe": { extensions: ['hx'] }, + "jinja": { extensions: ['jinja'] }, + "kotlin": { extensions: ['kt'] }, + "mustache": { extensions: ['mustache', 'mst', 'mu', 'stache'] }, + "nunjucks": { extensions: ['nunjucks', 'nunjs', 'nunj', 'nj', 'njk', 'tmpl', 'tpl'] }, + "ocaml": { extensions: ['ml', 'mli', 'mll', 'mly', 'eliom', 'eliomi'] }, + "puppet": { extensions: ['puppet'] }, + "r": { extensions: ['r', 'rhistory', 'rprofile', 'rt'] }, + "rescript": { extensions: ['res', 'resi'] }, + "sass": { extensions: ['sass'] }, + "stylus": { extensions: ['styl'] }, + "terraform": { extensions: ['tf', 'tfvars', 'hcl'] }, + "todo": { fileNames: ['todo'] }, + "vala": { extensions: ['vala'] }, + "vue": { extensions: ['vue'] } +}; + +// list of languagesId that inherit the icon from another language +const inheritIconFromLanguage = { + "jsonc": 'json', + "jsonl": 'json', + "postcss": 'css', + "django-html": 'html', + "blade": 'php' +}; + +const ignoreExtAssociation = { + "properties": true +}; + +const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo + +let font, fontMappingsFile, fileAssociationFile, colorsFile; +if (!FROM_DISK) { + font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; +} else { + font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; + fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; + fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; + colorsFile = '../../../seti-ui/styles/ui-variables.less'; +} + +function getCommitSha(repoId) { + const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; + return download(commitInfo).then(function (content) { + try { + const lastCommit = JSON.parse(content); + return Promise.resolve({ + commitSha: lastCommit.sha, + commitDate: lastCommit.commit.author.date + }); + } catch (e) { + console.error('Failed parsing ' + content); + return Promise.resolve(null); + } + }, function () { + console.error('Failed loading ' + commitInfo); + return Promise.resolve(null); + }); +} + +function download(source) { + if (source.startsWith('.')) { + return readFile(source); + } + return new Promise((c, e) => { + const _url = url.parse(source); + const options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' } }; + let content = ''; + https.get(options, function (response) { + response.on('data', function (data) { + content += data.toString(); + }).on('end', function () { + c(content); + }); + }).on('error', function (err) { + e(err.message); + }); + }); +} + +function readFile(fileName) { + return new Promise((c, e) => { + fs.readFile(fileName, function (err, data) { + if (err) { + e(err); + } else { + c(data.toString()); + } + }); + }); +} + +function downloadBinary(source, dest) { + if (source.startsWith('.')) { + return copyFile(source, dest); + } + + return new Promise((c, e) => { + https.get(source, function (response) { + switch (response.statusCode) { + case 200: { + const file = fs.createWriteStream(dest); + response.on('data', function (chunk) { + file.write(chunk); + }).on('end', function () { + file.end(); + c(null); + }).on('error', function (err) { + fs.unlink(dest); + e(err.message); + }); + break; + } + case 301: + case 302: + case 303: + case 307: + console.log('redirect to ' + response.headers.location); + downloadBinary(response.headers.location, dest).then(c, e); + break; + default: + e(new Error('Server responded with status code ' + response.statusCode)); + } + }); + }); +} + +function copyFile(fileName, dest) { + return new Promise((c, e) => { + let cbCalled = false; + function handleError(err) { + if (!cbCalled) { + e(err); + cbCalled = true; + } + } + const rd = fs.createReadStream(fileName); + rd.on("error", handleError); + const wr = fs.createWriteStream(dest); + wr.on("error", handleError); + wr.on("close", function () { + if (!cbCalled) { + c(); + cbCalled = true; + } + }); + rd.pipe(wr); + }); +} + +function darkenColor(color) { + let res = '#'; + for (let i = 1; i < 7; i += 2) { + const newVal = Math.round(parseInt('0x' + color.substr(i, 2), 16) * 0.9); + const hex = newVal.toString(16); + if (hex.length === 1) { + res += '0'; + } + res += hex; + } + return res; +} + +function mergeMapping(to, from, property) { + if (from[property]) { + if (to[property]) { + to[property].push(...from[property]); + } else { + to[property] = from[property]; + } + } +} + +function getLanguageMappings() { + const langMappings = {}; + const allExtensions = fs.readdirSync('..'); + for (let i = 0; i < allExtensions.length; i++) { + const dirPath = path.join('..', allExtensions[i], 'package.json'); + if (fs.existsSync(dirPath)) { + const content = fs.readFileSync(dirPath).toString(); + const jsonContent = JSON.parse(content); + const languages = jsonContent.contributes && jsonContent.contributes.languages; + if (Array.isArray(languages)) { + for (let k = 0; k < languages.length; k++) { + const languageId = languages[k].id; + if (languageId) { + const extensions = languages[k].extensions; + const mapping = {}; + if (Array.isArray(extensions)) { + mapping.extensions = extensions.map(function (e) { return e.substr(1).toLowerCase(); }); + } + const filenames = languages[k].filenames; + if (Array.isArray(filenames)) { + mapping.fileNames = filenames.map(function (f) { return f.toLowerCase(); }); + } + const filenamePatterns = languages[k].filenamePatterns; + if (Array.isArray(filenamePatterns)) { + mapping.filenamePatterns = filenamePatterns.map(function (f) { return f.toLowerCase(); }); + } + const existing = langMappings[languageId]; + + if (existing) { + // multiple contributions to the same language + // give preference to the contribution wth the configuration + if (languages[k].configuration) { + mergeMapping(mapping, existing, 'extensions'); + mergeMapping(mapping, existing, 'fileNames'); + mergeMapping(mapping, existing, 'filenamePatterns'); + langMappings[languageId] = mapping; + } else { + mergeMapping(existing, mapping, 'extensions'); + mergeMapping(existing, mapping, 'fileNames'); + mergeMapping(existing, mapping, 'filenamePatterns'); + } + } else { + langMappings[languageId] = mapping; + } + } + } + } + } + } + for (const languageId in nonBuiltInLanguages) { + langMappings[languageId] = nonBuiltInLanguages[languageId]; + } + return langMappings; +} + +exports.copyFont = function () { + return downloadBinary(font, './icons/seti.woff'); +}; + +exports.update = function () { + + console.log('Reading from ' + fontMappingsFile); + const def2Content = {}; + const ext2Def = {}; + const fileName2Def = {}; + const def2ColorId = {}; + const colorId2Value = {}; + const lang2Def = {}; + + function writeFileIconContent(info) { + const iconDefinitions = {}; + const allDefs = Object.keys(def2Content).sort(); + + for (let i = 0; i < allDefs.length; i++) { + const def = allDefs[i]; + const entry = { fontCharacter: def2Content[def] }; + const colorId = def2ColorId[def]; + if (colorId) { + const colorValue = colorId2Value[colorId]; + if (colorValue) { + entry.fontColor = colorValue; + + const entryInverse = { fontCharacter: entry.fontCharacter, fontColor: darkenColor(colorValue) }; + iconDefinitions[def + '_light'] = entryInverse; + } + } + iconDefinitions[def] = entry; + } + + function getInvertSet(input) { + const result = {}; + for (const assoc in input) { + const invertDef = input[assoc] + '_light'; + if (iconDefinitions[invertDef]) { + result[assoc] = invertDef; + } + } + return result; + } + + const res = { + information_for_contributors: [ + 'This file has been generated from data in https://github.com/jesseweed/seti-ui', + '- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less', + '- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less', + '- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less', + 'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.', + 'Once accepted there, we are happy to receive an update request.', + ], + fonts: [{ + id: "seti", + src: [{ "path": "./seti.woff", "format": "woff" }], + weight: "normal", + style: "normal", + size: "150%" + }], + iconDefinitions: iconDefinitions, + // folder: "_folder", + file: "_default", + fileExtensions: ext2Def, + fileNames: fileName2Def, + languageIds: lang2Def, + light: { + file: "_default_light", + fileExtensions: getInvertSet(ext2Def), + languageIds: getInvertSet(lang2Def), + fileNames: getInvertSet(fileName2Def) + }, + version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha, + }; + + const path = './icons/vs-seti-icon-theme.json'; + fs.writeFileSync(path, JSON.stringify(res, null, '\t')); + console.log('written ' + path); + } + + + let match; + + return download(fontMappingsFile).then(function (content) { + const regex = /@([\w-]+):\s*'(\\E[0-9A-F]+)';/g; + const contents = {}; + while ((match = regex.exec(content)) !== null) { + contents[match[1]] = match[2]; + } + + return download(fileAssociationFile).then(function (content) { + const regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.+]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; + while ((match = regex2.exec(content)) !== null) { + const pattern = match[1]; + let def = '_' + match[2]; + const colorId = match[3]; + let storedColorId = def2ColorId[def]; + let i = 1; + while (storedColorId && colorId !== storedColorId) { // different colors for the same def? + def = `_${match[2]}_${i}`; + storedColorId = def2ColorId[def]; + i++; + } + if (!def2ColorId[def]) { + def2ColorId[def] = colorId; + def2Content[def] = contents[match[2]]; + } + + if (def === '_default') { + continue; // no need to assign default color. + } + if (pattern[0] === '.') { + ext2Def[pattern.substr(1).toLowerCase()] = def; + } else { + fileName2Def[pattern.toLowerCase()] = def; + } + } + // replace extensions for languageId + const langMappings = getLanguageMappings(); + for (let lang in langMappings) { + const mappings = langMappings[lang]; + const exts = mappings.extensions || []; + const fileNames = mappings.fileNames || []; + const filenamePatterns = mappings.filenamePatterns || []; + let preferredDef = null; + // use the first file extension association for the preferred definition + for (let i1 = 0; i1 < exts.length && !preferredDef; i1++) { + preferredDef = ext2Def[exts[i1]]; + } + // use the first file name association for the preferred definition, if not availbale + for (let i1 = 0; i1 < fileNames.length && !preferredDef; i1++) { + preferredDef = fileName2Def[fileNames[i1]]; + } + for (let i1 = 0; i1 < filenamePatterns.length && !preferredDef; i1++) { + let pattern = filenamePatterns[i1]; + for (const name in fileName2Def) { + if (minimatch(name, pattern)) { + preferredDef = fileName2Def[name]; + break; + } + } + } + if (preferredDef) { + lang2Def[lang] = preferredDef; + if (!nonBuiltInLanguages[lang] && !inheritIconFromLanguage[lang]) { + for (let i2 = 0; i2 < exts.length; i2++) { + // remove the extension association, unless it is different from the preferred + if (ext2Def[exts[i2]] === preferredDef || ignoreExtAssociation[exts[i2]]) { + delete ext2Def[exts[i2]]; + } + } + for (let i2 = 0; i2 < fileNames.length; i2++) { + // remove the fileName association, unless it is different from the preferred + if (fileName2Def[fileNames[i2]] === preferredDef) { + delete fileName2Def[fileNames[i2]]; + } + } + for (let i2 = 0; i2 < filenamePatterns.length; i2++) { + let pattern = filenamePatterns[i2]; + // remove the filenamePatterns association, unless it is different from the preferred + for (const name in fileName2Def) { + if (minimatch(name, pattern) && fileName2Def[name] === preferredDef) { + delete fileName2Def[name]; + } + } + } + } + } + } + for (const lang in inheritIconFromLanguage) { + const superLang = inheritIconFromLanguage[lang]; + const def = lang2Def[superLang]; + if (def) { + lang2Def[lang] = def; + } else { + console.log('skipping icon def for ' + lang + ': no icon for ' + superLang + ' defined'); + } + + } + + + return download(colorsFile).then(function (content) { + const regex3 = /(@[\w-]+):\s*(#[0-9a-z]+)/g; + while ((match = regex3.exec(content)) !== null) { + colorId2Value[match[1]] = match[2]; + } + return getCommitSha('jesseweed/seti-ui').then(function (info) { + try { + writeFileIconContent(info); + + const cgmanifestPath = './cgmanifest.json'; + const cgmanifest = fs.readFileSync(cgmanifestPath).toString(); + const cgmanifestContent = JSON.parse(cgmanifest); + cgmanifestContent['registrations'][0]['component']['git']['commitHash'] = info.commitSha; + fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); + console.log('updated ' + cgmanifestPath); + + console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + + } catch (e) { + console.error(e); + } + }); + }); + }); + }, console.error); +}; + +if (path.basename(process.argv[1]) === 'update-icon-theme.js') { + exports.copyFont().then(() => exports.update()); +} + + + diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json new file mode 100644 index 0000000..8d50dd2 --- /dev/null +++ b/icons/theme-seti/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "seti-ui", + "repositoryUrl": "https://github.com/jesseweed/seti-ui", + "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} \ No newline at end of file diff --git a/icons/theme-seti/icons/preview.html b/icons/theme-seti/icons/preview.html new file mode 100644 index 0000000..84b5166 --- /dev/null +++ b/icons/theme-seti/icons/preview.html @@ -0,0 +1,104 @@ + + + + + + seti font preview + + + + + + + + diff --git a/icons/theme-seti/icons/seti-circular-128x128.png b/icons/theme-seti/icons/seti-circular-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe533bba1e34ed5618c971c93514e7a20937b67 GIT binary patch literal 4814 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 Js~^}!{tw;l6mb9m literal 0 HcmV?d00001 diff --git a/icons/theme-seti/icons/seti.woff b/icons/theme-seti/icons/seti.woff new file mode 100644 index 0000000000000000000000000000000000000000..edc69c7cf89fb7ce5be3b03334019278008f5ad0 GIT binary patch literal 37300 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 Date: Wed, 16 Apr 2025 13:43:41 -0400 Subject: [PATCH 02/93] Update theme content for VBA-LanguageServer --- icons/theme-seti/CONTRIBUTING.md | 33 --------------------- icons/theme-seti/README.md | 2 +- icons/theme-seti/ThirdPartyNotices.txt | 10 +++---- icons/theme-seti/build/update-icon-theme.js | 30 ++++++++++--------- icons/theme-seti/cgmanifest.json | 2 +- icons/theme-seti/package.json | 17 +++++++---- icons/theme-seti/package.nls.json | 6 ++-- 7 files changed, 38 insertions(+), 62 deletions(-) delete mode 100644 icons/theme-seti/CONTRIBUTING.md diff --git a/icons/theme-seti/CONTRIBUTING.md b/icons/theme-seti/CONTRIBUTING.md deleted file mode 100644 index df0501d..0000000 --- a/icons/theme-seti/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -# theme-seti - -This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). - -## Previewing icons - -There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. -To view this, it needs to be hosted by a web server. The easiest way is to open the file with the `Open with Live Server` command from the [Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer). - - -## Updating icons - -- Make a PR against https://github.com/jesseweed/seti-ui with your icon changes. -- Once accepted there, ping us or make a PR yourself that updates the theme and font here - -To adopt the latest changes from https://github.com/jesseweed/seti-ui: - -- have the main branches of `https://github.com/jesseweed/seti-ui` and `https://github.com/microsoft/vscode` cloned in the same parent folder -- in the `seti-ui` folder, run `npm install` and `npm run prepublishOnly`. This will generate updated icons and fonts. -- in the `vscode/extensions/theme-seti` folder run `npm run update`. This will launch the [icon theme update script](build/update-icon-theme.js) that updates the theme as well as the font based on content in `seti-ui`. -- to test the icon theme, look at the icon preview as described above. -- when done, create a PR with the changes in https://github.com/microsoft/vscode. -Add a screenshot of the preview page to accompany it. - - -### Languages not shipped with `vscode` - -Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. - -These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). - -Please try and keep this list in alphabetical order! Thank you. - diff --git a/icons/theme-seti/README.md b/icons/theme-seti/README.md index dcd9ba9..2c4d721 100644 --- a/icons/theme-seti/README.md +++ b/icons/theme-seti/README.md @@ -1,6 +1,6 @@ # theme-seti -This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). +This is an icon theme that uses the icons from a fork of [`seti-ui`](https://github.com/jesseweed/seti-ui). ## Updating icons diff --git a/icons/theme-seti/ThirdPartyNotices.txt b/icons/theme-seti/ThirdPartyNotices.txt index 29cbcd4..6a495c8 100644 --- a/icons/theme-seti/ThirdPartyNotices.txt +++ b/icons/theme-seti/ThirdPartyNotices.txt @@ -1,11 +1,11 @@ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -For Microsoft vscode-theme-seti +For VBA-LanguageServer vscode-theme-seti -This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright -notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice -are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for -the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +This theme is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which VBA-LanguageServer received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. VBA-LanguageServer licenses the Third Party OSS to you under the licensing terms for +the VBA-LanguageServer product or service. VBA-LanguageServer reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise.† 1. Seti UI - A subtle dark colored UI theme for Atom. (https://github.com/jesseweed/seti-ui) diff --git a/icons/theme-seti/build/update-icon-theme.js b/icons/theme-seti/build/update-icon-theme.js index 2b16374..bd7f518 100644 --- a/icons/theme-seti/build/update-icon-theme.js +++ b/icons/theme-seti/build/update-icon-theme.js @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +//@ts-nocheck + 'use strict'; const path = require('path'); @@ -54,14 +56,14 @@ const ignoreExtAssociation = { "properties": true }; -const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo +const FROM_DISK = false; // set to true to take content from a repo checked out next to the vscode repo let font, fontMappingsFile, fileAssociationFile, colorsFile; if (!FROM_DISK) { - font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; - fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; - fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; - colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; + font = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/ui-variables.less'; } else { font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; @@ -70,7 +72,7 @@ if (!FROM_DISK) { } function getCommitSha(repoId) { - const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; + const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/vba'; return download(commitInfo).then(function (content) { try { const lastCommit = JSON.parse(content); @@ -303,11 +305,11 @@ exports.update = function () { const res = { information_for_contributors: [ - 'This file has been generated from data in https://github.com/jesseweed/seti-ui', - '- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less', - '- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less', - '- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less', - 'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.', + 'This file has been generated from data in https://github.com/DecimalTurn/seti-ui', + '- icon definitions: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/_fonts/seti.less', + '- icon colors: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/ui-variables.less', + '- file associations: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/components/icons/mapping.less', + 'If you want to provide a fix or improvement, please create a pull request against the DecimalTurn/seti-ui repository.', 'Once accepted there, we are happy to receive an update request.', ], fonts: [{ @@ -329,7 +331,7 @@ exports.update = function () { languageIds: getInvertSet(lang2Def), fileNames: getInvertSet(fileName2Def) }, - version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha, + version: 'https://github.com/DecimalTurn/seti-ui/commit/' + info.commitSha, }; const path = './icons/vs-seti-icon-theme.json'; @@ -443,7 +445,7 @@ exports.update = function () { while ((match = regex3.exec(content)) !== null) { colorId2Value[match[1]] = match[2]; } - return getCommitSha('jesseweed/seti-ui').then(function (info) { + return getCommitSha('DecimalTurn/seti-ui').then(function (info) { try { writeFileIconContent(info); @@ -454,7 +456,7 @@ exports.update = function () { fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); console.log('updated ' + cgmanifestPath); - console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + console.log('Updated to DecimalTurn/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); } catch (e) { console.error(e); diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json index 8d50dd2..f34bcdd 100644 --- a/icons/theme-seti/cgmanifest.json +++ b/icons/theme-seti/cgmanifest.json @@ -5,7 +5,7 @@ "type": "git", "git": { "name": "seti-ui", - "repositoryUrl": "https://github.com/jesseweed/seti-ui", + "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" } }, diff --git a/icons/theme-seti/package.json b/icons/theme-seti/package.json index dab0dea..64b61ce 100644 --- a/icons/theme-seti/package.json +++ b/icons/theme-seti/package.json @@ -1,23 +1,26 @@ { - "name": "vscode-theme-seti", + "name": "vscode-theme-seti-vba", "private": true, - "version": "1.0.0", + "version": "1.1.0-vba", "displayName": "%displayName%", "description": "%description%", "publisher": "vscode", "license": "MIT", "icon": "icons/seti-circular-128x128.png", "scripts": { - "update": "node ./build/update-icon-theme.js" + "update": "node ./build/update-icon-theme.js", + "build": "vsce package" }, "engines": { "vscode": "*" }, - "categories": ["Themes"], + "categories": [ + "Themes" + ], "contributes": { "iconThemes": [ { - "id": "vs-seti", + "id": "vs-seti-vba", "label": "%themeLabel%", "path": "./icons/vs-seti-icon-theme.json" } @@ -26,5 +29,9 @@ "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" + }, + "devDependencies": { + "minimatch": "^3.0.4", + "vsce": "^2.15.0" } } diff --git a/icons/theme-seti/package.nls.json b/icons/theme-seti/package.nls.json index 3572c3c..3b95693 100644 --- a/icons/theme-seti/package.nls.json +++ b/icons/theme-seti/package.nls.json @@ -1,5 +1,5 @@ { - "displayName": "Seti File Icon Theme", - "description": "A file icon theme made out of the Seti UI file icons", - "themeLabel": "Seti (Visual Studio Code)" + "displayName": "Seti File Icon Theme + VBA", + "description": "A file icon theme made out of the Seti UI file icons + VBA icons.", + "themeLabel": "Seti + VBA" } From 27df41d88b1fa4401d57a8447c95dbd0cbb068de Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:48:22 -0400 Subject: [PATCH 03/93] Update icons and package.json --- icons/theme-seti/cgmanifest.json | 30 +- icons/theme-seti/icons/seti.woff | Bin 37300 -> 37452 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 4900 +++++++++-------- package.json | 5 + 4 files changed, 2515 insertions(+), 2420 deletions(-) diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json index f34bcdd..cd70a9f 100644 --- a/icons/theme-seti/cgmanifest.json +++ b/icons/theme-seti/cgmanifest.json @@ -1,16 +1,16 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "seti-ui", - "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", - "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" - } - }, - "version": "0.1.0" - } - ], - "version": 1 +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "seti-ui", + "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", + "commitHash": "7a8d51ccb32737be812549fe1c31fae8276b284f" + } + }, + "version": "0.1.0" + } + ], + "version": 1 } \ No newline at end of file diff --git a/icons/theme-seti/icons/seti.woff b/icons/theme-seti/icons/seti.woff index edc69c7cf89fb7ce5be3b03334019278008f5ad0..b20123e96bb1a4623c39609502ae9bfc79e83334 100644 GIT binary patch delta 37068 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 KFB8tfk^lgqHFY%r delta 36967 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 Nm6d-1n;gKf008BK5#;~? diff --git a/icons/theme-seti/icons/vs-seti-icon-theme.json b/icons/theme-seti/icons/vs-seti-icon-theme.json index 119ae70..7a69ca3 100644 --- a/icons/theme-seti/icons/vs-seti-icon-theme.json +++ b/icons/theme-seti/icons/vs-seti-icon-theme.json @@ -1,2406 +1,2496 @@ -{ - "information_for_contributors": [ - "This file has been generated from data in https://github.com/jesseweed/seti-ui", - "- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less", - "- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less", - "- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less", - "If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.", - "Once accepted there, we are happy to receive an update request." - ], - "fonts": [ - { - "id": "seti", - "src": [ - { - "path": "./seti.woff", - "format": "woff" - } - ], - "weight": "normal", - "style": "normal", - "size": "150%" - } - ], - "iconDefinitions": { - "_R_light": { - "fontCharacter": "\\E001", - "fontColor": "#498ba7" - }, - "_R": { - "fontCharacter": "\\E001", - "fontColor": "#519aba" - }, - "_argdown_light": { - "fontCharacter": "\\E003", - "fontColor": "#498ba7" - }, - "_argdown": { - "fontCharacter": "\\E003", - "fontColor": "#519aba" - }, - "_asm_light": { - "fontCharacter": "\\E004", - "fontColor": "#b8383d" - }, - "_asm": { - "fontCharacter": "\\E004", - "fontColor": "#cc3e44" - }, - "_audio_light": { - "fontCharacter": "\\E005", - "fontColor": "#9068b0" - }, - "_audio": { - "fontCharacter": "\\E005", - "fontColor": "#a074c4" - }, - "_babel_light": { - "fontCharacter": "\\E006", - "fontColor": "#b7b73b" - }, - "_babel": { - "fontCharacter": "\\E006", - "fontColor": "#cbcb41" - }, - "_bazel_light": { - "fontCharacter": "\\E007", - "fontColor": "#7fae42" - }, - "_bazel": { - "fontCharacter": "\\E007", - "fontColor": "#8dc149" - }, - "_bazel_1_light": { - "fontCharacter": "\\E007", - "fontColor": "#455155" - }, - "_bazel_1": { - "fontCharacter": "\\E007", - "fontColor": "#4d5a5e" - }, - "_bicep_light": { - "fontCharacter": "\\E008", - "fontColor": "#498ba7" - }, - "_bicep": { - "fontCharacter": "\\E008", - "fontColor": "#519aba" - }, - "_bower_light": { - "fontCharacter": "\\E009", - "fontColor": "#cc6d2e" - }, - "_bower": { - "fontCharacter": "\\E009", - "fontColor": "#e37933" - }, - "_bsl_light": { - "fontCharacter": "\\E00A", - "fontColor": "#b8383d" - }, - "_bsl": { - "fontCharacter": "\\E00A", - "fontColor": "#cc3e44" - }, - "_c_light": { - "fontCharacter": "\\E00C", - "fontColor": "#498ba7" - }, - "_c": { - "fontCharacter": "\\E00C", - "fontColor": "#519aba" - }, - "_c-sharp_light": { - "fontCharacter": "\\E00B", - "fontColor": "#498ba7" - }, - "_c-sharp": { - "fontCharacter": "\\E00B", - "fontColor": "#519aba" - }, - "_c_1_light": { - "fontCharacter": "\\E00C", - "fontColor": "#9068b0" - }, - "_c_1": { - "fontCharacter": "\\E00C", - "fontColor": "#a074c4" - }, - "_c_2_light": { - "fontCharacter": "\\E00C", - "fontColor": "#b7b73b" - }, - "_c_2": { - "fontCharacter": "\\E00C", - "fontColor": "#cbcb41" - }, - "_cake_light": { - "fontCharacter": "\\E00D", - "fontColor": "#b8383d" - }, - "_cake": { - "fontCharacter": "\\E00D", - "fontColor": "#cc3e44" - }, - "_cake_php_light": { - "fontCharacter": "\\E00E", - "fontColor": "#b8383d" - }, - "_cake_php": { - "fontCharacter": "\\E00E", - "fontColor": "#cc3e44" - }, - "_clock_light": { - "fontCharacter": "\\E012", - "fontColor": "#498ba7" - }, - "_clock": { - "fontCharacter": "\\E012", - "fontColor": "#519aba" - }, - "_clock_1_light": { - "fontCharacter": "\\E012", - "fontColor": "#627379" - }, - "_clock_1": { - "fontCharacter": "\\E012", - "fontColor": "#6d8086" - }, - "_clojure_light": { - "fontCharacter": "\\E013", - "fontColor": "#7fae42" - }, - "_clojure": { - "fontCharacter": "\\E013", - "fontColor": "#8dc149" - }, - "_clojure_1_light": { - "fontCharacter": "\\E013", - "fontColor": "#498ba7" - }, - "_clojure_1": { - "fontCharacter": "\\E013", - "fontColor": "#519aba" - }, - "_code-climate_light": { - "fontCharacter": "\\E014", - "fontColor": "#7fae42" - }, - "_code-climate": { - "fontCharacter": "\\E014", - "fontColor": "#8dc149" - }, - "_code-search_light": { - "fontCharacter": "\\E015", - "fontColor": "#9068b0" - }, - "_code-search": { - "fontCharacter": "\\E015", - "fontColor": "#a074c4" - }, - "_coffee_light": { - "fontCharacter": "\\E016", - "fontColor": "#b7b73b" - }, - "_coffee": { - "fontCharacter": "\\E016", - "fontColor": "#cbcb41" - }, - "_coldfusion_light": { - "fontCharacter": "\\E018", - "fontColor": "#498ba7" - }, - "_coldfusion": { - "fontCharacter": "\\E018", - "fontColor": "#519aba" - }, - "_config_light": { - "fontCharacter": "\\E019", - "fontColor": "#627379" - }, - "_config": { - "fontCharacter": "\\E019", - "fontColor": "#6d8086" - }, - "_cpp_light": { - "fontCharacter": "\\E01A", - "fontColor": "#498ba7" - }, - "_cpp": { - "fontCharacter": "\\E01A", - "fontColor": "#519aba" - }, - "_cpp_1_light": { - "fontCharacter": "\\E01A", - "fontColor": "#9068b0" - }, - "_cpp_1": { - "fontCharacter": "\\E01A", - "fontColor": "#a074c4" - }, - "_cpp_2_light": { - "fontCharacter": "\\E01A", - "fontColor": "#b7b73b" - }, - "_cpp_2": { - "fontCharacter": "\\E01A", - "fontColor": "#cbcb41" - }, - "_crystal_light": { - "fontCharacter": "\\E01B", - "fontColor": "#bfc2c1" - }, - "_crystal": { - "fontCharacter": "\\E01B", - "fontColor": "#d4d7d6" - }, - "_crystal_embedded_light": { - "fontCharacter": "\\E01C", - "fontColor": "#bfc2c1" - }, - "_crystal_embedded": { - "fontCharacter": "\\E01C", - "fontColor": "#d4d7d6" - }, - "_css_light": { - "fontCharacter": "\\E01D", - "fontColor": "#498ba7" - }, - "_css": { - "fontCharacter": "\\E01D", - "fontColor": "#519aba" - }, - "_csv_light": { - "fontCharacter": "\\E01E", - "fontColor": "#7fae42" - }, - "_csv": { - "fontCharacter": "\\E01E", - "fontColor": "#8dc149" - }, - "_cu_light": { - "fontCharacter": "\\E01F", - "fontColor": "#7fae42" - }, - "_cu": { - "fontCharacter": "\\E01F", - "fontColor": "#8dc149" - }, - "_cu_1_light": { - "fontCharacter": "\\E01F", - "fontColor": "#9068b0" - }, - "_cu_1": { - "fontCharacter": "\\E01F", - "fontColor": "#a074c4" - }, - "_d_light": { - "fontCharacter": "\\E020", - "fontColor": "#b8383d" - }, - "_d": { - "fontCharacter": "\\E020", - "fontColor": "#cc3e44" - }, - "_dart_light": { - "fontCharacter": "\\E021", - "fontColor": "#498ba7" - }, - "_dart": { - "fontCharacter": "\\E021", - "fontColor": "#519aba" - }, - "_db_light": { - "fontCharacter": "\\E022", - "fontColor": "#dd4b78" - }, - "_db": { - "fontCharacter": "\\E022", - "fontColor": "#f55385" - }, - "_db_1_light": { - "fontCharacter": "\\E022", - "fontColor": "#498ba7" - }, - "_db_1": { - "fontCharacter": "\\E022", - "fontColor": "#519aba" - }, - "_default_light": { - "fontCharacter": "\\E023", - "fontColor": "#bfc2c1" - }, - "_default": { - "fontCharacter": "\\E023", - "fontColor": "#d4d7d6" - }, - "_docker_light": { - "fontCharacter": "\\E025", - "fontColor": "#498ba7" - }, - "_docker": { - "fontCharacter": "\\E025", - "fontColor": "#519aba" - }, - "_docker_1_light": { - "fontCharacter": "\\E025", - "fontColor": "#455155" - }, - "_docker_1": { - "fontCharacter": "\\E025", - "fontColor": "#4d5a5e" - }, - "_docker_2_light": { - "fontCharacter": "\\E025", - "fontColor": "#7fae42" - }, - "_docker_2": { - "fontCharacter": "\\E025", - "fontColor": "#8dc149" - }, - "_docker_3_light": { - "fontCharacter": "\\E025", - "fontColor": "#dd4b78" - }, - "_docker_3": { - "fontCharacter": "\\E025", - "fontColor": "#f55385" - }, - "_ejs_light": { - "fontCharacter": "\\E027", - "fontColor": "#b7b73b" - }, - "_ejs": { - "fontCharacter": "\\E027", - "fontColor": "#cbcb41" - }, - "_elixir_light": { - "fontCharacter": "\\E028", - "fontColor": "#9068b0" - }, - "_elixir": { - "fontCharacter": "\\E028", - "fontColor": "#a074c4" - }, - "_elixir_script_light": { - "fontCharacter": "\\E029", - "fontColor": "#9068b0" - }, - "_elixir_script": { - "fontCharacter": "\\E029", - "fontColor": "#a074c4" - }, - "_elm_light": { - "fontCharacter": "\\E02A", - "fontColor": "#498ba7" - }, - "_elm": { - "fontCharacter": "\\E02A", - "fontColor": "#519aba" - }, - "_eslint_light": { - "fontCharacter": "\\E02C", - "fontColor": "#9068b0" - }, - "_eslint": { - "fontCharacter": "\\E02C", - "fontColor": "#a074c4" - }, - "_eslint_1_light": { - "fontCharacter": "\\E02C", - "fontColor": "#455155" - }, - "_eslint_1": { - "fontCharacter": "\\E02C", - "fontColor": "#4d5a5e" - }, - "_ethereum_light": { - "fontCharacter": "\\E02D", - "fontColor": "#498ba7" - }, - "_ethereum": { - "fontCharacter": "\\E02D", - "fontColor": "#519aba" - }, - "_f-sharp_light": { - "fontCharacter": "\\E02E", - "fontColor": "#498ba7" - }, - "_f-sharp": { - "fontCharacter": "\\E02E", - "fontColor": "#519aba" - }, - "_favicon_light": { - "fontCharacter": "\\E02F", - "fontColor": "#b7b73b" - }, - "_favicon": { - "fontCharacter": "\\E02F", - "fontColor": "#cbcb41" - }, - "_firebase_light": { - "fontCharacter": "\\E030", - "fontColor": "#cc6d2e" - }, - "_firebase": { - "fontCharacter": "\\E030", - "fontColor": "#e37933" - }, - "_firefox_light": { - "fontCharacter": "\\E031", - "fontColor": "#cc6d2e" - }, - "_firefox": { - "fontCharacter": "\\E031", - "fontColor": "#e37933" - }, - "_font_light": { - "fontCharacter": "\\E033", - "fontColor": "#b8383d" - }, - "_font": { - "fontCharacter": "\\E033", - "fontColor": "#cc3e44" - }, - "_git_light": { - "fontCharacter": "\\E034", - "fontColor": "#3b4b52" - }, - "_git": { - "fontCharacter": "\\E034", - "fontColor": "#41535b" - }, - "_github_light": { - "fontCharacter": "\\E037", - "fontColor": "#bfc2c1" - }, - "_github": { - "fontCharacter": "\\E037", - "fontColor": "#d4d7d6" - }, - "_gitlab_light": { - "fontCharacter": "\\E038", - "fontColor": "#cc6d2e" - }, - "_gitlab": { - "fontCharacter": "\\E038", - "fontColor": "#e37933" - }, - "_go_light": { - "fontCharacter": "\\E039", - "fontColor": "#498ba7" - }, - "_go": { - "fontCharacter": "\\E039", - "fontColor": "#519aba" - }, - "_go2_light": { - "fontCharacter": "\\E03A", - "fontColor": "#498ba7" - }, - "_go2": { - "fontCharacter": "\\E03A", - "fontColor": "#519aba" - }, - "_godot_light": { - "fontCharacter": "\\E03B", - "fontColor": "#498ba7" - }, - "_godot": { - "fontCharacter": "\\E03B", - "fontColor": "#519aba" - }, - "_godot_1_light": { - "fontCharacter": "\\E03B", - "fontColor": "#b8383d" - }, - "_godot_1": { - "fontCharacter": "\\E03B", - "fontColor": "#cc3e44" - }, - "_godot_2_light": { - "fontCharacter": "\\E03B", - "fontColor": "#b7b73b" - }, - "_godot_2": { - "fontCharacter": "\\E03B", - "fontColor": "#cbcb41" - }, - "_godot_3_light": { - "fontCharacter": "\\E03B", - "fontColor": "#9068b0" - }, - "_godot_3": { - "fontCharacter": "\\E03B", - "fontColor": "#a074c4" - }, - "_gradle_light": { - "fontCharacter": "\\E03C", - "fontColor": "#498ba7" - }, - "_gradle": { - "fontCharacter": "\\E03C", - "fontColor": "#519aba" - }, - "_grails_light": { - "fontCharacter": "\\E03D", - "fontColor": "#7fae42" - }, - "_grails": { - "fontCharacter": "\\E03D", - "fontColor": "#8dc149" - }, - "_graphql_light": { - "fontCharacter": "\\E03E", - "fontColor": "#dd4b78" - }, - "_graphql": { - "fontCharacter": "\\E03E", - "fontColor": "#f55385" - }, - "_grunt_light": { - "fontCharacter": "\\E03F", - "fontColor": "#cc6d2e" - }, - "_grunt": { - "fontCharacter": "\\E03F", - "fontColor": "#e37933" - }, - "_gulp_light": { - "fontCharacter": "\\E040", - "fontColor": "#b8383d" - }, - "_gulp": { - "fontCharacter": "\\E040", - "fontColor": "#cc3e44" - }, - "_hacklang_light": { - "fontCharacter": "\\E041", - "fontColor": "#cc6d2e" - }, - "_hacklang": { - "fontCharacter": "\\E041", - "fontColor": "#e37933" - }, - "_haml_light": { - "fontCharacter": "\\E042", - "fontColor": "#b8383d" - }, - "_haml": { - "fontCharacter": "\\E042", - "fontColor": "#cc3e44" - }, - "_happenings_light": { - "fontCharacter": "\\E043", - "fontColor": "#498ba7" - }, - "_happenings": { - "fontCharacter": "\\E043", - "fontColor": "#519aba" - }, - "_haskell_light": { - "fontCharacter": "\\E044", - "fontColor": "#9068b0" - }, - "_haskell": { - "fontCharacter": "\\E044", - "fontColor": "#a074c4" - }, - "_haxe_light": { - "fontCharacter": "\\E045", - "fontColor": "#cc6d2e" - }, - "_haxe": { - "fontCharacter": "\\E045", - "fontColor": "#e37933" - }, - "_haxe_1_light": { - "fontCharacter": "\\E045", - "fontColor": "#b7b73b" - }, - "_haxe_1": { - "fontCharacter": "\\E045", - "fontColor": "#cbcb41" - }, - "_haxe_2_light": { - "fontCharacter": "\\E045", - "fontColor": "#498ba7" - }, - "_haxe_2": { - "fontCharacter": "\\E045", - "fontColor": "#519aba" - }, - "_haxe_3_light": { - "fontCharacter": "\\E045", - "fontColor": "#9068b0" - }, - "_haxe_3": { - "fontCharacter": "\\E045", - "fontColor": "#a074c4" - }, - "_heroku_light": { - "fontCharacter": "\\E046", - "fontColor": "#9068b0" - }, - "_heroku": { - "fontCharacter": "\\E046", - "fontColor": "#a074c4" - }, - "_hex_light": { - "fontCharacter": "\\E047", - "fontColor": "#b8383d" - }, - "_hex": { - "fontCharacter": "\\E047", - "fontColor": "#cc3e44" - }, - "_html_light": { - "fontCharacter": "\\E048", - "fontColor": "#498ba7" - }, - "_html": { - "fontCharacter": "\\E048", - "fontColor": "#519aba" - }, - "_html_1_light": { - "fontCharacter": "\\E048", - "fontColor": "#7fae42" - }, - "_html_1": { - "fontCharacter": "\\E048", - "fontColor": "#8dc149" - }, - "_html_2_light": { - "fontCharacter": "\\E048", - "fontColor": "#b7b73b" - }, - "_html_2": { - "fontCharacter": "\\E048", - "fontColor": "#cbcb41" - }, - "_html_3_light": { - "fontCharacter": "\\E048", - "fontColor": "#cc6d2e" - }, - "_html_3": { - "fontCharacter": "\\E048", - "fontColor": "#e37933" - }, - "_html_erb_light": { - "fontCharacter": "\\E049", - "fontColor": "#b8383d" - }, - "_html_erb": { - "fontCharacter": "\\E049", - "fontColor": "#cc3e44" - }, - "_ignored_light": { - "fontCharacter": "\\E04A", - "fontColor": "#3b4b52" - }, - "_ignored": { - "fontCharacter": "\\E04A", - "fontColor": "#41535b" - }, - "_illustrator_light": { - "fontCharacter": "\\E04B", - "fontColor": "#b7b73b" - }, - "_illustrator": { - "fontCharacter": "\\E04B", - "fontColor": "#cbcb41" - }, - "_image_light": { - "fontCharacter": "\\E04C", - "fontColor": "#9068b0" - }, - "_image": { - "fontCharacter": "\\E04C", - "fontColor": "#a074c4" - }, - "_info_light": { - "fontCharacter": "\\E04D", - "fontColor": "#498ba7" - }, - "_info": { - "fontCharacter": "\\E04D", - "fontColor": "#519aba" - }, - "_ionic_light": { - "fontCharacter": "\\E04E", - "fontColor": "#498ba7" - }, - "_ionic": { - "fontCharacter": "\\E04E", - "fontColor": "#519aba" - }, - "_jade_light": { - "fontCharacter": "\\E04F", - "fontColor": "#b8383d" - }, - "_jade": { - "fontCharacter": "\\E04F", - "fontColor": "#cc3e44" - }, - "_java_light": { - "fontCharacter": "\\E050", - "fontColor": "#b8383d" - }, - "_java": { - "fontCharacter": "\\E050", - "fontColor": "#cc3e44" - }, - "_java_1_light": { - "fontCharacter": "\\E050", - "fontColor": "#498ba7" - }, - "_java_1": { - "fontCharacter": "\\E050", - "fontColor": "#519aba" - }, - "_javascript_light": { - "fontCharacter": "\\E051", - "fontColor": "#b7b73b" - }, - "_javascript": { - "fontCharacter": "\\E051", - "fontColor": "#cbcb41" - }, - "_javascript_1_light": { - "fontCharacter": "\\E051", - "fontColor": "#cc6d2e" - }, - "_javascript_1": { - "fontCharacter": "\\E051", - "fontColor": "#e37933" - }, - "_javascript_2_light": { - "fontCharacter": "\\E051", - "fontColor": "#498ba7" - }, - "_javascript_2": { - "fontCharacter": "\\E051", - "fontColor": "#519aba" - }, - "_jenkins_light": { - "fontCharacter": "\\E052", - "fontColor": "#b8383d" - }, - "_jenkins": { - "fontCharacter": "\\E052", - "fontColor": "#cc3e44" - }, - "_jinja_light": { - "fontCharacter": "\\E053", - "fontColor": "#b8383d" - }, - "_jinja": { - "fontCharacter": "\\E053", - "fontColor": "#cc3e44" - }, - "_json_light": { - "fontCharacter": "\\E055", - "fontColor": "#b7b73b" - }, - "_json": { - "fontCharacter": "\\E055", - "fontColor": "#cbcb41" - }, - "_json_1_light": { - "fontCharacter": "\\E055", - "fontColor": "#7fae42" - }, - "_json_1": { - "fontCharacter": "\\E055", - "fontColor": "#8dc149" - }, - "_julia_light": { - "fontCharacter": "\\E056", - "fontColor": "#9068b0" - }, - "_julia": { - "fontCharacter": "\\E056", - "fontColor": "#a074c4" - }, - "_karma_light": { - "fontCharacter": "\\E057", - "fontColor": "#7fae42" - }, - "_karma": { - "fontCharacter": "\\E057", - "fontColor": "#8dc149" - }, - "_kotlin_light": { - "fontCharacter": "\\E058", - "fontColor": "#cc6d2e" - }, - "_kotlin": { - "fontCharacter": "\\E058", - "fontColor": "#e37933" - }, - "_less_light": { - "fontCharacter": "\\E059", - "fontColor": "#498ba7" - }, - "_less": { - "fontCharacter": "\\E059", - "fontColor": "#519aba" - }, - "_license_light": { - "fontCharacter": "\\E05A", - "fontColor": "#b7b73b" - }, - "_license": { - "fontCharacter": "\\E05A", - "fontColor": "#cbcb41" - }, - "_license_1_light": { - "fontCharacter": "\\E05A", - "fontColor": "#cc6d2e" - }, - "_license_1": { - "fontCharacter": "\\E05A", - "fontColor": "#e37933" - }, - "_license_2_light": { - "fontCharacter": "\\E05A", - "fontColor": "#b8383d" - }, - "_license_2": { - "fontCharacter": "\\E05A", - "fontColor": "#cc3e44" - }, - "_liquid_light": { - "fontCharacter": "\\E05B", - "fontColor": "#7fae42" - }, - "_liquid": { - "fontCharacter": "\\E05B", - "fontColor": "#8dc149" - }, - "_livescript_light": { - "fontCharacter": "\\E05C", - "fontColor": "#498ba7" - }, - "_livescript": { - "fontCharacter": "\\E05C", - "fontColor": "#519aba" - }, - "_lock_light": { - "fontCharacter": "\\E05D", - "fontColor": "#7fae42" - }, - "_lock": { - "fontCharacter": "\\E05D", - "fontColor": "#8dc149" - }, - "_lua_light": { - "fontCharacter": "\\E05E", - "fontColor": "#498ba7" - }, - "_lua": { - "fontCharacter": "\\E05E", - "fontColor": "#519aba" - }, - "_makefile_light": { - "fontCharacter": "\\E05F", - "fontColor": "#cc6d2e" - }, - "_makefile": { - "fontCharacter": "\\E05F", - "fontColor": "#e37933" - }, - "_makefile_1_light": { - "fontCharacter": "\\E05F", - "fontColor": "#9068b0" - }, - "_makefile_1": { - "fontCharacter": "\\E05F", - "fontColor": "#a074c4" - }, - "_makefile_2_light": { - "fontCharacter": "\\E05F", - "fontColor": "#627379" - }, - "_makefile_2": { - "fontCharacter": "\\E05F", - "fontColor": "#6d8086" - }, - "_makefile_3_light": { - "fontCharacter": "\\E05F", - "fontColor": "#498ba7" - }, - "_makefile_3": { - "fontCharacter": "\\E05F", - "fontColor": "#519aba" - }, - "_markdown_light": { - "fontCharacter": "\\E060", - "fontColor": "#498ba7" - }, - "_markdown": { - "fontCharacter": "\\E060", - "fontColor": "#519aba" - }, - "_maven_light": { - "fontCharacter": "\\E061", - "fontColor": "#b8383d" - }, - "_maven": { - "fontCharacter": "\\E061", - "fontColor": "#cc3e44" - }, - "_mdo_light": { - "fontCharacter": "\\E062", - "fontColor": "#b8383d" - }, - "_mdo": { - "fontCharacter": "\\E062", - "fontColor": "#cc3e44" - }, - "_mustache_light": { - "fontCharacter": "\\E063", - "fontColor": "#cc6d2e" - }, - "_mustache": { - "fontCharacter": "\\E063", - "fontColor": "#e37933" - }, - "_nim_light": { - "fontCharacter": "\\E065", - "fontColor": "#b7b73b" - }, - "_nim": { - "fontCharacter": "\\E065", - "fontColor": "#cbcb41" - }, - "_notebook_light": { - "fontCharacter": "\\E066", - "fontColor": "#498ba7" - }, - "_notebook": { - "fontCharacter": "\\E066", - "fontColor": "#519aba" - }, - "_npm_light": { - "fontCharacter": "\\E067", - "fontColor": "#3b4b52" - }, - "_npm": { - "fontCharacter": "\\E067", - "fontColor": "#41535b" - }, - "_npm_1_light": { - "fontCharacter": "\\E067", - "fontColor": "#b8383d" - }, - "_npm_1": { - "fontCharacter": "\\E067", - "fontColor": "#cc3e44" - }, - "_npm_ignored_light": { - "fontCharacter": "\\E068", - "fontColor": "#3b4b52" - }, - "_npm_ignored": { - "fontCharacter": "\\E068", - "fontColor": "#41535b" - }, - "_nunjucks_light": { - "fontCharacter": "\\E069", - "fontColor": "#7fae42" - }, - "_nunjucks": { - "fontCharacter": "\\E069", - "fontColor": "#8dc149" - }, - "_ocaml_light": { - "fontCharacter": "\\E06A", - "fontColor": "#cc6d2e" - }, - "_ocaml": { - "fontCharacter": "\\E06A", - "fontColor": "#e37933" - }, - "_odata_light": { - "fontCharacter": "\\E06B", - "fontColor": "#cc6d2e" - }, - "_odata": { - "fontCharacter": "\\E06B", - "fontColor": "#e37933" - }, - "_pddl_light": { - "fontCharacter": "\\E06C", - "fontColor": "#9068b0" - }, - "_pddl": { - "fontCharacter": "\\E06C", - "fontColor": "#a074c4" - }, - "_pdf_light": { - "fontCharacter": "\\E06D", - "fontColor": "#b8383d" - }, - "_pdf": { - "fontCharacter": "\\E06D", - "fontColor": "#cc3e44" - }, - "_perl_light": { - "fontCharacter": "\\E06E", - "fontColor": "#498ba7" - }, - "_perl": { - "fontCharacter": "\\E06E", - "fontColor": "#519aba" - }, - "_photoshop_light": { - "fontCharacter": "\\E06F", - "fontColor": "#498ba7" - }, - "_photoshop": { - "fontCharacter": "\\E06F", - "fontColor": "#519aba" - }, - "_php_light": { - "fontCharacter": "\\E070", - "fontColor": "#9068b0" - }, - "_php": { - "fontCharacter": "\\E070", - "fontColor": "#a074c4" - }, - "_pipeline_light": { - "fontCharacter": "\\E071", - "fontColor": "#cc6d2e" - }, - "_pipeline": { - "fontCharacter": "\\E071", - "fontColor": "#e37933" - }, - "_plan_light": { - "fontCharacter": "\\E072", - "fontColor": "#7fae42" - }, - "_plan": { - "fontCharacter": "\\E072", - "fontColor": "#8dc149" - }, - "_platformio_light": { - "fontCharacter": "\\E073", - "fontColor": "#cc6d2e" - }, - "_platformio": { - "fontCharacter": "\\E073", - "fontColor": "#e37933" - }, - "_powershell_light": { - "fontCharacter": "\\E074", - "fontColor": "#498ba7" - }, - "_powershell": { - "fontCharacter": "\\E074", - "fontColor": "#519aba" - }, - "_prisma_light": { - "fontCharacter": "\\E075", - "fontColor": "#498ba7" - }, - "_prisma": { - "fontCharacter": "\\E075", - "fontColor": "#519aba" - }, - "_prolog_light": { - "fontCharacter": "\\E077", - "fontColor": "#cc6d2e" - }, - "_prolog": { - "fontCharacter": "\\E077", - "fontColor": "#e37933" - }, - "_pug_light": { - "fontCharacter": "\\E078", - "fontColor": "#b8383d" - }, - "_pug": { - "fontCharacter": "\\E078", - "fontColor": "#cc3e44" - }, - "_puppet_light": { - "fontCharacter": "\\E079", - "fontColor": "#b7b73b" - }, - "_puppet": { - "fontCharacter": "\\E079", - "fontColor": "#cbcb41" - }, - "_purescript_light": { - "fontCharacter": "\\E07A", - "fontColor": "#bfc2c1" - }, - "_purescript": { - "fontCharacter": "\\E07A", - "fontColor": "#d4d7d6" - }, - "_python_light": { - "fontCharacter": "\\E07B", - "fontColor": "#498ba7" - }, - "_python": { - "fontCharacter": "\\E07B", - "fontColor": "#519aba" - }, - "_react_light": { - "fontCharacter": "\\E07D", - "fontColor": "#498ba7" - }, - "_react": { - "fontCharacter": "\\E07D", - "fontColor": "#519aba" - }, - "_react_1_light": { - "fontCharacter": "\\E07D", - "fontColor": "#cc6d2e" - }, - "_react_1": { - "fontCharacter": "\\E07D", - "fontColor": "#e37933" - }, - "_reasonml_light": { - "fontCharacter": "\\E07E", - "fontColor": "#b8383d" - }, - "_reasonml": { - "fontCharacter": "\\E07E", - "fontColor": "#cc3e44" - }, - "_rescript_light": { - "fontCharacter": "\\E07F", - "fontColor": "#b8383d" - }, - "_rescript": { - "fontCharacter": "\\E07F", - "fontColor": "#cc3e44" - }, - "_rescript_1_light": { - "fontCharacter": "\\E07F", - "fontColor": "#dd4b78" - }, - "_rescript_1": { - "fontCharacter": "\\E07F", - "fontColor": "#f55385" - }, - "_rollup_light": { - "fontCharacter": "\\E080", - "fontColor": "#b8383d" - }, - "_rollup": { - "fontCharacter": "\\E080", - "fontColor": "#cc3e44" - }, - "_ruby_light": { - "fontCharacter": "\\E081", - "fontColor": "#b8383d" - }, - "_ruby": { - "fontCharacter": "\\E081", - "fontColor": "#cc3e44" - }, - "_rust_light": { - "fontCharacter": "\\E082", - "fontColor": "#627379" - }, - "_rust": { - "fontCharacter": "\\E082", - "fontColor": "#6d8086" - }, - "_salesforce_light": { - "fontCharacter": "\\E083", - "fontColor": "#498ba7" - }, - "_salesforce": { - "fontCharacter": "\\E083", - "fontColor": "#519aba" - }, - "_sass_light": { - "fontCharacter": "\\E084", - "fontColor": "#dd4b78" - }, - "_sass": { - "fontCharacter": "\\E084", - "fontColor": "#f55385" - }, - "_sbt_light": { - "fontCharacter": "\\E085", - "fontColor": "#498ba7" - }, - "_sbt": { - "fontCharacter": "\\E085", - "fontColor": "#519aba" - }, - "_scala_light": { - "fontCharacter": "\\E086", - "fontColor": "#b8383d" - }, - "_scala": { - "fontCharacter": "\\E086", - "fontColor": "#cc3e44" - }, - "_shell_light": { - "fontCharacter": "\\E089", - "fontColor": "#7fae42" - }, - "_shell": { - "fontCharacter": "\\E089", - "fontColor": "#8dc149" - }, - "_slim_light": { - "fontCharacter": "\\E08A", - "fontColor": "#cc6d2e" - }, - "_slim": { - "fontCharacter": "\\E08A", - "fontColor": "#e37933" - }, - "_smarty_light": { - "fontCharacter": "\\E08B", - "fontColor": "#b7b73b" - }, - "_smarty": { - "fontCharacter": "\\E08B", - "fontColor": "#cbcb41" - }, - "_spring_light": { - "fontCharacter": "\\E08C", - "fontColor": "#7fae42" - }, - "_spring": { - "fontCharacter": "\\E08C", - "fontColor": "#8dc149" - }, - "_stylelint_light": { - "fontCharacter": "\\E08D", - "fontColor": "#bfc2c1" - }, - "_stylelint": { - "fontCharacter": "\\E08D", - "fontColor": "#d4d7d6" - }, - "_stylelint_1_light": { - "fontCharacter": "\\E08D", - "fontColor": "#455155" - }, - "_stylelint_1": { - "fontCharacter": "\\E08D", - "fontColor": "#4d5a5e" - }, - "_stylus_light": { - "fontCharacter": "\\E08E", - "fontColor": "#7fae42" - }, - "_stylus": { - "fontCharacter": "\\E08E", - "fontColor": "#8dc149" - }, - "_sublime_light": { - "fontCharacter": "\\E08F", - "fontColor": "#cc6d2e" - }, - "_sublime": { - "fontCharacter": "\\E08F", - "fontColor": "#e37933" - }, - "_svelte_light": { - "fontCharacter": "\\E090", - "fontColor": "#b8383d" - }, - "_svelte": { - "fontCharacter": "\\E090", - "fontColor": "#cc3e44" - }, - "_svg_light": { - "fontCharacter": "\\E091", - "fontColor": "#9068b0" - }, - "_svg": { - "fontCharacter": "\\E091", - "fontColor": "#a074c4" - }, - "_svg_1_light": { - "fontCharacter": "\\E091", - "fontColor": "#498ba7" - }, - "_svg_1": { - "fontCharacter": "\\E091", - "fontColor": "#519aba" - }, - "_swift_light": { - "fontCharacter": "\\E092", - "fontColor": "#cc6d2e" - }, - "_swift": { - "fontCharacter": "\\E092", - "fontColor": "#e37933" - }, - "_terraform_light": { - "fontCharacter": "\\E093", - "fontColor": "#9068b0" - }, - "_terraform": { - "fontCharacter": "\\E093", - "fontColor": "#a074c4" - }, - "_tex_light": { - "fontCharacter": "\\E094", - "fontColor": "#498ba7" - }, - "_tex": { - "fontCharacter": "\\E094", - "fontColor": "#519aba" - }, - "_tex_1_light": { - "fontCharacter": "\\E094", - "fontColor": "#b7b73b" - }, - "_tex_1": { - "fontCharacter": "\\E094", - "fontColor": "#cbcb41" - }, - "_tex_2_light": { - "fontCharacter": "\\E094", - "fontColor": "#cc6d2e" - }, - "_tex_2": { - "fontCharacter": "\\E094", - "fontColor": "#e37933" - }, - "_tex_3_light": { - "fontCharacter": "\\E094", - "fontColor": "#bfc2c1" - }, - "_tex_3": { - "fontCharacter": "\\E094", - "fontColor": "#d4d7d6" - }, - "_todo": { - "fontCharacter": "\\E096" - }, - "_tsconfig_light": { - "fontCharacter": "\\E097", - "fontColor": "#498ba7" - }, - "_tsconfig": { - "fontCharacter": "\\E097", - "fontColor": "#519aba" - }, - "_twig_light": { - "fontCharacter": "\\E098", - "fontColor": "#7fae42" - }, - "_twig": { - "fontCharacter": "\\E098", - "fontColor": "#8dc149" - }, - "_typescript_light": { - "fontCharacter": "\\E099", - "fontColor": "#498ba7" - }, - "_typescript": { - "fontCharacter": "\\E099", - "fontColor": "#519aba" - }, - "_typescript_1_light": { - "fontCharacter": "\\E099", - "fontColor": "#cc6d2e" - }, - "_typescript_1": { - "fontCharacter": "\\E099", - "fontColor": "#e37933" - }, - "_vala_light": { - "fontCharacter": "\\E09A", - "fontColor": "#627379" - }, - "_vala": { - "fontCharacter": "\\E09A", - "fontColor": "#6d8086" - }, - "_video_light": { - "fontCharacter": "\\E09B", - "fontColor": "#dd4b78" - }, - "_video": { - "fontCharacter": "\\E09B", - "fontColor": "#f55385" - }, - "_vite_light": { - "fontCharacter": "\\E09C", - "fontColor": "#b7b73b" - }, - "_vite": { - "fontCharacter": "\\E09C", - "fontColor": "#cbcb41" - }, - "_vue_light": { - "fontCharacter": "\\E09D", - "fontColor": "#7fae42" - }, - "_vue": { - "fontCharacter": "\\E09D", - "fontColor": "#8dc149" - }, - "_wasm_light": { - "fontCharacter": "\\E09E", - "fontColor": "#9068b0" - }, - "_wasm": { - "fontCharacter": "\\E09E", - "fontColor": "#a074c4" - }, - "_wat_light": { - "fontCharacter": "\\E09F", - "fontColor": "#9068b0" - }, - "_wat": { - "fontCharacter": "\\E09F", - "fontColor": "#a074c4" - }, - "_webpack_light": { - "fontCharacter": "\\E0A0", - "fontColor": "#498ba7" - }, - "_webpack": { - "fontCharacter": "\\E0A0", - "fontColor": "#519aba" - }, - "_wgt_light": { - "fontCharacter": "\\E0A1", - "fontColor": "#498ba7" - }, - "_wgt": { - "fontCharacter": "\\E0A1", - "fontColor": "#519aba" - }, - "_windows_light": { - "fontCharacter": "\\E0A2", - "fontColor": "#498ba7" - }, - "_windows": { - "fontCharacter": "\\E0A2", - "fontColor": "#519aba" - }, - "_word_light": { - "fontCharacter": "\\E0A3", - "fontColor": "#498ba7" - }, - "_word": { - "fontCharacter": "\\E0A3", - "fontColor": "#519aba" - }, - "_xls_light": { - "fontCharacter": "\\E0A4", - "fontColor": "#7fae42" - }, - "_xls": { - "fontCharacter": "\\E0A4", - "fontColor": "#8dc149" - }, - "_xml_light": { - "fontCharacter": "\\E0A5", - "fontColor": "#cc6d2e" - }, - "_xml": { - "fontCharacter": "\\E0A5", - "fontColor": "#e37933" - }, - "_yarn_light": { - "fontCharacter": "\\E0A6", - "fontColor": "#498ba7" - }, - "_yarn": { - "fontCharacter": "\\E0A6", - "fontColor": "#519aba" - }, - "_yml_light": { - "fontCharacter": "\\E0A7", - "fontColor": "#9068b0" - }, - "_yml": { - "fontCharacter": "\\E0A7", - "fontColor": "#a074c4" - }, - "_zig_light": { - "fontCharacter": "\\E0A8", - "fontColor": "#cc6d2e" - }, - "_zig": { - "fontCharacter": "\\E0A8", - "fontColor": "#e37933" - }, - "_zip_light": { - "fontCharacter": "\\E0A9", - "fontColor": "#b8383d" - }, - "_zip": { - "fontCharacter": "\\E0A9", - "fontColor": "#cc3e44" - }, - "_zip_1_light": { - "fontCharacter": "\\E0A9", - "fontColor": "#627379" - }, - "_zip_1": { - "fontCharacter": "\\E0A9", - "fontColor": "#6d8086" - } - }, - "file": "_default", - "fileExtensions": { - "bsl": "_bsl", - "mdo": "_mdo", - "cls": "_salesforce", - "apex": "_salesforce", - "asm": "_asm", - "s": "_asm", - "bicep": "_bicep", - "bzl": "_bazel", - "bazel": "_bazel", - "build": "_bazel", - "workspace": "_bazel", - "bazelignore": "_bazel", - "bazelversion": "_bazel", - "h": "_c_1", - "aspx": "_html", - "ascx": "_html_1", - "asax": "_html_2", - "master": "_html_2", - "hh": "_cpp_1", - "hpp": "_cpp_1", - "hxx": "_cpp_1", - "h++": "_cpp_1", - "edn": "_clojure_1", - "cfc": "_coldfusion", - "cfm": "_coldfusion", - "litcoffee": "_coffee", - "config": "_config", - "cr": "_crystal", - "ecr": "_crystal_embedded", - "slang": "_crystal_embedded", - "cson": "_json", - "css.map": "_css", - "sss": "_css", - "csv": "_csv", - "xls": "_xls", - "xlsx": "_xls", - "cuh": "_cu_1", - "hu": "_cu_1", - "cake": "_cake", - "ctp": "_cake_php", - "d": "_d", - "doc": "_word", - "docx": "_word", - "ejs": "_ejs", - "ex": "_elixir", - "exs": "_elixir_script", - "elm": "_elm", - "ico": "_favicon", - "gitconfig": "_git", - "gitkeep": "_git", - "gitattributes": "_git", - "gitmodules": "_git", - "slide": "_go", - "article": "_go", - "gd": "_godot", - "godot": "_godot_1", - "tres": "_godot_2", - "tscn": "_godot_3", - "gradle": "_gradle", - "gsp": "_grails", - "gql": "_graphql", - "graphql": "_graphql", - "graphqls": "_graphql", - "hack": "_hacklang", - "haml": "_haml", - "hs": "_haskell", - "lhs": "_haskell", - "hx": "_haxe", - "hxs": "_haxe_1", - "hxp": "_haxe_2", - "hxml": "_haxe_3", - "jade": "_jade", - "class": "_java_1", - "classpath": "_java", - "js.map": "_javascript", - "cjs.map": "_javascript", - "mjs.map": "_javascript", - "spec.js": "_javascript_1", - "spec.cjs": "_javascript_1", - "spec.mjs": "_javascript_1", - "test.js": "_javascript_1", - "test.cjs": "_javascript_1", - "test.mjs": "_javascript_1", - "es": "_javascript", - "es5": "_javascript", - "es7": "_javascript", - "jinja": "_jinja", - "jinja2": "_jinja", - "kt": "_kotlin", - "kts": "_kotlin", - "liquid": "_liquid", - "ls": "_livescript", - "argdown": "_argdown", - "ad": "_argdown", - "mustache": "_mustache", - "stache": "_mustache", - "nim": "_nim", - "nims": "_nim", - "github-issues": "_github", - "ipynb": "_notebook", - "njk": "_nunjucks", - "nunjucks": "_nunjucks", - "nunjs": "_nunjucks", - "nunj": "_nunjucks", - "njs": "_nunjucks", - "nj": "_nunjucks", - "npm-debug.log": "_npm", - "npmignore": "_npm_1", - "npmrc": "_npm_1", - "ml": "_ocaml", - "mli": "_ocaml", - "cmx": "_ocaml", - "cmxa": "_ocaml", - "odata": "_odata", - "php.inc": "_php", - "pipeline": "_pipeline", - "pddl": "_pddl", - "plan": "_plan", - "happenings": "_happenings", - "prisma": "_prisma", - "pp": "_puppet", - "epp": "_puppet", - "purs": "_purescript", - "spec.jsx": "_react_1", - "test.jsx": "_react_1", - "cjsx": "_react", - "spec.tsx": "_react_1", - "test.tsx": "_react_1", - "re": "_reasonml", - "res": "_rescript", - "resi": "_rescript_1", - "r": "_R", - "rmd": "_R", - "erb": "_html_erb", - "erb.html": "_html_erb", - "html.erb": "_html_erb", - "sass": "_sass", - "springbeans": "_spring", - "slim": "_slim", - "smarty.tpl": "_smarty", - "tpl": "_smarty", - "sbt": "_sbt", - "scala": "_scala", - "sol": "_ethereum", - "styl": "_stylus", - "svelte": "_svelte", - "soql": "_db_1", - "tf": "_terraform", - "tf.json": "_terraform", - "tfvars": "_terraform", - "tfvars.json": "_terraform", - "dtx": "_tex_2", - "ins": "_tex_3", - "toml": "_config", - "twig": "_twig", - "spec.ts": "_typescript_1", - "test.ts": "_typescript_1", - "vala": "_vala", - "vapi": "_vala", - "component": "_html_3", - "vue": "_vue", - "wasm": "_wasm", - "wat": "_wat", - "pro": "_prolog", - "zig": "_zig", - "jar": "_zip", - "zip": "_zip_1", - "wgt": "_wgt", - "ai": "_illustrator", - "psd": "_photoshop", - "pdf": "_pdf", - "eot": "_font", - "ttf": "_font", - "woff": "_font", - "woff2": "_font", - "otf": "_font", - "avif": "_image", - "gif": "_image", - "jpg": "_image", - "jpeg": "_image", - "png": "_image", - "pxm": "_image", - "svg": "_svg", - "svgx": "_image", - "tiff": "_image", - "webp": "_image", - "sublime-project": "_sublime", - "sublime-workspace": "_sublime", - "mov": "_video", - "ogv": "_video", - "webm": "_video", - "avi": "_video", - "mpg": "_video", - "mp4": "_video", - "mp3": "_audio", - "ogg": "_audio", - "wav": "_audio", - "flac": "_audio", - "3ds": "_svg_1", - "3dm": "_svg_1", - "stl": "_svg_1", - "obj": "_svg_1", - "dae": "_svg_1", - "babelrc": "_babel", - "babelrc.js": "_babel", - "babelrc.cjs": "_babel", - "bazelrc": "_bazel_1", - "bowerrc": "_bower", - "dockerignore": "_docker_1", - "codeclimate.yml": "_code-climate", - "eslintrc": "_eslint", - "eslintrc.js": "_eslint", - "eslintrc.cjs": "_eslint", - "eslintrc.yaml": "_eslint", - "eslintrc.yml": "_eslint", - "eslintrc.json": "_eslint", - "eslintignore": "_eslint_1", - "firebaserc": "_firebase", - "gitlab-ci.yml": "_gitlab", - "jshintrc": "_javascript_2", - "jscsrc": "_javascript_2", - "stylelintrc": "_stylelint", - "stylelintrc.json": "_stylelint", - "stylelintrc.yaml": "_stylelint", - "stylelintrc.yml": "_stylelint", - "stylelintrc.js": "_stylelint", - "stylelintignore": "_stylelint_1", - "direnv": "_config", - "env": "_config", - "static": "_config", - "slugignore": "_config", - "tmp": "_clock_1", - "htaccess": "_config", - "key": "_lock", - "cert": "_lock", - "cer": "_lock", - "crt": "_lock", - "pem": "_lock", - "ds_store": "_ignored" - }, - "fileNames": { - "mix": "_hex", - "karma.conf.js": "_karma", - "karma.conf.cjs": "_karma", - "karma.conf.mjs": "_karma", - "karma.conf.coffee": "_karma", - "readme.md": "_info", - "readme.txt": "_info", - "readme": "_info", - "changelog.md": "_clock", - "changelog.txt": "_clock", - "changelog": "_clock", - "changes.md": "_clock", - "changes.txt": "_clock", - "changes": "_clock", - "version.md": "_clock", - "version.txt": "_clock", - "version": "_clock", - "mvnw": "_maven", - "pom.xml": "_maven", - "tsconfig.json": "_tsconfig", - "vite.config.js": "_vite", - "vite.config.ts": "_vite", - "vite.config.mjs": "_vite", - "vite.config.mts": "_vite", - "vite.config.cjs": "_vite", - "vite.config.cts": "_vite", - "swagger.json": "_json_1", - "swagger.yml": "_json_1", - "swagger.yaml": "_json_1", - "mime.types": "_config", - "jenkinsfile": "_jenkins", - "babel.config.js": "_babel", - "babel.config.json": "_babel", - "babel.config.cjs": "_babel", - "build": "_bazel", - "build.bazel": "_bazel", - "workspace": "_bazel", - "workspace.bazel": "_bazel", - "bower.json": "_bower", - "docker-healthcheck": "_docker_2", - "eslint.config.js": "_eslint", - "firebase.json": "_firebase", - "geckodriver": "_firefox", - "gruntfile.js": "_grunt", - "gruntfile.babel.js": "_grunt", - "gruntfile.coffee": "_grunt", - "gulpfile": "_gulp", - "gulpfile.js": "_gulp", - "ionic.config.json": "_ionic", - "ionic.project": "_ionic", - "platformio.ini": "_platformio", - "rollup.config.js": "_rollup", - "sass-lint.yml": "_sass", - "stylelint.config.js": "_stylelint", - "stylelint.config.cjs": "_stylelint", - "stylelint.config.mjs": "_stylelint", - "yarn.clean": "_yarn", - "yarn.lock": "_yarn", - "webpack.config.js": "_webpack", - "webpack.config.cjs": "_webpack", - "webpack.config.mjs": "_webpack", - "webpack.config.ts": "_webpack", - "webpack.config.build.js": "_webpack", - "webpack.config.build.cjs": "_webpack", - "webpack.config.build.mjs": "_webpack", - "webpack.config.build.ts": "_webpack", - "webpack.common.js": "_webpack", - "webpack.common.cjs": "_webpack", - "webpack.common.mjs": "_webpack", - "webpack.common.ts": "_webpack", - "webpack.dev.js": "_webpack", - "webpack.dev.cjs": "_webpack", - "webpack.dev.mjs": "_webpack", - "webpack.dev.ts": "_webpack", - "webpack.prod.js": "_webpack", - "webpack.prod.cjs": "_webpack", - "webpack.prod.mjs": "_webpack", - "webpack.prod.ts": "_webpack", - "license": "_license", - "licence": "_license", - "license.txt": "_license", - "licence.txt": "_license", - "license.md": "_license", - "licence.md": "_license", - "copying": "_license", - "copying.txt": "_license", - "copying.md": "_license", - "compiling": "_license_1", - "compiling.txt": "_license_1", - "compiling.md": "_license_1", - "contributing": "_license_2", - "contributing.txt": "_license_2", - "contributing.md": "_license_2", - "qmakefile": "_makefile_1", - "omakefile": "_makefile_2", - "cmakelists.txt": "_makefile_3", - "procfile": "_heroku", - "todo": "_todo", - "todo.txt": "_todo", - "todo.md": "_todo", - "npm-debug.log": "_npm_ignored" - }, - "languageIds": { - "bat": "_windows", - "clojure": "_clojure", - "coffeescript": "_coffee", - "jsonc": "_json", - "json": "_json", - "c": "_c", - "cpp": "_cpp", - "cuda-cpp": "_cu", - "csharp": "_c-sharp", - "css": "_css", - "dart": "_dart", - "dockerfile": "_docker", - "ignore": "_git", - "fsharp": "_f-sharp", - "git-commit": "_git", - "go": "_go2", - "groovy": "_grails", - "handlebars": "_mustache", - "html": "_html_3", - "properties": "_config", - "java": "_java", - "javascriptreact": "_react", - "javascript": "_javascript", - "julia": "_julia", - "tex": "_tex_1", - "latex": "_tex", - "less": "_less", - "lua": "_lua", - "makefile": "_makefile", - "markdown": "_markdown", - "objective-c": "_c_2", - "objective-cpp": "_cpp_2", - "perl": "_perl", - "php": "_php", - "powershell": "_powershell", - "jade": "_pug", - "python": "_python", - "r": "_R", - "razor": "_html", - "ruby": "_ruby", - "rust": "_rust", - "scss": "_sass", - "search-result": "_code-search", - "shellscript": "_shell", - "sql": "_db", - "swift": "_swift", - "typescript": "_typescript", - "typescriptreact": "_react", - "xml": "_xml", - "dockercompose": "_docker_3", - "yaml": "_yml", - "argdown": "_argdown", - "bicep": "_bicep", - "elixir": "_elixir", - "elm": "_elm", - "erb": "_html_erb", - "github-issues": "_github", - "gradle": "_gradle", - "godot": "_godot", - "haml": "_haml", - "haskell": "_haskell", - "haxe": "_haxe", - "jinja": "_jinja", - "kotlin": "_kotlin", - "mustache": "_mustache", - "nunjucks": "_nunjucks", - "ocaml": "_ocaml", - "rescript": "_rescript", - "sass": "_sass", - "stylus": "_stylus", - "terraform": "_terraform", - "todo": "_todo", - "vala": "_vala", - "vue": "_vue", - "jsonl": "_json", - "postcss": "_css", - "django-html": "_html_3", - "blade": "_php" - }, - "light": { - "file": "_default_light", - "fileExtensions": { - "bsl": "_bsl_light", - "mdo": "_mdo_light", - "cls": "_salesforce_light", - "apex": "_salesforce_light", - "asm": "_asm_light", - "s": "_asm_light", - "bicep": "_bicep_light", - "bzl": "_bazel_light", - "bazel": "_bazel_light", - "build": "_bazel_light", - "workspace": "_bazel_light", - "bazelignore": "_bazel_light", - "bazelversion": "_bazel_light", - "h": "_c_1_light", - "aspx": "_html_light", - "ascx": "_html_1_light", - "asax": "_html_2_light", - "master": "_html_2_light", - "hh": "_cpp_1_light", - "hpp": "_cpp_1_light", - "hxx": "_cpp_1_light", - "h++": "_cpp_1_light", - "edn": "_clojure_1_light", - "cfc": "_coldfusion_light", - "cfm": "_coldfusion_light", - "litcoffee": "_coffee_light", - "config": "_config_light", - "cr": "_crystal_light", - "ecr": "_crystal_embedded_light", - "slang": "_crystal_embedded_light", - "cson": "_json_light", - "css.map": "_css_light", - "sss": "_css_light", - "csv": "_csv_light", - "xls": "_xls_light", - "xlsx": "_xls_light", - "cuh": "_cu_1_light", - "hu": "_cu_1_light", - "cake": "_cake_light", - "ctp": "_cake_php_light", - "d": "_d_light", - "doc": "_word_light", - "docx": "_word_light", - "ejs": "_ejs_light", - "ex": "_elixir_light", - "exs": "_elixir_script_light", - "elm": "_elm_light", - "ico": "_favicon_light", - "gitconfig": "_git_light", - "gitkeep": "_git_light", - "gitattributes": "_git_light", - "gitmodules": "_git_light", - "slide": "_go_light", - "article": "_go_light", - "gd": "_godot_light", - "godot": "_godot_1_light", - "tres": "_godot_2_light", - "tscn": "_godot_3_light", - "gradle": "_gradle_light", - "gsp": "_grails_light", - "gql": "_graphql_light", - "graphql": "_graphql_light", - "graphqls": "_graphql_light", - "hack": "_hacklang_light", - "haml": "_haml_light", - "hs": "_haskell_light", - "lhs": "_haskell_light", - "hx": "_haxe_light", - "hxs": "_haxe_1_light", - "hxp": "_haxe_2_light", - "hxml": "_haxe_3_light", - "jade": "_jade_light", - "class": "_java_1_light", - "classpath": "_java_light", - "js.map": "_javascript_light", - "cjs.map": "_javascript_light", - "mjs.map": "_javascript_light", - "spec.js": "_javascript_1_light", - "spec.cjs": "_javascript_1_light", - "spec.mjs": "_javascript_1_light", - "test.js": "_javascript_1_light", - "test.cjs": "_javascript_1_light", - "test.mjs": "_javascript_1_light", - "es": "_javascript_light", - "es5": "_javascript_light", - "es7": "_javascript_light", - "jinja": "_jinja_light", - "jinja2": "_jinja_light", - "kt": "_kotlin_light", - "kts": "_kotlin_light", - "liquid": "_liquid_light", - "ls": "_livescript_light", - "argdown": "_argdown_light", - "ad": "_argdown_light", - "mustache": "_mustache_light", - "stache": "_mustache_light", - "nim": "_nim_light", - "nims": "_nim_light", - "github-issues": "_github_light", - "ipynb": "_notebook_light", - "njk": "_nunjucks_light", - "nunjucks": "_nunjucks_light", - "nunjs": "_nunjucks_light", - "nunj": "_nunjucks_light", - "njs": "_nunjucks_light", - "nj": "_nunjucks_light", - "npm-debug.log": "_npm_light", - "npmignore": "_npm_1_light", - "npmrc": "_npm_1_light", - "ml": "_ocaml_light", - "mli": "_ocaml_light", - "cmx": "_ocaml_light", - "cmxa": "_ocaml_light", - "odata": "_odata_light", - "php.inc": "_php_light", - "pipeline": "_pipeline_light", - "pddl": "_pddl_light", - "plan": "_plan_light", - "happenings": "_happenings_light", - "prisma": "_prisma_light", - "pp": "_puppet_light", - "epp": "_puppet_light", - "purs": "_purescript_light", - "spec.jsx": "_react_1_light", - "test.jsx": "_react_1_light", - "cjsx": "_react_light", - "spec.tsx": "_react_1_light", - "test.tsx": "_react_1_light", - "re": "_reasonml_light", - "res": "_rescript_light", - "resi": "_rescript_1_light", - "r": "_R_light", - "rmd": "_R_light", - "erb": "_html_erb_light", - "erb.html": "_html_erb_light", - "html.erb": "_html_erb_light", - "sass": "_sass_light", - "springbeans": "_spring_light", - "slim": "_slim_light", - "smarty.tpl": "_smarty_light", - "tpl": "_smarty_light", - "sbt": "_sbt_light", - "scala": "_scala_light", - "sol": "_ethereum_light", - "styl": "_stylus_light", - "svelte": "_svelte_light", - "soql": "_db_1_light", - "tf": "_terraform_light", - "tf.json": "_terraform_light", - "tfvars": "_terraform_light", - "tfvars.json": "_terraform_light", - "dtx": "_tex_2_light", - "ins": "_tex_3_light", - "toml": "_config_light", - "twig": "_twig_light", - "spec.ts": "_typescript_1_light", - "test.ts": "_typescript_1_light", - "vala": "_vala_light", - "vapi": "_vala_light", - "component": "_html_3_light", - "vue": "_vue_light", - "wasm": "_wasm_light", - "wat": "_wat_light", - "pro": "_prolog_light", - "zig": "_zig_light", - "jar": "_zip_light", - "zip": "_zip_1_light", - "wgt": "_wgt_light", - "ai": "_illustrator_light", - "psd": "_photoshop_light", - "pdf": "_pdf_light", - "eot": "_font_light", - "ttf": "_font_light", - "woff": "_font_light", - "woff2": "_font_light", - "otf": "_font_light", - "avif": "_image_light", - "gif": "_image_light", - "jpg": "_image_light", - "jpeg": "_image_light", - "png": "_image_light", - "pxm": "_image_light", - "svg": "_svg_light", - "svgx": "_image_light", - "tiff": "_image_light", - "webp": "_image_light", - "sublime-project": "_sublime_light", - "sublime-workspace": "_sublime_light", - "mov": "_video_light", - "ogv": "_video_light", - "webm": "_video_light", - "avi": "_video_light", - "mpg": "_video_light", - "mp4": "_video_light", - "mp3": "_audio_light", - "ogg": "_audio_light", - "wav": "_audio_light", - "flac": "_audio_light", - "3ds": "_svg_1_light", - "3dm": "_svg_1_light", - "stl": "_svg_1_light", - "obj": "_svg_1_light", - "dae": "_svg_1_light", - "babelrc": "_babel_light", - "babelrc.js": "_babel_light", - "babelrc.cjs": "_babel_light", - "bazelrc": "_bazel_1_light", - "bowerrc": "_bower_light", - "dockerignore": "_docker_1_light", - "codeclimate.yml": "_code-climate_light", - "eslintrc": "_eslint_light", - "eslintrc.js": "_eslint_light", - "eslintrc.cjs": "_eslint_light", - "eslintrc.yaml": "_eslint_light", - "eslintrc.yml": "_eslint_light", - "eslintrc.json": "_eslint_light", - "eslintignore": "_eslint_1_light", - "firebaserc": "_firebase_light", - "gitlab-ci.yml": "_gitlab_light", - "jshintrc": "_javascript_2_light", - "jscsrc": "_javascript_2_light", - "stylelintrc": "_stylelint_light", - "stylelintrc.json": "_stylelint_light", - "stylelintrc.yaml": "_stylelint_light", - "stylelintrc.yml": "_stylelint_light", - "stylelintrc.js": "_stylelint_light", - "stylelintignore": "_stylelint_1_light", - "direnv": "_config_light", - "env": "_config_light", - "static": "_config_light", - "slugignore": "_config_light", - "tmp": "_clock_1_light", - "htaccess": "_config_light", - "key": "_lock_light", - "cert": "_lock_light", - "cer": "_lock_light", - "crt": "_lock_light", - "pem": "_lock_light", - "ds_store": "_ignored_light" - }, - "languageIds": { - "bat": "_windows_light", - "clojure": "_clojure_light", - "coffeescript": "_coffee_light", - "jsonc": "_json_light", - "json": "_json_light", - "c": "_c_light", - "cpp": "_cpp_light", - "cuda-cpp": "_cu_light", - "csharp": "_c-sharp_light", - "css": "_css_light", - "dart": "_dart_light", - "dockerfile": "_docker_light", - "ignore": "_git_light", - "fsharp": "_f-sharp_light", - "git-commit": "_git_light", - "go": "_go2_light", - "groovy": "_grails_light", - "handlebars": "_mustache_light", - "html": "_html_3_light", - "properties": "_config_light", - "java": "_java_light", - "javascriptreact": "_react_light", - "javascript": "_javascript_light", - "julia": "_julia_light", - "tex": "_tex_1_light", - "latex": "_tex_light", - "less": "_less_light", - "lua": "_lua_light", - "makefile": "_makefile_light", - "markdown": "_markdown_light", - "objective-c": "_c_2_light", - "objective-cpp": "_cpp_2_light", - "perl": "_perl_light", - "php": "_php_light", - "powershell": "_powershell_light", - "jade": "_pug_light", - "python": "_python_light", - "r": "_R_light", - "razor": "_html_light", - "ruby": "_ruby_light", - "rust": "_rust_light", - "scss": "_sass_light", - "search-result": "_code-search_light", - "shellscript": "_shell_light", - "sql": "_db_light", - "swift": "_swift_light", - "typescript": "_typescript_light", - "typescriptreact": "_react_light", - "xml": "_xml_light", - "dockercompose": "_docker_3_light", - "yaml": "_yml_light", - "argdown": "_argdown_light", - "bicep": "_bicep_light", - "elixir": "_elixir_light", - "elm": "_elm_light", - "erb": "_html_erb_light", - "github-issues": "_github_light", - "gradle": "_gradle_light", - "godot": "_godot_light", - "haml": "_haml_light", - "haskell": "_haskell_light", - "haxe": "_haxe_light", - "jinja": "_jinja_light", - "kotlin": "_kotlin_light", - "mustache": "_mustache_light", - "nunjucks": "_nunjucks_light", - "ocaml": "_ocaml_light", - "rescript": "_rescript_light", - "sass": "_sass_light", - "stylus": "_stylus_light", - "terraform": "_terraform_light", - "vala": "_vala_light", - "vue": "_vue_light", - "jsonl": "_json_light", - "postcss": "_css_light", - "django-html": "_html_3_light", - "blade": "_php_light" - }, - "fileNames": { - "mix": "_hex_light", - "karma.conf.js": "_karma_light", - "karma.conf.cjs": "_karma_light", - "karma.conf.mjs": "_karma_light", - "karma.conf.coffee": "_karma_light", - "readme.md": "_info_light", - "readme.txt": "_info_light", - "readme": "_info_light", - "changelog.md": "_clock_light", - "changelog.txt": "_clock_light", - "changelog": "_clock_light", - "changes.md": "_clock_light", - "changes.txt": "_clock_light", - "changes": "_clock_light", - "version.md": "_clock_light", - "version.txt": "_clock_light", - "version": "_clock_light", - "mvnw": "_maven_light", - "pom.xml": "_maven_light", - "tsconfig.json": "_tsconfig_light", - "vite.config.js": "_vite_light", - "vite.config.ts": "_vite_light", - "vite.config.mjs": "_vite_light", - "vite.config.mts": "_vite_light", - "vite.config.cjs": "_vite_light", - "vite.config.cts": "_vite_light", - "swagger.json": "_json_1_light", - "swagger.yml": "_json_1_light", - "swagger.yaml": "_json_1_light", - "mime.types": "_config_light", - "jenkinsfile": "_jenkins_light", - "babel.config.js": "_babel_light", - "babel.config.json": "_babel_light", - "babel.config.cjs": "_babel_light", - "build": "_bazel_light", - "build.bazel": "_bazel_light", - "workspace": "_bazel_light", - "workspace.bazel": "_bazel_light", - "bower.json": "_bower_light", - "docker-healthcheck": "_docker_2_light", - "eslint.config.js": "_eslint_light", - "firebase.json": "_firebase_light", - "geckodriver": "_firefox_light", - "gruntfile.js": "_grunt_light", - "gruntfile.babel.js": "_grunt_light", - "gruntfile.coffee": "_grunt_light", - "gulpfile": "_gulp_light", - "gulpfile.js": "_gulp_light", - "ionic.config.json": "_ionic_light", - "ionic.project": "_ionic_light", - "platformio.ini": "_platformio_light", - "rollup.config.js": "_rollup_light", - "sass-lint.yml": "_sass_light", - "stylelint.config.js": "_stylelint_light", - "stylelint.config.cjs": "_stylelint_light", - "stylelint.config.mjs": "_stylelint_light", - "yarn.clean": "_yarn_light", - "yarn.lock": "_yarn_light", - "webpack.config.js": "_webpack_light", - "webpack.config.cjs": "_webpack_light", - "webpack.config.mjs": "_webpack_light", - "webpack.config.ts": "_webpack_light", - "webpack.config.build.js": "_webpack_light", - "webpack.config.build.cjs": "_webpack_light", - "webpack.config.build.mjs": "_webpack_light", - "webpack.config.build.ts": "_webpack_light", - "webpack.common.js": "_webpack_light", - "webpack.common.cjs": "_webpack_light", - "webpack.common.mjs": "_webpack_light", - "webpack.common.ts": "_webpack_light", - "webpack.dev.js": "_webpack_light", - "webpack.dev.cjs": "_webpack_light", - "webpack.dev.mjs": "_webpack_light", - "webpack.dev.ts": "_webpack_light", - "webpack.prod.js": "_webpack_light", - "webpack.prod.cjs": "_webpack_light", - "webpack.prod.mjs": "_webpack_light", - "webpack.prod.ts": "_webpack_light", - "license": "_license_light", - "licence": "_license_light", - "license.txt": "_license_light", - "licence.txt": "_license_light", - "license.md": "_license_light", - "licence.md": "_license_light", - "copying": "_license_light", - "copying.txt": "_license_light", - "copying.md": "_license_light", - "compiling": "_license_1_light", - "compiling.txt": "_license_1_light", - "compiling.md": "_license_1_light", - "contributing": "_license_2_light", - "contributing.txt": "_license_2_light", - "contributing.md": "_license_2_light", - "qmakefile": "_makefile_1_light", - "omakefile": "_makefile_2_light", - "cmakelists.txt": "_makefile_3_light", - "procfile": "_heroku_light", - "npm-debug.log": "_npm_ignored_light" - } - }, - "version": "https://github.com/jesseweed/seti-ui/commit/1cac4f30f93cc898103c62dde41823a09b0d7b74" +{ + "information_for_contributors": [ + "This file has been generated from data in https://github.com/DecimalTurn/seti-ui", + "- icon definitions: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/_fonts/seti.less", + "- icon colors: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/ui-variables.less", + "- file associations: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/components/icons/mapping.less", + "If you want to provide a fix or improvement, please create a pull request against the DecimalTurn/seti-ui repository.", + "Once accepted there, we are happy to receive an update request." + ], + "fonts": [ + { + "id": "seti", + "src": [ + { + "path": "./seti.woff", + "format": "woff" + } + ], + "weight": "normal", + "style": "normal", + "size": "150%" + } + ], + "iconDefinitions": { + "_R_light": { + "fontCharacter": "\\E07B", + "fontColor": "#498ba7" + }, + "_R": { + "fontCharacter": "\\E07B", + "fontColor": "#519aba" + }, + "_argdown_light": { + "fontCharacter": "\\E002", + "fontColor": "#498ba7" + }, + "_argdown": { + "fontCharacter": "\\E002", + "fontColor": "#519aba" + }, + "_asm_light": { + "fontCharacter": "\\E003", + "fontColor": "#b8383d" + }, + "_asm": { + "fontCharacter": "\\E003", + "fontColor": "#cc3e44" + }, + "_audio_light": { + "fontCharacter": "\\E004", + "fontColor": "#9068b0" + }, + "_audio": { + "fontCharacter": "\\E004", + "fontColor": "#a074c4" + }, + "_babel_light": { + "fontCharacter": "\\E005", + "fontColor": "#b7b73b" + }, + "_babel": { + "fontCharacter": "\\E005", + "fontColor": "#cbcb41" + }, + "_bazel_light": { + "fontCharacter": "\\E006", + "fontColor": "#7fae42" + }, + "_bazel": { + "fontCharacter": "\\E006", + "fontColor": "#8dc149" + }, + "_bazel_1_light": { + "fontCharacter": "\\E006", + "fontColor": "#455155" + }, + "_bazel_1": { + "fontCharacter": "\\E006", + "fontColor": "#4d5a5e" + }, + "_bicep_light": { + "fontCharacter": "\\E007", + "fontColor": "#498ba7" + }, + "_bicep": { + "fontCharacter": "\\E007", + "fontColor": "#519aba" + }, + "_bower_light": { + "fontCharacter": "\\E008", + "fontColor": "#cc6d2e" + }, + "_bower": { + "fontCharacter": "\\E008", + "fontColor": "#e37933" + }, + "_bsl_light": { + "fontCharacter": "\\E009", + "fontColor": "#b8383d" + }, + "_bsl": { + "fontCharacter": "\\E009", + "fontColor": "#cc3e44" + }, + "_c_light": { + "fontCharacter": "\\E00B", + "fontColor": "#498ba7" + }, + "_c": { + "fontCharacter": "\\E00B", + "fontColor": "#519aba" + }, + "_c-sharp_light": { + "fontCharacter": "\\E00A", + "fontColor": "#498ba7" + }, + "_c-sharp": { + "fontCharacter": "\\E00A", + "fontColor": "#519aba" + }, + "_c_1_light": { + "fontCharacter": "\\E00B", + "fontColor": "#9068b0" + }, + "_c_1": { + "fontCharacter": "\\E00B", + "fontColor": "#a074c4" + }, + "_c_2_light": { + "fontCharacter": "\\E00B", + "fontColor": "#b7b73b" + }, + "_c_2": { + "fontCharacter": "\\E00B", + "fontColor": "#cbcb41" + }, + "_cake_light": { + "fontCharacter": "\\E00C", + "fontColor": "#b8383d" + }, + "_cake": { + "fontCharacter": "\\E00C", + "fontColor": "#cc3e44" + }, + "_cake_php_light": { + "fontCharacter": "\\E00D", + "fontColor": "#b8383d" + }, + "_cake_php": { + "fontCharacter": "\\E00D", + "fontColor": "#cc3e44" + }, + "_clock_light": { + "fontCharacter": "\\E011", + "fontColor": "#498ba7" + }, + "_clock": { + "fontCharacter": "\\E011", + "fontColor": "#519aba" + }, + "_clock_1_light": { + "fontCharacter": "\\E011", + "fontColor": "#627379" + }, + "_clock_1": { + "fontCharacter": "\\E011", + "fontColor": "#6d8086" + }, + "_clojure_light": { + "fontCharacter": "\\E012", + "fontColor": "#7fae42" + }, + "_clojure": { + "fontCharacter": "\\E012", + "fontColor": "#8dc149" + }, + "_clojure_1_light": { + "fontCharacter": "\\E012", + "fontColor": "#498ba7" + }, + "_clojure_1": { + "fontCharacter": "\\E012", + "fontColor": "#519aba" + }, + "_code-climate_light": { + "fontCharacter": "\\E013", + "fontColor": "#7fae42" + }, + "_code-climate": { + "fontCharacter": "\\E013", + "fontColor": "#8dc149" + }, + "_code-search_light": { + "fontCharacter": "\\E014", + "fontColor": "#9068b0" + }, + "_code-search": { + "fontCharacter": "\\E014", + "fontColor": "#a074c4" + }, + "_coffee_light": { + "fontCharacter": "\\E015", + "fontColor": "#b7b73b" + }, + "_coffee": { + "fontCharacter": "\\E015", + "fontColor": "#cbcb41" + }, + "_coldfusion_light": { + "fontCharacter": "\\E017", + "fontColor": "#498ba7" + }, + "_coldfusion": { + "fontCharacter": "\\E017", + "fontColor": "#519aba" + }, + "_config_light": { + "fontCharacter": "\\E018", + "fontColor": "#627379" + }, + "_config": { + "fontCharacter": "\\E018", + "fontColor": "#6d8086" + }, + "_cpp_light": { + "fontCharacter": "\\E019", + "fontColor": "#498ba7" + }, + "_cpp": { + "fontCharacter": "\\E019", + "fontColor": "#519aba" + }, + "_cpp_1_light": { + "fontCharacter": "\\E019", + "fontColor": "#9068b0" + }, + "_cpp_1": { + "fontCharacter": "\\E019", + "fontColor": "#a074c4" + }, + "_cpp_2_light": { + "fontCharacter": "\\E019", + "fontColor": "#b7b73b" + }, + "_cpp_2": { + "fontCharacter": "\\E019", + "fontColor": "#cbcb41" + }, + "_crystal_light": { + "fontCharacter": "\\E01A", + "fontColor": "#bfc2c1" + }, + "_crystal": { + "fontCharacter": "\\E01A", + "fontColor": "#d4d7d6" + }, + "_crystal_embedded_light": { + "fontCharacter": "\\E01B", + "fontColor": "#bfc2c1" + }, + "_crystal_embedded": { + "fontCharacter": "\\E01B", + "fontColor": "#d4d7d6" + }, + "_css_light": { + "fontCharacter": "\\E01C", + "fontColor": "#498ba7" + }, + "_css": { + "fontCharacter": "\\E01C", + "fontColor": "#519aba" + }, + "_csv_light": { + "fontCharacter": "\\E01D", + "fontColor": "#7fae42" + }, + "_csv": { + "fontCharacter": "\\E01D", + "fontColor": "#8dc149" + }, + "_cu_light": { + "fontCharacter": "\\E01E", + "fontColor": "#7fae42" + }, + "_cu": { + "fontCharacter": "\\E01E", + "fontColor": "#8dc149" + }, + "_cu_1_light": { + "fontCharacter": "\\E01E", + "fontColor": "#9068b0" + }, + "_cu_1": { + "fontCharacter": "\\E01E", + "fontColor": "#a074c4" + }, + "_d_light": { + "fontCharacter": "\\E01F", + "fontColor": "#b8383d" + }, + "_d": { + "fontCharacter": "\\E01F", + "fontColor": "#cc3e44" + }, + "_dart_light": { + "fontCharacter": "\\E020", + "fontColor": "#498ba7" + }, + "_dart": { + "fontCharacter": "\\E020", + "fontColor": "#519aba" + }, + "_db_light": { + "fontCharacter": "\\E021", + "fontColor": "#dd4b78" + }, + "_db": { + "fontCharacter": "\\E021", + "fontColor": "#f55385" + }, + "_db_1_light": { + "fontCharacter": "\\E021", + "fontColor": "#498ba7" + }, + "_db_1": { + "fontCharacter": "\\E021", + "fontColor": "#519aba" + }, + "_default_light": { + "fontCharacter": "\\E022", + "fontColor": "#bfc2c1" + }, + "_default": { + "fontCharacter": "\\E022", + "fontColor": "#d4d7d6" + }, + "_docker_light": { + "fontCharacter": "\\E024", + "fontColor": "#498ba7" + }, + "_docker": { + "fontCharacter": "\\E024", + "fontColor": "#519aba" + }, + "_docker_1_light": { + "fontCharacter": "\\E024", + "fontColor": "#455155" + }, + "_docker_1": { + "fontCharacter": "\\E024", + "fontColor": "#4d5a5e" + }, + "_docker_2_light": { + "fontCharacter": "\\E024", + "fontColor": "#7fae42" + }, + "_docker_2": { + "fontCharacter": "\\E024", + "fontColor": "#8dc149" + }, + "_docker_3_light": { + "fontCharacter": "\\E024", + "fontColor": "#dd4b78" + }, + "_docker_3": { + "fontCharacter": "\\E024", + "fontColor": "#f55385" + }, + "_ejs_light": { + "fontCharacter": "\\E026", + "fontColor": "#b7b73b" + }, + "_ejs": { + "fontCharacter": "\\E026", + "fontColor": "#cbcb41" + }, + "_elixir_light": { + "fontCharacter": "\\E027", + "fontColor": "#9068b0" + }, + "_elixir": { + "fontCharacter": "\\E027", + "fontColor": "#a074c4" + }, + "_elixir_script_light": { + "fontCharacter": "\\E028", + "fontColor": "#9068b0" + }, + "_elixir_script": { + "fontCharacter": "\\E028", + "fontColor": "#a074c4" + }, + "_elm_light": { + "fontCharacter": "\\E029", + "fontColor": "#498ba7" + }, + "_elm": { + "fontCharacter": "\\E029", + "fontColor": "#519aba" + }, + "_eslint_light": { + "fontCharacter": "\\E02B", + "fontColor": "#9068b0" + }, + "_eslint": { + "fontCharacter": "\\E02B", + "fontColor": "#a074c4" + }, + "_eslint_1_light": { + "fontCharacter": "\\E02B", + "fontColor": "#455155" + }, + "_eslint_1": { + "fontCharacter": "\\E02B", + "fontColor": "#4d5a5e" + }, + "_ethereum_light": { + "fontCharacter": "\\E02C", + "fontColor": "#498ba7" + }, + "_ethereum": { + "fontCharacter": "\\E02C", + "fontColor": "#519aba" + }, + "_f-sharp_light": { + "fontCharacter": "\\E02D", + "fontColor": "#498ba7" + }, + "_f-sharp": { + "fontCharacter": "\\E02D", + "fontColor": "#519aba" + }, + "_favicon_light": { + "fontCharacter": "\\E02E", + "fontColor": "#b7b73b" + }, + "_favicon": { + "fontCharacter": "\\E02E", + "fontColor": "#cbcb41" + }, + "_firebase_light": { + "fontCharacter": "\\E02F", + "fontColor": "#cc6d2e" + }, + "_firebase": { + "fontCharacter": "\\E02F", + "fontColor": "#e37933" + }, + "_firefox_light": { + "fontCharacter": "\\E030", + "fontColor": "#cc6d2e" + }, + "_firefox": { + "fontCharacter": "\\E030", + "fontColor": "#e37933" + }, + "_font_light": { + "fontCharacter": "\\E032", + "fontColor": "#b8383d" + }, + "_font": { + "fontCharacter": "\\E032", + "fontColor": "#cc3e44" + }, + "_git_light": { + "fontCharacter": "\\E033", + "fontColor": "#3b4b52" + }, + "_git": { + "fontCharacter": "\\E033", + "fontColor": "#41535b" + }, + "_github_light": { + "fontCharacter": "\\E034", + "fontColor": "#bfc2c1" + }, + "_github": { + "fontCharacter": "\\E034", + "fontColor": "#d4d7d6" + }, + "_gitlab_light": { + "fontCharacter": "\\E035", + "fontColor": "#cc6d2e" + }, + "_gitlab": { + "fontCharacter": "\\E035", + "fontColor": "#e37933" + }, + "_go_light": { + "fontCharacter": "\\E038", + "fontColor": "#498ba7" + }, + "_go": { + "fontCharacter": "\\E038", + "fontColor": "#519aba" + }, + "_go2_light": { + "fontCharacter": "\\E039", + "fontColor": "#498ba7" + }, + "_go2": { + "fontCharacter": "\\E039", + "fontColor": "#519aba" + }, + "_godot_light": { + "fontCharacter": "\\E03A", + "fontColor": "#498ba7" + }, + "_godot": { + "fontCharacter": "\\E03A", + "fontColor": "#519aba" + }, + "_godot_1_light": { + "fontCharacter": "\\E03A", + "fontColor": "#b8383d" + }, + "_godot_1": { + "fontCharacter": "\\E03A", + "fontColor": "#cc3e44" + }, + "_godot_2_light": { + "fontCharacter": "\\E03A", + "fontColor": "#b7b73b" + }, + "_godot_2": { + "fontCharacter": "\\E03A", + "fontColor": "#cbcb41" + }, + "_godot_3_light": { + "fontCharacter": "\\E03A", + "fontColor": "#9068b0" + }, + "_godot_3": { + "fontCharacter": "\\E03A", + "fontColor": "#a074c4" + }, + "_gradle_light": { + "fontCharacter": "\\E03B", + "fontColor": "#498ba7" + }, + "_gradle": { + "fontCharacter": "\\E03B", + "fontColor": "#519aba" + }, + "_grails_light": { + "fontCharacter": "\\E03C", + "fontColor": "#7fae42" + }, + "_grails": { + "fontCharacter": "\\E03C", + "fontColor": "#8dc149" + }, + "_graphql_light": { + "fontCharacter": "\\E03D", + "fontColor": "#dd4b78" + }, + "_graphql": { + "fontCharacter": "\\E03D", + "fontColor": "#f55385" + }, + "_grunt_light": { + "fontCharacter": "\\E03E", + "fontColor": "#cc6d2e" + }, + "_grunt": { + "fontCharacter": "\\E03E", + "fontColor": "#e37933" + }, + "_gulp_light": { + "fontCharacter": "\\E03F", + "fontColor": "#b8383d" + }, + "_gulp": { + "fontCharacter": "\\E03F", + "fontColor": "#cc3e44" + }, + "_hacklang_light": { + "fontCharacter": "\\E040", + "fontColor": "#cc6d2e" + }, + "_hacklang": { + "fontCharacter": "\\E040", + "fontColor": "#e37933" + }, + "_haml_light": { + "fontCharacter": "\\E041", + "fontColor": "#b8383d" + }, + "_haml": { + "fontCharacter": "\\E041", + "fontColor": "#cc3e44" + }, + "_happenings_light": { + "fontCharacter": "\\E042", + "fontColor": "#498ba7" + }, + "_happenings": { + "fontCharacter": "\\E042", + "fontColor": "#519aba" + }, + "_haskell_light": { + "fontCharacter": "\\E043", + "fontColor": "#9068b0" + }, + "_haskell": { + "fontCharacter": "\\E043", + "fontColor": "#a074c4" + }, + "_haxe_light": { + "fontCharacter": "\\E044", + "fontColor": "#cc6d2e" + }, + "_haxe": { + "fontCharacter": "\\E044", + "fontColor": "#e37933" + }, + "_haxe_1_light": { + "fontCharacter": "\\E044", + "fontColor": "#b7b73b" + }, + "_haxe_1": { + "fontCharacter": "\\E044", + "fontColor": "#cbcb41" + }, + "_haxe_2_light": { + "fontCharacter": "\\E044", + "fontColor": "#498ba7" + }, + "_haxe_2": { + "fontCharacter": "\\E044", + "fontColor": "#519aba" + }, + "_haxe_3_light": { + "fontCharacter": "\\E044", + "fontColor": "#9068b0" + }, + "_haxe_3": { + "fontCharacter": "\\E044", + "fontColor": "#a074c4" + }, + "_heroku_light": { + "fontCharacter": "\\E045", + "fontColor": "#9068b0" + }, + "_heroku": { + "fontCharacter": "\\E045", + "fontColor": "#a074c4" + }, + "_hex_light": { + "fontCharacter": "\\E046", + "fontColor": "#b8383d" + }, + "_hex": { + "fontCharacter": "\\E046", + "fontColor": "#cc3e44" + }, + "_html_light": { + "fontCharacter": "\\E047", + "fontColor": "#498ba7" + }, + "_html": { + "fontCharacter": "\\E047", + "fontColor": "#519aba" + }, + "_html_1_light": { + "fontCharacter": "\\E047", + "fontColor": "#7fae42" + }, + "_html_1": { + "fontCharacter": "\\E047", + "fontColor": "#8dc149" + }, + "_html_2_light": { + "fontCharacter": "\\E047", + "fontColor": "#b7b73b" + }, + "_html_2": { + "fontCharacter": "\\E047", + "fontColor": "#cbcb41" + }, + "_html_3_light": { + "fontCharacter": "\\E047", + "fontColor": "#cc6d2e" + }, + "_html_3": { + "fontCharacter": "\\E047", + "fontColor": "#e37933" + }, + "_html_erb_light": { + "fontCharacter": "\\E048", + "fontColor": "#b8383d" + }, + "_html_erb": { + "fontCharacter": "\\E048", + "fontColor": "#cc3e44" + }, + "_ignored_light": { + "fontCharacter": "\\E049", + "fontColor": "#3b4b52" + }, + "_ignored": { + "fontCharacter": "\\E049", + "fontColor": "#41535b" + }, + "_illustrator_light": { + "fontCharacter": "\\E04A", + "fontColor": "#b7b73b" + }, + "_illustrator": { + "fontCharacter": "\\E04A", + "fontColor": "#cbcb41" + }, + "_image_light": { + "fontCharacter": "\\E04B", + "fontColor": "#9068b0" + }, + "_image": { + "fontCharacter": "\\E04B", + "fontColor": "#a074c4" + }, + "_info_light": { + "fontCharacter": "\\E04C", + "fontColor": "#498ba7" + }, + "_info": { + "fontCharacter": "\\E04C", + "fontColor": "#519aba" + }, + "_ionic_light": { + "fontCharacter": "\\E04D", + "fontColor": "#498ba7" + }, + "_ionic": { + "fontCharacter": "\\E04D", + "fontColor": "#519aba" + }, + "_jade_light": { + "fontCharacter": "\\E04E", + "fontColor": "#b8383d" + }, + "_jade": { + "fontCharacter": "\\E04E", + "fontColor": "#cc3e44" + }, + "_java_light": { + "fontCharacter": "\\E04F", + "fontColor": "#b8383d" + }, + "_java": { + "fontCharacter": "\\E04F", + "fontColor": "#cc3e44" + }, + "_java_1_light": { + "fontCharacter": "\\E04F", + "fontColor": "#498ba7" + }, + "_java_1": { + "fontCharacter": "\\E04F", + "fontColor": "#519aba" + }, + "_javascript_light": { + "fontCharacter": "\\E050", + "fontColor": "#b7b73b" + }, + "_javascript": { + "fontCharacter": "\\E050", + "fontColor": "#cbcb41" + }, + "_javascript_1_light": { + "fontCharacter": "\\E050", + "fontColor": "#cc6d2e" + }, + "_javascript_1": { + "fontCharacter": "\\E050", + "fontColor": "#e37933" + }, + "_javascript_2_light": { + "fontCharacter": "\\E050", + "fontColor": "#498ba7" + }, + "_javascript_2": { + "fontCharacter": "\\E050", + "fontColor": "#519aba" + }, + "_jenkins_light": { + "fontCharacter": "\\E051", + "fontColor": "#b8383d" + }, + "_jenkins": { + "fontCharacter": "\\E051", + "fontColor": "#cc3e44" + }, + "_jinja_light": { + "fontCharacter": "\\E052", + "fontColor": "#b8383d" + }, + "_jinja": { + "fontCharacter": "\\E052", + "fontColor": "#cc3e44" + }, + "_json_light": { + "fontCharacter": "\\E053", + "fontColor": "#b7b73b" + }, + "_json": { + "fontCharacter": "\\E053", + "fontColor": "#cbcb41" + }, + "_json_1_light": { + "fontCharacter": "\\E053", + "fontColor": "#7fae42" + }, + "_json_1": { + "fontCharacter": "\\E053", + "fontColor": "#8dc149" + }, + "_julia_light": { + "fontCharacter": "\\E055", + "fontColor": "#9068b0" + }, + "_julia": { + "fontCharacter": "\\E055", + "fontColor": "#a074c4" + }, + "_karma_light": { + "fontCharacter": "\\E056", + "fontColor": "#7fae42" + }, + "_karma": { + "fontCharacter": "\\E056", + "fontColor": "#8dc149" + }, + "_kotlin_light": { + "fontCharacter": "\\E057", + "fontColor": "#cc6d2e" + }, + "_kotlin": { + "fontCharacter": "\\E057", + "fontColor": "#e37933" + }, + "_less_light": { + "fontCharacter": "\\E058", + "fontColor": "#498ba7" + }, + "_less": { + "fontCharacter": "\\E058", + "fontColor": "#519aba" + }, + "_license_light": { + "fontCharacter": "\\E059", + "fontColor": "#b7b73b" + }, + "_license": { + "fontCharacter": "\\E059", + "fontColor": "#cbcb41" + }, + "_license_1_light": { + "fontCharacter": "\\E059", + "fontColor": "#cc6d2e" + }, + "_license_1": { + "fontCharacter": "\\E059", + "fontColor": "#e37933" + }, + "_license_2_light": { + "fontCharacter": "\\E059", + "fontColor": "#b8383d" + }, + "_license_2": { + "fontCharacter": "\\E059", + "fontColor": "#cc3e44" + }, + "_liquid_light": { + "fontCharacter": "\\E05A", + "fontColor": "#7fae42" + }, + "_liquid": { + "fontCharacter": "\\E05A", + "fontColor": "#8dc149" + }, + "_livescript_light": { + "fontCharacter": "\\E05B", + "fontColor": "#498ba7" + }, + "_livescript": { + "fontCharacter": "\\E05B", + "fontColor": "#519aba" + }, + "_lock_light": { + "fontCharacter": "\\E05C", + "fontColor": "#7fae42" + }, + "_lock": { + "fontCharacter": "\\E05C", + "fontColor": "#8dc149" + }, + "_lua_light": { + "fontCharacter": "\\E05D", + "fontColor": "#498ba7" + }, + "_lua": { + "fontCharacter": "\\E05D", + "fontColor": "#519aba" + }, + "_makefile_light": { + "fontCharacter": "\\E05E", + "fontColor": "#cc6d2e" + }, + "_makefile": { + "fontCharacter": "\\E05E", + "fontColor": "#e37933" + }, + "_makefile_1_light": { + "fontCharacter": "\\E05E", + "fontColor": "#9068b0" + }, + "_makefile_1": { + "fontCharacter": "\\E05E", + "fontColor": "#a074c4" + }, + "_makefile_2_light": { + "fontCharacter": "\\E05E", + "fontColor": "#627379" + }, + "_makefile_2": { + "fontCharacter": "\\E05E", + "fontColor": "#6d8086" + }, + "_makefile_3_light": { + "fontCharacter": "\\E05E", + "fontColor": "#498ba7" + }, + "_makefile_3": { + "fontCharacter": "\\E05E", + "fontColor": "#519aba" + }, + "_markdown_light": { + "fontCharacter": "\\E05F", + "fontColor": "#498ba7" + }, + "_markdown": { + "fontCharacter": "\\E05F", + "fontColor": "#519aba" + }, + "_maven_light": { + "fontCharacter": "\\E060", + "fontColor": "#b8383d" + }, + "_maven": { + "fontCharacter": "\\E060", + "fontColor": "#cc3e44" + }, + "_mdo_light": { + "fontCharacter": "\\E061", + "fontColor": "#b8383d" + }, + "_mdo": { + "fontCharacter": "\\E061", + "fontColor": "#cc3e44" + }, + "_mustache_light": { + "fontCharacter": "\\E062", + "fontColor": "#cc6d2e" + }, + "_mustache": { + "fontCharacter": "\\E062", + "fontColor": "#e37933" + }, + "_nim_light": { + "fontCharacter": "\\E064", + "fontColor": "#b7b73b" + }, + "_nim": { + "fontCharacter": "\\E064", + "fontColor": "#cbcb41" + }, + "_notebook_light": { + "fontCharacter": "\\E065", + "fontColor": "#498ba7" + }, + "_notebook": { + "fontCharacter": "\\E065", + "fontColor": "#519aba" + }, + "_npm_light": { + "fontCharacter": "\\E066", + "fontColor": "#3b4b52" + }, + "_npm": { + "fontCharacter": "\\E066", + "fontColor": "#41535b" + }, + "_npm_1_light": { + "fontCharacter": "\\E066", + "fontColor": "#b8383d" + }, + "_npm_1": { + "fontCharacter": "\\E066", + "fontColor": "#cc3e44" + }, + "_npm_ignored_light": { + "fontCharacter": "\\E067", + "fontColor": "#3b4b52" + }, + "_npm_ignored": { + "fontCharacter": "\\E067", + "fontColor": "#41535b" + }, + "_nunjucks_light": { + "fontCharacter": "\\E068", + "fontColor": "#7fae42" + }, + "_nunjucks": { + "fontCharacter": "\\E068", + "fontColor": "#8dc149" + }, + "_ocaml_light": { + "fontCharacter": "\\E069", + "fontColor": "#cc6d2e" + }, + "_ocaml": { + "fontCharacter": "\\E069", + "fontColor": "#e37933" + }, + "_odata_light": { + "fontCharacter": "\\E06A", + "fontColor": "#cc6d2e" + }, + "_odata": { + "fontCharacter": "\\E06A", + "fontColor": "#e37933" + }, + "_pddl_light": { + "fontCharacter": "\\E06B", + "fontColor": "#9068b0" + }, + "_pddl": { + "fontCharacter": "\\E06B", + "fontColor": "#a074c4" + }, + "_pdf_light": { + "fontCharacter": "\\E06C", + "fontColor": "#b8383d" + }, + "_pdf": { + "fontCharacter": "\\E06C", + "fontColor": "#cc3e44" + }, + "_perl_light": { + "fontCharacter": "\\E06D", + "fontColor": "#498ba7" + }, + "_perl": { + "fontCharacter": "\\E06D", + "fontColor": "#519aba" + }, + "_photoshop_light": { + "fontCharacter": "\\E06E", + "fontColor": "#498ba7" + }, + "_photoshop": { + "fontCharacter": "\\E06E", + "fontColor": "#519aba" + }, + "_php_light": { + "fontCharacter": "\\E06F", + "fontColor": "#9068b0" + }, + "_php": { + "fontCharacter": "\\E06F", + "fontColor": "#a074c4" + }, + "_pipeline_light": { + "fontCharacter": "\\E070", + "fontColor": "#cc6d2e" + }, + "_pipeline": { + "fontCharacter": "\\E070", + "fontColor": "#e37933" + }, + "_plan_light": { + "fontCharacter": "\\E071", + "fontColor": "#7fae42" + }, + "_plan": { + "fontCharacter": "\\E071", + "fontColor": "#8dc149" + }, + "_platformio_light": { + "fontCharacter": "\\E072", + "fontColor": "#cc6d2e" + }, + "_platformio": { + "fontCharacter": "\\E072", + "fontColor": "#e37933" + }, + "_powershell_light": { + "fontCharacter": "\\E073", + "fontColor": "#498ba7" + }, + "_powershell": { + "fontCharacter": "\\E073", + "fontColor": "#519aba" + }, + "_prisma_light": { + "fontCharacter": "\\E074", + "fontColor": "#498ba7" + }, + "_prisma": { + "fontCharacter": "\\E074", + "fontColor": "#519aba" + }, + "_prolog_light": { + "fontCharacter": "\\E076", + "fontColor": "#cc6d2e" + }, + "_prolog": { + "fontCharacter": "\\E076", + "fontColor": "#e37933" + }, + "_pug_light": { + "fontCharacter": "\\E077", + "fontColor": "#b8383d" + }, + "_pug": { + "fontCharacter": "\\E077", + "fontColor": "#cc3e44" + }, + "_puppet_light": { + "fontCharacter": "\\E078", + "fontColor": "#b7b73b" + }, + "_puppet": { + "fontCharacter": "\\E078", + "fontColor": "#cbcb41" + }, + "_purescript_light": { + "fontCharacter": "\\E079", + "fontColor": "#bfc2c1" + }, + "_purescript": { + "fontCharacter": "\\E079", + "fontColor": "#d4d7d6" + }, + "_python_light": { + "fontCharacter": "\\E07A", + "fontColor": "#498ba7" + }, + "_python": { + "fontCharacter": "\\E07A", + "fontColor": "#519aba" + }, + "_react_light": { + "fontCharacter": "\\E07D", + "fontColor": "#498ba7" + }, + "_react": { + "fontCharacter": "\\E07D", + "fontColor": "#519aba" + }, + "_react_1_light": { + "fontCharacter": "\\E07D", + "fontColor": "#cc6d2e" + }, + "_react_1": { + "fontCharacter": "\\E07D", + "fontColor": "#e37933" + }, + "_reasonml_light": { + "fontCharacter": "\\E07E", + "fontColor": "#b8383d" + }, + "_reasonml": { + "fontCharacter": "\\E07E", + "fontColor": "#cc3e44" + }, + "_rescript_light": { + "fontCharacter": "\\E07F", + "fontColor": "#b8383d" + }, + "_rescript": { + "fontCharacter": "\\E07F", + "fontColor": "#cc3e44" + }, + "_rescript_1_light": { + "fontCharacter": "\\E07F", + "fontColor": "#dd4b78" + }, + "_rescript_1": { + "fontCharacter": "\\E07F", + "fontColor": "#f55385" + }, + "_rollup_light": { + "fontCharacter": "\\E080", + "fontColor": "#b8383d" + }, + "_rollup": { + "fontCharacter": "\\E080", + "fontColor": "#cc3e44" + }, + "_ruby_light": { + "fontCharacter": "\\E081", + "fontColor": "#b8383d" + }, + "_ruby": { + "fontCharacter": "\\E081", + "fontColor": "#cc3e44" + }, + "_rust_light": { + "fontCharacter": "\\E082", + "fontColor": "#627379" + }, + "_rust": { + "fontCharacter": "\\E082", + "fontColor": "#6d8086" + }, + "_salesforce_light": { + "fontCharacter": "\\E083", + "fontColor": "#498ba7" + }, + "_salesforce": { + "fontCharacter": "\\E083", + "fontColor": "#519aba" + }, + "_sass_light": { + "fontCharacter": "\\E084", + "fontColor": "#dd4b78" + }, + "_sass": { + "fontCharacter": "\\E084", + "fontColor": "#f55385" + }, + "_sbt_light": { + "fontCharacter": "\\E085", + "fontColor": "#498ba7" + }, + "_sbt": { + "fontCharacter": "\\E085", + "fontColor": "#519aba" + }, + "_scala_light": { + "fontCharacter": "\\E086", + "fontColor": "#b8383d" + }, + "_scala": { + "fontCharacter": "\\E086", + "fontColor": "#cc3e44" + }, + "_shell_light": { + "fontCharacter": "\\E089", + "fontColor": "#7fae42" + }, + "_shell": { + "fontCharacter": "\\E089", + "fontColor": "#8dc149" + }, + "_slim_light": { + "fontCharacter": "\\E08A", + "fontColor": "#cc6d2e" + }, + "_slim": { + "fontCharacter": "\\E08A", + "fontColor": "#e37933" + }, + "_smarty_light": { + "fontCharacter": "\\E08B", + "fontColor": "#b7b73b" + }, + "_smarty": { + "fontCharacter": "\\E08B", + "fontColor": "#cbcb41" + }, + "_spring_light": { + "fontCharacter": "\\E08C", + "fontColor": "#7fae42" + }, + "_spring": { + "fontCharacter": "\\E08C", + "fontColor": "#8dc149" + }, + "_stylelint_light": { + "fontCharacter": "\\E08D", + "fontColor": "#bfc2c1" + }, + "_stylelint": { + "fontCharacter": "\\E08D", + "fontColor": "#d4d7d6" + }, + "_stylelint_1_light": { + "fontCharacter": "\\E08D", + "fontColor": "#455155" + }, + "_stylelint_1": { + "fontCharacter": "\\E08D", + "fontColor": "#4d5a5e" + }, + "_stylus_light": { + "fontCharacter": "\\E08E", + "fontColor": "#7fae42" + }, + "_stylus": { + "fontCharacter": "\\E08E", + "fontColor": "#8dc149" + }, + "_sublime_light": { + "fontCharacter": "\\E08F", + "fontColor": "#cc6d2e" + }, + "_sublime": { + "fontCharacter": "\\E08F", + "fontColor": "#e37933" + }, + "_svelte_light": { + "fontCharacter": "\\E090", + "fontColor": "#b8383d" + }, + "_svelte": { + "fontCharacter": "\\E090", + "fontColor": "#cc3e44" + }, + "_svg_light": { + "fontCharacter": "\\E091", + "fontColor": "#9068b0" + }, + "_svg": { + "fontCharacter": "\\E091", + "fontColor": "#a074c4" + }, + "_svg_1_light": { + "fontCharacter": "\\E091", + "fontColor": "#498ba7" + }, + "_svg_1": { + "fontCharacter": "\\E091", + "fontColor": "#519aba" + }, + "_swift_light": { + "fontCharacter": "\\E092", + "fontColor": "#cc6d2e" + }, + "_swift": { + "fontCharacter": "\\E092", + "fontColor": "#e37933" + }, + "_terraform_light": { + "fontCharacter": "\\E093", + "fontColor": "#9068b0" + }, + "_terraform": { + "fontCharacter": "\\E093", + "fontColor": "#a074c4" + }, + "_tex_light": { + "fontCharacter": "\\E094", + "fontColor": "#498ba7" + }, + "_tex": { + "fontCharacter": "\\E094", + "fontColor": "#519aba" + }, + "_tex_1_light": { + "fontCharacter": "\\E094", + "fontColor": "#b7b73b" + }, + "_tex_1": { + "fontCharacter": "\\E094", + "fontColor": "#cbcb41" + }, + "_tex_2_light": { + "fontCharacter": "\\E094", + "fontColor": "#cc6d2e" + }, + "_tex_2": { + "fontCharacter": "\\E094", + "fontColor": "#e37933" + }, + "_tex_3_light": { + "fontCharacter": "\\E094", + "fontColor": "#bfc2c1" + }, + "_tex_3": { + "fontCharacter": "\\E094", + "fontColor": "#d4d7d6" + }, + "_todo": { + "fontCharacter": "\\E096" + }, + "_tsconfig_light": { + "fontCharacter": "\\E097", + "fontColor": "#498ba7" + }, + "_tsconfig": { + "fontCharacter": "\\E097", + "fontColor": "#519aba" + }, + "_twig_light": { + "fontCharacter": "\\E098", + "fontColor": "#7fae42" + }, + "_twig": { + "fontCharacter": "\\E098", + "fontColor": "#8dc149" + }, + "_typescript_light": { + "fontCharacter": "\\E099", + "fontColor": "#498ba7" + }, + "_typescript": { + "fontCharacter": "\\E099", + "fontColor": "#519aba" + }, + "_typescript_1_light": { + "fontCharacter": "\\E099", + "fontColor": "#cc6d2e" + }, + "_typescript_1": { + "fontCharacter": "\\E099", + "fontColor": "#e37933" + }, + "_vala_light": { + "fontCharacter": "\\E09A", + "fontColor": "#627379" + }, + "_vala": { + "fontCharacter": "\\E09A", + "fontColor": "#6d8086" + }, + "_vba_light": { + "fontCharacter": "\\E09B", + "fontColor": "#9068b0" + }, + "_vba": { + "fontCharacter": "\\E09B", + "fontColor": "#a074c4" + }, + "_vba_1_light": { + "fontCharacter": "\\E09B", + "fontColor": "#7fae42" + }, + "_vba_1": { + "fontCharacter": "\\E09B", + "fontColor": "#8dc149" + }, + "_vba_2_light": { + "fontCharacter": "\\E09B", + "fontColor": "#b7b73b" + }, + "_vba_2": { + "fontCharacter": "\\E09B", + "fontColor": "#cbcb41" + }, + "_video_light": { + "fontCharacter": "\\E09C", + "fontColor": "#dd4b78" + }, + "_video": { + "fontCharacter": "\\E09C", + "fontColor": "#f55385" + }, + "_vite_light": { + "fontCharacter": "\\E09D", + "fontColor": "#b7b73b" + }, + "_vite": { + "fontCharacter": "\\E09D", + "fontColor": "#cbcb41" + }, + "_vue_light": { + "fontCharacter": "\\E09E", + "fontColor": "#7fae42" + }, + "_vue": { + "fontCharacter": "\\E09E", + "fontColor": "#8dc149" + }, + "_wasm_light": { + "fontCharacter": "\\E09F", + "fontColor": "#9068b0" + }, + "_wasm": { + "fontCharacter": "\\E09F", + "fontColor": "#a074c4" + }, + "_wat_light": { + "fontCharacter": "\\E0A0", + "fontColor": "#9068b0" + }, + "_wat": { + "fontCharacter": "\\E0A0", + "fontColor": "#a074c4" + }, + "_webpack_light": { + "fontCharacter": "\\E0A1", + "fontColor": "#498ba7" + }, + "_webpack": { + "fontCharacter": "\\E0A1", + "fontColor": "#519aba" + }, + "_wgt_light": { + "fontCharacter": "\\E0A2", + "fontColor": "#498ba7" + }, + "_wgt": { + "fontCharacter": "\\E0A2", + "fontColor": "#519aba" + }, + "_windows_light": { + "fontCharacter": "\\E0A3", + "fontColor": "#498ba7" + }, + "_windows": { + "fontCharacter": "\\E0A3", + "fontColor": "#519aba" + }, + "_word_light": { + "fontCharacter": "\\E0A4", + "fontColor": "#498ba7" + }, + "_word": { + "fontCharacter": "\\E0A4", + "fontColor": "#519aba" + }, + "_xls_light": { + "fontCharacter": "\\E0A5", + "fontColor": "#7fae42" + }, + "_xls": { + "fontCharacter": "\\E0A5", + "fontColor": "#8dc149" + }, + "_xml_light": { + "fontCharacter": "\\E0A6", + "fontColor": "#cc6d2e" + }, + "_xml": { + "fontCharacter": "\\E0A6", + "fontColor": "#e37933" + }, + "_yarn_light": { + "fontCharacter": "\\E0A7", + "fontColor": "#498ba7" + }, + "_yarn": { + "fontCharacter": "\\E0A7", + "fontColor": "#519aba" + }, + "_yml_light": { + "fontCharacter": "\\E0A8", + "fontColor": "#9068b0" + }, + "_yml": { + "fontCharacter": "\\E0A8", + "fontColor": "#a074c4" + }, + "_zig_light": { + "fontCharacter": "\\E0A9", + "fontColor": "#cc6d2e" + }, + "_zig": { + "fontCharacter": "\\E0A9", + "fontColor": "#e37933" + }, + "_zip_light": { + "fontCharacter": "\\E0AA", + "fontColor": "#b8383d" + }, + "_zip": { + "fontCharacter": "\\E0AA", + "fontColor": "#cc3e44" + }, + "_zip_1_light": { + "fontCharacter": "\\E0AA", + "fontColor": "#627379" + }, + "_zip_1": { + "fontCharacter": "\\E0AA", + "fontColor": "#6d8086" + } + }, + "file": "_default", + "fileExtensions": { + "bsl": "_bsl", + "mdo": "_mdo", + "apex": "_salesforce", + "asm": "_asm", + "s": "_asm", + "bicep": "_bicep", + "bzl": "_bazel", + "bazel": "_bazel", + "build": "_bazel", + "workspace": "_bazel", + "bazelignore": "_bazel", + "bazelversion": "_bazel", + "c": "_c", + "h": "_c_1", + "m": "_c_2", + "cs": "_c-sharp", + "cshtml": "_html", + "aspx": "_html", + "ascx": "_html_1", + "asax": "_html_2", + "master": "_html_2", + "cc": "_cpp", + "cpp": "_cpp", + "cxx": "_cpp", + "c++": "_cpp", + "hh": "_cpp_1", + "hpp": "_cpp_1", + "hxx": "_cpp_1", + "h++": "_cpp_1", + "mm": "_cpp_2", + "clj": "_clojure", + "cljs": "_clojure", + "cljc": "_clojure", + "edn": "_clojure_1", + "cfc": "_coldfusion", + "cfm": "_coldfusion", + "coffee": "_coffee", + "litcoffee": "_coffee", + "config": "_config", + "cfg": "_config", + "conf": "_config", + "cr": "_crystal", + "ecr": "_crystal_embedded", + "slang": "_crystal_embedded", + "cson": "_json", + "css": "_css", + "css.map": "_css", + "sss": "_css", + "csv": "_csv", + "xla": "_xls", + "xlam": "_xls", + "xls": "_xls", + "xlsb": "_xls", + "xlsm": "_xls", + "xlsx": "_xls", + "xlt": "_xls", + "xltm": "_xls", + "cu": "_cu", + "cuh": "_cu_1", + "hu": "_cu_1", + "cake": "_cake", + "ctp": "_cake_php", + "d": "_d", + "doc": "_word", + "docx": "_word", + "dot": "_word", + "dotm": "_word", + "ejs": "_ejs", + "ex": "_elixir", + "exs": "_elixir_script", + "elm": "_elm", + "ico": "_favicon", + "fs": "_f-sharp", + "fsx": "_f-sharp", + "gitignore": "_git", + "gitconfig": "_git", + "gitkeep": "_git", + "gitattributes": "_git", + "gitmodules": "_git", + "go": "_go2", + "slide": "_go", + "article": "_go", + "gd": "_godot", + "godot": "_godot_1", + "tres": "_godot_2", + "tscn": "_godot_3", + "gradle": "_gradle", + "groovy": "_grails", + "gsp": "_grails", + "gql": "_graphql", + "graphql": "_graphql", + "graphqls": "_graphql", + "hack": "_hacklang", + "haml": "_haml", + "handlebars": "_mustache", + "hbs": "_mustache", + "hjs": "_mustache", + "hs": "_haskell", + "lhs": "_haskell", + "hx": "_haxe", + "hxs": "_haxe_1", + "hxp": "_haxe_2", + "hxml": "_haxe_3", + "html": "_html_3", + "jade": "_jade", + "java": "_java", + "class": "_java_1", + "classpath": "_java", + "properties": "_java", + "js": "_javascript", + "js.map": "_javascript", + "cjs": "_javascript", + "cjs.map": "_javascript", + "mjs": "_javascript", + "mjs.map": "_javascript", + "spec.js": "_javascript_1", + "spec.cjs": "_javascript_1", + "spec.mjs": "_javascript_1", + "test.js": "_javascript_1", + "test.cjs": "_javascript_1", + "test.mjs": "_javascript_1", + "es": "_javascript", + "es5": "_javascript", + "es6": "_javascript", + "es7": "_javascript", + "jinja": "_jinja", + "jinja2": "_jinja", + "json": "_json", + "jl": "_julia", + "kt": "_kotlin", + "kts": "_kotlin", + "dart": "_dart", + "less": "_less", + "liquid": "_liquid", + "ls": "_livescript", + "lua": "_lua", + "markdown": "_markdown", + "md": "_markdown", + "argdown": "_argdown", + "ad": "_argdown", + "mustache": "_mustache", + "stache": "_mustache", + "nim": "_nim", + "nims": "_nim", + "github-issues": "_github", + "ipynb": "_notebook", + "njk": "_nunjucks", + "nunjucks": "_nunjucks", + "nunjs": "_nunjucks", + "nunj": "_nunjucks", + "njs": "_nunjucks", + "nj": "_nunjucks", + "npm-debug.log": "_npm", + "npmignore": "_npm_1", + "npmrc": "_npm_1", + "ml": "_ocaml", + "mli": "_ocaml", + "cmx": "_ocaml", + "cmxa": "_ocaml", + "odata": "_odata", + "pl": "_perl", + "php": "_php", + "php.inc": "_php", + "pipeline": "_pipeline", + "pddl": "_pddl", + "plan": "_plan", + "happenings": "_happenings", + "ps1": "_powershell", + "psd1": "_powershell", + "psm1": "_powershell", + "prisma": "_prisma", + "pug": "_pug", + "pp": "_puppet", + "epp": "_puppet", + "purs": "_purescript", + "py": "_python", + "jsx": "_react", + "spec.jsx": "_react_1", + "test.jsx": "_react_1", + "cjsx": "_react", + "tsx": "_react", + "spec.tsx": "_react_1", + "test.tsx": "_react_1", + "re": "_reasonml", + "res": "_rescript", + "resi": "_rescript_1", + "r": "_R", + "rmd": "_R", + "rb": "_ruby", + "erb": "_html_erb", + "erb.html": "_html_erb", + "html.erb": "_html_erb", + "rs": "_rust", + "sass": "_sass", + "scss": "_sass", + "springbeans": "_spring", + "slim": "_slim", + "smarty.tpl": "_smarty", + "tpl": "_smarty", + "sbt": "_sbt", + "scala": "_scala", + "sol": "_ethereum", + "styl": "_stylus", + "svelte": "_svelte", + "swift": "_swift", + "sql": "_db", + "soql": "_db_1", + "tf": "_terraform", + "tf.json": "_terraform", + "tfvars": "_terraform", + "tfvars.json": "_terraform", + "tex": "_tex", + "sty": "_tex_1", + "dtx": "_tex_2", + "ins": "_tex_3", + "toml": "_config", + "twig": "_twig", + "ts": "_typescript", + "spec.ts": "_typescript_1", + "test.ts": "_typescript_1", + "vala": "_vala", + "vapi": "_vala", + "bas": "_vba", + "cls": "_vba_1", + "frm": "_vba_2", + "component": "_html_3", + "vue": "_vue", + "wasm": "_wasm", + "wat": "_wat", + "xml": "_xml", + "yml": "_yml", + "yaml": "_yml", + "pro": "_prolog", + "zig": "_zig", + "jar": "_zip", + "zip": "_zip_1", + "wgt": "_wgt", + "ai": "_illustrator", + "psd": "_photoshop", + "pdf": "_pdf", + "eot": "_font", + "ttf": "_font", + "woff": "_font", + "woff2": "_font", + "otf": "_font", + "avif": "_image", + "gif": "_image", + "jpg": "_image", + "jpeg": "_image", + "png": "_image", + "pxm": "_image", + "svg": "_svg", + "svgx": "_image", + "tiff": "_image", + "webp": "_image", + "sublime-project": "_sublime", + "sublime-workspace": "_sublime", + "code-search": "_code-search", + "sh": "_shell", + "zsh": "_shell", + "fish": "_shell", + "zshrc": "_shell", + "bashrc": "_shell", + "mov": "_video", + "ogv": "_video", + "webm": "_video", + "avi": "_video", + "mpg": "_video", + "mp4": "_video", + "mp3": "_audio", + "ogg": "_audio", + "wav": "_audio", + "flac": "_audio", + "3ds": "_svg_1", + "3dm": "_svg_1", + "stl": "_svg_1", + "obj": "_svg_1", + "dae": "_svg_1", + "bat": "_windows", + "cmd": "_windows", + "babelrc": "_babel", + "babelrc.js": "_babel", + "babelrc.cjs": "_babel", + "bazelrc": "_bazel_1", + "bowerrc": "_bower", + "dockerignore": "_docker_1", + "codeclimate.yml": "_code-climate", + "eslintrc": "_eslint", + "eslintrc.js": "_eslint", + "eslintrc.cjs": "_eslint", + "eslintrc.yaml": "_eslint", + "eslintrc.yml": "_eslint", + "eslintrc.json": "_eslint", + "eslintignore": "_eslint_1", + "firebaserc": "_firebase", + "gitlab-ci.yml": "_gitlab", + "jshintrc": "_javascript_2", + "jscsrc": "_javascript_2", + "stylelintrc": "_stylelint", + "stylelintrc.json": "_stylelint", + "stylelintrc.yaml": "_stylelint", + "stylelintrc.yml": "_stylelint", + "stylelintrc.js": "_stylelint", + "stylelintignore": "_stylelint_1", + "direnv": "_config", + "env": "_config", + "static": "_config", + "editorconfig": "_config", + "slugignore": "_config", + "tmp": "_clock_1", + "htaccess": "_config", + "key": "_lock", + "cert": "_lock", + "cer": "_lock", + "crt": "_lock", + "pem": "_lock", + "ds_store": "_ignored" + }, + "fileNames": { + "mix": "_hex", + "commit_editmsg": "_git", + "merge_msg": "_git", + "karma.conf.js": "_karma", + "karma.conf.cjs": "_karma", + "karma.conf.mjs": "_karma", + "karma.conf.coffee": "_karma", + "readme.md": "_info", + "readme.txt": "_info", + "readme": "_info", + "changelog.md": "_clock", + "changelog.txt": "_clock", + "changelog": "_clock", + "changes.md": "_clock", + "changes.txt": "_clock", + "changes": "_clock", + "version.md": "_clock", + "version.txt": "_clock", + "version": "_clock", + "mvnw": "_maven", + "pom.xml": "_maven", + "gemfile": "_ruby", + "tsconfig.json": "_tsconfig", + "vite.config.js": "_vite", + "vite.config.ts": "_vite", + "vite.config.mjs": "_vite", + "vite.config.mts": "_vite", + "vite.config.cjs": "_vite", + "vite.config.cts": "_vite", + "swagger.json": "_json_1", + "swagger.yml": "_json_1", + "swagger.yaml": "_json_1", + "mime.types": "_config", + "jenkinsfile": "_jenkins", + "babel.config.js": "_babel", + "babel.config.json": "_babel", + "babel.config.cjs": "_babel", + "build": "_bazel", + "build.bazel": "_bazel", + "workspace": "_bazel", + "workspace.bazel": "_bazel", + "bower.json": "_bower", + "dockerfile": "_docker", + "docker-healthcheck": "_docker_2", + "docker-compose.yml": "_docker_3", + "docker-compose.yaml": "_docker_3", + "docker-compose.override.yml": "_docker_3", + "docker-compose.override.yaml": "_docker_3", + "eslint.config.js": "_eslint", + "firebase.json": "_firebase", + "geckodriver": "_firefox", + "gruntfile.js": "_grunt", + "gruntfile.babel.js": "_grunt", + "gruntfile.coffee": "_grunt", + "gulpfile": "_gulp", + "gulpfile.js": "_gulp", + "ionic.config.json": "_ionic", + "ionic.project": "_ionic", + "platformio.ini": "_platformio", + "rollup.config.js": "_rollup", + "sass-lint.yml": "_sass", + "stylelint.config.js": "_stylelint", + "stylelint.config.cjs": "_stylelint", + "stylelint.config.mjs": "_stylelint", + "yarn.clean": "_yarn", + "yarn.lock": "_yarn", + "webpack.config.js": "_webpack", + "webpack.config.cjs": "_webpack", + "webpack.config.mjs": "_webpack", + "webpack.config.ts": "_webpack", + "webpack.config.build.js": "_webpack", + "webpack.config.build.cjs": "_webpack", + "webpack.config.build.mjs": "_webpack", + "webpack.config.build.ts": "_webpack", + "webpack.common.js": "_webpack", + "webpack.common.cjs": "_webpack", + "webpack.common.mjs": "_webpack", + "webpack.common.ts": "_webpack", + "webpack.dev.js": "_webpack", + "webpack.dev.cjs": "_webpack", + "webpack.dev.mjs": "_webpack", + "webpack.dev.ts": "_webpack", + "webpack.prod.js": "_webpack", + "webpack.prod.cjs": "_webpack", + "webpack.prod.mjs": "_webpack", + "webpack.prod.ts": "_webpack", + "license": "_license", + "licence": "_license", + "license.txt": "_license", + "licence.txt": "_license", + "license.md": "_license", + "licence.md": "_license", + "copying": "_license", + "copying.txt": "_license", + "copying.md": "_license", + "compiling": "_license_1", + "compiling.txt": "_license_1", + "compiling.md": "_license_1", + "contributing": "_license_2", + "contributing.txt": "_license_2", + "contributing.md": "_license_2", + "makefile": "_makefile", + "qmakefile": "_makefile_1", + "omakefile": "_makefile_2", + "cmakelists.txt": "_makefile_3", + "procfile": "_heroku", + "todo": "_todo", + "todo.txt": "_todo", + "todo.md": "_todo", + "npm-debug.log": "_npm_ignored" + }, + "languageIds": { + "argdown": "_argdown", + "bicep": "_bicep", + "elixir": "_elixir", + "elm": "_elm", + "erb": "_html_erb", + "github-issues": "_github", + "gradle": "_gradle", + "godot": "_godot", + "haml": "_haml", + "haskell": "_haskell", + "haxe": "_haxe", + "jinja": "_jinja", + "kotlin": "_kotlin", + "mustache": "_mustache", + "nunjucks": "_nunjucks", + "ocaml": "_ocaml", + "r": "_R", + "rescript": "_rescript", + "sass": "_sass", + "stylus": "_stylus", + "terraform": "_terraform", + "todo": "_todo", + "vala": "_vala", + "vue": "_vue" + }, + "light": { + "file": "_default_light", + "fileExtensions": { + "bsl": "_bsl_light", + "mdo": "_mdo_light", + "apex": "_salesforce_light", + "asm": "_asm_light", + "s": "_asm_light", + "bicep": "_bicep_light", + "bzl": "_bazel_light", + "bazel": "_bazel_light", + "build": "_bazel_light", + "workspace": "_bazel_light", + "bazelignore": "_bazel_light", + "bazelversion": "_bazel_light", + "c": "_c_light", + "h": "_c_1_light", + "m": "_c_2_light", + "cs": "_c-sharp_light", + "cshtml": "_html_light", + "aspx": "_html_light", + "ascx": "_html_1_light", + "asax": "_html_2_light", + "master": "_html_2_light", + "cc": "_cpp_light", + "cpp": "_cpp_light", + "cxx": "_cpp_light", + "c++": "_cpp_light", + "hh": "_cpp_1_light", + "hpp": "_cpp_1_light", + "hxx": "_cpp_1_light", + "h++": "_cpp_1_light", + "mm": "_cpp_2_light", + "clj": "_clojure_light", + "cljs": "_clojure_light", + "cljc": "_clojure_light", + "edn": "_clojure_1_light", + "cfc": "_coldfusion_light", + "cfm": "_coldfusion_light", + "coffee": "_coffee_light", + "litcoffee": "_coffee_light", + "config": "_config_light", + "cfg": "_config_light", + "conf": "_config_light", + "cr": "_crystal_light", + "ecr": "_crystal_embedded_light", + "slang": "_crystal_embedded_light", + "cson": "_json_light", + "css": "_css_light", + "css.map": "_css_light", + "sss": "_css_light", + "csv": "_csv_light", + "xla": "_xls_light", + "xlam": "_xls_light", + "xls": "_xls_light", + "xlsb": "_xls_light", + "xlsm": "_xls_light", + "xlsx": "_xls_light", + "xlt": "_xls_light", + "xltm": "_xls_light", + "cu": "_cu_light", + "cuh": "_cu_1_light", + "hu": "_cu_1_light", + "cake": "_cake_light", + "ctp": "_cake_php_light", + "d": "_d_light", + "doc": "_word_light", + "docx": "_word_light", + "dot": "_word_light", + "dotm": "_word_light", + "ejs": "_ejs_light", + "ex": "_elixir_light", + "exs": "_elixir_script_light", + "elm": "_elm_light", + "ico": "_favicon_light", + "fs": "_f-sharp_light", + "fsx": "_f-sharp_light", + "gitignore": "_git_light", + "gitconfig": "_git_light", + "gitkeep": "_git_light", + "gitattributes": "_git_light", + "gitmodules": "_git_light", + "go": "_go2_light", + "slide": "_go_light", + "article": "_go_light", + "gd": "_godot_light", + "godot": "_godot_1_light", + "tres": "_godot_2_light", + "tscn": "_godot_3_light", + "gradle": "_gradle_light", + "groovy": "_grails_light", + "gsp": "_grails_light", + "gql": "_graphql_light", + "graphql": "_graphql_light", + "graphqls": "_graphql_light", + "hack": "_hacklang_light", + "haml": "_haml_light", + "handlebars": "_mustache_light", + "hbs": "_mustache_light", + "hjs": "_mustache_light", + "hs": "_haskell_light", + "lhs": "_haskell_light", + "hx": "_haxe_light", + "hxs": "_haxe_1_light", + "hxp": "_haxe_2_light", + "hxml": "_haxe_3_light", + "html": "_html_3_light", + "jade": "_jade_light", + "java": "_java_light", + "class": "_java_1_light", + "classpath": "_java_light", + "properties": "_java_light", + "js": "_javascript_light", + "js.map": "_javascript_light", + "cjs": "_javascript_light", + "cjs.map": "_javascript_light", + "mjs": "_javascript_light", + "mjs.map": "_javascript_light", + "spec.js": "_javascript_1_light", + "spec.cjs": "_javascript_1_light", + "spec.mjs": "_javascript_1_light", + "test.js": "_javascript_1_light", + "test.cjs": "_javascript_1_light", + "test.mjs": "_javascript_1_light", + "es": "_javascript_light", + "es5": "_javascript_light", + "es6": "_javascript_light", + "es7": "_javascript_light", + "jinja": "_jinja_light", + "jinja2": "_jinja_light", + "json": "_json_light", + "jl": "_julia_light", + "kt": "_kotlin_light", + "kts": "_kotlin_light", + "dart": "_dart_light", + "less": "_less_light", + "liquid": "_liquid_light", + "ls": "_livescript_light", + "lua": "_lua_light", + "markdown": "_markdown_light", + "md": "_markdown_light", + "argdown": "_argdown_light", + "ad": "_argdown_light", + "mustache": "_mustache_light", + "stache": "_mustache_light", + "nim": "_nim_light", + "nims": "_nim_light", + "github-issues": "_github_light", + "ipynb": "_notebook_light", + "njk": "_nunjucks_light", + "nunjucks": "_nunjucks_light", + "nunjs": "_nunjucks_light", + "nunj": "_nunjucks_light", + "njs": "_nunjucks_light", + "nj": "_nunjucks_light", + "npm-debug.log": "_npm_light", + "npmignore": "_npm_1_light", + "npmrc": "_npm_1_light", + "ml": "_ocaml_light", + "mli": "_ocaml_light", + "cmx": "_ocaml_light", + "cmxa": "_ocaml_light", + "odata": "_odata_light", + "pl": "_perl_light", + "php": "_php_light", + "php.inc": "_php_light", + "pipeline": "_pipeline_light", + "pddl": "_pddl_light", + "plan": "_plan_light", + "happenings": "_happenings_light", + "ps1": "_powershell_light", + "psd1": "_powershell_light", + "psm1": "_powershell_light", + "prisma": "_prisma_light", + "pug": "_pug_light", + "pp": "_puppet_light", + "epp": "_puppet_light", + "purs": "_purescript_light", + "py": "_python_light", + "jsx": "_react_light", + "spec.jsx": "_react_1_light", + "test.jsx": "_react_1_light", + "cjsx": "_react_light", + "tsx": "_react_light", + "spec.tsx": "_react_1_light", + "test.tsx": "_react_1_light", + "re": "_reasonml_light", + "res": "_rescript_light", + "resi": "_rescript_1_light", + "r": "_R_light", + "rmd": "_R_light", + "rb": "_ruby_light", + "erb": "_html_erb_light", + "erb.html": "_html_erb_light", + "html.erb": "_html_erb_light", + "rs": "_rust_light", + "sass": "_sass_light", + "scss": "_sass_light", + "springbeans": "_spring_light", + "slim": "_slim_light", + "smarty.tpl": "_smarty_light", + "tpl": "_smarty_light", + "sbt": "_sbt_light", + "scala": "_scala_light", + "sol": "_ethereum_light", + "styl": "_stylus_light", + "svelte": "_svelte_light", + "swift": "_swift_light", + "sql": "_db_light", + "soql": "_db_1_light", + "tf": "_terraform_light", + "tf.json": "_terraform_light", + "tfvars": "_terraform_light", + "tfvars.json": "_terraform_light", + "tex": "_tex_light", + "sty": "_tex_1_light", + "dtx": "_tex_2_light", + "ins": "_tex_3_light", + "toml": "_config_light", + "twig": "_twig_light", + "ts": "_typescript_light", + "spec.ts": "_typescript_1_light", + "test.ts": "_typescript_1_light", + "vala": "_vala_light", + "vapi": "_vala_light", + "bas": "_vba_light", + "cls": "_vba_1_light", + "frm": "_vba_2_light", + "component": "_html_3_light", + "vue": "_vue_light", + "wasm": "_wasm_light", + "wat": "_wat_light", + "xml": "_xml_light", + "yml": "_yml_light", + "yaml": "_yml_light", + "pro": "_prolog_light", + "zig": "_zig_light", + "jar": "_zip_light", + "zip": "_zip_1_light", + "wgt": "_wgt_light", + "ai": "_illustrator_light", + "psd": "_photoshop_light", + "pdf": "_pdf_light", + "eot": "_font_light", + "ttf": "_font_light", + "woff": "_font_light", + "woff2": "_font_light", + "otf": "_font_light", + "avif": "_image_light", + "gif": "_image_light", + "jpg": "_image_light", + "jpeg": "_image_light", + "png": "_image_light", + "pxm": "_image_light", + "svg": "_svg_light", + "svgx": "_image_light", + "tiff": "_image_light", + "webp": "_image_light", + "sublime-project": "_sublime_light", + "sublime-workspace": "_sublime_light", + "code-search": "_code-search_light", + "sh": "_shell_light", + "zsh": "_shell_light", + "fish": "_shell_light", + "zshrc": "_shell_light", + "bashrc": "_shell_light", + "mov": "_video_light", + "ogv": "_video_light", + "webm": "_video_light", + "avi": "_video_light", + "mpg": "_video_light", + "mp4": "_video_light", + "mp3": "_audio_light", + "ogg": "_audio_light", + "wav": "_audio_light", + "flac": "_audio_light", + "3ds": "_svg_1_light", + "3dm": "_svg_1_light", + "stl": "_svg_1_light", + "obj": "_svg_1_light", + "dae": "_svg_1_light", + "bat": "_windows_light", + "cmd": "_windows_light", + "babelrc": "_babel_light", + "babelrc.js": "_babel_light", + "babelrc.cjs": "_babel_light", + "bazelrc": "_bazel_1_light", + "bowerrc": "_bower_light", + "dockerignore": "_docker_1_light", + "codeclimate.yml": "_code-climate_light", + "eslintrc": "_eslint_light", + "eslintrc.js": "_eslint_light", + "eslintrc.cjs": "_eslint_light", + "eslintrc.yaml": "_eslint_light", + "eslintrc.yml": "_eslint_light", + "eslintrc.json": "_eslint_light", + "eslintignore": "_eslint_1_light", + "firebaserc": "_firebase_light", + "gitlab-ci.yml": "_gitlab_light", + "jshintrc": "_javascript_2_light", + "jscsrc": "_javascript_2_light", + "stylelintrc": "_stylelint_light", + "stylelintrc.json": "_stylelint_light", + "stylelintrc.yaml": "_stylelint_light", + "stylelintrc.yml": "_stylelint_light", + "stylelintrc.js": "_stylelint_light", + "stylelintignore": "_stylelint_1_light", + "direnv": "_config_light", + "env": "_config_light", + "static": "_config_light", + "editorconfig": "_config_light", + "slugignore": "_config_light", + "tmp": "_clock_1_light", + "htaccess": "_config_light", + "key": "_lock_light", + "cert": "_lock_light", + "cer": "_lock_light", + "crt": "_lock_light", + "pem": "_lock_light", + "ds_store": "_ignored_light" + }, + "languageIds": { + "argdown": "_argdown_light", + "bicep": "_bicep_light", + "elixir": "_elixir_light", + "elm": "_elm_light", + "erb": "_html_erb_light", + "github-issues": "_github_light", + "gradle": "_gradle_light", + "godot": "_godot_light", + "haml": "_haml_light", + "haskell": "_haskell_light", + "haxe": "_haxe_light", + "jinja": "_jinja_light", + "kotlin": "_kotlin_light", + "mustache": "_mustache_light", + "nunjucks": "_nunjucks_light", + "ocaml": "_ocaml_light", + "r": "_R_light", + "rescript": "_rescript_light", + "sass": "_sass_light", + "stylus": "_stylus_light", + "terraform": "_terraform_light", + "vala": "_vala_light", + "vue": "_vue_light" + }, + "fileNames": { + "mix": "_hex_light", + "commit_editmsg": "_git_light", + "merge_msg": "_git_light", + "karma.conf.js": "_karma_light", + "karma.conf.cjs": "_karma_light", + "karma.conf.mjs": "_karma_light", + "karma.conf.coffee": "_karma_light", + "readme.md": "_info_light", + "readme.txt": "_info_light", + "readme": "_info_light", + "changelog.md": "_clock_light", + "changelog.txt": "_clock_light", + "changelog": "_clock_light", + "changes.md": "_clock_light", + "changes.txt": "_clock_light", + "changes": "_clock_light", + "version.md": "_clock_light", + "version.txt": "_clock_light", + "version": "_clock_light", + "mvnw": "_maven_light", + "pom.xml": "_maven_light", + "gemfile": "_ruby_light", + "tsconfig.json": "_tsconfig_light", + "vite.config.js": "_vite_light", + "vite.config.ts": "_vite_light", + "vite.config.mjs": "_vite_light", + "vite.config.mts": "_vite_light", + "vite.config.cjs": "_vite_light", + "vite.config.cts": "_vite_light", + "swagger.json": "_json_1_light", + "swagger.yml": "_json_1_light", + "swagger.yaml": "_json_1_light", + "mime.types": "_config_light", + "jenkinsfile": "_jenkins_light", + "babel.config.js": "_babel_light", + "babel.config.json": "_babel_light", + "babel.config.cjs": "_babel_light", + "build": "_bazel_light", + "build.bazel": "_bazel_light", + "workspace": "_bazel_light", + "workspace.bazel": "_bazel_light", + "bower.json": "_bower_light", + "dockerfile": "_docker_light", + "docker-healthcheck": "_docker_2_light", + "docker-compose.yml": "_docker_3_light", + "docker-compose.yaml": "_docker_3_light", + "docker-compose.override.yml": "_docker_3_light", + "docker-compose.override.yaml": "_docker_3_light", + "eslint.config.js": "_eslint_light", + "firebase.json": "_firebase_light", + "geckodriver": "_firefox_light", + "gruntfile.js": "_grunt_light", + "gruntfile.babel.js": "_grunt_light", + "gruntfile.coffee": "_grunt_light", + "gulpfile": "_gulp_light", + "gulpfile.js": "_gulp_light", + "ionic.config.json": "_ionic_light", + "ionic.project": "_ionic_light", + "platformio.ini": "_platformio_light", + "rollup.config.js": "_rollup_light", + "sass-lint.yml": "_sass_light", + "stylelint.config.js": "_stylelint_light", + "stylelint.config.cjs": "_stylelint_light", + "stylelint.config.mjs": "_stylelint_light", + "yarn.clean": "_yarn_light", + "yarn.lock": "_yarn_light", + "webpack.config.js": "_webpack_light", + "webpack.config.cjs": "_webpack_light", + "webpack.config.mjs": "_webpack_light", + "webpack.config.ts": "_webpack_light", + "webpack.config.build.js": "_webpack_light", + "webpack.config.build.cjs": "_webpack_light", + "webpack.config.build.mjs": "_webpack_light", + "webpack.config.build.ts": "_webpack_light", + "webpack.common.js": "_webpack_light", + "webpack.common.cjs": "_webpack_light", + "webpack.common.mjs": "_webpack_light", + "webpack.common.ts": "_webpack_light", + "webpack.dev.js": "_webpack_light", + "webpack.dev.cjs": "_webpack_light", + "webpack.dev.mjs": "_webpack_light", + "webpack.dev.ts": "_webpack_light", + "webpack.prod.js": "_webpack_light", + "webpack.prod.cjs": "_webpack_light", + "webpack.prod.mjs": "_webpack_light", + "webpack.prod.ts": "_webpack_light", + "license": "_license_light", + "licence": "_license_light", + "license.txt": "_license_light", + "licence.txt": "_license_light", + "license.md": "_license_light", + "licence.md": "_license_light", + "copying": "_license_light", + "copying.txt": "_license_light", + "copying.md": "_license_light", + "compiling": "_license_1_light", + "compiling.txt": "_license_1_light", + "compiling.md": "_license_1_light", + "contributing": "_license_2_light", + "contributing.txt": "_license_2_light", + "contributing.md": "_license_2_light", + "makefile": "_makefile_light", + "qmakefile": "_makefile_1_light", + "omakefile": "_makefile_2_light", + "cmakelists.txt": "_makefile_3_light", + "procfile": "_heroku_light", + "npm-debug.log": "_npm_ignored_light" + } + }, + "version": "https://github.com/DecimalTurn/seti-ui/commit/7a8d51ccb32737be812549fe1c31fae8276b284f" } \ No newline at end of file diff --git a/package.json b/package.json index 0cca9b2..910e524 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,11 @@ "id": "vba-lsp", "label": "VBA Icons", "path": "icon-theme.json" + }, + { + "id": "vs-seti-vba", + "label": "Seti + VBA", + "path": "icons/theme-seti/icons/vs-seti-icon-theme.json" } ], "configurationDefaults": { From b0f8a543f5e93ff7b1c11dae30ec963dc948a54e Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Thu, 17 Apr 2025 02:46:07 +0000 Subject: [PATCH 04/93] Remove vba-lsp icon theme --- package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package.json b/package.json index 910e524..8ce047a 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,6 @@ } ], "iconThemes": [ - { - "id": "vba-lsp", - "label": "VBA Icons", - "path": "icon-theme.json" - }, { "id": "vs-seti-vba", "label": "Seti + VBA", From 69831e450b638aad29affc92b912b41101442228 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:46:55 +0800 Subject: [PATCH 05/93] Helper function to walk a directory and return a list of files --- server/src/utils/helpers.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index cea83e3..28b6e0a 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,3 +1,8 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { Services } from '../injection/services'; +import { fileURLToPath } from 'url'; + export class Dictionary extends Map { private defaultFactory: (...args: any) => V; @@ -34,4 +39,22 @@ export function ioEvents(): Promise { export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); +} + +export function walk(uri: string, pattern?: RegExp, files?: Map): Map +export function walk(dir: string, pattern?: RegExp, files?: Map): Map +export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { + Services.logger.debug(`Walking ${dirOrUri}`); + const dir = dirOrUri.startsWith('file://') ? fileURLToPath(dirOrUri) : dirOrUri; + + for (const name of fs.readdirSync(dir)) { + const p = path.join(dir, name); + if (fs.statSync(p).isDirectory()) { + walk(p, pattern, files); + } else if (pattern?.test(name) ?? true) { + Services.logger.debug(`Found ${p}`, 1); + files.set(p, fs.readFileSync(p, 'utf-8')); + } + } + return files; } \ No newline at end of file From 9df7996c1da1e29f068721eb32747914c9467b15 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:11 +0800 Subject: [PATCH 06/93] Removed "async" from methods to align with common TS convention --- server/src/project/document.ts | 10 ++--- server/src/project/parser/vbaListener.ts | 6 +-- server/src/project/parser/vbaParser.ts | 50 +++++------------------- 3 files changed, 18 insertions(+), 48 deletions(-) diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 31d3458..d50022c 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -173,14 +173,14 @@ export abstract class BaseProjectDocument { async formatParseVisit(token: CancellationToken): Promise { try { - return await (new SyntaxParser(Services.logger)).formatParseAsync(token, this); + return await (new SyntaxParser(Services.logger)).formatParse(token, this); } catch (e) { Services.logger.debug('caught doc'); throw e; } } - async parseAsync(token: CancellationToken): Promise { + async parse(token: CancellationToken): Promise { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); @@ -190,7 +190,7 @@ export abstract class BaseProjectDocument { } // Parse the document. - await (new SyntaxParser(Services.logger)).parseAsync(token, this); + await (new SyntaxParser(Services.logger)).parse(token, this); const projectScope = this.currentScope.project; const buildScope = projectScope?.isDirty ? projectScope : this.currentScope; buildScope.build(); @@ -203,7 +203,7 @@ export abstract class BaseProjectDocument { this._isBusy = false; }; - async formatParseAsync(token: CancellationToken): Promise { + async formatParse(token: CancellationToken): Promise { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); @@ -212,7 +212,7 @@ export abstract class BaseProjectDocument { } // Parse the document. - return await (new SyntaxParser(Services.logger)).formatParseAsync(token, this); + return await (new SyntaxParser(Services.logger)).formatParse(token, this); } /** diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index f33080f..78d861a 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -91,7 +91,7 @@ export class VbaListener extends vbaListener { this.document = document; } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaListener(document); await result.ensureHasSettingsAsync(); return result; @@ -207,7 +207,7 @@ export class VbaPreListener extends vbapreListener { this.common = new CommonParserCapability(document); } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaPreListener(document); await result.common.ensureHasSettingsAsync(); return result; @@ -277,7 +277,7 @@ export class VbaFmtListener extends vbafmtListener { this.indentationKeys = new Array(document.textDocument.lineCount); } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaFmtListener(document); await result.common.ensureHasSettingsAsync(); return result; diff --git a/server/src/project/parser/vbaParser.ts b/server/src/project/parser/vbaParser.ts index 9c02671..3f3b0bf 100644 --- a/server/src/project/parser/vbaParser.ts +++ b/server/src/project/parser/vbaParser.ts @@ -1,6 +1,6 @@ // Antlr import { ParseCancellationException, ParseTreeWalker } from 'antlr4ng'; -import { VbaErrorHandler, VbaFmtParser, VbaParser, VbaPreParser } from './vbaAntlr'; +import { VbaFmtParser, VbaParser, VbaPreParser } from './vbaAntlr'; import { VbaFmtListener, VbaListener, VbaPreListener } from './vbaListener'; // Project @@ -12,69 +12,39 @@ import { Logger } from '../../injection/interface'; export class SyntaxParser { constructor(private logger: Logger) { } - async parseAsync(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument): Promise { + async parse(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument): Promise { // Preparse the document if we find a precompiler statement. const regexp = new RegExp(/^\s*#If/gmi); let docText = document.textDocument.getText(); if (regexp.test(docText)) { this.logger.debug(`Beginning pre-parse.`); - const prelistener = await VbaPreListener.createAsync(document); + const prelistener = await VbaPreListener.create(document); const preparser = VbaPreParser.create(docText); - await this.parseDocumentAsync(token, prelistener, preparser); + await this.parseDocument(token, prelistener, preparser); docText = prelistener.text; this.logger.debug(`Completed pre-parse.`); } // Perform main document parse without compiler directives. this.logger.debug(`Beginning main parse.`); - const listener = await VbaListener.createAsync(document); + const listener = await VbaListener.create(document); const parser = VbaParser.create(docText); - await this.parseDocumentAsync(token, listener, parser); + await this.parseDocument(token, listener, parser); this.logger.debug(`Completed main parse.`); } - async formatParseAsync(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { + async formatParse(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { // Special parser focused on document format. this.logger.debug(`Beginning format parse.`); - const listener = await VbaFmtListener.createAsync(document); + const listener = await VbaFmtListener.create(document); const parser = VbaFmtParser.create(document.textDocument.getText(range)); - await this.parseDocumentAsync(token, listener, parser); + await this.parseDocument(token, listener, parser); this.logger.debug(`Completed format parse.`); return listener; } - // async formatVisit(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { - // // // Handle already cancelled. - // if (token.isCancellationRequested) { - // this.logger.debug(`Format visit cancelled before start.`); - // throw new ParseCancellationException(Error('Parse operation cancelled before it started.')); - // } - - // // Listen for cancellation event. - // token.onCancellationRequested(() => { - // this.logger.debug(`Format visit cancelled during run.`); - // throw new ParseCancellationException(new Error('Parse operation cancelled during parse.')); - // }); - - - // this.logger.debug(`Beginning format visit.`); - // const parser = VbaFmtParser.create(document.textDocument.getText(range)); - // const tree = parser.startRule(); - - // // const visitor = container.resolve(VbaFormatVisitor); - // // const visitor = forceAsync(container.resolve(VbaFormatVisitor)); - // // const visitor = forceAsync(new VbaFormatVisitor(this.logger)); - // const visitor = new VbaFormatVisitor(this.logger); - // visitor.token = token; - // await visitor.visit(tree); - // this.logger.debug(`Operation ${token.isCancellationRequested ? 'cancelled' : 'completed'}.`) - - // // Temporary call so I don't have to refactor everything just to call it. - // return await VbaFmtListener.createAsync(document); - // } - - private async parseDocumentAsync(token: CancellationToken, listener: VbaListener | VbaPreListener | VbaFmtListener, parser: VbaParser | VbaPreParser | VbaFmtParser) { + private async parseDocument(token: CancellationToken, listener: VbaListener | VbaPreListener | VbaFmtListener, parser: VbaParser | VbaPreParser | VbaFmtParser) { // Handle already cancelled. if (token.isCancellationRequested) { throw new ParseCancellationException(Error('Parse operation cancelled before it started.')); From 2f9edd0b6c506a3a58fb2a6732349465feb56d11 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:29 +0800 Subject: [PATCH 07/93] Inlined property --- server/src/server.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index f42fff0..fe370eb 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -70,7 +70,6 @@ export class LanguageServer implements ILanguageServer { export class LanguageServerConfiguration { - params: InitializeParams; capabilities: ServerCapabilities = { // Implemented documentSymbolProvider: true, @@ -111,9 +110,7 @@ export class LanguageServerConfiguration { experimental: undefined, }; - constructor(params: InitializeParams) { - this.params = params; - } + constructor(public params: InitializeParams) { } } From d5dff1017fd3e2cbb284ed5693b3e748dbe126bf Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:53 +0800 Subject: [PATCH 08/93] Added method to Workspace interface --- server/src/injection/interface.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index 5e11959..220c3ba 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -2,7 +2,7 @@ import { LanguageServerConfiguration } from '../server'; import { BaseProjectDocument } from '../project/document'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { VbaFmtListener } from '../project/parser/vbaListener'; -import { CancellationToken } from 'vscode-languageserver'; +import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; export interface Logger { error(msg: string, lvl?: number): void; @@ -40,4 +40,5 @@ export interface IWorkspace { parseDocument(projectDocument: BaseProjectDocument): Promise; openDocument(document: TextDocument): void; closeDocument(document: TextDocument): void; + addWorkspaceFolder(params: WorkspaceFolder): void; } \ No newline at end of file From 56c9d62a80222307661dd0d63da0049a6002bb4e Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:53:35 +0800 Subject: [PATCH 09/93] Code actions now properly async --- server/src/project/workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 14ab298..600092c 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -280,7 +280,7 @@ class WorkspaceEvents { connection.onHover(params => this.onHover(params)); connection.onDocumentFormatting(async (params, token) => await this.onDocumentFormatting(params, token)); connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); - connection.onCodeAction((params, token) => this.onCodeActionRequest(params, token)); + connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); if (hasWorkspaceConfigurationCapability(Services.server)) { connection.onFoldingRanges(async (params, token) => await cancellableOnFoldingRanges(params, token)); From c87d0d1fb7e84ca8ad75d90e0f9479f742191698 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:56:38 +0800 Subject: [PATCH 10/93] Removed the concept of an "active" document --- server/src/project/workspace.ts | 36 +++++++++------------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 600092c..656db2b 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -56,10 +56,8 @@ export interface ExtensionConfiguration { @injectable() export class Workspace implements IWorkspace { private events?: WorkspaceEvents; - private documents: BaseProjectDocument[] = []; private parseCancellationTokenSource?: CancellationTokenSource; - private _activeDocument?: BaseProjectDocument; private readonly _hasConfigurationCapability: boolean; private _extensionConfiguration?: ExtensionConfiguration; @@ -82,10 +80,6 @@ export class Workspace implements IWorkspace { })(); } - get activeDocument() { - return this._activeDocument; - } - constructor( @inject("_Connection") public readonly connection: _Connection, @inject("ILanguageServer") private server: ILanguageServer) { @@ -101,27 +95,16 @@ export class Workspace implements IWorkspace { Services.registerProjectScope(projectScope); } - activateDocument(document?: BaseProjectDocument) { - if (document) { - this._activeDocument = document; - } - } - - async parseDocument(document?: BaseProjectDocument) { - this.activateDocument(document); + async parseDocument(document: BaseProjectDocument) { + // this.activateDocument(document); this.parseCancellationTokenSource?.cancel(); this.parseCancellationTokenSource = new CancellationTokenSource(); - if (!this.activeDocument) { - this.logger.error('No active document.'); - return; - } - // Exceptions thrown by the parser should be ignored. try { - await this.activeDocument.parseAsync(this.parseCancellationTokenSource.token); - this.logger.info(`Parsed ${this.activeDocument.name}`); - this.connection.sendDiagnostics(this.activeDocument.languageServerDiagnostics()); + await document.parse(this.parseCancellationTokenSource.token); + this.logger.info(`Parsed ${document.name}`); + this.connection.sendDiagnostics(document.languageServerDiagnostics()); } catch (e) { if (e instanceof ParseCancellationException) { // Swallow cancellation exceptions. They're good. We like these. @@ -135,14 +118,12 @@ export class Workspace implements IWorkspace { this.parseCancellationTokenSource = undefined; } - async formatParseDocument(document: TextDocument): Promise { - this.parseCancellationTokenSource?.cancel(); - this.parseCancellationTokenSource = new CancellationTokenSource(); - + async formatParseDocument(document: TextDocument, token: CancellationToken): Promise { // Exceptions thrown by the parser should be ignored. let result: VbaFmtListener | undefined; try { - result = await this.activeDocument?.formatParseAsync(this.parseCancellationTokenSource.token); + const projectDocument = this.projectDocuments.get(document.uri); + result = await projectDocument?.formatParse(token); this.logger.info(`Formatted ${document.uri}`); } catch (e) { @@ -163,6 +144,7 @@ export class Workspace implements IWorkspace { const projectDocument = this.projectDocuments.get(document.uri); if (document.version === projectDocument?.version) { projectDocument.open(); + this.parseDocument(projectDocument); this.connection.sendDiagnostics(projectDocument.languageServerDiagnostics()); } } From 67eedb9a3aba9eb1ca557ce4d3d21bcb09a8d24f Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:57:20 +0800 Subject: [PATCH 11/93] Added support for parsing a whole workspace --- server/src/project/workspace.ts | 55 ++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 656db2b..b5218ea 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -20,13 +20,14 @@ import { SymbolInformation, TextDocuments, TextEdit, + WorkspaceFolder, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; import { BaseProjectDocument } from './document'; import { hasWorkspaceConfigurationCapability } from '../capabilities/workspaceFolder'; -import { sleep } from '../utils/helpers'; +import { sleep, walk } from '../utils/helpers'; import { ParseCancellationException } from 'antlr4ng'; import { getFormattingEdits } from './formatter'; import { VbaFmtListener } from './parser/vbaListener'; @@ -35,6 +36,7 @@ import { inject, injectable } from 'tsyringe'; import { Logger, ILanguageServer, IWorkspace } from '../injection/interface'; import { Services } from '../injection/services'; import { ItemType, ScopeItemCapability } from '../capabilities/capabilities'; +import { SyntaxParser } from './parser/vbaParser'; export interface ExtensionConfiguration { maxDocumentLines: number; @@ -95,6 +97,52 @@ export class Workspace implements IWorkspace { Services.registerProjectScope(projectScope); } + // Initially parse everything in the folder. + // ToDo: Handle removal of a workspace folder. + async addWorkspaceFolder(params: WorkspaceFolder): Promise { + this.logger.info(`Adding workspace: ${params.name}`); + const workspaceFiles = walk(params.uri, /\.(cls|bas|frm)$/i); + + // No need to continue if we have no files. + if (workspaceFiles.size === 0) { + return; + } + + // Set up parser and dummy token because we won't cancel this. + const parser = new SyntaxParser(this.logger); + const token = new CancellationTokenSource().token; + + // Handle each file in the workspace. + for (const [uri, file] of workspaceFiles) { + // Don't parse files that we're already tracking. + if (this.projectDocuments.has(uri)) { + this.logger.debug(`Skipping file: ${uri}`, 1); + continue; + } + + try { + // Read and parse the project document. + this.logger.debug(`Reading file: ${uri}`, 1); + const textDocument = TextDocument.create(`${uri}`, 'vba', 1, file); + const projectDocument = BaseProjectDocument.create(textDocument); + await parser.parse(token, projectDocument); + this.projectDocuments.set(uri, projectDocument); + this.logger.info(`Parsed ${projectDocument.name}`, 1); + } catch (e) { + // Log errors and anything else without failing. + this.logger.warn(`Failed to parse ${uri}`); + if (e instanceof Error) { + this.logger.stack(e); + } else { + this.logger.error(`Something went wrong: ${e}`); + } + } + } + + // Rebuild scopes from Project level. + Services.projectScope.build(); + } + async parseDocument(document: BaseProjectDocument) { // this.activateDocument(document); this.parseCancellationTokenSource?.cancel(); @@ -361,6 +409,11 @@ class WorkspaceEvents { ); connection.client.register(DidChangeConfigurationNotification.type, undefined); } + + // Read workspace folders if we have them. + Services.server.configuration?.params + .workspaceFolders?.forEach(folder => setImmediate(() => + Services.workspace.addWorkspaceFolder(folder))); } private async onDocumentFormatting(params: DocumentFormattingParams, token: CancellationToken): Promise { From 03f0e13ce4c74b9830b6df93c0a0bdc0fb098799 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:07:59 +0800 Subject: [PATCH 12/93] Only modules declare public members in the project scope --- server/src/capabilities/capabilities.ts | 14 +++++++++----- server/src/project/elements/module.ts | 6 +++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 23b0839..93adaee 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -163,7 +163,9 @@ export enum ItemType { APPLICATION, /** The user's project. */ PROJECT, - /** Class/Module/Form. */ + /** Class/Form. */ + CLASS, + /** Module. */ MODULE, /** Function declaration. */ FUNCTION, @@ -444,7 +446,7 @@ export class ScopeItemCapability { /** Returns the module this scope item falls under */ get module(): ScopeItemCapability | undefined { - if (this.type == ItemType.MODULE) { + if (this.type === ItemType.MODULE || this.type === ItemType.CLASS) { return this; } return this.parent?.module; @@ -519,10 +521,12 @@ export class ScopeItemCapability { /** * Visibility on a method-scoped variable does nothing but isn't invalid. * These should declare as if they're private and raise a warning. + * + * Only MODULE scoped items are accessible implicitly in PROJECT scope and therefore + * only they should be 'escalated' to that scope. */ - const getParent = (item: ScopeItemCapability): ScopeItemCapability => - (item.isPublicScope ? this.project : this) ?? this; + (item.isPublicScope && this.type === ItemType.MODULE ? this.project : this) ?? this; // Method-scoped variables are always private. if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { @@ -594,7 +598,7 @@ export class ScopeItemCapability { } // Handle module registration - if (item.type === ItemType.MODULE) { + if (item.type === ItemType.MODULE || item.type === ItemType.CLASS) { item.parent.modules ??= new Map(); item.parent.addItem(item.parent.modules, item); return item; diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index c8bdc2f..f868bcb 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -30,6 +30,7 @@ abstract class BaseModuleElement extends BaseIdenti abstract attrubutes: ParserRuleContext[]; abstract diagnosticCapability: DiagnosticCapability; abstract hasOptionExplicit: boolean; + abstract scopeItemCapability: ScopeItemCapability; settings: DocumentSettings; symbolInformationCapability: SymbolInformationCapability; @@ -38,7 +39,6 @@ abstract class BaseModuleElement extends BaseIdenti super(ctx, doc); this.settings = documentSettings; this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); } // Helpers @@ -104,6 +104,7 @@ abstract class BaseModuleElement extends BaseIdenti export class ModuleElement extends BaseModuleElement { diagnosticCapability: DiagnosticCapability; identifierCapability: IdentifierCapability; + scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; hasOptionExplicit: boolean; @@ -112,6 +113,7 @@ export class ModuleElement extends BaseModuleElement { super(ctx, doc, documentSettings, SymbolKind.File); this.attrubutes = ctx.proceduralModuleHeader().proceduralModuleAttr(); this.diagnosticCapability = new DiagnosticCapability(this); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx .proceduralModuleBody() @@ -142,6 +144,7 @@ export class ModuleElement extends BaseModuleElement { export class ClassElement extends BaseModuleElement { diagnosticCapability: DiagnosticCapability; identifierCapability: IdentifierCapability; + scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; hasOptionExplicit: boolean; @@ -154,6 +157,7 @@ export class ClassElement extends BaseModuleElement { ctx.classModuleHeader().ignoredClassAttr() ].flat(); this.diagnosticCapability = new DiagnosticCapability(this); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.CLASS); this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx .classModuleBody() From 8535208ce4b7e2c3b13886965273f20d3ca377e9 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:31:32 +0800 Subject: [PATCH 13/93] walk function now returns URIs if passed a URI rather than paths --- server/src/utils/helpers.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 28b6e0a..d1dee2f 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { Services } from '../injection/services'; -import { fileURLToPath } from 'url'; +import { fileURLToPath, pathToFileURL } from 'url'; export class Dictionary extends Map { private defaultFactory: (...args: any) => V; @@ -45,7 +45,8 @@ export function walk(uri: string, pattern?: RegExp, files?: Map) export function walk(dir: string, pattern?: RegExp, files?: Map): Map export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { Services.logger.debug(`Walking ${dirOrUri}`); - const dir = dirOrUri.startsWith('file://') ? fileURLToPath(dirOrUri) : dirOrUri; + const isUri = dirOrUri.startsWith('file://'); + const dir = isUri ? fileURLToPath(dirOrUri) : dirOrUri; for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name); @@ -53,7 +54,7 @@ export function walk(dirOrUri: string, pattern?: RegExp, files: Map Date: Tue, 22 Apr 2025 08:47:03 +0800 Subject: [PATCH 14/93] Can now log error stack as warning --- server/src/injection/interface.ts | 2 +- server/src/utils/logger.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index 220c3ba..ae15b1f 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -10,7 +10,7 @@ export interface Logger { info(msg: string, lvl?: number): void; log(msg: string, lvl?: number): void; debug(msg: string, lvl?: number): void; - stack(e: Error): void; + stack(e: Error, onlyWarn?: boolean): void; } export interface ClientConfiguration { diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index 2a79dd9..0608125 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -28,7 +28,7 @@ export class LspLogger implements Logger { info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl); log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl); debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl); - stack = (e: Error) => this.emit(LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); + stack = (e: Error, onlyWarn?: boolean) => this.emit(onlyWarn ? LogLevel.warn : LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); private emit(logLevel: LogLevel, msgText: string, msgLevel?: number): void { // Async get the configuration and then emit. From 440999871a18103334b6834cc181d9c6f3f4d0b6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 08:47:59 +0800 Subject: [PATCH 15/93] Prevent async related errors on shut down --- server/src/server.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/server.ts b/server/src/server.ts index fe370eb..d4cb7a9 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -45,6 +45,10 @@ export class LanguageServer implements ILanguageServer { activateSemanticTokenProvider(result); return result; }); + // Register shutdown actions + this.connection.onShutdown(() => {}); + this.connection.onExit(() => process.exit(0)); + // Register for client configuration notification changes. this.connection.onInitialized(() => { this.connection.client.register(DidChangeConfigurationNotification.type, undefined); }); this.connection.onDidChangeConfiguration(() => this._clientConfiguration = undefined); From 62e044d51890cd0bac53901b86b15b9b6a6ef672 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:10:12 +0800 Subject: [PATCH 16/93] Added error handling and uri conversion to walk function --- server/src/utils/helpers.ts | 49 +++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index d1dee2f..8d826c1 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -41,20 +41,53 @@ export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -export function walk(uri: string, pattern?: RegExp, files?: Map): Map -export function walk(dir: string, pattern?: RegExp, files?: Map): Map -export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { +/** + * Recursively walks a directory structure and returns a map of file path to string content. + * @param dirOrUri A directory as a path or 'file://' uri. + * @param pattern A predicate to filter results. + * @param files Used for internal recursive calls. + * @param wasUriConverted Used for internal recursive calls. + */ +export function walk(uri: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map +export function walk(dir: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map +export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map(), wasUriConverted?: boolean): Map { Services.logger.debug(`Walking ${dirOrUri}`); - const isUri = dirOrUri.startsWith('file://'); - const dir = isUri ? fileURLToPath(dirOrUri) : dirOrUri; + // Check whether the original directory was a dir or uri and convert as required. + const shouldConvertUri = wasUriConverted || (wasUriConverted === undefined && dirOrUri.startsWith('file://')); + const dir = (wasUriConverted === undefined && shouldConvertUri) + ? fileURLToPath(dirOrUri) + : dirOrUri; + + // Walk the contents of the directory. for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name); - if (fs.statSync(p).isDirectory()) { - walk(p, pattern, files); + + // Check if we have a directory. This can occasionally throw at an OS level. + let pIsDirectory: boolean | undefined; + try { pIsDirectory = fs.statSync(p).isDirectory(); } + catch (e) { + Services.logger.warn(`The OS threw an exception checking whether ${p} is a directory.`); + if (e instanceof Error) { + Services.logger.stack(e, true); + continue; + } + } + if (pIsDirectory) { + // Recursive call for directories. + walk(p, pattern, files, shouldConvertUri); } else if (pattern?.test(name) ?? true) { + // Track files that match the pattern. Services.logger.debug(`Found ${p}`, 1); - files.set(isUri ? pathToFileURL(p).href : p, fs.readFileSync(p, 'utf-8')); + let fileContent = ''; + try { fileContent = fs.readFileSync(p, 'utf-8'); } + catch (e) { + Services.logger.error(`The OS threw an exception reading ${p}.`); + if (e instanceof Error) { + Services.logger.stack(e); + } + } + files.set(shouldConvertUri ? pathToFileURL(p).href : p, fileContent); } } return files; From 8770a10fd346bd3b5a48cd57f3427ba16be77ecd Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:24:15 +0800 Subject: [PATCH 17/93] Better way of logging stack traces --- server/src/injection/interface.ts | 12 ++++++------ server/src/project/workspace.ts | 15 +++------------ server/src/utils/helpers.ts | 15 ++------------- server/src/utils/logger.ts | 19 ++++++++++++------- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index ae15b1f..8effea3 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -5,12 +5,12 @@ import { VbaFmtListener } from '../project/parser/vbaListener'; import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; export interface Logger { - error(msg: string, lvl?: number): void; - warn(msg: string, lvl?: number): void; - info(msg: string, lvl?: number): void; - log(msg: string, lvl?: number): void; - debug(msg: string, lvl?: number): void; - stack(e: Error, onlyWarn?: boolean): void; + error(msg: string, lvl?: number, e?: unknown): void; + warn(msg: string, lvl?: number, e?: unknown): void; + info(msg: string, lvl?: number, e?: unknown): void; + log(msg: string, lvl?: number, e?: unknown): void; + debug(msg: string, lvl?: number, e?: unknown): void; + stack(e: Error): void; } export interface ClientConfiguration { diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index b5218ea..16b68ef 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -130,12 +130,7 @@ export class Workspace implements IWorkspace { this.logger.info(`Parsed ${projectDocument.name}`, 1); } catch (e) { // Log errors and anything else without failing. - this.logger.warn(`Failed to parse ${uri}`); - if (e instanceof Error) { - this.logger.stack(e); - } else { - this.logger.error(`Something went wrong: ${e}`); - } + this.logger.error(`Failed to parse ${uri}`, 0, e); } } @@ -156,10 +151,8 @@ export class Workspace implements IWorkspace { } catch (e) { if (e instanceof ParseCancellationException) { // Swallow cancellation exceptions. They're good. We like these. - } else if (e instanceof Error) { - this.logger.stack(e); } else { - this.logger.error('Something went wrong.'); + this.logger.error('Something went wrong.', 0, e); } } @@ -177,10 +170,8 @@ export class Workspace implements IWorkspace { catch (e) { if (e instanceof ParseCancellationException) { this.logger.debug('Parse cancelled successfully.'); - } else if (e instanceof Error) { - this.logger.stack(e); } else { - this.logger.error(`Parse failed: ${e}`); + this.logger.error(`Parse failed.`, 0, e); } } diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 8d826c1..82b2982 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -66,13 +66,7 @@ export function walk(dirOrUri: string, pattern?: RegExp, files: Map this.emit(LogLevel.error, msg, lvl); - warn = (msg: string, lvl?: number) => this.emit(LogLevel.warn, msg, lvl); - info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl); - log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl); - debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl); - stack = (e: Error, onlyWarn?: boolean) => this.emit(onlyWarn ? LogLevel.warn : LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); + error = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.error, msg, lvl); + warn = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.warn, msg, lvl); + info = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.info, msg, lvl); + log = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.log, msg, lvl); + debug = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.debug, msg, lvl); + stack = (e: Error, logLevel?: LogLevel) => this.emit(logLevel ?? LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); - private emit(logLevel: LogLevel, msgText: string, msgLevel?: number): void { + private emit(logLevel: LogLevel, msgText: string, msgLevel?: number, e?: unknown): void { // Async get the configuration and then emit. this.server.clientConfiguration.then(config => { try { @@ -54,6 +54,11 @@ export class LspLogger implements Logger { message: msgText, level: msgLevel ?? 0 }); + + // If we have an error, then log stack trace too. + if (e instanceof Error) { + this.stack(e, logLevel); + } }); } From 014070f5e3ffa286d7a80c8c616ee38508cbd6ff Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:26:46 +0800 Subject: [PATCH 18/93] Refactor tests for class and module --- client/src/test/codeActions.test.ts | 2 +- client/src/test/diagnostics.test.ts | 48 ++++++++++++++------------- client/src/test/foldingRanges.test.ts | 2 +- client/src/test/formatting.test.ts | 4 ++- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/client/src/test/codeActions.test.ts b/client/src/test/codeActions.test.ts index 07c78b5..6412459 100644 --- a/client/src/test/codeActions.test.ts +++ b/client/src/test/codeActions.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get code actions', () => { +suite('Should get module code actions', () => { test('actions.module.missingOptionExplicitCodeAction', async () => { const docUri = getDocUri('EmptyModule.bas'); const edits = new vscode.WorkspaceEdit(); diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts index 18f1ebf..e72603c 100644 --- a/client/src/test/diagnostics.test.ts +++ b/client/src/test/diagnostics.test.ts @@ -8,18 +8,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get diagnostics', () => { - test('diagnostics.class.missingNameAttributeError', async () => { - await testDiagnostics(getDocUri('DiagnosticsMissingAttributeClass.cls'), [ - { - message: 'Module missing attribute VB_NAME.', - range: toRange(4, 3, 4, 3), - severity: vscode.DiagnosticSeverity.Error, - source: 'ex' - } - ]); - }); - +suite('Should get module diagnostics', () => { test('diagnostics.module.missingNameAttributeError', async () => { await testDiagnostics(getDocUri('DiagnosticsMissingAttributeModule.bas'), [ { @@ -31,17 +20,6 @@ suite('Should get diagnostics', () => { ]); }); - test('diagnostics.class.noOptionExplicitWarning', async () => { - await testDiagnostics(getDocUri('EmptyClass.cls'), [ - { - message: 'Option Explicit is missing from module header.', - range: toRange(11, 1, 11, 1), - severity: vscode.DiagnosticSeverity.Warning, - source: 'ex' - } - ]); - }); - test('diagnostics.module.noOptionExplicitWarning', async () => { await testDiagnostics(getDocUri('EmptyModule.bas'), [ { @@ -100,6 +78,30 @@ suite('Should get diagnostics', () => { } ]); }); +}); + +suite('Should get class diagnostics', () => { + test('diagnostics.class.missingNameAttributeError', async () => { + await testDiagnostics(getDocUri('DiagnosticsMissingAttributeClass.cls'), [ + { + message: 'Module missing attribute VB_NAME.', + range: toRange(4, 3, 4, 3), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } + ]); + }); + + test('diagnostics.class.noOptionExplicitWarning', async () => { + await testDiagnostics(getDocUri('EmptyClass.cls'), [ + { + message: 'Option Explicit is missing from module header.', + range: toRange(11, 1, 11, 1), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + } + ]); + }); test('diagnostics.class.generalDiagnostics', async () => { // Don't seem to be able to sort these. Good luck! diff --git a/client/src/test/foldingRanges.test.ts b/client/src/test/foldingRanges.test.ts index 6b80841..835841e 100644 --- a/client/src/test/foldingRanges.test.ts +++ b/client/src/test/foldingRanges.test.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; -suite('Should get folding ranges', () => { +suite('Should get class folding ranges', () => { test('formatting.class.template', async () => { const subFoo = {start: 23, end: 42}; const subBar = {start: 44, end: 58}; diff --git a/client/src/test/formatting.test.ts b/client/src/test/formatting.test.ts index ce539d1..40443d4 100644 --- a/client/src/test/formatting.test.ts +++ b/client/src/test/formatting.test.ts @@ -3,7 +3,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get text edits', () => { +suite('Should get class text edits', () => { test('formatting.class.template', async () => { await testTextEdits(getDocUri('FormatTemplateClass.cls'), [ {range: toRange(3, 0, 3, 0), newText: ' '}, @@ -12,7 +12,9 @@ suite('Should get text edits', () => { {range: toRange(8, 0, 8, 8), newText: ''} ]); }); +}); +suite('Should get module text edits', () => { test('formatting.module.directives', async () => { await testTextEdits(getDocUri('FormatPrecompilerDirectives.bas'), [ // Staggered (half) indentation. From 6ada07ca28695431cadad05de1cf209a9f000e5a Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:30:02 -0400 Subject: [PATCH 19/93] Duplicate seti icon theme from VS Code --- icons/theme-seti/.vscodeignore | 4 + icons/theme-seti/CONTRIBUTING.md | 33 + icons/theme-seti/README.md | 32 + icons/theme-seti/ThirdPartyNotices.txt | 32 + icons/theme-seti/build/update-icon-theme.js | 473 ++++ icons/theme-seti/cgmanifest.json | 16 + icons/theme-seti/icons/preview.html | 104 + .../icons/seti-circular-128x128.png | Bin 0 -> 4814 bytes icons/theme-seti/icons/seti.woff | Bin 0 -> 37300 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 2406 +++++++++++++++++ icons/theme-seti/package.json | 30 + icons/theme-seti/package.nls.json | 5 + 12 files changed, 3135 insertions(+) create mode 100644 icons/theme-seti/.vscodeignore create mode 100644 icons/theme-seti/CONTRIBUTING.md create mode 100644 icons/theme-seti/README.md create mode 100644 icons/theme-seti/ThirdPartyNotices.txt create mode 100644 icons/theme-seti/build/update-icon-theme.js create mode 100644 icons/theme-seti/cgmanifest.json create mode 100644 icons/theme-seti/icons/preview.html create mode 100644 icons/theme-seti/icons/seti-circular-128x128.png create mode 100644 icons/theme-seti/icons/seti.woff create mode 100644 icons/theme-seti/icons/vs-seti-icon-theme.json create mode 100644 icons/theme-seti/package.json create mode 100644 icons/theme-seti/package.nls.json diff --git a/icons/theme-seti/.vscodeignore b/icons/theme-seti/.vscodeignore new file mode 100644 index 0000000..25699ef --- /dev/null +++ b/icons/theme-seti/.vscodeignore @@ -0,0 +1,4 @@ +build/** +cgmanifest.json +icons/preview.html +CONTRIBUTING.md diff --git a/icons/theme-seti/CONTRIBUTING.md b/icons/theme-seti/CONTRIBUTING.md new file mode 100644 index 0000000..df0501d --- /dev/null +++ b/icons/theme-seti/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# theme-seti + +This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). + +## Previewing icons + +There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. +To view this, it needs to be hosted by a web server. The easiest way is to open the file with the `Open with Live Server` command from the [Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer). + + +## Updating icons + +- Make a PR against https://github.com/jesseweed/seti-ui with your icon changes. +- Once accepted there, ping us or make a PR yourself that updates the theme and font here + +To adopt the latest changes from https://github.com/jesseweed/seti-ui: + +- have the main branches of `https://github.com/jesseweed/seti-ui` and `https://github.com/microsoft/vscode` cloned in the same parent folder +- in the `seti-ui` folder, run `npm install` and `npm run prepublishOnly`. This will generate updated icons and fonts. +- in the `vscode/extensions/theme-seti` folder run `npm run update`. This will launch the [icon theme update script](build/update-icon-theme.js) that updates the theme as well as the font based on content in `seti-ui`. +- to test the icon theme, look at the icon preview as described above. +- when done, create a PR with the changes in https://github.com/microsoft/vscode. +Add a screenshot of the preview page to accompany it. + + +### Languages not shipped with `vscode` + +Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. + +These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). + +Please try and keep this list in alphabetical order! Thank you. + diff --git a/icons/theme-seti/README.md b/icons/theme-seti/README.md new file mode 100644 index 0000000..dcd9ba9 --- /dev/null +++ b/icons/theme-seti/README.md @@ -0,0 +1,32 @@ +# theme-seti + +This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). + +## Updating icons + +There is script that can be used to update icons, [./build/update-icon-theme.js](build/update-icon-theme.js). + +To run this script, run `npm run update` from the `theme-seti` directory. + +This can be run in one of two ways: looking at a local copy of `seti-ui` for icons, or getting them straight from GitHub. + +If you want to run it from a local copy of `seti-ui`, first clone [`seti-ui`](https://github.com/jesseweed/seti-ui) to the folder next to your `vscode` repo (from the `theme-seti` directory, `../../`). +Then, inside the `set-ui` directory, run `npm install` followed by `npm run prepublishOnly`. This will generate updated icons. + +If you want to download the icons straight from GitHub, change the `FROM_DISK` variable to `false` inside of `update-icon-theme.js`. + +### Languages not shipped with `vscode` + +Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. + +These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). + +Please try and keep this list in alphabetical order! Thank you. + +## Previewing icons + +There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. +Note that to view this, it needs to be hosted by a web server. + +When updating icons, it is always a good idea to make sure that they work properly by looking at this page. +When submitting a PR that updates these icons, a screenshot of the preview page should accompany it. diff --git a/icons/theme-seti/ThirdPartyNotices.txt b/icons/theme-seti/ThirdPartyNotices.txt new file mode 100644 index 0000000..29cbcd4 --- /dev/null +++ b/icons/theme-seti/ThirdPartyNotices.txt @@ -0,0 +1,32 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-theme-seti + +This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for +the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +by implication, estoppel or otherwise.† + +1. Seti UI - A subtle dark colored UI theme for Atom. (https://github.com/jesseweed/seti-ui) + +Copyright (c) 2014 Jesse Weed + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/icons/theme-seti/build/update-icon-theme.js b/icons/theme-seti/build/update-icon-theme.js new file mode 100644 index 0000000..2b16374 --- /dev/null +++ b/icons/theme-seti/build/update-icon-theme.js @@ -0,0 +1,473 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const https = require('https'); +const url = require('url'); +const minimatch = require('minimatch'); + +// list of languagesId not shipped with VSCode. The information is used to associate an icon with a language association +// Please try and keep this list in alphabetical order! Thank you. +const nonBuiltInLanguages = { // { fileNames, extensions } + "argdown": { extensions: ['ad', 'adown', 'argdown', 'argdn'] }, + "bicep": { extensions: ['bicep'] }, + "elixir": { extensions: ['ex'] }, + "elm": { extensions: ['elm'] }, + "erb": { extensions: ['erb', 'rhtml', 'html.erb'] }, + "github-issues": { extensions: ['github-issues'] }, + "gradle": { extensions: ['gradle'] }, + "godot": { extensions: ['gd', 'godot', 'tres', 'tscn'] }, + "haml": { extensions: ['haml'] }, + "haskell": { extensions: ['hs'] }, + "haxe": { extensions: ['hx'] }, + "jinja": { extensions: ['jinja'] }, + "kotlin": { extensions: ['kt'] }, + "mustache": { extensions: ['mustache', 'mst', 'mu', 'stache'] }, + "nunjucks": { extensions: ['nunjucks', 'nunjs', 'nunj', 'nj', 'njk', 'tmpl', 'tpl'] }, + "ocaml": { extensions: ['ml', 'mli', 'mll', 'mly', 'eliom', 'eliomi'] }, + "puppet": { extensions: ['puppet'] }, + "r": { extensions: ['r', 'rhistory', 'rprofile', 'rt'] }, + "rescript": { extensions: ['res', 'resi'] }, + "sass": { extensions: ['sass'] }, + "stylus": { extensions: ['styl'] }, + "terraform": { extensions: ['tf', 'tfvars', 'hcl'] }, + "todo": { fileNames: ['todo'] }, + "vala": { extensions: ['vala'] }, + "vue": { extensions: ['vue'] } +}; + +// list of languagesId that inherit the icon from another language +const inheritIconFromLanguage = { + "jsonc": 'json', + "jsonl": 'json', + "postcss": 'css', + "django-html": 'html', + "blade": 'php' +}; + +const ignoreExtAssociation = { + "properties": true +}; + +const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo + +let font, fontMappingsFile, fileAssociationFile, colorsFile; +if (!FROM_DISK) { + font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; +} else { + font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; + fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; + fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; + colorsFile = '../../../seti-ui/styles/ui-variables.less'; +} + +function getCommitSha(repoId) { + const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; + return download(commitInfo).then(function (content) { + try { + const lastCommit = JSON.parse(content); + return Promise.resolve({ + commitSha: lastCommit.sha, + commitDate: lastCommit.commit.author.date + }); + } catch (e) { + console.error('Failed parsing ' + content); + return Promise.resolve(null); + } + }, function () { + console.error('Failed loading ' + commitInfo); + return Promise.resolve(null); + }); +} + +function download(source) { + if (source.startsWith('.')) { + return readFile(source); + } + return new Promise((c, e) => { + const _url = url.parse(source); + const options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' } }; + let content = ''; + https.get(options, function (response) { + response.on('data', function (data) { + content += data.toString(); + }).on('end', function () { + c(content); + }); + }).on('error', function (err) { + e(err.message); + }); + }); +} + +function readFile(fileName) { + return new Promise((c, e) => { + fs.readFile(fileName, function (err, data) { + if (err) { + e(err); + } else { + c(data.toString()); + } + }); + }); +} + +function downloadBinary(source, dest) { + if (source.startsWith('.')) { + return copyFile(source, dest); + } + + return new Promise((c, e) => { + https.get(source, function (response) { + switch (response.statusCode) { + case 200: { + const file = fs.createWriteStream(dest); + response.on('data', function (chunk) { + file.write(chunk); + }).on('end', function () { + file.end(); + c(null); + }).on('error', function (err) { + fs.unlink(dest); + e(err.message); + }); + break; + } + case 301: + case 302: + case 303: + case 307: + console.log('redirect to ' + response.headers.location); + downloadBinary(response.headers.location, dest).then(c, e); + break; + default: + e(new Error('Server responded with status code ' + response.statusCode)); + } + }); + }); +} + +function copyFile(fileName, dest) { + return new Promise((c, e) => { + let cbCalled = false; + function handleError(err) { + if (!cbCalled) { + e(err); + cbCalled = true; + } + } + const rd = fs.createReadStream(fileName); + rd.on("error", handleError); + const wr = fs.createWriteStream(dest); + wr.on("error", handleError); + wr.on("close", function () { + if (!cbCalled) { + c(); + cbCalled = true; + } + }); + rd.pipe(wr); + }); +} + +function darkenColor(color) { + let res = '#'; + for (let i = 1; i < 7; i += 2) { + const newVal = Math.round(parseInt('0x' + color.substr(i, 2), 16) * 0.9); + const hex = newVal.toString(16); + if (hex.length === 1) { + res += '0'; + } + res += hex; + } + return res; +} + +function mergeMapping(to, from, property) { + if (from[property]) { + if (to[property]) { + to[property].push(...from[property]); + } else { + to[property] = from[property]; + } + } +} + +function getLanguageMappings() { + const langMappings = {}; + const allExtensions = fs.readdirSync('..'); + for (let i = 0; i < allExtensions.length; i++) { + const dirPath = path.join('..', allExtensions[i], 'package.json'); + if (fs.existsSync(dirPath)) { + const content = fs.readFileSync(dirPath).toString(); + const jsonContent = JSON.parse(content); + const languages = jsonContent.contributes && jsonContent.contributes.languages; + if (Array.isArray(languages)) { + for (let k = 0; k < languages.length; k++) { + const languageId = languages[k].id; + if (languageId) { + const extensions = languages[k].extensions; + const mapping = {}; + if (Array.isArray(extensions)) { + mapping.extensions = extensions.map(function (e) { return e.substr(1).toLowerCase(); }); + } + const filenames = languages[k].filenames; + if (Array.isArray(filenames)) { + mapping.fileNames = filenames.map(function (f) { return f.toLowerCase(); }); + } + const filenamePatterns = languages[k].filenamePatterns; + if (Array.isArray(filenamePatterns)) { + mapping.filenamePatterns = filenamePatterns.map(function (f) { return f.toLowerCase(); }); + } + const existing = langMappings[languageId]; + + if (existing) { + // multiple contributions to the same language + // give preference to the contribution wth the configuration + if (languages[k].configuration) { + mergeMapping(mapping, existing, 'extensions'); + mergeMapping(mapping, existing, 'fileNames'); + mergeMapping(mapping, existing, 'filenamePatterns'); + langMappings[languageId] = mapping; + } else { + mergeMapping(existing, mapping, 'extensions'); + mergeMapping(existing, mapping, 'fileNames'); + mergeMapping(existing, mapping, 'filenamePatterns'); + } + } else { + langMappings[languageId] = mapping; + } + } + } + } + } + } + for (const languageId in nonBuiltInLanguages) { + langMappings[languageId] = nonBuiltInLanguages[languageId]; + } + return langMappings; +} + +exports.copyFont = function () { + return downloadBinary(font, './icons/seti.woff'); +}; + +exports.update = function () { + + console.log('Reading from ' + fontMappingsFile); + const def2Content = {}; + const ext2Def = {}; + const fileName2Def = {}; + const def2ColorId = {}; + const colorId2Value = {}; + const lang2Def = {}; + + function writeFileIconContent(info) { + const iconDefinitions = {}; + const allDefs = Object.keys(def2Content).sort(); + + for (let i = 0; i < allDefs.length; i++) { + const def = allDefs[i]; + const entry = { fontCharacter: def2Content[def] }; + const colorId = def2ColorId[def]; + if (colorId) { + const colorValue = colorId2Value[colorId]; + if (colorValue) { + entry.fontColor = colorValue; + + const entryInverse = { fontCharacter: entry.fontCharacter, fontColor: darkenColor(colorValue) }; + iconDefinitions[def + '_light'] = entryInverse; + } + } + iconDefinitions[def] = entry; + } + + function getInvertSet(input) { + const result = {}; + for (const assoc in input) { + const invertDef = input[assoc] + '_light'; + if (iconDefinitions[invertDef]) { + result[assoc] = invertDef; + } + } + return result; + } + + const res = { + information_for_contributors: [ + 'This file has been generated from data in https://github.com/jesseweed/seti-ui', + '- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less', + '- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less', + '- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less', + 'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.', + 'Once accepted there, we are happy to receive an update request.', + ], + fonts: [{ + id: "seti", + src: [{ "path": "./seti.woff", "format": "woff" }], + weight: "normal", + style: "normal", + size: "150%" + }], + iconDefinitions: iconDefinitions, + // folder: "_folder", + file: "_default", + fileExtensions: ext2Def, + fileNames: fileName2Def, + languageIds: lang2Def, + light: { + file: "_default_light", + fileExtensions: getInvertSet(ext2Def), + languageIds: getInvertSet(lang2Def), + fileNames: getInvertSet(fileName2Def) + }, + version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha, + }; + + const path = './icons/vs-seti-icon-theme.json'; + fs.writeFileSync(path, JSON.stringify(res, null, '\t')); + console.log('written ' + path); + } + + + let match; + + return download(fontMappingsFile).then(function (content) { + const regex = /@([\w-]+):\s*'(\\E[0-9A-F]+)';/g; + const contents = {}; + while ((match = regex.exec(content)) !== null) { + contents[match[1]] = match[2]; + } + + return download(fileAssociationFile).then(function (content) { + const regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.+]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; + while ((match = regex2.exec(content)) !== null) { + const pattern = match[1]; + let def = '_' + match[2]; + const colorId = match[3]; + let storedColorId = def2ColorId[def]; + let i = 1; + while (storedColorId && colorId !== storedColorId) { // different colors for the same def? + def = `_${match[2]}_${i}`; + storedColorId = def2ColorId[def]; + i++; + } + if (!def2ColorId[def]) { + def2ColorId[def] = colorId; + def2Content[def] = contents[match[2]]; + } + + if (def === '_default') { + continue; // no need to assign default color. + } + if (pattern[0] === '.') { + ext2Def[pattern.substr(1).toLowerCase()] = def; + } else { + fileName2Def[pattern.toLowerCase()] = def; + } + } + // replace extensions for languageId + const langMappings = getLanguageMappings(); + for (let lang in langMappings) { + const mappings = langMappings[lang]; + const exts = mappings.extensions || []; + const fileNames = mappings.fileNames || []; + const filenamePatterns = mappings.filenamePatterns || []; + let preferredDef = null; + // use the first file extension association for the preferred definition + for (let i1 = 0; i1 < exts.length && !preferredDef; i1++) { + preferredDef = ext2Def[exts[i1]]; + } + // use the first file name association for the preferred definition, if not availbale + for (let i1 = 0; i1 < fileNames.length && !preferredDef; i1++) { + preferredDef = fileName2Def[fileNames[i1]]; + } + for (let i1 = 0; i1 < filenamePatterns.length && !preferredDef; i1++) { + let pattern = filenamePatterns[i1]; + for (const name in fileName2Def) { + if (minimatch(name, pattern)) { + preferredDef = fileName2Def[name]; + break; + } + } + } + if (preferredDef) { + lang2Def[lang] = preferredDef; + if (!nonBuiltInLanguages[lang] && !inheritIconFromLanguage[lang]) { + for (let i2 = 0; i2 < exts.length; i2++) { + // remove the extension association, unless it is different from the preferred + if (ext2Def[exts[i2]] === preferredDef || ignoreExtAssociation[exts[i2]]) { + delete ext2Def[exts[i2]]; + } + } + for (let i2 = 0; i2 < fileNames.length; i2++) { + // remove the fileName association, unless it is different from the preferred + if (fileName2Def[fileNames[i2]] === preferredDef) { + delete fileName2Def[fileNames[i2]]; + } + } + for (let i2 = 0; i2 < filenamePatterns.length; i2++) { + let pattern = filenamePatterns[i2]; + // remove the filenamePatterns association, unless it is different from the preferred + for (const name in fileName2Def) { + if (minimatch(name, pattern) && fileName2Def[name] === preferredDef) { + delete fileName2Def[name]; + } + } + } + } + } + } + for (const lang in inheritIconFromLanguage) { + const superLang = inheritIconFromLanguage[lang]; + const def = lang2Def[superLang]; + if (def) { + lang2Def[lang] = def; + } else { + console.log('skipping icon def for ' + lang + ': no icon for ' + superLang + ' defined'); + } + + } + + + return download(colorsFile).then(function (content) { + const regex3 = /(@[\w-]+):\s*(#[0-9a-z]+)/g; + while ((match = regex3.exec(content)) !== null) { + colorId2Value[match[1]] = match[2]; + } + return getCommitSha('jesseweed/seti-ui').then(function (info) { + try { + writeFileIconContent(info); + + const cgmanifestPath = './cgmanifest.json'; + const cgmanifest = fs.readFileSync(cgmanifestPath).toString(); + const cgmanifestContent = JSON.parse(cgmanifest); + cgmanifestContent['registrations'][0]['component']['git']['commitHash'] = info.commitSha; + fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); + console.log('updated ' + cgmanifestPath); + + console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + + } catch (e) { + console.error(e); + } + }); + }); + }); + }, console.error); +}; + +if (path.basename(process.argv[1]) === 'update-icon-theme.js') { + exports.copyFont().then(() => exports.update()); +} + + + diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json new file mode 100644 index 0000000..8d50dd2 --- /dev/null +++ b/icons/theme-seti/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "seti-ui", + "repositoryUrl": "https://github.com/jesseweed/seti-ui", + "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} \ No newline at end of file diff --git a/icons/theme-seti/icons/preview.html b/icons/theme-seti/icons/preview.html new file mode 100644 index 0000000..84b5166 --- /dev/null +++ b/icons/theme-seti/icons/preview.html @@ -0,0 +1,104 @@ + + + + + + seti font preview + + + + + + + + diff --git a/icons/theme-seti/icons/seti-circular-128x128.png b/icons/theme-seti/icons/seti-circular-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe533bba1e34ed5618c971c93514e7a20937b67 GIT binary patch literal 4814 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 Js~^}!{tw;l6mb9m literal 0 HcmV?d00001 diff --git a/icons/theme-seti/icons/seti.woff b/icons/theme-seti/icons/seti.woff new file mode 100644 index 0000000000000000000000000000000000000000..edc69c7cf89fb7ce5be3b03334019278008f5ad0 GIT binary patch literal 37300 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 Date: Wed, 16 Apr 2025 13:43:41 -0400 Subject: [PATCH 20/93] Update theme content for VBA-LanguageServer --- icons/theme-seti/CONTRIBUTING.md | 33 --------------------- icons/theme-seti/README.md | 2 +- icons/theme-seti/ThirdPartyNotices.txt | 10 +++---- icons/theme-seti/build/update-icon-theme.js | 30 ++++++++++--------- icons/theme-seti/cgmanifest.json | 2 +- icons/theme-seti/package.json | 17 +++++++---- icons/theme-seti/package.nls.json | 6 ++-- 7 files changed, 38 insertions(+), 62 deletions(-) delete mode 100644 icons/theme-seti/CONTRIBUTING.md diff --git a/icons/theme-seti/CONTRIBUTING.md b/icons/theme-seti/CONTRIBUTING.md deleted file mode 100644 index df0501d..0000000 --- a/icons/theme-seti/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -# theme-seti - -This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). - -## Previewing icons - -There is a [`./icons/preview.html`](./icons/preview.html) file that can be opened to see all of the icons included in the theme. -To view this, it needs to be hosted by a web server. The easiest way is to open the file with the `Open with Live Server` command from the [Live Server extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer). - - -## Updating icons - -- Make a PR against https://github.com/jesseweed/seti-ui with your icon changes. -- Once accepted there, ping us or make a PR yourself that updates the theme and font here - -To adopt the latest changes from https://github.com/jesseweed/seti-ui: - -- have the main branches of `https://github.com/jesseweed/seti-ui` and `https://github.com/microsoft/vscode` cloned in the same parent folder -- in the `seti-ui` folder, run `npm install` and `npm run prepublishOnly`. This will generate updated icons and fonts. -- in the `vscode/extensions/theme-seti` folder run `npm run update`. This will launch the [icon theme update script](build/update-icon-theme.js) that updates the theme as well as the font based on content in `seti-ui`. -- to test the icon theme, look at the icon preview as described above. -- when done, create a PR with the changes in https://github.com/microsoft/vscode. -Add a screenshot of the preview page to accompany it. - - -### Languages not shipped with `vscode` - -Languages that are not shipped with `vscode` must be added to the `nonBuiltInLanguages` object inside of `update-icon-theme.js`. - -These should match [the file mapping in `seti-ui`](https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less). - -Please try and keep this list in alphabetical order! Thank you. - diff --git a/icons/theme-seti/README.md b/icons/theme-seti/README.md index dcd9ba9..2c4d721 100644 --- a/icons/theme-seti/README.md +++ b/icons/theme-seti/README.md @@ -1,6 +1,6 @@ # theme-seti -This is an icon theme that uses the icons from [`seti-ui`](https://github.com/jesseweed/seti-ui). +This is an icon theme that uses the icons from a fork of [`seti-ui`](https://github.com/jesseweed/seti-ui). ## Updating icons diff --git a/icons/theme-seti/ThirdPartyNotices.txt b/icons/theme-seti/ThirdPartyNotices.txt index 29cbcd4..6a495c8 100644 --- a/icons/theme-seti/ThirdPartyNotices.txt +++ b/icons/theme-seti/ThirdPartyNotices.txt @@ -1,11 +1,11 @@ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -For Microsoft vscode-theme-seti +For VBA-LanguageServer vscode-theme-seti -This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright -notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice -are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for -the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +This theme is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which VBA-LanguageServer received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. VBA-LanguageServer licenses the Third Party OSS to you under the licensing terms for +the VBA-LanguageServer product or service. VBA-LanguageServer reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise.† 1. Seti UI - A subtle dark colored UI theme for Atom. (https://github.com/jesseweed/seti-ui) diff --git a/icons/theme-seti/build/update-icon-theme.js b/icons/theme-seti/build/update-icon-theme.js index 2b16374..bd7f518 100644 --- a/icons/theme-seti/build/update-icon-theme.js +++ b/icons/theme-seti/build/update-icon-theme.js @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +//@ts-nocheck + 'use strict'; const path = require('path'); @@ -54,14 +56,14 @@ const ignoreExtAssociation = { "properties": true }; -const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo +const FROM_DISK = false; // set to true to take content from a repo checked out next to the vscode repo let font, fontMappingsFile, fileAssociationFile, colorsFile; if (!FROM_DISK) { - font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; - fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; - fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; - colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; + font = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/DecimalTurn/seti-ui/vba/styles/ui-variables.less'; } else { font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; @@ -70,7 +72,7 @@ if (!FROM_DISK) { } function getCommitSha(repoId) { - const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; + const commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/vba'; return download(commitInfo).then(function (content) { try { const lastCommit = JSON.parse(content); @@ -303,11 +305,11 @@ exports.update = function () { const res = { information_for_contributors: [ - 'This file has been generated from data in https://github.com/jesseweed/seti-ui', - '- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less', - '- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less', - '- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less', - 'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.', + 'This file has been generated from data in https://github.com/DecimalTurn/seti-ui', + '- icon definitions: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/_fonts/seti.less', + '- icon colors: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/ui-variables.less', + '- file associations: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/components/icons/mapping.less', + 'If you want to provide a fix or improvement, please create a pull request against the DecimalTurn/seti-ui repository.', 'Once accepted there, we are happy to receive an update request.', ], fonts: [{ @@ -329,7 +331,7 @@ exports.update = function () { languageIds: getInvertSet(lang2Def), fileNames: getInvertSet(fileName2Def) }, - version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha, + version: 'https://github.com/DecimalTurn/seti-ui/commit/' + info.commitSha, }; const path = './icons/vs-seti-icon-theme.json'; @@ -443,7 +445,7 @@ exports.update = function () { while ((match = regex3.exec(content)) !== null) { colorId2Value[match[1]] = match[2]; } - return getCommitSha('jesseweed/seti-ui').then(function (info) { + return getCommitSha('DecimalTurn/seti-ui').then(function (info) { try { writeFileIconContent(info); @@ -454,7 +456,7 @@ exports.update = function () { fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); console.log('updated ' + cgmanifestPath); - console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + console.log('Updated to DecimalTurn/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); } catch (e) { console.error(e); diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json index 8d50dd2..f34bcdd 100644 --- a/icons/theme-seti/cgmanifest.json +++ b/icons/theme-seti/cgmanifest.json @@ -5,7 +5,7 @@ "type": "git", "git": { "name": "seti-ui", - "repositoryUrl": "https://github.com/jesseweed/seti-ui", + "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" } }, diff --git a/icons/theme-seti/package.json b/icons/theme-seti/package.json index dab0dea..64b61ce 100644 --- a/icons/theme-seti/package.json +++ b/icons/theme-seti/package.json @@ -1,23 +1,26 @@ { - "name": "vscode-theme-seti", + "name": "vscode-theme-seti-vba", "private": true, - "version": "1.0.0", + "version": "1.1.0-vba", "displayName": "%displayName%", "description": "%description%", "publisher": "vscode", "license": "MIT", "icon": "icons/seti-circular-128x128.png", "scripts": { - "update": "node ./build/update-icon-theme.js" + "update": "node ./build/update-icon-theme.js", + "build": "vsce package" }, "engines": { "vscode": "*" }, - "categories": ["Themes"], + "categories": [ + "Themes" + ], "contributes": { "iconThemes": [ { - "id": "vs-seti", + "id": "vs-seti-vba", "label": "%themeLabel%", "path": "./icons/vs-seti-icon-theme.json" } @@ -26,5 +29,9 @@ "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" + }, + "devDependencies": { + "minimatch": "^3.0.4", + "vsce": "^2.15.0" } } diff --git a/icons/theme-seti/package.nls.json b/icons/theme-seti/package.nls.json index 3572c3c..3b95693 100644 --- a/icons/theme-seti/package.nls.json +++ b/icons/theme-seti/package.nls.json @@ -1,5 +1,5 @@ { - "displayName": "Seti File Icon Theme", - "description": "A file icon theme made out of the Seti UI file icons", - "themeLabel": "Seti (Visual Studio Code)" + "displayName": "Seti File Icon Theme + VBA", + "description": "A file icon theme made out of the Seti UI file icons + VBA icons.", + "themeLabel": "Seti + VBA" } From 63f60cc0a2197e5b85439bf484cadd4838a295e6 Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:48:22 -0400 Subject: [PATCH 21/93] Update icons and package.json --- icons/theme-seti/cgmanifest.json | 30 +- icons/theme-seti/icons/seti.woff | Bin 37300 -> 37452 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 4900 +++++++++-------- package.json | 5 + 4 files changed, 2515 insertions(+), 2420 deletions(-) diff --git a/icons/theme-seti/cgmanifest.json b/icons/theme-seti/cgmanifest.json index f34bcdd..cd70a9f 100644 --- a/icons/theme-seti/cgmanifest.json +++ b/icons/theme-seti/cgmanifest.json @@ -1,16 +1,16 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "seti-ui", - "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", - "commitHash": "1cac4f30f93cc898103c62dde41823a09b0d7b74" - } - }, - "version": "0.1.0" - } - ], - "version": 1 +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "seti-ui", + "repositoryUrl": "https://github.com/DecimalTurn/seti-ui", + "commitHash": "7a8d51ccb32737be812549fe1c31fae8276b284f" + } + }, + "version": "0.1.0" + } + ], + "version": 1 } \ No newline at end of file diff --git a/icons/theme-seti/icons/seti.woff b/icons/theme-seti/icons/seti.woff index edc69c7cf89fb7ce5be3b03334019278008f5ad0..b20123e96bb1a4623c39609502ae9bfc79e83334 100644 GIT binary patch delta 37068 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 KFB8tfk^lgqHFY%r delta 36967 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 Nm6d-1n;gKf008BK5#;~? diff --git a/icons/theme-seti/icons/vs-seti-icon-theme.json b/icons/theme-seti/icons/vs-seti-icon-theme.json index 119ae70..7a69ca3 100644 --- a/icons/theme-seti/icons/vs-seti-icon-theme.json +++ b/icons/theme-seti/icons/vs-seti-icon-theme.json @@ -1,2406 +1,2496 @@ -{ - "information_for_contributors": [ - "This file has been generated from data in https://github.com/jesseweed/seti-ui", - "- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less", - "- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less", - "- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less", - "If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.", - "Once accepted there, we are happy to receive an update request." - ], - "fonts": [ - { - "id": "seti", - "src": [ - { - "path": "./seti.woff", - "format": "woff" - } - ], - "weight": "normal", - "style": "normal", - "size": "150%" - } - ], - "iconDefinitions": { - "_R_light": { - "fontCharacter": "\\E001", - "fontColor": "#498ba7" - }, - "_R": { - "fontCharacter": "\\E001", - "fontColor": "#519aba" - }, - "_argdown_light": { - "fontCharacter": "\\E003", - "fontColor": "#498ba7" - }, - "_argdown": { - "fontCharacter": "\\E003", - "fontColor": "#519aba" - }, - "_asm_light": { - "fontCharacter": "\\E004", - "fontColor": "#b8383d" - }, - "_asm": { - "fontCharacter": "\\E004", - "fontColor": "#cc3e44" - }, - "_audio_light": { - "fontCharacter": "\\E005", - "fontColor": "#9068b0" - }, - "_audio": { - "fontCharacter": "\\E005", - "fontColor": "#a074c4" - }, - "_babel_light": { - "fontCharacter": "\\E006", - "fontColor": "#b7b73b" - }, - "_babel": { - "fontCharacter": "\\E006", - "fontColor": "#cbcb41" - }, - "_bazel_light": { - "fontCharacter": "\\E007", - "fontColor": "#7fae42" - }, - "_bazel": { - "fontCharacter": "\\E007", - "fontColor": "#8dc149" - }, - "_bazel_1_light": { - "fontCharacter": "\\E007", - "fontColor": "#455155" - }, - "_bazel_1": { - "fontCharacter": "\\E007", - "fontColor": "#4d5a5e" - }, - "_bicep_light": { - "fontCharacter": "\\E008", - "fontColor": "#498ba7" - }, - "_bicep": { - "fontCharacter": "\\E008", - "fontColor": "#519aba" - }, - "_bower_light": { - "fontCharacter": "\\E009", - "fontColor": "#cc6d2e" - }, - "_bower": { - "fontCharacter": "\\E009", - "fontColor": "#e37933" - }, - "_bsl_light": { - "fontCharacter": "\\E00A", - "fontColor": "#b8383d" - }, - "_bsl": { - "fontCharacter": "\\E00A", - "fontColor": "#cc3e44" - }, - "_c_light": { - "fontCharacter": "\\E00C", - "fontColor": "#498ba7" - }, - "_c": { - "fontCharacter": "\\E00C", - "fontColor": "#519aba" - }, - "_c-sharp_light": { - "fontCharacter": "\\E00B", - "fontColor": "#498ba7" - }, - "_c-sharp": { - "fontCharacter": "\\E00B", - "fontColor": "#519aba" - }, - "_c_1_light": { - "fontCharacter": "\\E00C", - "fontColor": "#9068b0" - }, - "_c_1": { - "fontCharacter": "\\E00C", - "fontColor": "#a074c4" - }, - "_c_2_light": { - "fontCharacter": "\\E00C", - "fontColor": "#b7b73b" - }, - "_c_2": { - "fontCharacter": "\\E00C", - "fontColor": "#cbcb41" - }, - "_cake_light": { - "fontCharacter": "\\E00D", - "fontColor": "#b8383d" - }, - "_cake": { - "fontCharacter": "\\E00D", - "fontColor": "#cc3e44" - }, - "_cake_php_light": { - "fontCharacter": "\\E00E", - "fontColor": "#b8383d" - }, - "_cake_php": { - "fontCharacter": "\\E00E", - "fontColor": "#cc3e44" - }, - "_clock_light": { - "fontCharacter": "\\E012", - "fontColor": "#498ba7" - }, - "_clock": { - "fontCharacter": "\\E012", - "fontColor": "#519aba" - }, - "_clock_1_light": { - "fontCharacter": "\\E012", - "fontColor": "#627379" - }, - "_clock_1": { - "fontCharacter": "\\E012", - "fontColor": "#6d8086" - }, - "_clojure_light": { - "fontCharacter": "\\E013", - "fontColor": "#7fae42" - }, - "_clojure": { - "fontCharacter": "\\E013", - "fontColor": "#8dc149" - }, - "_clojure_1_light": { - "fontCharacter": "\\E013", - "fontColor": "#498ba7" - }, - "_clojure_1": { - "fontCharacter": "\\E013", - "fontColor": "#519aba" - }, - "_code-climate_light": { - "fontCharacter": "\\E014", - "fontColor": "#7fae42" - }, - "_code-climate": { - "fontCharacter": "\\E014", - "fontColor": "#8dc149" - }, - "_code-search_light": { - "fontCharacter": "\\E015", - "fontColor": "#9068b0" - }, - "_code-search": { - "fontCharacter": "\\E015", - "fontColor": "#a074c4" - }, - "_coffee_light": { - "fontCharacter": "\\E016", - "fontColor": "#b7b73b" - }, - "_coffee": { - "fontCharacter": "\\E016", - "fontColor": "#cbcb41" - }, - "_coldfusion_light": { - "fontCharacter": "\\E018", - "fontColor": "#498ba7" - }, - "_coldfusion": { - "fontCharacter": "\\E018", - "fontColor": "#519aba" - }, - "_config_light": { - "fontCharacter": "\\E019", - "fontColor": "#627379" - }, - "_config": { - "fontCharacter": "\\E019", - "fontColor": "#6d8086" - }, - "_cpp_light": { - "fontCharacter": "\\E01A", - "fontColor": "#498ba7" - }, - "_cpp": { - "fontCharacter": "\\E01A", - "fontColor": "#519aba" - }, - "_cpp_1_light": { - "fontCharacter": "\\E01A", - "fontColor": "#9068b0" - }, - "_cpp_1": { - "fontCharacter": "\\E01A", - "fontColor": "#a074c4" - }, - "_cpp_2_light": { - "fontCharacter": "\\E01A", - "fontColor": "#b7b73b" - }, - "_cpp_2": { - "fontCharacter": "\\E01A", - "fontColor": "#cbcb41" - }, - "_crystal_light": { - "fontCharacter": "\\E01B", - "fontColor": "#bfc2c1" - }, - "_crystal": { - "fontCharacter": "\\E01B", - "fontColor": "#d4d7d6" - }, - "_crystal_embedded_light": { - "fontCharacter": "\\E01C", - "fontColor": "#bfc2c1" - }, - "_crystal_embedded": { - "fontCharacter": "\\E01C", - "fontColor": "#d4d7d6" - }, - "_css_light": { - "fontCharacter": "\\E01D", - "fontColor": "#498ba7" - }, - "_css": { - "fontCharacter": "\\E01D", - "fontColor": "#519aba" - }, - "_csv_light": { - "fontCharacter": "\\E01E", - "fontColor": "#7fae42" - }, - "_csv": { - "fontCharacter": "\\E01E", - "fontColor": "#8dc149" - }, - "_cu_light": { - "fontCharacter": "\\E01F", - "fontColor": "#7fae42" - }, - "_cu": { - "fontCharacter": "\\E01F", - "fontColor": "#8dc149" - }, - "_cu_1_light": { - "fontCharacter": "\\E01F", - "fontColor": "#9068b0" - }, - "_cu_1": { - "fontCharacter": "\\E01F", - "fontColor": "#a074c4" - }, - "_d_light": { - "fontCharacter": "\\E020", - "fontColor": "#b8383d" - }, - "_d": { - "fontCharacter": "\\E020", - "fontColor": "#cc3e44" - }, - "_dart_light": { - "fontCharacter": "\\E021", - "fontColor": "#498ba7" - }, - "_dart": { - "fontCharacter": "\\E021", - "fontColor": "#519aba" - }, - "_db_light": { - "fontCharacter": "\\E022", - "fontColor": "#dd4b78" - }, - "_db": { - "fontCharacter": "\\E022", - "fontColor": "#f55385" - }, - "_db_1_light": { - "fontCharacter": "\\E022", - "fontColor": "#498ba7" - }, - "_db_1": { - "fontCharacter": "\\E022", - "fontColor": "#519aba" - }, - "_default_light": { - "fontCharacter": "\\E023", - "fontColor": "#bfc2c1" - }, - "_default": { - "fontCharacter": "\\E023", - "fontColor": "#d4d7d6" - }, - "_docker_light": { - "fontCharacter": "\\E025", - "fontColor": "#498ba7" - }, - "_docker": { - "fontCharacter": "\\E025", - "fontColor": "#519aba" - }, - "_docker_1_light": { - "fontCharacter": "\\E025", - "fontColor": "#455155" - }, - "_docker_1": { - "fontCharacter": "\\E025", - "fontColor": "#4d5a5e" - }, - "_docker_2_light": { - "fontCharacter": "\\E025", - "fontColor": "#7fae42" - }, - "_docker_2": { - "fontCharacter": "\\E025", - "fontColor": "#8dc149" - }, - "_docker_3_light": { - "fontCharacter": "\\E025", - "fontColor": "#dd4b78" - }, - "_docker_3": { - "fontCharacter": "\\E025", - "fontColor": "#f55385" - }, - "_ejs_light": { - "fontCharacter": "\\E027", - "fontColor": "#b7b73b" - }, - "_ejs": { - "fontCharacter": "\\E027", - "fontColor": "#cbcb41" - }, - "_elixir_light": { - "fontCharacter": "\\E028", - "fontColor": "#9068b0" - }, - "_elixir": { - "fontCharacter": "\\E028", - "fontColor": "#a074c4" - }, - "_elixir_script_light": { - "fontCharacter": "\\E029", - "fontColor": "#9068b0" - }, - "_elixir_script": { - "fontCharacter": "\\E029", - "fontColor": "#a074c4" - }, - "_elm_light": { - "fontCharacter": "\\E02A", - "fontColor": "#498ba7" - }, - "_elm": { - "fontCharacter": "\\E02A", - "fontColor": "#519aba" - }, - "_eslint_light": { - "fontCharacter": "\\E02C", - "fontColor": "#9068b0" - }, - "_eslint": { - "fontCharacter": "\\E02C", - "fontColor": "#a074c4" - }, - "_eslint_1_light": { - "fontCharacter": "\\E02C", - "fontColor": "#455155" - }, - "_eslint_1": { - "fontCharacter": "\\E02C", - "fontColor": "#4d5a5e" - }, - "_ethereum_light": { - "fontCharacter": "\\E02D", - "fontColor": "#498ba7" - }, - "_ethereum": { - "fontCharacter": "\\E02D", - "fontColor": "#519aba" - }, - "_f-sharp_light": { - "fontCharacter": "\\E02E", - "fontColor": "#498ba7" - }, - "_f-sharp": { - "fontCharacter": "\\E02E", - "fontColor": "#519aba" - }, - "_favicon_light": { - "fontCharacter": "\\E02F", - "fontColor": "#b7b73b" - }, - "_favicon": { - "fontCharacter": "\\E02F", - "fontColor": "#cbcb41" - }, - "_firebase_light": { - "fontCharacter": "\\E030", - "fontColor": "#cc6d2e" - }, - "_firebase": { - "fontCharacter": "\\E030", - "fontColor": "#e37933" - }, - "_firefox_light": { - "fontCharacter": "\\E031", - "fontColor": "#cc6d2e" - }, - "_firefox": { - "fontCharacter": "\\E031", - "fontColor": "#e37933" - }, - "_font_light": { - "fontCharacter": "\\E033", - "fontColor": "#b8383d" - }, - "_font": { - "fontCharacter": "\\E033", - "fontColor": "#cc3e44" - }, - "_git_light": { - "fontCharacter": "\\E034", - "fontColor": "#3b4b52" - }, - "_git": { - "fontCharacter": "\\E034", - "fontColor": "#41535b" - }, - "_github_light": { - "fontCharacter": "\\E037", - "fontColor": "#bfc2c1" - }, - "_github": { - "fontCharacter": "\\E037", - "fontColor": "#d4d7d6" - }, - "_gitlab_light": { - "fontCharacter": "\\E038", - "fontColor": "#cc6d2e" - }, - "_gitlab": { - "fontCharacter": "\\E038", - "fontColor": "#e37933" - }, - "_go_light": { - "fontCharacter": "\\E039", - "fontColor": "#498ba7" - }, - "_go": { - "fontCharacter": "\\E039", - "fontColor": "#519aba" - }, - "_go2_light": { - "fontCharacter": "\\E03A", - "fontColor": "#498ba7" - }, - "_go2": { - "fontCharacter": "\\E03A", - "fontColor": "#519aba" - }, - "_godot_light": { - "fontCharacter": "\\E03B", - "fontColor": "#498ba7" - }, - "_godot": { - "fontCharacter": "\\E03B", - "fontColor": "#519aba" - }, - "_godot_1_light": { - "fontCharacter": "\\E03B", - "fontColor": "#b8383d" - }, - "_godot_1": { - "fontCharacter": "\\E03B", - "fontColor": "#cc3e44" - }, - "_godot_2_light": { - "fontCharacter": "\\E03B", - "fontColor": "#b7b73b" - }, - "_godot_2": { - "fontCharacter": "\\E03B", - "fontColor": "#cbcb41" - }, - "_godot_3_light": { - "fontCharacter": "\\E03B", - "fontColor": "#9068b0" - }, - "_godot_3": { - "fontCharacter": "\\E03B", - "fontColor": "#a074c4" - }, - "_gradle_light": { - "fontCharacter": "\\E03C", - "fontColor": "#498ba7" - }, - "_gradle": { - "fontCharacter": "\\E03C", - "fontColor": "#519aba" - }, - "_grails_light": { - "fontCharacter": "\\E03D", - "fontColor": "#7fae42" - }, - "_grails": { - "fontCharacter": "\\E03D", - "fontColor": "#8dc149" - }, - "_graphql_light": { - "fontCharacter": "\\E03E", - "fontColor": "#dd4b78" - }, - "_graphql": { - "fontCharacter": "\\E03E", - "fontColor": "#f55385" - }, - "_grunt_light": { - "fontCharacter": "\\E03F", - "fontColor": "#cc6d2e" - }, - "_grunt": { - "fontCharacter": "\\E03F", - "fontColor": "#e37933" - }, - "_gulp_light": { - "fontCharacter": "\\E040", - "fontColor": "#b8383d" - }, - "_gulp": { - "fontCharacter": "\\E040", - "fontColor": "#cc3e44" - }, - "_hacklang_light": { - "fontCharacter": "\\E041", - "fontColor": "#cc6d2e" - }, - "_hacklang": { - "fontCharacter": "\\E041", - "fontColor": "#e37933" - }, - "_haml_light": { - "fontCharacter": "\\E042", - "fontColor": "#b8383d" - }, - "_haml": { - "fontCharacter": "\\E042", - "fontColor": "#cc3e44" - }, - "_happenings_light": { - "fontCharacter": "\\E043", - "fontColor": "#498ba7" - }, - "_happenings": { - "fontCharacter": "\\E043", - "fontColor": "#519aba" - }, - "_haskell_light": { - "fontCharacter": "\\E044", - "fontColor": "#9068b0" - }, - "_haskell": { - "fontCharacter": "\\E044", - "fontColor": "#a074c4" - }, - "_haxe_light": { - "fontCharacter": "\\E045", - "fontColor": "#cc6d2e" - }, - "_haxe": { - "fontCharacter": "\\E045", - "fontColor": "#e37933" - }, - "_haxe_1_light": { - "fontCharacter": "\\E045", - "fontColor": "#b7b73b" - }, - "_haxe_1": { - "fontCharacter": "\\E045", - "fontColor": "#cbcb41" - }, - "_haxe_2_light": { - "fontCharacter": "\\E045", - "fontColor": "#498ba7" - }, - "_haxe_2": { - "fontCharacter": "\\E045", - "fontColor": "#519aba" - }, - "_haxe_3_light": { - "fontCharacter": "\\E045", - "fontColor": "#9068b0" - }, - "_haxe_3": { - "fontCharacter": "\\E045", - "fontColor": "#a074c4" - }, - "_heroku_light": { - "fontCharacter": "\\E046", - "fontColor": "#9068b0" - }, - "_heroku": { - "fontCharacter": "\\E046", - "fontColor": "#a074c4" - }, - "_hex_light": { - "fontCharacter": "\\E047", - "fontColor": "#b8383d" - }, - "_hex": { - "fontCharacter": "\\E047", - "fontColor": "#cc3e44" - }, - "_html_light": { - "fontCharacter": "\\E048", - "fontColor": "#498ba7" - }, - "_html": { - "fontCharacter": "\\E048", - "fontColor": "#519aba" - }, - "_html_1_light": { - "fontCharacter": "\\E048", - "fontColor": "#7fae42" - }, - "_html_1": { - "fontCharacter": "\\E048", - "fontColor": "#8dc149" - }, - "_html_2_light": { - "fontCharacter": "\\E048", - "fontColor": "#b7b73b" - }, - "_html_2": { - "fontCharacter": "\\E048", - "fontColor": "#cbcb41" - }, - "_html_3_light": { - "fontCharacter": "\\E048", - "fontColor": "#cc6d2e" - }, - "_html_3": { - "fontCharacter": "\\E048", - "fontColor": "#e37933" - }, - "_html_erb_light": { - "fontCharacter": "\\E049", - "fontColor": "#b8383d" - }, - "_html_erb": { - "fontCharacter": "\\E049", - "fontColor": "#cc3e44" - }, - "_ignored_light": { - "fontCharacter": "\\E04A", - "fontColor": "#3b4b52" - }, - "_ignored": { - "fontCharacter": "\\E04A", - "fontColor": "#41535b" - }, - "_illustrator_light": { - "fontCharacter": "\\E04B", - "fontColor": "#b7b73b" - }, - "_illustrator": { - "fontCharacter": "\\E04B", - "fontColor": "#cbcb41" - }, - "_image_light": { - "fontCharacter": "\\E04C", - "fontColor": "#9068b0" - }, - "_image": { - "fontCharacter": "\\E04C", - "fontColor": "#a074c4" - }, - "_info_light": { - "fontCharacter": "\\E04D", - "fontColor": "#498ba7" - }, - "_info": { - "fontCharacter": "\\E04D", - "fontColor": "#519aba" - }, - "_ionic_light": { - "fontCharacter": "\\E04E", - "fontColor": "#498ba7" - }, - "_ionic": { - "fontCharacter": "\\E04E", - "fontColor": "#519aba" - }, - "_jade_light": { - "fontCharacter": "\\E04F", - "fontColor": "#b8383d" - }, - "_jade": { - "fontCharacter": "\\E04F", - "fontColor": "#cc3e44" - }, - "_java_light": { - "fontCharacter": "\\E050", - "fontColor": "#b8383d" - }, - "_java": { - "fontCharacter": "\\E050", - "fontColor": "#cc3e44" - }, - "_java_1_light": { - "fontCharacter": "\\E050", - "fontColor": "#498ba7" - }, - "_java_1": { - "fontCharacter": "\\E050", - "fontColor": "#519aba" - }, - "_javascript_light": { - "fontCharacter": "\\E051", - "fontColor": "#b7b73b" - }, - "_javascript": { - "fontCharacter": "\\E051", - "fontColor": "#cbcb41" - }, - "_javascript_1_light": { - "fontCharacter": "\\E051", - "fontColor": "#cc6d2e" - }, - "_javascript_1": { - "fontCharacter": "\\E051", - "fontColor": "#e37933" - }, - "_javascript_2_light": { - "fontCharacter": "\\E051", - "fontColor": "#498ba7" - }, - "_javascript_2": { - "fontCharacter": "\\E051", - "fontColor": "#519aba" - }, - "_jenkins_light": { - "fontCharacter": "\\E052", - "fontColor": "#b8383d" - }, - "_jenkins": { - "fontCharacter": "\\E052", - "fontColor": "#cc3e44" - }, - "_jinja_light": { - "fontCharacter": "\\E053", - "fontColor": "#b8383d" - }, - "_jinja": { - "fontCharacter": "\\E053", - "fontColor": "#cc3e44" - }, - "_json_light": { - "fontCharacter": "\\E055", - "fontColor": "#b7b73b" - }, - "_json": { - "fontCharacter": "\\E055", - "fontColor": "#cbcb41" - }, - "_json_1_light": { - "fontCharacter": "\\E055", - "fontColor": "#7fae42" - }, - "_json_1": { - "fontCharacter": "\\E055", - "fontColor": "#8dc149" - }, - "_julia_light": { - "fontCharacter": "\\E056", - "fontColor": "#9068b0" - }, - "_julia": { - "fontCharacter": "\\E056", - "fontColor": "#a074c4" - }, - "_karma_light": { - "fontCharacter": "\\E057", - "fontColor": "#7fae42" - }, - "_karma": { - "fontCharacter": "\\E057", - "fontColor": "#8dc149" - }, - "_kotlin_light": { - "fontCharacter": "\\E058", - "fontColor": "#cc6d2e" - }, - "_kotlin": { - "fontCharacter": "\\E058", - "fontColor": "#e37933" - }, - "_less_light": { - "fontCharacter": "\\E059", - "fontColor": "#498ba7" - }, - "_less": { - "fontCharacter": "\\E059", - "fontColor": "#519aba" - }, - "_license_light": { - "fontCharacter": "\\E05A", - "fontColor": "#b7b73b" - }, - "_license": { - "fontCharacter": "\\E05A", - "fontColor": "#cbcb41" - }, - "_license_1_light": { - "fontCharacter": "\\E05A", - "fontColor": "#cc6d2e" - }, - "_license_1": { - "fontCharacter": "\\E05A", - "fontColor": "#e37933" - }, - "_license_2_light": { - "fontCharacter": "\\E05A", - "fontColor": "#b8383d" - }, - "_license_2": { - "fontCharacter": "\\E05A", - "fontColor": "#cc3e44" - }, - "_liquid_light": { - "fontCharacter": "\\E05B", - "fontColor": "#7fae42" - }, - "_liquid": { - "fontCharacter": "\\E05B", - "fontColor": "#8dc149" - }, - "_livescript_light": { - "fontCharacter": "\\E05C", - "fontColor": "#498ba7" - }, - "_livescript": { - "fontCharacter": "\\E05C", - "fontColor": "#519aba" - }, - "_lock_light": { - "fontCharacter": "\\E05D", - "fontColor": "#7fae42" - }, - "_lock": { - "fontCharacter": "\\E05D", - "fontColor": "#8dc149" - }, - "_lua_light": { - "fontCharacter": "\\E05E", - "fontColor": "#498ba7" - }, - "_lua": { - "fontCharacter": "\\E05E", - "fontColor": "#519aba" - }, - "_makefile_light": { - "fontCharacter": "\\E05F", - "fontColor": "#cc6d2e" - }, - "_makefile": { - "fontCharacter": "\\E05F", - "fontColor": "#e37933" - }, - "_makefile_1_light": { - "fontCharacter": "\\E05F", - "fontColor": "#9068b0" - }, - "_makefile_1": { - "fontCharacter": "\\E05F", - "fontColor": "#a074c4" - }, - "_makefile_2_light": { - "fontCharacter": "\\E05F", - "fontColor": "#627379" - }, - "_makefile_2": { - "fontCharacter": "\\E05F", - "fontColor": "#6d8086" - }, - "_makefile_3_light": { - "fontCharacter": "\\E05F", - "fontColor": "#498ba7" - }, - "_makefile_3": { - "fontCharacter": "\\E05F", - "fontColor": "#519aba" - }, - "_markdown_light": { - "fontCharacter": "\\E060", - "fontColor": "#498ba7" - }, - "_markdown": { - "fontCharacter": "\\E060", - "fontColor": "#519aba" - }, - "_maven_light": { - "fontCharacter": "\\E061", - "fontColor": "#b8383d" - }, - "_maven": { - "fontCharacter": "\\E061", - "fontColor": "#cc3e44" - }, - "_mdo_light": { - "fontCharacter": "\\E062", - "fontColor": "#b8383d" - }, - "_mdo": { - "fontCharacter": "\\E062", - "fontColor": "#cc3e44" - }, - "_mustache_light": { - "fontCharacter": "\\E063", - "fontColor": "#cc6d2e" - }, - "_mustache": { - "fontCharacter": "\\E063", - "fontColor": "#e37933" - }, - "_nim_light": { - "fontCharacter": "\\E065", - "fontColor": "#b7b73b" - }, - "_nim": { - "fontCharacter": "\\E065", - "fontColor": "#cbcb41" - }, - "_notebook_light": { - "fontCharacter": "\\E066", - "fontColor": "#498ba7" - }, - "_notebook": { - "fontCharacter": "\\E066", - "fontColor": "#519aba" - }, - "_npm_light": { - "fontCharacter": "\\E067", - "fontColor": "#3b4b52" - }, - "_npm": { - "fontCharacter": "\\E067", - "fontColor": "#41535b" - }, - "_npm_1_light": { - "fontCharacter": "\\E067", - "fontColor": "#b8383d" - }, - "_npm_1": { - "fontCharacter": "\\E067", - "fontColor": "#cc3e44" - }, - "_npm_ignored_light": { - "fontCharacter": "\\E068", - "fontColor": "#3b4b52" - }, - "_npm_ignored": { - "fontCharacter": "\\E068", - "fontColor": "#41535b" - }, - "_nunjucks_light": { - "fontCharacter": "\\E069", - "fontColor": "#7fae42" - }, - "_nunjucks": { - "fontCharacter": "\\E069", - "fontColor": "#8dc149" - }, - "_ocaml_light": { - "fontCharacter": "\\E06A", - "fontColor": "#cc6d2e" - }, - "_ocaml": { - "fontCharacter": "\\E06A", - "fontColor": "#e37933" - }, - "_odata_light": { - "fontCharacter": "\\E06B", - "fontColor": "#cc6d2e" - }, - "_odata": { - "fontCharacter": "\\E06B", - "fontColor": "#e37933" - }, - "_pddl_light": { - "fontCharacter": "\\E06C", - "fontColor": "#9068b0" - }, - "_pddl": { - "fontCharacter": "\\E06C", - "fontColor": "#a074c4" - }, - "_pdf_light": { - "fontCharacter": "\\E06D", - "fontColor": "#b8383d" - }, - "_pdf": { - "fontCharacter": "\\E06D", - "fontColor": "#cc3e44" - }, - "_perl_light": { - "fontCharacter": "\\E06E", - "fontColor": "#498ba7" - }, - "_perl": { - "fontCharacter": "\\E06E", - "fontColor": "#519aba" - }, - "_photoshop_light": { - "fontCharacter": "\\E06F", - "fontColor": "#498ba7" - }, - "_photoshop": { - "fontCharacter": "\\E06F", - "fontColor": "#519aba" - }, - "_php_light": { - "fontCharacter": "\\E070", - "fontColor": "#9068b0" - }, - "_php": { - "fontCharacter": "\\E070", - "fontColor": "#a074c4" - }, - "_pipeline_light": { - "fontCharacter": "\\E071", - "fontColor": "#cc6d2e" - }, - "_pipeline": { - "fontCharacter": "\\E071", - "fontColor": "#e37933" - }, - "_plan_light": { - "fontCharacter": "\\E072", - "fontColor": "#7fae42" - }, - "_plan": { - "fontCharacter": "\\E072", - "fontColor": "#8dc149" - }, - "_platformio_light": { - "fontCharacter": "\\E073", - "fontColor": "#cc6d2e" - }, - "_platformio": { - "fontCharacter": "\\E073", - "fontColor": "#e37933" - }, - "_powershell_light": { - "fontCharacter": "\\E074", - "fontColor": "#498ba7" - }, - "_powershell": { - "fontCharacter": "\\E074", - "fontColor": "#519aba" - }, - "_prisma_light": { - "fontCharacter": "\\E075", - "fontColor": "#498ba7" - }, - "_prisma": { - "fontCharacter": "\\E075", - "fontColor": "#519aba" - }, - "_prolog_light": { - "fontCharacter": "\\E077", - "fontColor": "#cc6d2e" - }, - "_prolog": { - "fontCharacter": "\\E077", - "fontColor": "#e37933" - }, - "_pug_light": { - "fontCharacter": "\\E078", - "fontColor": "#b8383d" - }, - "_pug": { - "fontCharacter": "\\E078", - "fontColor": "#cc3e44" - }, - "_puppet_light": { - "fontCharacter": "\\E079", - "fontColor": "#b7b73b" - }, - "_puppet": { - "fontCharacter": "\\E079", - "fontColor": "#cbcb41" - }, - "_purescript_light": { - "fontCharacter": "\\E07A", - "fontColor": "#bfc2c1" - }, - "_purescript": { - "fontCharacter": "\\E07A", - "fontColor": "#d4d7d6" - }, - "_python_light": { - "fontCharacter": "\\E07B", - "fontColor": "#498ba7" - }, - "_python": { - "fontCharacter": "\\E07B", - "fontColor": "#519aba" - }, - "_react_light": { - "fontCharacter": "\\E07D", - "fontColor": "#498ba7" - }, - "_react": { - "fontCharacter": "\\E07D", - "fontColor": "#519aba" - }, - "_react_1_light": { - "fontCharacter": "\\E07D", - "fontColor": "#cc6d2e" - }, - "_react_1": { - "fontCharacter": "\\E07D", - "fontColor": "#e37933" - }, - "_reasonml_light": { - "fontCharacter": "\\E07E", - "fontColor": "#b8383d" - }, - "_reasonml": { - "fontCharacter": "\\E07E", - "fontColor": "#cc3e44" - }, - "_rescript_light": { - "fontCharacter": "\\E07F", - "fontColor": "#b8383d" - }, - "_rescript": { - "fontCharacter": "\\E07F", - "fontColor": "#cc3e44" - }, - "_rescript_1_light": { - "fontCharacter": "\\E07F", - "fontColor": "#dd4b78" - }, - "_rescript_1": { - "fontCharacter": "\\E07F", - "fontColor": "#f55385" - }, - "_rollup_light": { - "fontCharacter": "\\E080", - "fontColor": "#b8383d" - }, - "_rollup": { - "fontCharacter": "\\E080", - "fontColor": "#cc3e44" - }, - "_ruby_light": { - "fontCharacter": "\\E081", - "fontColor": "#b8383d" - }, - "_ruby": { - "fontCharacter": "\\E081", - "fontColor": "#cc3e44" - }, - "_rust_light": { - "fontCharacter": "\\E082", - "fontColor": "#627379" - }, - "_rust": { - "fontCharacter": "\\E082", - "fontColor": "#6d8086" - }, - "_salesforce_light": { - "fontCharacter": "\\E083", - "fontColor": "#498ba7" - }, - "_salesforce": { - "fontCharacter": "\\E083", - "fontColor": "#519aba" - }, - "_sass_light": { - "fontCharacter": "\\E084", - "fontColor": "#dd4b78" - }, - "_sass": { - "fontCharacter": "\\E084", - "fontColor": "#f55385" - }, - "_sbt_light": { - "fontCharacter": "\\E085", - "fontColor": "#498ba7" - }, - "_sbt": { - "fontCharacter": "\\E085", - "fontColor": "#519aba" - }, - "_scala_light": { - "fontCharacter": "\\E086", - "fontColor": "#b8383d" - }, - "_scala": { - "fontCharacter": "\\E086", - "fontColor": "#cc3e44" - }, - "_shell_light": { - "fontCharacter": "\\E089", - "fontColor": "#7fae42" - }, - "_shell": { - "fontCharacter": "\\E089", - "fontColor": "#8dc149" - }, - "_slim_light": { - "fontCharacter": "\\E08A", - "fontColor": "#cc6d2e" - }, - "_slim": { - "fontCharacter": "\\E08A", - "fontColor": "#e37933" - }, - "_smarty_light": { - "fontCharacter": "\\E08B", - "fontColor": "#b7b73b" - }, - "_smarty": { - "fontCharacter": "\\E08B", - "fontColor": "#cbcb41" - }, - "_spring_light": { - "fontCharacter": "\\E08C", - "fontColor": "#7fae42" - }, - "_spring": { - "fontCharacter": "\\E08C", - "fontColor": "#8dc149" - }, - "_stylelint_light": { - "fontCharacter": "\\E08D", - "fontColor": "#bfc2c1" - }, - "_stylelint": { - "fontCharacter": "\\E08D", - "fontColor": "#d4d7d6" - }, - "_stylelint_1_light": { - "fontCharacter": "\\E08D", - "fontColor": "#455155" - }, - "_stylelint_1": { - "fontCharacter": "\\E08D", - "fontColor": "#4d5a5e" - }, - "_stylus_light": { - "fontCharacter": "\\E08E", - "fontColor": "#7fae42" - }, - "_stylus": { - "fontCharacter": "\\E08E", - "fontColor": "#8dc149" - }, - "_sublime_light": { - "fontCharacter": "\\E08F", - "fontColor": "#cc6d2e" - }, - "_sublime": { - "fontCharacter": "\\E08F", - "fontColor": "#e37933" - }, - "_svelte_light": { - "fontCharacter": "\\E090", - "fontColor": "#b8383d" - }, - "_svelte": { - "fontCharacter": "\\E090", - "fontColor": "#cc3e44" - }, - "_svg_light": { - "fontCharacter": "\\E091", - "fontColor": "#9068b0" - }, - "_svg": { - "fontCharacter": "\\E091", - "fontColor": "#a074c4" - }, - "_svg_1_light": { - "fontCharacter": "\\E091", - "fontColor": "#498ba7" - }, - "_svg_1": { - "fontCharacter": "\\E091", - "fontColor": "#519aba" - }, - "_swift_light": { - "fontCharacter": "\\E092", - "fontColor": "#cc6d2e" - }, - "_swift": { - "fontCharacter": "\\E092", - "fontColor": "#e37933" - }, - "_terraform_light": { - "fontCharacter": "\\E093", - "fontColor": "#9068b0" - }, - "_terraform": { - "fontCharacter": "\\E093", - "fontColor": "#a074c4" - }, - "_tex_light": { - "fontCharacter": "\\E094", - "fontColor": "#498ba7" - }, - "_tex": { - "fontCharacter": "\\E094", - "fontColor": "#519aba" - }, - "_tex_1_light": { - "fontCharacter": "\\E094", - "fontColor": "#b7b73b" - }, - "_tex_1": { - "fontCharacter": "\\E094", - "fontColor": "#cbcb41" - }, - "_tex_2_light": { - "fontCharacter": "\\E094", - "fontColor": "#cc6d2e" - }, - "_tex_2": { - "fontCharacter": "\\E094", - "fontColor": "#e37933" - }, - "_tex_3_light": { - "fontCharacter": "\\E094", - "fontColor": "#bfc2c1" - }, - "_tex_3": { - "fontCharacter": "\\E094", - "fontColor": "#d4d7d6" - }, - "_todo": { - "fontCharacter": "\\E096" - }, - "_tsconfig_light": { - "fontCharacter": "\\E097", - "fontColor": "#498ba7" - }, - "_tsconfig": { - "fontCharacter": "\\E097", - "fontColor": "#519aba" - }, - "_twig_light": { - "fontCharacter": "\\E098", - "fontColor": "#7fae42" - }, - "_twig": { - "fontCharacter": "\\E098", - "fontColor": "#8dc149" - }, - "_typescript_light": { - "fontCharacter": "\\E099", - "fontColor": "#498ba7" - }, - "_typescript": { - "fontCharacter": "\\E099", - "fontColor": "#519aba" - }, - "_typescript_1_light": { - "fontCharacter": "\\E099", - "fontColor": "#cc6d2e" - }, - "_typescript_1": { - "fontCharacter": "\\E099", - "fontColor": "#e37933" - }, - "_vala_light": { - "fontCharacter": "\\E09A", - "fontColor": "#627379" - }, - "_vala": { - "fontCharacter": "\\E09A", - "fontColor": "#6d8086" - }, - "_video_light": { - "fontCharacter": "\\E09B", - "fontColor": "#dd4b78" - }, - "_video": { - "fontCharacter": "\\E09B", - "fontColor": "#f55385" - }, - "_vite_light": { - "fontCharacter": "\\E09C", - "fontColor": "#b7b73b" - }, - "_vite": { - "fontCharacter": "\\E09C", - "fontColor": "#cbcb41" - }, - "_vue_light": { - "fontCharacter": "\\E09D", - "fontColor": "#7fae42" - }, - "_vue": { - "fontCharacter": "\\E09D", - "fontColor": "#8dc149" - }, - "_wasm_light": { - "fontCharacter": "\\E09E", - "fontColor": "#9068b0" - }, - "_wasm": { - "fontCharacter": "\\E09E", - "fontColor": "#a074c4" - }, - "_wat_light": { - "fontCharacter": "\\E09F", - "fontColor": "#9068b0" - }, - "_wat": { - "fontCharacter": "\\E09F", - "fontColor": "#a074c4" - }, - "_webpack_light": { - "fontCharacter": "\\E0A0", - "fontColor": "#498ba7" - }, - "_webpack": { - "fontCharacter": "\\E0A0", - "fontColor": "#519aba" - }, - "_wgt_light": { - "fontCharacter": "\\E0A1", - "fontColor": "#498ba7" - }, - "_wgt": { - "fontCharacter": "\\E0A1", - "fontColor": "#519aba" - }, - "_windows_light": { - "fontCharacter": "\\E0A2", - "fontColor": "#498ba7" - }, - "_windows": { - "fontCharacter": "\\E0A2", - "fontColor": "#519aba" - }, - "_word_light": { - "fontCharacter": "\\E0A3", - "fontColor": "#498ba7" - }, - "_word": { - "fontCharacter": "\\E0A3", - "fontColor": "#519aba" - }, - "_xls_light": { - "fontCharacter": "\\E0A4", - "fontColor": "#7fae42" - }, - "_xls": { - "fontCharacter": "\\E0A4", - "fontColor": "#8dc149" - }, - "_xml_light": { - "fontCharacter": "\\E0A5", - "fontColor": "#cc6d2e" - }, - "_xml": { - "fontCharacter": "\\E0A5", - "fontColor": "#e37933" - }, - "_yarn_light": { - "fontCharacter": "\\E0A6", - "fontColor": "#498ba7" - }, - "_yarn": { - "fontCharacter": "\\E0A6", - "fontColor": "#519aba" - }, - "_yml_light": { - "fontCharacter": "\\E0A7", - "fontColor": "#9068b0" - }, - "_yml": { - "fontCharacter": "\\E0A7", - "fontColor": "#a074c4" - }, - "_zig_light": { - "fontCharacter": "\\E0A8", - "fontColor": "#cc6d2e" - }, - "_zig": { - "fontCharacter": "\\E0A8", - "fontColor": "#e37933" - }, - "_zip_light": { - "fontCharacter": "\\E0A9", - "fontColor": "#b8383d" - }, - "_zip": { - "fontCharacter": "\\E0A9", - "fontColor": "#cc3e44" - }, - "_zip_1_light": { - "fontCharacter": "\\E0A9", - "fontColor": "#627379" - }, - "_zip_1": { - "fontCharacter": "\\E0A9", - "fontColor": "#6d8086" - } - }, - "file": "_default", - "fileExtensions": { - "bsl": "_bsl", - "mdo": "_mdo", - "cls": "_salesforce", - "apex": "_salesforce", - "asm": "_asm", - "s": "_asm", - "bicep": "_bicep", - "bzl": "_bazel", - "bazel": "_bazel", - "build": "_bazel", - "workspace": "_bazel", - "bazelignore": "_bazel", - "bazelversion": "_bazel", - "h": "_c_1", - "aspx": "_html", - "ascx": "_html_1", - "asax": "_html_2", - "master": "_html_2", - "hh": "_cpp_1", - "hpp": "_cpp_1", - "hxx": "_cpp_1", - "h++": "_cpp_1", - "edn": "_clojure_1", - "cfc": "_coldfusion", - "cfm": "_coldfusion", - "litcoffee": "_coffee", - "config": "_config", - "cr": "_crystal", - "ecr": "_crystal_embedded", - "slang": "_crystal_embedded", - "cson": "_json", - "css.map": "_css", - "sss": "_css", - "csv": "_csv", - "xls": "_xls", - "xlsx": "_xls", - "cuh": "_cu_1", - "hu": "_cu_1", - "cake": "_cake", - "ctp": "_cake_php", - "d": "_d", - "doc": "_word", - "docx": "_word", - "ejs": "_ejs", - "ex": "_elixir", - "exs": "_elixir_script", - "elm": "_elm", - "ico": "_favicon", - "gitconfig": "_git", - "gitkeep": "_git", - "gitattributes": "_git", - "gitmodules": "_git", - "slide": "_go", - "article": "_go", - "gd": "_godot", - "godot": "_godot_1", - "tres": "_godot_2", - "tscn": "_godot_3", - "gradle": "_gradle", - "gsp": "_grails", - "gql": "_graphql", - "graphql": "_graphql", - "graphqls": "_graphql", - "hack": "_hacklang", - "haml": "_haml", - "hs": "_haskell", - "lhs": "_haskell", - "hx": "_haxe", - "hxs": "_haxe_1", - "hxp": "_haxe_2", - "hxml": "_haxe_3", - "jade": "_jade", - "class": "_java_1", - "classpath": "_java", - "js.map": "_javascript", - "cjs.map": "_javascript", - "mjs.map": "_javascript", - "spec.js": "_javascript_1", - "spec.cjs": "_javascript_1", - "spec.mjs": "_javascript_1", - "test.js": "_javascript_1", - "test.cjs": "_javascript_1", - "test.mjs": "_javascript_1", - "es": "_javascript", - "es5": "_javascript", - "es7": "_javascript", - "jinja": "_jinja", - "jinja2": "_jinja", - "kt": "_kotlin", - "kts": "_kotlin", - "liquid": "_liquid", - "ls": "_livescript", - "argdown": "_argdown", - "ad": "_argdown", - "mustache": "_mustache", - "stache": "_mustache", - "nim": "_nim", - "nims": "_nim", - "github-issues": "_github", - "ipynb": "_notebook", - "njk": "_nunjucks", - "nunjucks": "_nunjucks", - "nunjs": "_nunjucks", - "nunj": "_nunjucks", - "njs": "_nunjucks", - "nj": "_nunjucks", - "npm-debug.log": "_npm", - "npmignore": "_npm_1", - "npmrc": "_npm_1", - "ml": "_ocaml", - "mli": "_ocaml", - "cmx": "_ocaml", - "cmxa": "_ocaml", - "odata": "_odata", - "php.inc": "_php", - "pipeline": "_pipeline", - "pddl": "_pddl", - "plan": "_plan", - "happenings": "_happenings", - "prisma": "_prisma", - "pp": "_puppet", - "epp": "_puppet", - "purs": "_purescript", - "spec.jsx": "_react_1", - "test.jsx": "_react_1", - "cjsx": "_react", - "spec.tsx": "_react_1", - "test.tsx": "_react_1", - "re": "_reasonml", - "res": "_rescript", - "resi": "_rescript_1", - "r": "_R", - "rmd": "_R", - "erb": "_html_erb", - "erb.html": "_html_erb", - "html.erb": "_html_erb", - "sass": "_sass", - "springbeans": "_spring", - "slim": "_slim", - "smarty.tpl": "_smarty", - "tpl": "_smarty", - "sbt": "_sbt", - "scala": "_scala", - "sol": "_ethereum", - "styl": "_stylus", - "svelte": "_svelte", - "soql": "_db_1", - "tf": "_terraform", - "tf.json": "_terraform", - "tfvars": "_terraform", - "tfvars.json": "_terraform", - "dtx": "_tex_2", - "ins": "_tex_3", - "toml": "_config", - "twig": "_twig", - "spec.ts": "_typescript_1", - "test.ts": "_typescript_1", - "vala": "_vala", - "vapi": "_vala", - "component": "_html_3", - "vue": "_vue", - "wasm": "_wasm", - "wat": "_wat", - "pro": "_prolog", - "zig": "_zig", - "jar": "_zip", - "zip": "_zip_1", - "wgt": "_wgt", - "ai": "_illustrator", - "psd": "_photoshop", - "pdf": "_pdf", - "eot": "_font", - "ttf": "_font", - "woff": "_font", - "woff2": "_font", - "otf": "_font", - "avif": "_image", - "gif": "_image", - "jpg": "_image", - "jpeg": "_image", - "png": "_image", - "pxm": "_image", - "svg": "_svg", - "svgx": "_image", - "tiff": "_image", - "webp": "_image", - "sublime-project": "_sublime", - "sublime-workspace": "_sublime", - "mov": "_video", - "ogv": "_video", - "webm": "_video", - "avi": "_video", - "mpg": "_video", - "mp4": "_video", - "mp3": "_audio", - "ogg": "_audio", - "wav": "_audio", - "flac": "_audio", - "3ds": "_svg_1", - "3dm": "_svg_1", - "stl": "_svg_1", - "obj": "_svg_1", - "dae": "_svg_1", - "babelrc": "_babel", - "babelrc.js": "_babel", - "babelrc.cjs": "_babel", - "bazelrc": "_bazel_1", - "bowerrc": "_bower", - "dockerignore": "_docker_1", - "codeclimate.yml": "_code-climate", - "eslintrc": "_eslint", - "eslintrc.js": "_eslint", - "eslintrc.cjs": "_eslint", - "eslintrc.yaml": "_eslint", - "eslintrc.yml": "_eslint", - "eslintrc.json": "_eslint", - "eslintignore": "_eslint_1", - "firebaserc": "_firebase", - "gitlab-ci.yml": "_gitlab", - "jshintrc": "_javascript_2", - "jscsrc": "_javascript_2", - "stylelintrc": "_stylelint", - "stylelintrc.json": "_stylelint", - "stylelintrc.yaml": "_stylelint", - "stylelintrc.yml": "_stylelint", - "stylelintrc.js": "_stylelint", - "stylelintignore": "_stylelint_1", - "direnv": "_config", - "env": "_config", - "static": "_config", - "slugignore": "_config", - "tmp": "_clock_1", - "htaccess": "_config", - "key": "_lock", - "cert": "_lock", - "cer": "_lock", - "crt": "_lock", - "pem": "_lock", - "ds_store": "_ignored" - }, - "fileNames": { - "mix": "_hex", - "karma.conf.js": "_karma", - "karma.conf.cjs": "_karma", - "karma.conf.mjs": "_karma", - "karma.conf.coffee": "_karma", - "readme.md": "_info", - "readme.txt": "_info", - "readme": "_info", - "changelog.md": "_clock", - "changelog.txt": "_clock", - "changelog": "_clock", - "changes.md": "_clock", - "changes.txt": "_clock", - "changes": "_clock", - "version.md": "_clock", - "version.txt": "_clock", - "version": "_clock", - "mvnw": "_maven", - "pom.xml": "_maven", - "tsconfig.json": "_tsconfig", - "vite.config.js": "_vite", - "vite.config.ts": "_vite", - "vite.config.mjs": "_vite", - "vite.config.mts": "_vite", - "vite.config.cjs": "_vite", - "vite.config.cts": "_vite", - "swagger.json": "_json_1", - "swagger.yml": "_json_1", - "swagger.yaml": "_json_1", - "mime.types": "_config", - "jenkinsfile": "_jenkins", - "babel.config.js": "_babel", - "babel.config.json": "_babel", - "babel.config.cjs": "_babel", - "build": "_bazel", - "build.bazel": "_bazel", - "workspace": "_bazel", - "workspace.bazel": "_bazel", - "bower.json": "_bower", - "docker-healthcheck": "_docker_2", - "eslint.config.js": "_eslint", - "firebase.json": "_firebase", - "geckodriver": "_firefox", - "gruntfile.js": "_grunt", - "gruntfile.babel.js": "_grunt", - "gruntfile.coffee": "_grunt", - "gulpfile": "_gulp", - "gulpfile.js": "_gulp", - "ionic.config.json": "_ionic", - "ionic.project": "_ionic", - "platformio.ini": "_platformio", - "rollup.config.js": "_rollup", - "sass-lint.yml": "_sass", - "stylelint.config.js": "_stylelint", - "stylelint.config.cjs": "_stylelint", - "stylelint.config.mjs": "_stylelint", - "yarn.clean": "_yarn", - "yarn.lock": "_yarn", - "webpack.config.js": "_webpack", - "webpack.config.cjs": "_webpack", - "webpack.config.mjs": "_webpack", - "webpack.config.ts": "_webpack", - "webpack.config.build.js": "_webpack", - "webpack.config.build.cjs": "_webpack", - "webpack.config.build.mjs": "_webpack", - "webpack.config.build.ts": "_webpack", - "webpack.common.js": "_webpack", - "webpack.common.cjs": "_webpack", - "webpack.common.mjs": "_webpack", - "webpack.common.ts": "_webpack", - "webpack.dev.js": "_webpack", - "webpack.dev.cjs": "_webpack", - "webpack.dev.mjs": "_webpack", - "webpack.dev.ts": "_webpack", - "webpack.prod.js": "_webpack", - "webpack.prod.cjs": "_webpack", - "webpack.prod.mjs": "_webpack", - "webpack.prod.ts": "_webpack", - "license": "_license", - "licence": "_license", - "license.txt": "_license", - "licence.txt": "_license", - "license.md": "_license", - "licence.md": "_license", - "copying": "_license", - "copying.txt": "_license", - "copying.md": "_license", - "compiling": "_license_1", - "compiling.txt": "_license_1", - "compiling.md": "_license_1", - "contributing": "_license_2", - "contributing.txt": "_license_2", - "contributing.md": "_license_2", - "qmakefile": "_makefile_1", - "omakefile": "_makefile_2", - "cmakelists.txt": "_makefile_3", - "procfile": "_heroku", - "todo": "_todo", - "todo.txt": "_todo", - "todo.md": "_todo", - "npm-debug.log": "_npm_ignored" - }, - "languageIds": { - "bat": "_windows", - "clojure": "_clojure", - "coffeescript": "_coffee", - "jsonc": "_json", - "json": "_json", - "c": "_c", - "cpp": "_cpp", - "cuda-cpp": "_cu", - "csharp": "_c-sharp", - "css": "_css", - "dart": "_dart", - "dockerfile": "_docker", - "ignore": "_git", - "fsharp": "_f-sharp", - "git-commit": "_git", - "go": "_go2", - "groovy": "_grails", - "handlebars": "_mustache", - "html": "_html_3", - "properties": "_config", - "java": "_java", - "javascriptreact": "_react", - "javascript": "_javascript", - "julia": "_julia", - "tex": "_tex_1", - "latex": "_tex", - "less": "_less", - "lua": "_lua", - "makefile": "_makefile", - "markdown": "_markdown", - "objective-c": "_c_2", - "objective-cpp": "_cpp_2", - "perl": "_perl", - "php": "_php", - "powershell": "_powershell", - "jade": "_pug", - "python": "_python", - "r": "_R", - "razor": "_html", - "ruby": "_ruby", - "rust": "_rust", - "scss": "_sass", - "search-result": "_code-search", - "shellscript": "_shell", - "sql": "_db", - "swift": "_swift", - "typescript": "_typescript", - "typescriptreact": "_react", - "xml": "_xml", - "dockercompose": "_docker_3", - "yaml": "_yml", - "argdown": "_argdown", - "bicep": "_bicep", - "elixir": "_elixir", - "elm": "_elm", - "erb": "_html_erb", - "github-issues": "_github", - "gradle": "_gradle", - "godot": "_godot", - "haml": "_haml", - "haskell": "_haskell", - "haxe": "_haxe", - "jinja": "_jinja", - "kotlin": "_kotlin", - "mustache": "_mustache", - "nunjucks": "_nunjucks", - "ocaml": "_ocaml", - "rescript": "_rescript", - "sass": "_sass", - "stylus": "_stylus", - "terraform": "_terraform", - "todo": "_todo", - "vala": "_vala", - "vue": "_vue", - "jsonl": "_json", - "postcss": "_css", - "django-html": "_html_3", - "blade": "_php" - }, - "light": { - "file": "_default_light", - "fileExtensions": { - "bsl": "_bsl_light", - "mdo": "_mdo_light", - "cls": "_salesforce_light", - "apex": "_salesforce_light", - "asm": "_asm_light", - "s": "_asm_light", - "bicep": "_bicep_light", - "bzl": "_bazel_light", - "bazel": "_bazel_light", - "build": "_bazel_light", - "workspace": "_bazel_light", - "bazelignore": "_bazel_light", - "bazelversion": "_bazel_light", - "h": "_c_1_light", - "aspx": "_html_light", - "ascx": "_html_1_light", - "asax": "_html_2_light", - "master": "_html_2_light", - "hh": "_cpp_1_light", - "hpp": "_cpp_1_light", - "hxx": "_cpp_1_light", - "h++": "_cpp_1_light", - "edn": "_clojure_1_light", - "cfc": "_coldfusion_light", - "cfm": "_coldfusion_light", - "litcoffee": "_coffee_light", - "config": "_config_light", - "cr": "_crystal_light", - "ecr": "_crystal_embedded_light", - "slang": "_crystal_embedded_light", - "cson": "_json_light", - "css.map": "_css_light", - "sss": "_css_light", - "csv": "_csv_light", - "xls": "_xls_light", - "xlsx": "_xls_light", - "cuh": "_cu_1_light", - "hu": "_cu_1_light", - "cake": "_cake_light", - "ctp": "_cake_php_light", - "d": "_d_light", - "doc": "_word_light", - "docx": "_word_light", - "ejs": "_ejs_light", - "ex": "_elixir_light", - "exs": "_elixir_script_light", - "elm": "_elm_light", - "ico": "_favicon_light", - "gitconfig": "_git_light", - "gitkeep": "_git_light", - "gitattributes": "_git_light", - "gitmodules": "_git_light", - "slide": "_go_light", - "article": "_go_light", - "gd": "_godot_light", - "godot": "_godot_1_light", - "tres": "_godot_2_light", - "tscn": "_godot_3_light", - "gradle": "_gradle_light", - "gsp": "_grails_light", - "gql": "_graphql_light", - "graphql": "_graphql_light", - "graphqls": "_graphql_light", - "hack": "_hacklang_light", - "haml": "_haml_light", - "hs": "_haskell_light", - "lhs": "_haskell_light", - "hx": "_haxe_light", - "hxs": "_haxe_1_light", - "hxp": "_haxe_2_light", - "hxml": "_haxe_3_light", - "jade": "_jade_light", - "class": "_java_1_light", - "classpath": "_java_light", - "js.map": "_javascript_light", - "cjs.map": "_javascript_light", - "mjs.map": "_javascript_light", - "spec.js": "_javascript_1_light", - "spec.cjs": "_javascript_1_light", - "spec.mjs": "_javascript_1_light", - "test.js": "_javascript_1_light", - "test.cjs": "_javascript_1_light", - "test.mjs": "_javascript_1_light", - "es": "_javascript_light", - "es5": "_javascript_light", - "es7": "_javascript_light", - "jinja": "_jinja_light", - "jinja2": "_jinja_light", - "kt": "_kotlin_light", - "kts": "_kotlin_light", - "liquid": "_liquid_light", - "ls": "_livescript_light", - "argdown": "_argdown_light", - "ad": "_argdown_light", - "mustache": "_mustache_light", - "stache": "_mustache_light", - "nim": "_nim_light", - "nims": "_nim_light", - "github-issues": "_github_light", - "ipynb": "_notebook_light", - "njk": "_nunjucks_light", - "nunjucks": "_nunjucks_light", - "nunjs": "_nunjucks_light", - "nunj": "_nunjucks_light", - "njs": "_nunjucks_light", - "nj": "_nunjucks_light", - "npm-debug.log": "_npm_light", - "npmignore": "_npm_1_light", - "npmrc": "_npm_1_light", - "ml": "_ocaml_light", - "mli": "_ocaml_light", - "cmx": "_ocaml_light", - "cmxa": "_ocaml_light", - "odata": "_odata_light", - "php.inc": "_php_light", - "pipeline": "_pipeline_light", - "pddl": "_pddl_light", - "plan": "_plan_light", - "happenings": "_happenings_light", - "prisma": "_prisma_light", - "pp": "_puppet_light", - "epp": "_puppet_light", - "purs": "_purescript_light", - "spec.jsx": "_react_1_light", - "test.jsx": "_react_1_light", - "cjsx": "_react_light", - "spec.tsx": "_react_1_light", - "test.tsx": "_react_1_light", - "re": "_reasonml_light", - "res": "_rescript_light", - "resi": "_rescript_1_light", - "r": "_R_light", - "rmd": "_R_light", - "erb": "_html_erb_light", - "erb.html": "_html_erb_light", - "html.erb": "_html_erb_light", - "sass": "_sass_light", - "springbeans": "_spring_light", - "slim": "_slim_light", - "smarty.tpl": "_smarty_light", - "tpl": "_smarty_light", - "sbt": "_sbt_light", - "scala": "_scala_light", - "sol": "_ethereum_light", - "styl": "_stylus_light", - "svelte": "_svelte_light", - "soql": "_db_1_light", - "tf": "_terraform_light", - "tf.json": "_terraform_light", - "tfvars": "_terraform_light", - "tfvars.json": "_terraform_light", - "dtx": "_tex_2_light", - "ins": "_tex_3_light", - "toml": "_config_light", - "twig": "_twig_light", - "spec.ts": "_typescript_1_light", - "test.ts": "_typescript_1_light", - "vala": "_vala_light", - "vapi": "_vala_light", - "component": "_html_3_light", - "vue": "_vue_light", - "wasm": "_wasm_light", - "wat": "_wat_light", - "pro": "_prolog_light", - "zig": "_zig_light", - "jar": "_zip_light", - "zip": "_zip_1_light", - "wgt": "_wgt_light", - "ai": "_illustrator_light", - "psd": "_photoshop_light", - "pdf": "_pdf_light", - "eot": "_font_light", - "ttf": "_font_light", - "woff": "_font_light", - "woff2": "_font_light", - "otf": "_font_light", - "avif": "_image_light", - "gif": "_image_light", - "jpg": "_image_light", - "jpeg": "_image_light", - "png": "_image_light", - "pxm": "_image_light", - "svg": "_svg_light", - "svgx": "_image_light", - "tiff": "_image_light", - "webp": "_image_light", - "sublime-project": "_sublime_light", - "sublime-workspace": "_sublime_light", - "mov": "_video_light", - "ogv": "_video_light", - "webm": "_video_light", - "avi": "_video_light", - "mpg": "_video_light", - "mp4": "_video_light", - "mp3": "_audio_light", - "ogg": "_audio_light", - "wav": "_audio_light", - "flac": "_audio_light", - "3ds": "_svg_1_light", - "3dm": "_svg_1_light", - "stl": "_svg_1_light", - "obj": "_svg_1_light", - "dae": "_svg_1_light", - "babelrc": "_babel_light", - "babelrc.js": "_babel_light", - "babelrc.cjs": "_babel_light", - "bazelrc": "_bazel_1_light", - "bowerrc": "_bower_light", - "dockerignore": "_docker_1_light", - "codeclimate.yml": "_code-climate_light", - "eslintrc": "_eslint_light", - "eslintrc.js": "_eslint_light", - "eslintrc.cjs": "_eslint_light", - "eslintrc.yaml": "_eslint_light", - "eslintrc.yml": "_eslint_light", - "eslintrc.json": "_eslint_light", - "eslintignore": "_eslint_1_light", - "firebaserc": "_firebase_light", - "gitlab-ci.yml": "_gitlab_light", - "jshintrc": "_javascript_2_light", - "jscsrc": "_javascript_2_light", - "stylelintrc": "_stylelint_light", - "stylelintrc.json": "_stylelint_light", - "stylelintrc.yaml": "_stylelint_light", - "stylelintrc.yml": "_stylelint_light", - "stylelintrc.js": "_stylelint_light", - "stylelintignore": "_stylelint_1_light", - "direnv": "_config_light", - "env": "_config_light", - "static": "_config_light", - "slugignore": "_config_light", - "tmp": "_clock_1_light", - "htaccess": "_config_light", - "key": "_lock_light", - "cert": "_lock_light", - "cer": "_lock_light", - "crt": "_lock_light", - "pem": "_lock_light", - "ds_store": "_ignored_light" - }, - "languageIds": { - "bat": "_windows_light", - "clojure": "_clojure_light", - "coffeescript": "_coffee_light", - "jsonc": "_json_light", - "json": "_json_light", - "c": "_c_light", - "cpp": "_cpp_light", - "cuda-cpp": "_cu_light", - "csharp": "_c-sharp_light", - "css": "_css_light", - "dart": "_dart_light", - "dockerfile": "_docker_light", - "ignore": "_git_light", - "fsharp": "_f-sharp_light", - "git-commit": "_git_light", - "go": "_go2_light", - "groovy": "_grails_light", - "handlebars": "_mustache_light", - "html": "_html_3_light", - "properties": "_config_light", - "java": "_java_light", - "javascriptreact": "_react_light", - "javascript": "_javascript_light", - "julia": "_julia_light", - "tex": "_tex_1_light", - "latex": "_tex_light", - "less": "_less_light", - "lua": "_lua_light", - "makefile": "_makefile_light", - "markdown": "_markdown_light", - "objective-c": "_c_2_light", - "objective-cpp": "_cpp_2_light", - "perl": "_perl_light", - "php": "_php_light", - "powershell": "_powershell_light", - "jade": "_pug_light", - "python": "_python_light", - "r": "_R_light", - "razor": "_html_light", - "ruby": "_ruby_light", - "rust": "_rust_light", - "scss": "_sass_light", - "search-result": "_code-search_light", - "shellscript": "_shell_light", - "sql": "_db_light", - "swift": "_swift_light", - "typescript": "_typescript_light", - "typescriptreact": "_react_light", - "xml": "_xml_light", - "dockercompose": "_docker_3_light", - "yaml": "_yml_light", - "argdown": "_argdown_light", - "bicep": "_bicep_light", - "elixir": "_elixir_light", - "elm": "_elm_light", - "erb": "_html_erb_light", - "github-issues": "_github_light", - "gradle": "_gradle_light", - "godot": "_godot_light", - "haml": "_haml_light", - "haskell": "_haskell_light", - "haxe": "_haxe_light", - "jinja": "_jinja_light", - "kotlin": "_kotlin_light", - "mustache": "_mustache_light", - "nunjucks": "_nunjucks_light", - "ocaml": "_ocaml_light", - "rescript": "_rescript_light", - "sass": "_sass_light", - "stylus": "_stylus_light", - "terraform": "_terraform_light", - "vala": "_vala_light", - "vue": "_vue_light", - "jsonl": "_json_light", - "postcss": "_css_light", - "django-html": "_html_3_light", - "blade": "_php_light" - }, - "fileNames": { - "mix": "_hex_light", - "karma.conf.js": "_karma_light", - "karma.conf.cjs": "_karma_light", - "karma.conf.mjs": "_karma_light", - "karma.conf.coffee": "_karma_light", - "readme.md": "_info_light", - "readme.txt": "_info_light", - "readme": "_info_light", - "changelog.md": "_clock_light", - "changelog.txt": "_clock_light", - "changelog": "_clock_light", - "changes.md": "_clock_light", - "changes.txt": "_clock_light", - "changes": "_clock_light", - "version.md": "_clock_light", - "version.txt": "_clock_light", - "version": "_clock_light", - "mvnw": "_maven_light", - "pom.xml": "_maven_light", - "tsconfig.json": "_tsconfig_light", - "vite.config.js": "_vite_light", - "vite.config.ts": "_vite_light", - "vite.config.mjs": "_vite_light", - "vite.config.mts": "_vite_light", - "vite.config.cjs": "_vite_light", - "vite.config.cts": "_vite_light", - "swagger.json": "_json_1_light", - "swagger.yml": "_json_1_light", - "swagger.yaml": "_json_1_light", - "mime.types": "_config_light", - "jenkinsfile": "_jenkins_light", - "babel.config.js": "_babel_light", - "babel.config.json": "_babel_light", - "babel.config.cjs": "_babel_light", - "build": "_bazel_light", - "build.bazel": "_bazel_light", - "workspace": "_bazel_light", - "workspace.bazel": "_bazel_light", - "bower.json": "_bower_light", - "docker-healthcheck": "_docker_2_light", - "eslint.config.js": "_eslint_light", - "firebase.json": "_firebase_light", - "geckodriver": "_firefox_light", - "gruntfile.js": "_grunt_light", - "gruntfile.babel.js": "_grunt_light", - "gruntfile.coffee": "_grunt_light", - "gulpfile": "_gulp_light", - "gulpfile.js": "_gulp_light", - "ionic.config.json": "_ionic_light", - "ionic.project": "_ionic_light", - "platformio.ini": "_platformio_light", - "rollup.config.js": "_rollup_light", - "sass-lint.yml": "_sass_light", - "stylelint.config.js": "_stylelint_light", - "stylelint.config.cjs": "_stylelint_light", - "stylelint.config.mjs": "_stylelint_light", - "yarn.clean": "_yarn_light", - "yarn.lock": "_yarn_light", - "webpack.config.js": "_webpack_light", - "webpack.config.cjs": "_webpack_light", - "webpack.config.mjs": "_webpack_light", - "webpack.config.ts": "_webpack_light", - "webpack.config.build.js": "_webpack_light", - "webpack.config.build.cjs": "_webpack_light", - "webpack.config.build.mjs": "_webpack_light", - "webpack.config.build.ts": "_webpack_light", - "webpack.common.js": "_webpack_light", - "webpack.common.cjs": "_webpack_light", - "webpack.common.mjs": "_webpack_light", - "webpack.common.ts": "_webpack_light", - "webpack.dev.js": "_webpack_light", - "webpack.dev.cjs": "_webpack_light", - "webpack.dev.mjs": "_webpack_light", - "webpack.dev.ts": "_webpack_light", - "webpack.prod.js": "_webpack_light", - "webpack.prod.cjs": "_webpack_light", - "webpack.prod.mjs": "_webpack_light", - "webpack.prod.ts": "_webpack_light", - "license": "_license_light", - "licence": "_license_light", - "license.txt": "_license_light", - "licence.txt": "_license_light", - "license.md": "_license_light", - "licence.md": "_license_light", - "copying": "_license_light", - "copying.txt": "_license_light", - "copying.md": "_license_light", - "compiling": "_license_1_light", - "compiling.txt": "_license_1_light", - "compiling.md": "_license_1_light", - "contributing": "_license_2_light", - "contributing.txt": "_license_2_light", - "contributing.md": "_license_2_light", - "qmakefile": "_makefile_1_light", - "omakefile": "_makefile_2_light", - "cmakelists.txt": "_makefile_3_light", - "procfile": "_heroku_light", - "npm-debug.log": "_npm_ignored_light" - } - }, - "version": "https://github.com/jesseweed/seti-ui/commit/1cac4f30f93cc898103c62dde41823a09b0d7b74" +{ + "information_for_contributors": [ + "This file has been generated from data in https://github.com/DecimalTurn/seti-ui", + "- icon definitions: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/_fonts/seti.less", + "- icon colors: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/ui-variables.less", + "- file associations: https://github.com/DecimalTurn/seti-ui/blob/vba/styles/components/icons/mapping.less", + "If you want to provide a fix or improvement, please create a pull request against the DecimalTurn/seti-ui repository.", + "Once accepted there, we are happy to receive an update request." + ], + "fonts": [ + { + "id": "seti", + "src": [ + { + "path": "./seti.woff", + "format": "woff" + } + ], + "weight": "normal", + "style": "normal", + "size": "150%" + } + ], + "iconDefinitions": { + "_R_light": { + "fontCharacter": "\\E07B", + "fontColor": "#498ba7" + }, + "_R": { + "fontCharacter": "\\E07B", + "fontColor": "#519aba" + }, + "_argdown_light": { + "fontCharacter": "\\E002", + "fontColor": "#498ba7" + }, + "_argdown": { + "fontCharacter": "\\E002", + "fontColor": "#519aba" + }, + "_asm_light": { + "fontCharacter": "\\E003", + "fontColor": "#b8383d" + }, + "_asm": { + "fontCharacter": "\\E003", + "fontColor": "#cc3e44" + }, + "_audio_light": { + "fontCharacter": "\\E004", + "fontColor": "#9068b0" + }, + "_audio": { + "fontCharacter": "\\E004", + "fontColor": "#a074c4" + }, + "_babel_light": { + "fontCharacter": "\\E005", + "fontColor": "#b7b73b" + }, + "_babel": { + "fontCharacter": "\\E005", + "fontColor": "#cbcb41" + }, + "_bazel_light": { + "fontCharacter": "\\E006", + "fontColor": "#7fae42" + }, + "_bazel": { + "fontCharacter": "\\E006", + "fontColor": "#8dc149" + }, + "_bazel_1_light": { + "fontCharacter": "\\E006", + "fontColor": "#455155" + }, + "_bazel_1": { + "fontCharacter": "\\E006", + "fontColor": "#4d5a5e" + }, + "_bicep_light": { + "fontCharacter": "\\E007", + "fontColor": "#498ba7" + }, + "_bicep": { + "fontCharacter": "\\E007", + "fontColor": "#519aba" + }, + "_bower_light": { + "fontCharacter": "\\E008", + "fontColor": "#cc6d2e" + }, + "_bower": { + "fontCharacter": "\\E008", + "fontColor": "#e37933" + }, + "_bsl_light": { + "fontCharacter": "\\E009", + "fontColor": "#b8383d" + }, + "_bsl": { + "fontCharacter": "\\E009", + "fontColor": "#cc3e44" + }, + "_c_light": { + "fontCharacter": "\\E00B", + "fontColor": "#498ba7" + }, + "_c": { + "fontCharacter": "\\E00B", + "fontColor": "#519aba" + }, + "_c-sharp_light": { + "fontCharacter": "\\E00A", + "fontColor": "#498ba7" + }, + "_c-sharp": { + "fontCharacter": "\\E00A", + "fontColor": "#519aba" + }, + "_c_1_light": { + "fontCharacter": "\\E00B", + "fontColor": "#9068b0" + }, + "_c_1": { + "fontCharacter": "\\E00B", + "fontColor": "#a074c4" + }, + "_c_2_light": { + "fontCharacter": "\\E00B", + "fontColor": "#b7b73b" + }, + "_c_2": { + "fontCharacter": "\\E00B", + "fontColor": "#cbcb41" + }, + "_cake_light": { + "fontCharacter": "\\E00C", + "fontColor": "#b8383d" + }, + "_cake": { + "fontCharacter": "\\E00C", + "fontColor": "#cc3e44" + }, + "_cake_php_light": { + "fontCharacter": "\\E00D", + "fontColor": "#b8383d" + }, + "_cake_php": { + "fontCharacter": "\\E00D", + "fontColor": "#cc3e44" + }, + "_clock_light": { + "fontCharacter": "\\E011", + "fontColor": "#498ba7" + }, + "_clock": { + "fontCharacter": "\\E011", + "fontColor": "#519aba" + }, + "_clock_1_light": { + "fontCharacter": "\\E011", + "fontColor": "#627379" + }, + "_clock_1": { + "fontCharacter": "\\E011", + "fontColor": "#6d8086" + }, + "_clojure_light": { + "fontCharacter": "\\E012", + "fontColor": "#7fae42" + }, + "_clojure": { + "fontCharacter": "\\E012", + "fontColor": "#8dc149" + }, + "_clojure_1_light": { + "fontCharacter": "\\E012", + "fontColor": "#498ba7" + }, + "_clojure_1": { + "fontCharacter": "\\E012", + "fontColor": "#519aba" + }, + "_code-climate_light": { + "fontCharacter": "\\E013", + "fontColor": "#7fae42" + }, + "_code-climate": { + "fontCharacter": "\\E013", + "fontColor": "#8dc149" + }, + "_code-search_light": { + "fontCharacter": "\\E014", + "fontColor": "#9068b0" + }, + "_code-search": { + "fontCharacter": "\\E014", + "fontColor": "#a074c4" + }, + "_coffee_light": { + "fontCharacter": "\\E015", + "fontColor": "#b7b73b" + }, + "_coffee": { + "fontCharacter": "\\E015", + "fontColor": "#cbcb41" + }, + "_coldfusion_light": { + "fontCharacter": "\\E017", + "fontColor": "#498ba7" + }, + "_coldfusion": { + "fontCharacter": "\\E017", + "fontColor": "#519aba" + }, + "_config_light": { + "fontCharacter": "\\E018", + "fontColor": "#627379" + }, + "_config": { + "fontCharacter": "\\E018", + "fontColor": "#6d8086" + }, + "_cpp_light": { + "fontCharacter": "\\E019", + "fontColor": "#498ba7" + }, + "_cpp": { + "fontCharacter": "\\E019", + "fontColor": "#519aba" + }, + "_cpp_1_light": { + "fontCharacter": "\\E019", + "fontColor": "#9068b0" + }, + "_cpp_1": { + "fontCharacter": "\\E019", + "fontColor": "#a074c4" + }, + "_cpp_2_light": { + "fontCharacter": "\\E019", + "fontColor": "#b7b73b" + }, + "_cpp_2": { + "fontCharacter": "\\E019", + "fontColor": "#cbcb41" + }, + "_crystal_light": { + "fontCharacter": "\\E01A", + "fontColor": "#bfc2c1" + }, + "_crystal": { + "fontCharacter": "\\E01A", + "fontColor": "#d4d7d6" + }, + "_crystal_embedded_light": { + "fontCharacter": "\\E01B", + "fontColor": "#bfc2c1" + }, + "_crystal_embedded": { + "fontCharacter": "\\E01B", + "fontColor": "#d4d7d6" + }, + "_css_light": { + "fontCharacter": "\\E01C", + "fontColor": "#498ba7" + }, + "_css": { + "fontCharacter": "\\E01C", + "fontColor": "#519aba" + }, + "_csv_light": { + "fontCharacter": "\\E01D", + "fontColor": "#7fae42" + }, + "_csv": { + "fontCharacter": "\\E01D", + "fontColor": "#8dc149" + }, + "_cu_light": { + "fontCharacter": "\\E01E", + "fontColor": "#7fae42" + }, + "_cu": { + "fontCharacter": "\\E01E", + "fontColor": "#8dc149" + }, + "_cu_1_light": { + "fontCharacter": "\\E01E", + "fontColor": "#9068b0" + }, + "_cu_1": { + "fontCharacter": "\\E01E", + "fontColor": "#a074c4" + }, + "_d_light": { + "fontCharacter": "\\E01F", + "fontColor": "#b8383d" + }, + "_d": { + "fontCharacter": "\\E01F", + "fontColor": "#cc3e44" + }, + "_dart_light": { + "fontCharacter": "\\E020", + "fontColor": "#498ba7" + }, + "_dart": { + "fontCharacter": "\\E020", + "fontColor": "#519aba" + }, + "_db_light": { + "fontCharacter": "\\E021", + "fontColor": "#dd4b78" + }, + "_db": { + "fontCharacter": "\\E021", + "fontColor": "#f55385" + }, + "_db_1_light": { + "fontCharacter": "\\E021", + "fontColor": "#498ba7" + }, + "_db_1": { + "fontCharacter": "\\E021", + "fontColor": "#519aba" + }, + "_default_light": { + "fontCharacter": "\\E022", + "fontColor": "#bfc2c1" + }, + "_default": { + "fontCharacter": "\\E022", + "fontColor": "#d4d7d6" + }, + "_docker_light": { + "fontCharacter": "\\E024", + "fontColor": "#498ba7" + }, + "_docker": { + "fontCharacter": "\\E024", + "fontColor": "#519aba" + }, + "_docker_1_light": { + "fontCharacter": "\\E024", + "fontColor": "#455155" + }, + "_docker_1": { + "fontCharacter": "\\E024", + "fontColor": "#4d5a5e" + }, + "_docker_2_light": { + "fontCharacter": "\\E024", + "fontColor": "#7fae42" + }, + "_docker_2": { + "fontCharacter": "\\E024", + "fontColor": "#8dc149" + }, + "_docker_3_light": { + "fontCharacter": "\\E024", + "fontColor": "#dd4b78" + }, + "_docker_3": { + "fontCharacter": "\\E024", + "fontColor": "#f55385" + }, + "_ejs_light": { + "fontCharacter": "\\E026", + "fontColor": "#b7b73b" + }, + "_ejs": { + "fontCharacter": "\\E026", + "fontColor": "#cbcb41" + }, + "_elixir_light": { + "fontCharacter": "\\E027", + "fontColor": "#9068b0" + }, + "_elixir": { + "fontCharacter": "\\E027", + "fontColor": "#a074c4" + }, + "_elixir_script_light": { + "fontCharacter": "\\E028", + "fontColor": "#9068b0" + }, + "_elixir_script": { + "fontCharacter": "\\E028", + "fontColor": "#a074c4" + }, + "_elm_light": { + "fontCharacter": "\\E029", + "fontColor": "#498ba7" + }, + "_elm": { + "fontCharacter": "\\E029", + "fontColor": "#519aba" + }, + "_eslint_light": { + "fontCharacter": "\\E02B", + "fontColor": "#9068b0" + }, + "_eslint": { + "fontCharacter": "\\E02B", + "fontColor": "#a074c4" + }, + "_eslint_1_light": { + "fontCharacter": "\\E02B", + "fontColor": "#455155" + }, + "_eslint_1": { + "fontCharacter": "\\E02B", + "fontColor": "#4d5a5e" + }, + "_ethereum_light": { + "fontCharacter": "\\E02C", + "fontColor": "#498ba7" + }, + "_ethereum": { + "fontCharacter": "\\E02C", + "fontColor": "#519aba" + }, + "_f-sharp_light": { + "fontCharacter": "\\E02D", + "fontColor": "#498ba7" + }, + "_f-sharp": { + "fontCharacter": "\\E02D", + "fontColor": "#519aba" + }, + "_favicon_light": { + "fontCharacter": "\\E02E", + "fontColor": "#b7b73b" + }, + "_favicon": { + "fontCharacter": "\\E02E", + "fontColor": "#cbcb41" + }, + "_firebase_light": { + "fontCharacter": "\\E02F", + "fontColor": "#cc6d2e" + }, + "_firebase": { + "fontCharacter": "\\E02F", + "fontColor": "#e37933" + }, + "_firefox_light": { + "fontCharacter": "\\E030", + "fontColor": "#cc6d2e" + }, + "_firefox": { + "fontCharacter": "\\E030", + "fontColor": "#e37933" + }, + "_font_light": { + "fontCharacter": "\\E032", + "fontColor": "#b8383d" + }, + "_font": { + "fontCharacter": "\\E032", + "fontColor": "#cc3e44" + }, + "_git_light": { + "fontCharacter": "\\E033", + "fontColor": "#3b4b52" + }, + "_git": { + "fontCharacter": "\\E033", + "fontColor": "#41535b" + }, + "_github_light": { + "fontCharacter": "\\E034", + "fontColor": "#bfc2c1" + }, + "_github": { + "fontCharacter": "\\E034", + "fontColor": "#d4d7d6" + }, + "_gitlab_light": { + "fontCharacter": "\\E035", + "fontColor": "#cc6d2e" + }, + "_gitlab": { + "fontCharacter": "\\E035", + "fontColor": "#e37933" + }, + "_go_light": { + "fontCharacter": "\\E038", + "fontColor": "#498ba7" + }, + "_go": { + "fontCharacter": "\\E038", + "fontColor": "#519aba" + }, + "_go2_light": { + "fontCharacter": "\\E039", + "fontColor": "#498ba7" + }, + "_go2": { + "fontCharacter": "\\E039", + "fontColor": "#519aba" + }, + "_godot_light": { + "fontCharacter": "\\E03A", + "fontColor": "#498ba7" + }, + "_godot": { + "fontCharacter": "\\E03A", + "fontColor": "#519aba" + }, + "_godot_1_light": { + "fontCharacter": "\\E03A", + "fontColor": "#b8383d" + }, + "_godot_1": { + "fontCharacter": "\\E03A", + "fontColor": "#cc3e44" + }, + "_godot_2_light": { + "fontCharacter": "\\E03A", + "fontColor": "#b7b73b" + }, + "_godot_2": { + "fontCharacter": "\\E03A", + "fontColor": "#cbcb41" + }, + "_godot_3_light": { + "fontCharacter": "\\E03A", + "fontColor": "#9068b0" + }, + "_godot_3": { + "fontCharacter": "\\E03A", + "fontColor": "#a074c4" + }, + "_gradle_light": { + "fontCharacter": "\\E03B", + "fontColor": "#498ba7" + }, + "_gradle": { + "fontCharacter": "\\E03B", + "fontColor": "#519aba" + }, + "_grails_light": { + "fontCharacter": "\\E03C", + "fontColor": "#7fae42" + }, + "_grails": { + "fontCharacter": "\\E03C", + "fontColor": "#8dc149" + }, + "_graphql_light": { + "fontCharacter": "\\E03D", + "fontColor": "#dd4b78" + }, + "_graphql": { + "fontCharacter": "\\E03D", + "fontColor": "#f55385" + }, + "_grunt_light": { + "fontCharacter": "\\E03E", + "fontColor": "#cc6d2e" + }, + "_grunt": { + "fontCharacter": "\\E03E", + "fontColor": "#e37933" + }, + "_gulp_light": { + "fontCharacter": "\\E03F", + "fontColor": "#b8383d" + }, + "_gulp": { + "fontCharacter": "\\E03F", + "fontColor": "#cc3e44" + }, + "_hacklang_light": { + "fontCharacter": "\\E040", + "fontColor": "#cc6d2e" + }, + "_hacklang": { + "fontCharacter": "\\E040", + "fontColor": "#e37933" + }, + "_haml_light": { + "fontCharacter": "\\E041", + "fontColor": "#b8383d" + }, + "_haml": { + "fontCharacter": "\\E041", + "fontColor": "#cc3e44" + }, + "_happenings_light": { + "fontCharacter": "\\E042", + "fontColor": "#498ba7" + }, + "_happenings": { + "fontCharacter": "\\E042", + "fontColor": "#519aba" + }, + "_haskell_light": { + "fontCharacter": "\\E043", + "fontColor": "#9068b0" + }, + "_haskell": { + "fontCharacter": "\\E043", + "fontColor": "#a074c4" + }, + "_haxe_light": { + "fontCharacter": "\\E044", + "fontColor": "#cc6d2e" + }, + "_haxe": { + "fontCharacter": "\\E044", + "fontColor": "#e37933" + }, + "_haxe_1_light": { + "fontCharacter": "\\E044", + "fontColor": "#b7b73b" + }, + "_haxe_1": { + "fontCharacter": "\\E044", + "fontColor": "#cbcb41" + }, + "_haxe_2_light": { + "fontCharacter": "\\E044", + "fontColor": "#498ba7" + }, + "_haxe_2": { + "fontCharacter": "\\E044", + "fontColor": "#519aba" + }, + "_haxe_3_light": { + "fontCharacter": "\\E044", + "fontColor": "#9068b0" + }, + "_haxe_3": { + "fontCharacter": "\\E044", + "fontColor": "#a074c4" + }, + "_heroku_light": { + "fontCharacter": "\\E045", + "fontColor": "#9068b0" + }, + "_heroku": { + "fontCharacter": "\\E045", + "fontColor": "#a074c4" + }, + "_hex_light": { + "fontCharacter": "\\E046", + "fontColor": "#b8383d" + }, + "_hex": { + "fontCharacter": "\\E046", + "fontColor": "#cc3e44" + }, + "_html_light": { + "fontCharacter": "\\E047", + "fontColor": "#498ba7" + }, + "_html": { + "fontCharacter": "\\E047", + "fontColor": "#519aba" + }, + "_html_1_light": { + "fontCharacter": "\\E047", + "fontColor": "#7fae42" + }, + "_html_1": { + "fontCharacter": "\\E047", + "fontColor": "#8dc149" + }, + "_html_2_light": { + "fontCharacter": "\\E047", + "fontColor": "#b7b73b" + }, + "_html_2": { + "fontCharacter": "\\E047", + "fontColor": "#cbcb41" + }, + "_html_3_light": { + "fontCharacter": "\\E047", + "fontColor": "#cc6d2e" + }, + "_html_3": { + "fontCharacter": "\\E047", + "fontColor": "#e37933" + }, + "_html_erb_light": { + "fontCharacter": "\\E048", + "fontColor": "#b8383d" + }, + "_html_erb": { + "fontCharacter": "\\E048", + "fontColor": "#cc3e44" + }, + "_ignored_light": { + "fontCharacter": "\\E049", + "fontColor": "#3b4b52" + }, + "_ignored": { + "fontCharacter": "\\E049", + "fontColor": "#41535b" + }, + "_illustrator_light": { + "fontCharacter": "\\E04A", + "fontColor": "#b7b73b" + }, + "_illustrator": { + "fontCharacter": "\\E04A", + "fontColor": "#cbcb41" + }, + "_image_light": { + "fontCharacter": "\\E04B", + "fontColor": "#9068b0" + }, + "_image": { + "fontCharacter": "\\E04B", + "fontColor": "#a074c4" + }, + "_info_light": { + "fontCharacter": "\\E04C", + "fontColor": "#498ba7" + }, + "_info": { + "fontCharacter": "\\E04C", + "fontColor": "#519aba" + }, + "_ionic_light": { + "fontCharacter": "\\E04D", + "fontColor": "#498ba7" + }, + "_ionic": { + "fontCharacter": "\\E04D", + "fontColor": "#519aba" + }, + "_jade_light": { + "fontCharacter": "\\E04E", + "fontColor": "#b8383d" + }, + "_jade": { + "fontCharacter": "\\E04E", + "fontColor": "#cc3e44" + }, + "_java_light": { + "fontCharacter": "\\E04F", + "fontColor": "#b8383d" + }, + "_java": { + "fontCharacter": "\\E04F", + "fontColor": "#cc3e44" + }, + "_java_1_light": { + "fontCharacter": "\\E04F", + "fontColor": "#498ba7" + }, + "_java_1": { + "fontCharacter": "\\E04F", + "fontColor": "#519aba" + }, + "_javascript_light": { + "fontCharacter": "\\E050", + "fontColor": "#b7b73b" + }, + "_javascript": { + "fontCharacter": "\\E050", + "fontColor": "#cbcb41" + }, + "_javascript_1_light": { + "fontCharacter": "\\E050", + "fontColor": "#cc6d2e" + }, + "_javascript_1": { + "fontCharacter": "\\E050", + "fontColor": "#e37933" + }, + "_javascript_2_light": { + "fontCharacter": "\\E050", + "fontColor": "#498ba7" + }, + "_javascript_2": { + "fontCharacter": "\\E050", + "fontColor": "#519aba" + }, + "_jenkins_light": { + "fontCharacter": "\\E051", + "fontColor": "#b8383d" + }, + "_jenkins": { + "fontCharacter": "\\E051", + "fontColor": "#cc3e44" + }, + "_jinja_light": { + "fontCharacter": "\\E052", + "fontColor": "#b8383d" + }, + "_jinja": { + "fontCharacter": "\\E052", + "fontColor": "#cc3e44" + }, + "_json_light": { + "fontCharacter": "\\E053", + "fontColor": "#b7b73b" + }, + "_json": { + "fontCharacter": "\\E053", + "fontColor": "#cbcb41" + }, + "_json_1_light": { + "fontCharacter": "\\E053", + "fontColor": "#7fae42" + }, + "_json_1": { + "fontCharacter": "\\E053", + "fontColor": "#8dc149" + }, + "_julia_light": { + "fontCharacter": "\\E055", + "fontColor": "#9068b0" + }, + "_julia": { + "fontCharacter": "\\E055", + "fontColor": "#a074c4" + }, + "_karma_light": { + "fontCharacter": "\\E056", + "fontColor": "#7fae42" + }, + "_karma": { + "fontCharacter": "\\E056", + "fontColor": "#8dc149" + }, + "_kotlin_light": { + "fontCharacter": "\\E057", + "fontColor": "#cc6d2e" + }, + "_kotlin": { + "fontCharacter": "\\E057", + "fontColor": "#e37933" + }, + "_less_light": { + "fontCharacter": "\\E058", + "fontColor": "#498ba7" + }, + "_less": { + "fontCharacter": "\\E058", + "fontColor": "#519aba" + }, + "_license_light": { + "fontCharacter": "\\E059", + "fontColor": "#b7b73b" + }, + "_license": { + "fontCharacter": "\\E059", + "fontColor": "#cbcb41" + }, + "_license_1_light": { + "fontCharacter": "\\E059", + "fontColor": "#cc6d2e" + }, + "_license_1": { + "fontCharacter": "\\E059", + "fontColor": "#e37933" + }, + "_license_2_light": { + "fontCharacter": "\\E059", + "fontColor": "#b8383d" + }, + "_license_2": { + "fontCharacter": "\\E059", + "fontColor": "#cc3e44" + }, + "_liquid_light": { + "fontCharacter": "\\E05A", + "fontColor": "#7fae42" + }, + "_liquid": { + "fontCharacter": "\\E05A", + "fontColor": "#8dc149" + }, + "_livescript_light": { + "fontCharacter": "\\E05B", + "fontColor": "#498ba7" + }, + "_livescript": { + "fontCharacter": "\\E05B", + "fontColor": "#519aba" + }, + "_lock_light": { + "fontCharacter": "\\E05C", + "fontColor": "#7fae42" + }, + "_lock": { + "fontCharacter": "\\E05C", + "fontColor": "#8dc149" + }, + "_lua_light": { + "fontCharacter": "\\E05D", + "fontColor": "#498ba7" + }, + "_lua": { + "fontCharacter": "\\E05D", + "fontColor": "#519aba" + }, + "_makefile_light": { + "fontCharacter": "\\E05E", + "fontColor": "#cc6d2e" + }, + "_makefile": { + "fontCharacter": "\\E05E", + "fontColor": "#e37933" + }, + "_makefile_1_light": { + "fontCharacter": "\\E05E", + "fontColor": "#9068b0" + }, + "_makefile_1": { + "fontCharacter": "\\E05E", + "fontColor": "#a074c4" + }, + "_makefile_2_light": { + "fontCharacter": "\\E05E", + "fontColor": "#627379" + }, + "_makefile_2": { + "fontCharacter": "\\E05E", + "fontColor": "#6d8086" + }, + "_makefile_3_light": { + "fontCharacter": "\\E05E", + "fontColor": "#498ba7" + }, + "_makefile_3": { + "fontCharacter": "\\E05E", + "fontColor": "#519aba" + }, + "_markdown_light": { + "fontCharacter": "\\E05F", + "fontColor": "#498ba7" + }, + "_markdown": { + "fontCharacter": "\\E05F", + "fontColor": "#519aba" + }, + "_maven_light": { + "fontCharacter": "\\E060", + "fontColor": "#b8383d" + }, + "_maven": { + "fontCharacter": "\\E060", + "fontColor": "#cc3e44" + }, + "_mdo_light": { + "fontCharacter": "\\E061", + "fontColor": "#b8383d" + }, + "_mdo": { + "fontCharacter": "\\E061", + "fontColor": "#cc3e44" + }, + "_mustache_light": { + "fontCharacter": "\\E062", + "fontColor": "#cc6d2e" + }, + "_mustache": { + "fontCharacter": "\\E062", + "fontColor": "#e37933" + }, + "_nim_light": { + "fontCharacter": "\\E064", + "fontColor": "#b7b73b" + }, + "_nim": { + "fontCharacter": "\\E064", + "fontColor": "#cbcb41" + }, + "_notebook_light": { + "fontCharacter": "\\E065", + "fontColor": "#498ba7" + }, + "_notebook": { + "fontCharacter": "\\E065", + "fontColor": "#519aba" + }, + "_npm_light": { + "fontCharacter": "\\E066", + "fontColor": "#3b4b52" + }, + "_npm": { + "fontCharacter": "\\E066", + "fontColor": "#41535b" + }, + "_npm_1_light": { + "fontCharacter": "\\E066", + "fontColor": "#b8383d" + }, + "_npm_1": { + "fontCharacter": "\\E066", + "fontColor": "#cc3e44" + }, + "_npm_ignored_light": { + "fontCharacter": "\\E067", + "fontColor": "#3b4b52" + }, + "_npm_ignored": { + "fontCharacter": "\\E067", + "fontColor": "#41535b" + }, + "_nunjucks_light": { + "fontCharacter": "\\E068", + "fontColor": "#7fae42" + }, + "_nunjucks": { + "fontCharacter": "\\E068", + "fontColor": "#8dc149" + }, + "_ocaml_light": { + "fontCharacter": "\\E069", + "fontColor": "#cc6d2e" + }, + "_ocaml": { + "fontCharacter": "\\E069", + "fontColor": "#e37933" + }, + "_odata_light": { + "fontCharacter": "\\E06A", + "fontColor": "#cc6d2e" + }, + "_odata": { + "fontCharacter": "\\E06A", + "fontColor": "#e37933" + }, + "_pddl_light": { + "fontCharacter": "\\E06B", + "fontColor": "#9068b0" + }, + "_pddl": { + "fontCharacter": "\\E06B", + "fontColor": "#a074c4" + }, + "_pdf_light": { + "fontCharacter": "\\E06C", + "fontColor": "#b8383d" + }, + "_pdf": { + "fontCharacter": "\\E06C", + "fontColor": "#cc3e44" + }, + "_perl_light": { + "fontCharacter": "\\E06D", + "fontColor": "#498ba7" + }, + "_perl": { + "fontCharacter": "\\E06D", + "fontColor": "#519aba" + }, + "_photoshop_light": { + "fontCharacter": "\\E06E", + "fontColor": "#498ba7" + }, + "_photoshop": { + "fontCharacter": "\\E06E", + "fontColor": "#519aba" + }, + "_php_light": { + "fontCharacter": "\\E06F", + "fontColor": "#9068b0" + }, + "_php": { + "fontCharacter": "\\E06F", + "fontColor": "#a074c4" + }, + "_pipeline_light": { + "fontCharacter": "\\E070", + "fontColor": "#cc6d2e" + }, + "_pipeline": { + "fontCharacter": "\\E070", + "fontColor": "#e37933" + }, + "_plan_light": { + "fontCharacter": "\\E071", + "fontColor": "#7fae42" + }, + "_plan": { + "fontCharacter": "\\E071", + "fontColor": "#8dc149" + }, + "_platformio_light": { + "fontCharacter": "\\E072", + "fontColor": "#cc6d2e" + }, + "_platformio": { + "fontCharacter": "\\E072", + "fontColor": "#e37933" + }, + "_powershell_light": { + "fontCharacter": "\\E073", + "fontColor": "#498ba7" + }, + "_powershell": { + "fontCharacter": "\\E073", + "fontColor": "#519aba" + }, + "_prisma_light": { + "fontCharacter": "\\E074", + "fontColor": "#498ba7" + }, + "_prisma": { + "fontCharacter": "\\E074", + "fontColor": "#519aba" + }, + "_prolog_light": { + "fontCharacter": "\\E076", + "fontColor": "#cc6d2e" + }, + "_prolog": { + "fontCharacter": "\\E076", + "fontColor": "#e37933" + }, + "_pug_light": { + "fontCharacter": "\\E077", + "fontColor": "#b8383d" + }, + "_pug": { + "fontCharacter": "\\E077", + "fontColor": "#cc3e44" + }, + "_puppet_light": { + "fontCharacter": "\\E078", + "fontColor": "#b7b73b" + }, + "_puppet": { + "fontCharacter": "\\E078", + "fontColor": "#cbcb41" + }, + "_purescript_light": { + "fontCharacter": "\\E079", + "fontColor": "#bfc2c1" + }, + "_purescript": { + "fontCharacter": "\\E079", + "fontColor": "#d4d7d6" + }, + "_python_light": { + "fontCharacter": "\\E07A", + "fontColor": "#498ba7" + }, + "_python": { + "fontCharacter": "\\E07A", + "fontColor": "#519aba" + }, + "_react_light": { + "fontCharacter": "\\E07D", + "fontColor": "#498ba7" + }, + "_react": { + "fontCharacter": "\\E07D", + "fontColor": "#519aba" + }, + "_react_1_light": { + "fontCharacter": "\\E07D", + "fontColor": "#cc6d2e" + }, + "_react_1": { + "fontCharacter": "\\E07D", + "fontColor": "#e37933" + }, + "_reasonml_light": { + "fontCharacter": "\\E07E", + "fontColor": "#b8383d" + }, + "_reasonml": { + "fontCharacter": "\\E07E", + "fontColor": "#cc3e44" + }, + "_rescript_light": { + "fontCharacter": "\\E07F", + "fontColor": "#b8383d" + }, + "_rescript": { + "fontCharacter": "\\E07F", + "fontColor": "#cc3e44" + }, + "_rescript_1_light": { + "fontCharacter": "\\E07F", + "fontColor": "#dd4b78" + }, + "_rescript_1": { + "fontCharacter": "\\E07F", + "fontColor": "#f55385" + }, + "_rollup_light": { + "fontCharacter": "\\E080", + "fontColor": "#b8383d" + }, + "_rollup": { + "fontCharacter": "\\E080", + "fontColor": "#cc3e44" + }, + "_ruby_light": { + "fontCharacter": "\\E081", + "fontColor": "#b8383d" + }, + "_ruby": { + "fontCharacter": "\\E081", + "fontColor": "#cc3e44" + }, + "_rust_light": { + "fontCharacter": "\\E082", + "fontColor": "#627379" + }, + "_rust": { + "fontCharacter": "\\E082", + "fontColor": "#6d8086" + }, + "_salesforce_light": { + "fontCharacter": "\\E083", + "fontColor": "#498ba7" + }, + "_salesforce": { + "fontCharacter": "\\E083", + "fontColor": "#519aba" + }, + "_sass_light": { + "fontCharacter": "\\E084", + "fontColor": "#dd4b78" + }, + "_sass": { + "fontCharacter": "\\E084", + "fontColor": "#f55385" + }, + "_sbt_light": { + "fontCharacter": "\\E085", + "fontColor": "#498ba7" + }, + "_sbt": { + "fontCharacter": "\\E085", + "fontColor": "#519aba" + }, + "_scala_light": { + "fontCharacter": "\\E086", + "fontColor": "#b8383d" + }, + "_scala": { + "fontCharacter": "\\E086", + "fontColor": "#cc3e44" + }, + "_shell_light": { + "fontCharacter": "\\E089", + "fontColor": "#7fae42" + }, + "_shell": { + "fontCharacter": "\\E089", + "fontColor": "#8dc149" + }, + "_slim_light": { + "fontCharacter": "\\E08A", + "fontColor": "#cc6d2e" + }, + "_slim": { + "fontCharacter": "\\E08A", + "fontColor": "#e37933" + }, + "_smarty_light": { + "fontCharacter": "\\E08B", + "fontColor": "#b7b73b" + }, + "_smarty": { + "fontCharacter": "\\E08B", + "fontColor": "#cbcb41" + }, + "_spring_light": { + "fontCharacter": "\\E08C", + "fontColor": "#7fae42" + }, + "_spring": { + "fontCharacter": "\\E08C", + "fontColor": "#8dc149" + }, + "_stylelint_light": { + "fontCharacter": "\\E08D", + "fontColor": "#bfc2c1" + }, + "_stylelint": { + "fontCharacter": "\\E08D", + "fontColor": "#d4d7d6" + }, + "_stylelint_1_light": { + "fontCharacter": "\\E08D", + "fontColor": "#455155" + }, + "_stylelint_1": { + "fontCharacter": "\\E08D", + "fontColor": "#4d5a5e" + }, + "_stylus_light": { + "fontCharacter": "\\E08E", + "fontColor": "#7fae42" + }, + "_stylus": { + "fontCharacter": "\\E08E", + "fontColor": "#8dc149" + }, + "_sublime_light": { + "fontCharacter": "\\E08F", + "fontColor": "#cc6d2e" + }, + "_sublime": { + "fontCharacter": "\\E08F", + "fontColor": "#e37933" + }, + "_svelte_light": { + "fontCharacter": "\\E090", + "fontColor": "#b8383d" + }, + "_svelte": { + "fontCharacter": "\\E090", + "fontColor": "#cc3e44" + }, + "_svg_light": { + "fontCharacter": "\\E091", + "fontColor": "#9068b0" + }, + "_svg": { + "fontCharacter": "\\E091", + "fontColor": "#a074c4" + }, + "_svg_1_light": { + "fontCharacter": "\\E091", + "fontColor": "#498ba7" + }, + "_svg_1": { + "fontCharacter": "\\E091", + "fontColor": "#519aba" + }, + "_swift_light": { + "fontCharacter": "\\E092", + "fontColor": "#cc6d2e" + }, + "_swift": { + "fontCharacter": "\\E092", + "fontColor": "#e37933" + }, + "_terraform_light": { + "fontCharacter": "\\E093", + "fontColor": "#9068b0" + }, + "_terraform": { + "fontCharacter": "\\E093", + "fontColor": "#a074c4" + }, + "_tex_light": { + "fontCharacter": "\\E094", + "fontColor": "#498ba7" + }, + "_tex": { + "fontCharacter": "\\E094", + "fontColor": "#519aba" + }, + "_tex_1_light": { + "fontCharacter": "\\E094", + "fontColor": "#b7b73b" + }, + "_tex_1": { + "fontCharacter": "\\E094", + "fontColor": "#cbcb41" + }, + "_tex_2_light": { + "fontCharacter": "\\E094", + "fontColor": "#cc6d2e" + }, + "_tex_2": { + "fontCharacter": "\\E094", + "fontColor": "#e37933" + }, + "_tex_3_light": { + "fontCharacter": "\\E094", + "fontColor": "#bfc2c1" + }, + "_tex_3": { + "fontCharacter": "\\E094", + "fontColor": "#d4d7d6" + }, + "_todo": { + "fontCharacter": "\\E096" + }, + "_tsconfig_light": { + "fontCharacter": "\\E097", + "fontColor": "#498ba7" + }, + "_tsconfig": { + "fontCharacter": "\\E097", + "fontColor": "#519aba" + }, + "_twig_light": { + "fontCharacter": "\\E098", + "fontColor": "#7fae42" + }, + "_twig": { + "fontCharacter": "\\E098", + "fontColor": "#8dc149" + }, + "_typescript_light": { + "fontCharacter": "\\E099", + "fontColor": "#498ba7" + }, + "_typescript": { + "fontCharacter": "\\E099", + "fontColor": "#519aba" + }, + "_typescript_1_light": { + "fontCharacter": "\\E099", + "fontColor": "#cc6d2e" + }, + "_typescript_1": { + "fontCharacter": "\\E099", + "fontColor": "#e37933" + }, + "_vala_light": { + "fontCharacter": "\\E09A", + "fontColor": "#627379" + }, + "_vala": { + "fontCharacter": "\\E09A", + "fontColor": "#6d8086" + }, + "_vba_light": { + "fontCharacter": "\\E09B", + "fontColor": "#9068b0" + }, + "_vba": { + "fontCharacter": "\\E09B", + "fontColor": "#a074c4" + }, + "_vba_1_light": { + "fontCharacter": "\\E09B", + "fontColor": "#7fae42" + }, + "_vba_1": { + "fontCharacter": "\\E09B", + "fontColor": "#8dc149" + }, + "_vba_2_light": { + "fontCharacter": "\\E09B", + "fontColor": "#b7b73b" + }, + "_vba_2": { + "fontCharacter": "\\E09B", + "fontColor": "#cbcb41" + }, + "_video_light": { + "fontCharacter": "\\E09C", + "fontColor": "#dd4b78" + }, + "_video": { + "fontCharacter": "\\E09C", + "fontColor": "#f55385" + }, + "_vite_light": { + "fontCharacter": "\\E09D", + "fontColor": "#b7b73b" + }, + "_vite": { + "fontCharacter": "\\E09D", + "fontColor": "#cbcb41" + }, + "_vue_light": { + "fontCharacter": "\\E09E", + "fontColor": "#7fae42" + }, + "_vue": { + "fontCharacter": "\\E09E", + "fontColor": "#8dc149" + }, + "_wasm_light": { + "fontCharacter": "\\E09F", + "fontColor": "#9068b0" + }, + "_wasm": { + "fontCharacter": "\\E09F", + "fontColor": "#a074c4" + }, + "_wat_light": { + "fontCharacter": "\\E0A0", + "fontColor": "#9068b0" + }, + "_wat": { + "fontCharacter": "\\E0A0", + "fontColor": "#a074c4" + }, + "_webpack_light": { + "fontCharacter": "\\E0A1", + "fontColor": "#498ba7" + }, + "_webpack": { + "fontCharacter": "\\E0A1", + "fontColor": "#519aba" + }, + "_wgt_light": { + "fontCharacter": "\\E0A2", + "fontColor": "#498ba7" + }, + "_wgt": { + "fontCharacter": "\\E0A2", + "fontColor": "#519aba" + }, + "_windows_light": { + "fontCharacter": "\\E0A3", + "fontColor": "#498ba7" + }, + "_windows": { + "fontCharacter": "\\E0A3", + "fontColor": "#519aba" + }, + "_word_light": { + "fontCharacter": "\\E0A4", + "fontColor": "#498ba7" + }, + "_word": { + "fontCharacter": "\\E0A4", + "fontColor": "#519aba" + }, + "_xls_light": { + "fontCharacter": "\\E0A5", + "fontColor": "#7fae42" + }, + "_xls": { + "fontCharacter": "\\E0A5", + "fontColor": "#8dc149" + }, + "_xml_light": { + "fontCharacter": "\\E0A6", + "fontColor": "#cc6d2e" + }, + "_xml": { + "fontCharacter": "\\E0A6", + "fontColor": "#e37933" + }, + "_yarn_light": { + "fontCharacter": "\\E0A7", + "fontColor": "#498ba7" + }, + "_yarn": { + "fontCharacter": "\\E0A7", + "fontColor": "#519aba" + }, + "_yml_light": { + "fontCharacter": "\\E0A8", + "fontColor": "#9068b0" + }, + "_yml": { + "fontCharacter": "\\E0A8", + "fontColor": "#a074c4" + }, + "_zig_light": { + "fontCharacter": "\\E0A9", + "fontColor": "#cc6d2e" + }, + "_zig": { + "fontCharacter": "\\E0A9", + "fontColor": "#e37933" + }, + "_zip_light": { + "fontCharacter": "\\E0AA", + "fontColor": "#b8383d" + }, + "_zip": { + "fontCharacter": "\\E0AA", + "fontColor": "#cc3e44" + }, + "_zip_1_light": { + "fontCharacter": "\\E0AA", + "fontColor": "#627379" + }, + "_zip_1": { + "fontCharacter": "\\E0AA", + "fontColor": "#6d8086" + } + }, + "file": "_default", + "fileExtensions": { + "bsl": "_bsl", + "mdo": "_mdo", + "apex": "_salesforce", + "asm": "_asm", + "s": "_asm", + "bicep": "_bicep", + "bzl": "_bazel", + "bazel": "_bazel", + "build": "_bazel", + "workspace": "_bazel", + "bazelignore": "_bazel", + "bazelversion": "_bazel", + "c": "_c", + "h": "_c_1", + "m": "_c_2", + "cs": "_c-sharp", + "cshtml": "_html", + "aspx": "_html", + "ascx": "_html_1", + "asax": "_html_2", + "master": "_html_2", + "cc": "_cpp", + "cpp": "_cpp", + "cxx": "_cpp", + "c++": "_cpp", + "hh": "_cpp_1", + "hpp": "_cpp_1", + "hxx": "_cpp_1", + "h++": "_cpp_1", + "mm": "_cpp_2", + "clj": "_clojure", + "cljs": "_clojure", + "cljc": "_clojure", + "edn": "_clojure_1", + "cfc": "_coldfusion", + "cfm": "_coldfusion", + "coffee": "_coffee", + "litcoffee": "_coffee", + "config": "_config", + "cfg": "_config", + "conf": "_config", + "cr": "_crystal", + "ecr": "_crystal_embedded", + "slang": "_crystal_embedded", + "cson": "_json", + "css": "_css", + "css.map": "_css", + "sss": "_css", + "csv": "_csv", + "xla": "_xls", + "xlam": "_xls", + "xls": "_xls", + "xlsb": "_xls", + "xlsm": "_xls", + "xlsx": "_xls", + "xlt": "_xls", + "xltm": "_xls", + "cu": "_cu", + "cuh": "_cu_1", + "hu": "_cu_1", + "cake": "_cake", + "ctp": "_cake_php", + "d": "_d", + "doc": "_word", + "docx": "_word", + "dot": "_word", + "dotm": "_word", + "ejs": "_ejs", + "ex": "_elixir", + "exs": "_elixir_script", + "elm": "_elm", + "ico": "_favicon", + "fs": "_f-sharp", + "fsx": "_f-sharp", + "gitignore": "_git", + "gitconfig": "_git", + "gitkeep": "_git", + "gitattributes": "_git", + "gitmodules": "_git", + "go": "_go2", + "slide": "_go", + "article": "_go", + "gd": "_godot", + "godot": "_godot_1", + "tres": "_godot_2", + "tscn": "_godot_3", + "gradle": "_gradle", + "groovy": "_grails", + "gsp": "_grails", + "gql": "_graphql", + "graphql": "_graphql", + "graphqls": "_graphql", + "hack": "_hacklang", + "haml": "_haml", + "handlebars": "_mustache", + "hbs": "_mustache", + "hjs": "_mustache", + "hs": "_haskell", + "lhs": "_haskell", + "hx": "_haxe", + "hxs": "_haxe_1", + "hxp": "_haxe_2", + "hxml": "_haxe_3", + "html": "_html_3", + "jade": "_jade", + "java": "_java", + "class": "_java_1", + "classpath": "_java", + "properties": "_java", + "js": "_javascript", + "js.map": "_javascript", + "cjs": "_javascript", + "cjs.map": "_javascript", + "mjs": "_javascript", + "mjs.map": "_javascript", + "spec.js": "_javascript_1", + "spec.cjs": "_javascript_1", + "spec.mjs": "_javascript_1", + "test.js": "_javascript_1", + "test.cjs": "_javascript_1", + "test.mjs": "_javascript_1", + "es": "_javascript", + "es5": "_javascript", + "es6": "_javascript", + "es7": "_javascript", + "jinja": "_jinja", + "jinja2": "_jinja", + "json": "_json", + "jl": "_julia", + "kt": "_kotlin", + "kts": "_kotlin", + "dart": "_dart", + "less": "_less", + "liquid": "_liquid", + "ls": "_livescript", + "lua": "_lua", + "markdown": "_markdown", + "md": "_markdown", + "argdown": "_argdown", + "ad": "_argdown", + "mustache": "_mustache", + "stache": "_mustache", + "nim": "_nim", + "nims": "_nim", + "github-issues": "_github", + "ipynb": "_notebook", + "njk": "_nunjucks", + "nunjucks": "_nunjucks", + "nunjs": "_nunjucks", + "nunj": "_nunjucks", + "njs": "_nunjucks", + "nj": "_nunjucks", + "npm-debug.log": "_npm", + "npmignore": "_npm_1", + "npmrc": "_npm_1", + "ml": "_ocaml", + "mli": "_ocaml", + "cmx": "_ocaml", + "cmxa": "_ocaml", + "odata": "_odata", + "pl": "_perl", + "php": "_php", + "php.inc": "_php", + "pipeline": "_pipeline", + "pddl": "_pddl", + "plan": "_plan", + "happenings": "_happenings", + "ps1": "_powershell", + "psd1": "_powershell", + "psm1": "_powershell", + "prisma": "_prisma", + "pug": "_pug", + "pp": "_puppet", + "epp": "_puppet", + "purs": "_purescript", + "py": "_python", + "jsx": "_react", + "spec.jsx": "_react_1", + "test.jsx": "_react_1", + "cjsx": "_react", + "tsx": "_react", + "spec.tsx": "_react_1", + "test.tsx": "_react_1", + "re": "_reasonml", + "res": "_rescript", + "resi": "_rescript_1", + "r": "_R", + "rmd": "_R", + "rb": "_ruby", + "erb": "_html_erb", + "erb.html": "_html_erb", + "html.erb": "_html_erb", + "rs": "_rust", + "sass": "_sass", + "scss": "_sass", + "springbeans": "_spring", + "slim": "_slim", + "smarty.tpl": "_smarty", + "tpl": "_smarty", + "sbt": "_sbt", + "scala": "_scala", + "sol": "_ethereum", + "styl": "_stylus", + "svelte": "_svelte", + "swift": "_swift", + "sql": "_db", + "soql": "_db_1", + "tf": "_terraform", + "tf.json": "_terraform", + "tfvars": "_terraform", + "tfvars.json": "_terraform", + "tex": "_tex", + "sty": "_tex_1", + "dtx": "_tex_2", + "ins": "_tex_3", + "toml": "_config", + "twig": "_twig", + "ts": "_typescript", + "spec.ts": "_typescript_1", + "test.ts": "_typescript_1", + "vala": "_vala", + "vapi": "_vala", + "bas": "_vba", + "cls": "_vba_1", + "frm": "_vba_2", + "component": "_html_3", + "vue": "_vue", + "wasm": "_wasm", + "wat": "_wat", + "xml": "_xml", + "yml": "_yml", + "yaml": "_yml", + "pro": "_prolog", + "zig": "_zig", + "jar": "_zip", + "zip": "_zip_1", + "wgt": "_wgt", + "ai": "_illustrator", + "psd": "_photoshop", + "pdf": "_pdf", + "eot": "_font", + "ttf": "_font", + "woff": "_font", + "woff2": "_font", + "otf": "_font", + "avif": "_image", + "gif": "_image", + "jpg": "_image", + "jpeg": "_image", + "png": "_image", + "pxm": "_image", + "svg": "_svg", + "svgx": "_image", + "tiff": "_image", + "webp": "_image", + "sublime-project": "_sublime", + "sublime-workspace": "_sublime", + "code-search": "_code-search", + "sh": "_shell", + "zsh": "_shell", + "fish": "_shell", + "zshrc": "_shell", + "bashrc": "_shell", + "mov": "_video", + "ogv": "_video", + "webm": "_video", + "avi": "_video", + "mpg": "_video", + "mp4": "_video", + "mp3": "_audio", + "ogg": "_audio", + "wav": "_audio", + "flac": "_audio", + "3ds": "_svg_1", + "3dm": "_svg_1", + "stl": "_svg_1", + "obj": "_svg_1", + "dae": "_svg_1", + "bat": "_windows", + "cmd": "_windows", + "babelrc": "_babel", + "babelrc.js": "_babel", + "babelrc.cjs": "_babel", + "bazelrc": "_bazel_1", + "bowerrc": "_bower", + "dockerignore": "_docker_1", + "codeclimate.yml": "_code-climate", + "eslintrc": "_eslint", + "eslintrc.js": "_eslint", + "eslintrc.cjs": "_eslint", + "eslintrc.yaml": "_eslint", + "eslintrc.yml": "_eslint", + "eslintrc.json": "_eslint", + "eslintignore": "_eslint_1", + "firebaserc": "_firebase", + "gitlab-ci.yml": "_gitlab", + "jshintrc": "_javascript_2", + "jscsrc": "_javascript_2", + "stylelintrc": "_stylelint", + "stylelintrc.json": "_stylelint", + "stylelintrc.yaml": "_stylelint", + "stylelintrc.yml": "_stylelint", + "stylelintrc.js": "_stylelint", + "stylelintignore": "_stylelint_1", + "direnv": "_config", + "env": "_config", + "static": "_config", + "editorconfig": "_config", + "slugignore": "_config", + "tmp": "_clock_1", + "htaccess": "_config", + "key": "_lock", + "cert": "_lock", + "cer": "_lock", + "crt": "_lock", + "pem": "_lock", + "ds_store": "_ignored" + }, + "fileNames": { + "mix": "_hex", + "commit_editmsg": "_git", + "merge_msg": "_git", + "karma.conf.js": "_karma", + "karma.conf.cjs": "_karma", + "karma.conf.mjs": "_karma", + "karma.conf.coffee": "_karma", + "readme.md": "_info", + "readme.txt": "_info", + "readme": "_info", + "changelog.md": "_clock", + "changelog.txt": "_clock", + "changelog": "_clock", + "changes.md": "_clock", + "changes.txt": "_clock", + "changes": "_clock", + "version.md": "_clock", + "version.txt": "_clock", + "version": "_clock", + "mvnw": "_maven", + "pom.xml": "_maven", + "gemfile": "_ruby", + "tsconfig.json": "_tsconfig", + "vite.config.js": "_vite", + "vite.config.ts": "_vite", + "vite.config.mjs": "_vite", + "vite.config.mts": "_vite", + "vite.config.cjs": "_vite", + "vite.config.cts": "_vite", + "swagger.json": "_json_1", + "swagger.yml": "_json_1", + "swagger.yaml": "_json_1", + "mime.types": "_config", + "jenkinsfile": "_jenkins", + "babel.config.js": "_babel", + "babel.config.json": "_babel", + "babel.config.cjs": "_babel", + "build": "_bazel", + "build.bazel": "_bazel", + "workspace": "_bazel", + "workspace.bazel": "_bazel", + "bower.json": "_bower", + "dockerfile": "_docker", + "docker-healthcheck": "_docker_2", + "docker-compose.yml": "_docker_3", + "docker-compose.yaml": "_docker_3", + "docker-compose.override.yml": "_docker_3", + "docker-compose.override.yaml": "_docker_3", + "eslint.config.js": "_eslint", + "firebase.json": "_firebase", + "geckodriver": "_firefox", + "gruntfile.js": "_grunt", + "gruntfile.babel.js": "_grunt", + "gruntfile.coffee": "_grunt", + "gulpfile": "_gulp", + "gulpfile.js": "_gulp", + "ionic.config.json": "_ionic", + "ionic.project": "_ionic", + "platformio.ini": "_platformio", + "rollup.config.js": "_rollup", + "sass-lint.yml": "_sass", + "stylelint.config.js": "_stylelint", + "stylelint.config.cjs": "_stylelint", + "stylelint.config.mjs": "_stylelint", + "yarn.clean": "_yarn", + "yarn.lock": "_yarn", + "webpack.config.js": "_webpack", + "webpack.config.cjs": "_webpack", + "webpack.config.mjs": "_webpack", + "webpack.config.ts": "_webpack", + "webpack.config.build.js": "_webpack", + "webpack.config.build.cjs": "_webpack", + "webpack.config.build.mjs": "_webpack", + "webpack.config.build.ts": "_webpack", + "webpack.common.js": "_webpack", + "webpack.common.cjs": "_webpack", + "webpack.common.mjs": "_webpack", + "webpack.common.ts": "_webpack", + "webpack.dev.js": "_webpack", + "webpack.dev.cjs": "_webpack", + "webpack.dev.mjs": "_webpack", + "webpack.dev.ts": "_webpack", + "webpack.prod.js": "_webpack", + "webpack.prod.cjs": "_webpack", + "webpack.prod.mjs": "_webpack", + "webpack.prod.ts": "_webpack", + "license": "_license", + "licence": "_license", + "license.txt": "_license", + "licence.txt": "_license", + "license.md": "_license", + "licence.md": "_license", + "copying": "_license", + "copying.txt": "_license", + "copying.md": "_license", + "compiling": "_license_1", + "compiling.txt": "_license_1", + "compiling.md": "_license_1", + "contributing": "_license_2", + "contributing.txt": "_license_2", + "contributing.md": "_license_2", + "makefile": "_makefile", + "qmakefile": "_makefile_1", + "omakefile": "_makefile_2", + "cmakelists.txt": "_makefile_3", + "procfile": "_heroku", + "todo": "_todo", + "todo.txt": "_todo", + "todo.md": "_todo", + "npm-debug.log": "_npm_ignored" + }, + "languageIds": { + "argdown": "_argdown", + "bicep": "_bicep", + "elixir": "_elixir", + "elm": "_elm", + "erb": "_html_erb", + "github-issues": "_github", + "gradle": "_gradle", + "godot": "_godot", + "haml": "_haml", + "haskell": "_haskell", + "haxe": "_haxe", + "jinja": "_jinja", + "kotlin": "_kotlin", + "mustache": "_mustache", + "nunjucks": "_nunjucks", + "ocaml": "_ocaml", + "r": "_R", + "rescript": "_rescript", + "sass": "_sass", + "stylus": "_stylus", + "terraform": "_terraform", + "todo": "_todo", + "vala": "_vala", + "vue": "_vue" + }, + "light": { + "file": "_default_light", + "fileExtensions": { + "bsl": "_bsl_light", + "mdo": "_mdo_light", + "apex": "_salesforce_light", + "asm": "_asm_light", + "s": "_asm_light", + "bicep": "_bicep_light", + "bzl": "_bazel_light", + "bazel": "_bazel_light", + "build": "_bazel_light", + "workspace": "_bazel_light", + "bazelignore": "_bazel_light", + "bazelversion": "_bazel_light", + "c": "_c_light", + "h": "_c_1_light", + "m": "_c_2_light", + "cs": "_c-sharp_light", + "cshtml": "_html_light", + "aspx": "_html_light", + "ascx": "_html_1_light", + "asax": "_html_2_light", + "master": "_html_2_light", + "cc": "_cpp_light", + "cpp": "_cpp_light", + "cxx": "_cpp_light", + "c++": "_cpp_light", + "hh": "_cpp_1_light", + "hpp": "_cpp_1_light", + "hxx": "_cpp_1_light", + "h++": "_cpp_1_light", + "mm": "_cpp_2_light", + "clj": "_clojure_light", + "cljs": "_clojure_light", + "cljc": "_clojure_light", + "edn": "_clojure_1_light", + "cfc": "_coldfusion_light", + "cfm": "_coldfusion_light", + "coffee": "_coffee_light", + "litcoffee": "_coffee_light", + "config": "_config_light", + "cfg": "_config_light", + "conf": "_config_light", + "cr": "_crystal_light", + "ecr": "_crystal_embedded_light", + "slang": "_crystal_embedded_light", + "cson": "_json_light", + "css": "_css_light", + "css.map": "_css_light", + "sss": "_css_light", + "csv": "_csv_light", + "xla": "_xls_light", + "xlam": "_xls_light", + "xls": "_xls_light", + "xlsb": "_xls_light", + "xlsm": "_xls_light", + "xlsx": "_xls_light", + "xlt": "_xls_light", + "xltm": "_xls_light", + "cu": "_cu_light", + "cuh": "_cu_1_light", + "hu": "_cu_1_light", + "cake": "_cake_light", + "ctp": "_cake_php_light", + "d": "_d_light", + "doc": "_word_light", + "docx": "_word_light", + "dot": "_word_light", + "dotm": "_word_light", + "ejs": "_ejs_light", + "ex": "_elixir_light", + "exs": "_elixir_script_light", + "elm": "_elm_light", + "ico": "_favicon_light", + "fs": "_f-sharp_light", + "fsx": "_f-sharp_light", + "gitignore": "_git_light", + "gitconfig": "_git_light", + "gitkeep": "_git_light", + "gitattributes": "_git_light", + "gitmodules": "_git_light", + "go": "_go2_light", + "slide": "_go_light", + "article": "_go_light", + "gd": "_godot_light", + "godot": "_godot_1_light", + "tres": "_godot_2_light", + "tscn": "_godot_3_light", + "gradle": "_gradle_light", + "groovy": "_grails_light", + "gsp": "_grails_light", + "gql": "_graphql_light", + "graphql": "_graphql_light", + "graphqls": "_graphql_light", + "hack": "_hacklang_light", + "haml": "_haml_light", + "handlebars": "_mustache_light", + "hbs": "_mustache_light", + "hjs": "_mustache_light", + "hs": "_haskell_light", + "lhs": "_haskell_light", + "hx": "_haxe_light", + "hxs": "_haxe_1_light", + "hxp": "_haxe_2_light", + "hxml": "_haxe_3_light", + "html": "_html_3_light", + "jade": "_jade_light", + "java": "_java_light", + "class": "_java_1_light", + "classpath": "_java_light", + "properties": "_java_light", + "js": "_javascript_light", + "js.map": "_javascript_light", + "cjs": "_javascript_light", + "cjs.map": "_javascript_light", + "mjs": "_javascript_light", + "mjs.map": "_javascript_light", + "spec.js": "_javascript_1_light", + "spec.cjs": "_javascript_1_light", + "spec.mjs": "_javascript_1_light", + "test.js": "_javascript_1_light", + "test.cjs": "_javascript_1_light", + "test.mjs": "_javascript_1_light", + "es": "_javascript_light", + "es5": "_javascript_light", + "es6": "_javascript_light", + "es7": "_javascript_light", + "jinja": "_jinja_light", + "jinja2": "_jinja_light", + "json": "_json_light", + "jl": "_julia_light", + "kt": "_kotlin_light", + "kts": "_kotlin_light", + "dart": "_dart_light", + "less": "_less_light", + "liquid": "_liquid_light", + "ls": "_livescript_light", + "lua": "_lua_light", + "markdown": "_markdown_light", + "md": "_markdown_light", + "argdown": "_argdown_light", + "ad": "_argdown_light", + "mustache": "_mustache_light", + "stache": "_mustache_light", + "nim": "_nim_light", + "nims": "_nim_light", + "github-issues": "_github_light", + "ipynb": "_notebook_light", + "njk": "_nunjucks_light", + "nunjucks": "_nunjucks_light", + "nunjs": "_nunjucks_light", + "nunj": "_nunjucks_light", + "njs": "_nunjucks_light", + "nj": "_nunjucks_light", + "npm-debug.log": "_npm_light", + "npmignore": "_npm_1_light", + "npmrc": "_npm_1_light", + "ml": "_ocaml_light", + "mli": "_ocaml_light", + "cmx": "_ocaml_light", + "cmxa": "_ocaml_light", + "odata": "_odata_light", + "pl": "_perl_light", + "php": "_php_light", + "php.inc": "_php_light", + "pipeline": "_pipeline_light", + "pddl": "_pddl_light", + "plan": "_plan_light", + "happenings": "_happenings_light", + "ps1": "_powershell_light", + "psd1": "_powershell_light", + "psm1": "_powershell_light", + "prisma": "_prisma_light", + "pug": "_pug_light", + "pp": "_puppet_light", + "epp": "_puppet_light", + "purs": "_purescript_light", + "py": "_python_light", + "jsx": "_react_light", + "spec.jsx": "_react_1_light", + "test.jsx": "_react_1_light", + "cjsx": "_react_light", + "tsx": "_react_light", + "spec.tsx": "_react_1_light", + "test.tsx": "_react_1_light", + "re": "_reasonml_light", + "res": "_rescript_light", + "resi": "_rescript_1_light", + "r": "_R_light", + "rmd": "_R_light", + "rb": "_ruby_light", + "erb": "_html_erb_light", + "erb.html": "_html_erb_light", + "html.erb": "_html_erb_light", + "rs": "_rust_light", + "sass": "_sass_light", + "scss": "_sass_light", + "springbeans": "_spring_light", + "slim": "_slim_light", + "smarty.tpl": "_smarty_light", + "tpl": "_smarty_light", + "sbt": "_sbt_light", + "scala": "_scala_light", + "sol": "_ethereum_light", + "styl": "_stylus_light", + "svelte": "_svelte_light", + "swift": "_swift_light", + "sql": "_db_light", + "soql": "_db_1_light", + "tf": "_terraform_light", + "tf.json": "_terraform_light", + "tfvars": "_terraform_light", + "tfvars.json": "_terraform_light", + "tex": "_tex_light", + "sty": "_tex_1_light", + "dtx": "_tex_2_light", + "ins": "_tex_3_light", + "toml": "_config_light", + "twig": "_twig_light", + "ts": "_typescript_light", + "spec.ts": "_typescript_1_light", + "test.ts": "_typescript_1_light", + "vala": "_vala_light", + "vapi": "_vala_light", + "bas": "_vba_light", + "cls": "_vba_1_light", + "frm": "_vba_2_light", + "component": "_html_3_light", + "vue": "_vue_light", + "wasm": "_wasm_light", + "wat": "_wat_light", + "xml": "_xml_light", + "yml": "_yml_light", + "yaml": "_yml_light", + "pro": "_prolog_light", + "zig": "_zig_light", + "jar": "_zip_light", + "zip": "_zip_1_light", + "wgt": "_wgt_light", + "ai": "_illustrator_light", + "psd": "_photoshop_light", + "pdf": "_pdf_light", + "eot": "_font_light", + "ttf": "_font_light", + "woff": "_font_light", + "woff2": "_font_light", + "otf": "_font_light", + "avif": "_image_light", + "gif": "_image_light", + "jpg": "_image_light", + "jpeg": "_image_light", + "png": "_image_light", + "pxm": "_image_light", + "svg": "_svg_light", + "svgx": "_image_light", + "tiff": "_image_light", + "webp": "_image_light", + "sublime-project": "_sublime_light", + "sublime-workspace": "_sublime_light", + "code-search": "_code-search_light", + "sh": "_shell_light", + "zsh": "_shell_light", + "fish": "_shell_light", + "zshrc": "_shell_light", + "bashrc": "_shell_light", + "mov": "_video_light", + "ogv": "_video_light", + "webm": "_video_light", + "avi": "_video_light", + "mpg": "_video_light", + "mp4": "_video_light", + "mp3": "_audio_light", + "ogg": "_audio_light", + "wav": "_audio_light", + "flac": "_audio_light", + "3ds": "_svg_1_light", + "3dm": "_svg_1_light", + "stl": "_svg_1_light", + "obj": "_svg_1_light", + "dae": "_svg_1_light", + "bat": "_windows_light", + "cmd": "_windows_light", + "babelrc": "_babel_light", + "babelrc.js": "_babel_light", + "babelrc.cjs": "_babel_light", + "bazelrc": "_bazel_1_light", + "bowerrc": "_bower_light", + "dockerignore": "_docker_1_light", + "codeclimate.yml": "_code-climate_light", + "eslintrc": "_eslint_light", + "eslintrc.js": "_eslint_light", + "eslintrc.cjs": "_eslint_light", + "eslintrc.yaml": "_eslint_light", + "eslintrc.yml": "_eslint_light", + "eslintrc.json": "_eslint_light", + "eslintignore": "_eslint_1_light", + "firebaserc": "_firebase_light", + "gitlab-ci.yml": "_gitlab_light", + "jshintrc": "_javascript_2_light", + "jscsrc": "_javascript_2_light", + "stylelintrc": "_stylelint_light", + "stylelintrc.json": "_stylelint_light", + "stylelintrc.yaml": "_stylelint_light", + "stylelintrc.yml": "_stylelint_light", + "stylelintrc.js": "_stylelint_light", + "stylelintignore": "_stylelint_1_light", + "direnv": "_config_light", + "env": "_config_light", + "static": "_config_light", + "editorconfig": "_config_light", + "slugignore": "_config_light", + "tmp": "_clock_1_light", + "htaccess": "_config_light", + "key": "_lock_light", + "cert": "_lock_light", + "cer": "_lock_light", + "crt": "_lock_light", + "pem": "_lock_light", + "ds_store": "_ignored_light" + }, + "languageIds": { + "argdown": "_argdown_light", + "bicep": "_bicep_light", + "elixir": "_elixir_light", + "elm": "_elm_light", + "erb": "_html_erb_light", + "github-issues": "_github_light", + "gradle": "_gradle_light", + "godot": "_godot_light", + "haml": "_haml_light", + "haskell": "_haskell_light", + "haxe": "_haxe_light", + "jinja": "_jinja_light", + "kotlin": "_kotlin_light", + "mustache": "_mustache_light", + "nunjucks": "_nunjucks_light", + "ocaml": "_ocaml_light", + "r": "_R_light", + "rescript": "_rescript_light", + "sass": "_sass_light", + "stylus": "_stylus_light", + "terraform": "_terraform_light", + "vala": "_vala_light", + "vue": "_vue_light" + }, + "fileNames": { + "mix": "_hex_light", + "commit_editmsg": "_git_light", + "merge_msg": "_git_light", + "karma.conf.js": "_karma_light", + "karma.conf.cjs": "_karma_light", + "karma.conf.mjs": "_karma_light", + "karma.conf.coffee": "_karma_light", + "readme.md": "_info_light", + "readme.txt": "_info_light", + "readme": "_info_light", + "changelog.md": "_clock_light", + "changelog.txt": "_clock_light", + "changelog": "_clock_light", + "changes.md": "_clock_light", + "changes.txt": "_clock_light", + "changes": "_clock_light", + "version.md": "_clock_light", + "version.txt": "_clock_light", + "version": "_clock_light", + "mvnw": "_maven_light", + "pom.xml": "_maven_light", + "gemfile": "_ruby_light", + "tsconfig.json": "_tsconfig_light", + "vite.config.js": "_vite_light", + "vite.config.ts": "_vite_light", + "vite.config.mjs": "_vite_light", + "vite.config.mts": "_vite_light", + "vite.config.cjs": "_vite_light", + "vite.config.cts": "_vite_light", + "swagger.json": "_json_1_light", + "swagger.yml": "_json_1_light", + "swagger.yaml": "_json_1_light", + "mime.types": "_config_light", + "jenkinsfile": "_jenkins_light", + "babel.config.js": "_babel_light", + "babel.config.json": "_babel_light", + "babel.config.cjs": "_babel_light", + "build": "_bazel_light", + "build.bazel": "_bazel_light", + "workspace": "_bazel_light", + "workspace.bazel": "_bazel_light", + "bower.json": "_bower_light", + "dockerfile": "_docker_light", + "docker-healthcheck": "_docker_2_light", + "docker-compose.yml": "_docker_3_light", + "docker-compose.yaml": "_docker_3_light", + "docker-compose.override.yml": "_docker_3_light", + "docker-compose.override.yaml": "_docker_3_light", + "eslint.config.js": "_eslint_light", + "firebase.json": "_firebase_light", + "geckodriver": "_firefox_light", + "gruntfile.js": "_grunt_light", + "gruntfile.babel.js": "_grunt_light", + "gruntfile.coffee": "_grunt_light", + "gulpfile": "_gulp_light", + "gulpfile.js": "_gulp_light", + "ionic.config.json": "_ionic_light", + "ionic.project": "_ionic_light", + "platformio.ini": "_platformio_light", + "rollup.config.js": "_rollup_light", + "sass-lint.yml": "_sass_light", + "stylelint.config.js": "_stylelint_light", + "stylelint.config.cjs": "_stylelint_light", + "stylelint.config.mjs": "_stylelint_light", + "yarn.clean": "_yarn_light", + "yarn.lock": "_yarn_light", + "webpack.config.js": "_webpack_light", + "webpack.config.cjs": "_webpack_light", + "webpack.config.mjs": "_webpack_light", + "webpack.config.ts": "_webpack_light", + "webpack.config.build.js": "_webpack_light", + "webpack.config.build.cjs": "_webpack_light", + "webpack.config.build.mjs": "_webpack_light", + "webpack.config.build.ts": "_webpack_light", + "webpack.common.js": "_webpack_light", + "webpack.common.cjs": "_webpack_light", + "webpack.common.mjs": "_webpack_light", + "webpack.common.ts": "_webpack_light", + "webpack.dev.js": "_webpack_light", + "webpack.dev.cjs": "_webpack_light", + "webpack.dev.mjs": "_webpack_light", + "webpack.dev.ts": "_webpack_light", + "webpack.prod.js": "_webpack_light", + "webpack.prod.cjs": "_webpack_light", + "webpack.prod.mjs": "_webpack_light", + "webpack.prod.ts": "_webpack_light", + "license": "_license_light", + "licence": "_license_light", + "license.txt": "_license_light", + "licence.txt": "_license_light", + "license.md": "_license_light", + "licence.md": "_license_light", + "copying": "_license_light", + "copying.txt": "_license_light", + "copying.md": "_license_light", + "compiling": "_license_1_light", + "compiling.txt": "_license_1_light", + "compiling.md": "_license_1_light", + "contributing": "_license_2_light", + "contributing.txt": "_license_2_light", + "contributing.md": "_license_2_light", + "makefile": "_makefile_light", + "qmakefile": "_makefile_1_light", + "omakefile": "_makefile_2_light", + "cmakelists.txt": "_makefile_3_light", + "procfile": "_heroku_light", + "npm-debug.log": "_npm_ignored_light" + } + }, + "version": "https://github.com/DecimalTurn/seti-ui/commit/7a8d51ccb32737be812549fe1c31fae8276b284f" } \ No newline at end of file diff --git a/package.json b/package.json index f91c057..292a6e6 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,11 @@ "id": "vba-lsp", "label": "VBA Icons", "path": "icon-theme.json" + }, + { + "id": "vs-seti-vba", + "label": "Seti + VBA", + "path": "icons/theme-seti/icons/vs-seti-icon-theme.json" } ], "configurationDefaults": { From 6610ff10364874f6d3fba2e437c89c1854e9540a Mon Sep 17 00:00:00 2001 From: Martin Leduc <31558169+DecimalTurn@users.noreply.github.com> Date: Thu, 17 Apr 2025 02:46:07 +0000 Subject: [PATCH 22/93] Remove vba-lsp icon theme --- package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package.json b/package.json index 292a6e6..38d41ed 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,6 @@ } ], "iconThemes": [ - { - "id": "vba-lsp", - "label": "VBA Icons", - "path": "icon-theme.json" - }, { "id": "vs-seti-vba", "label": "Seti + VBA", From 9777b887949b81000b52ca30e6664e9117a60b52 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:46:55 +0800 Subject: [PATCH 23/93] Helper function to walk a directory and return a list of files --- server/src/utils/helpers.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index cea83e3..28b6e0a 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,3 +1,8 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { Services } from '../injection/services'; +import { fileURLToPath } from 'url'; + export class Dictionary extends Map { private defaultFactory: (...args: any) => V; @@ -34,4 +39,22 @@ export function ioEvents(): Promise { export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); +} + +export function walk(uri: string, pattern?: RegExp, files?: Map): Map +export function walk(dir: string, pattern?: RegExp, files?: Map): Map +export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { + Services.logger.debug(`Walking ${dirOrUri}`); + const dir = dirOrUri.startsWith('file://') ? fileURLToPath(dirOrUri) : dirOrUri; + + for (const name of fs.readdirSync(dir)) { + const p = path.join(dir, name); + if (fs.statSync(p).isDirectory()) { + walk(p, pattern, files); + } else if (pattern?.test(name) ?? true) { + Services.logger.debug(`Found ${p}`, 1); + files.set(p, fs.readFileSync(p, 'utf-8')); + } + } + return files; } \ No newline at end of file From acf097ce0b774c7e79703faf705e852dc4328f2e Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:11 +0800 Subject: [PATCH 24/93] Removed "async" from methods to align with common TS convention --- server/src/project/document.ts | 10 ++--- server/src/project/parser/vbaListener.ts | 6 +-- server/src/project/parser/vbaParser.ts | 50 +++++------------------- 3 files changed, 18 insertions(+), 48 deletions(-) diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 31d3458..d50022c 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -173,14 +173,14 @@ export abstract class BaseProjectDocument { async formatParseVisit(token: CancellationToken): Promise { try { - return await (new SyntaxParser(Services.logger)).formatParseAsync(token, this); + return await (new SyntaxParser(Services.logger)).formatParse(token, this); } catch (e) { Services.logger.debug('caught doc'); throw e; } } - async parseAsync(token: CancellationToken): Promise { + async parse(token: CancellationToken): Promise { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); @@ -190,7 +190,7 @@ export abstract class BaseProjectDocument { } // Parse the document. - await (new SyntaxParser(Services.logger)).parseAsync(token, this); + await (new SyntaxParser(Services.logger)).parse(token, this); const projectScope = this.currentScope.project; const buildScope = projectScope?.isDirty ? projectScope : this.currentScope; buildScope.build(); @@ -203,7 +203,7 @@ export abstract class BaseProjectDocument { this._isBusy = false; }; - async formatParseAsync(token: CancellationToken): Promise { + async formatParse(token: CancellationToken): Promise { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); @@ -212,7 +212,7 @@ export abstract class BaseProjectDocument { } // Parse the document. - return await (new SyntaxParser(Services.logger)).formatParseAsync(token, this); + return await (new SyntaxParser(Services.logger)).formatParse(token, this); } /** diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index f33080f..78d861a 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -91,7 +91,7 @@ export class VbaListener extends vbaListener { this.document = document; } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaListener(document); await result.ensureHasSettingsAsync(); return result; @@ -207,7 +207,7 @@ export class VbaPreListener extends vbapreListener { this.common = new CommonParserCapability(document); } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaPreListener(document); await result.common.ensureHasSettingsAsync(); return result; @@ -277,7 +277,7 @@ export class VbaFmtListener extends vbafmtListener { this.indentationKeys = new Array(document.textDocument.lineCount); } - static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + static async create(document: VbaClassDocument | VbaModuleDocument): Promise { const result = new VbaFmtListener(document); await result.common.ensureHasSettingsAsync(); return result; diff --git a/server/src/project/parser/vbaParser.ts b/server/src/project/parser/vbaParser.ts index 9c02671..3f3b0bf 100644 --- a/server/src/project/parser/vbaParser.ts +++ b/server/src/project/parser/vbaParser.ts @@ -1,6 +1,6 @@ // Antlr import { ParseCancellationException, ParseTreeWalker } from 'antlr4ng'; -import { VbaErrorHandler, VbaFmtParser, VbaParser, VbaPreParser } from './vbaAntlr'; +import { VbaFmtParser, VbaParser, VbaPreParser } from './vbaAntlr'; import { VbaFmtListener, VbaListener, VbaPreListener } from './vbaListener'; // Project @@ -12,69 +12,39 @@ import { Logger } from '../../injection/interface'; export class SyntaxParser { constructor(private logger: Logger) { } - async parseAsync(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument): Promise { + async parse(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument): Promise { // Preparse the document if we find a precompiler statement. const regexp = new RegExp(/^\s*#If/gmi); let docText = document.textDocument.getText(); if (regexp.test(docText)) { this.logger.debug(`Beginning pre-parse.`); - const prelistener = await VbaPreListener.createAsync(document); + const prelistener = await VbaPreListener.create(document); const preparser = VbaPreParser.create(docText); - await this.parseDocumentAsync(token, prelistener, preparser); + await this.parseDocument(token, prelistener, preparser); docText = prelistener.text; this.logger.debug(`Completed pre-parse.`); } // Perform main document parse without compiler directives. this.logger.debug(`Beginning main parse.`); - const listener = await VbaListener.createAsync(document); + const listener = await VbaListener.create(document); const parser = VbaParser.create(docText); - await this.parseDocumentAsync(token, listener, parser); + await this.parseDocument(token, listener, parser); this.logger.debug(`Completed main parse.`); } - async formatParseAsync(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { + async formatParse(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { // Special parser focused on document format. this.logger.debug(`Beginning format parse.`); - const listener = await VbaFmtListener.createAsync(document); + const listener = await VbaFmtListener.create(document); const parser = VbaFmtParser.create(document.textDocument.getText(range)); - await this.parseDocumentAsync(token, listener, parser); + await this.parseDocument(token, listener, parser); this.logger.debug(`Completed format parse.`); return listener; } - // async formatVisit(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument, range?: Range): Promise { - // // // Handle already cancelled. - // if (token.isCancellationRequested) { - // this.logger.debug(`Format visit cancelled before start.`); - // throw new ParseCancellationException(Error('Parse operation cancelled before it started.')); - // } - - // // Listen for cancellation event. - // token.onCancellationRequested(() => { - // this.logger.debug(`Format visit cancelled during run.`); - // throw new ParseCancellationException(new Error('Parse operation cancelled during parse.')); - // }); - - - // this.logger.debug(`Beginning format visit.`); - // const parser = VbaFmtParser.create(document.textDocument.getText(range)); - // const tree = parser.startRule(); - - // // const visitor = container.resolve(VbaFormatVisitor); - // // const visitor = forceAsync(container.resolve(VbaFormatVisitor)); - // // const visitor = forceAsync(new VbaFormatVisitor(this.logger)); - // const visitor = new VbaFormatVisitor(this.logger); - // visitor.token = token; - // await visitor.visit(tree); - // this.logger.debug(`Operation ${token.isCancellationRequested ? 'cancelled' : 'completed'}.`) - - // // Temporary call so I don't have to refactor everything just to call it. - // return await VbaFmtListener.createAsync(document); - // } - - private async parseDocumentAsync(token: CancellationToken, listener: VbaListener | VbaPreListener | VbaFmtListener, parser: VbaParser | VbaPreParser | VbaFmtParser) { + private async parseDocument(token: CancellationToken, listener: VbaListener | VbaPreListener | VbaFmtListener, parser: VbaParser | VbaPreParser | VbaFmtParser) { // Handle already cancelled. if (token.isCancellationRequested) { throw new ParseCancellationException(Error('Parse operation cancelled before it started.')); From a367146f6da54345d943cc3266bdfd2eeab74d3c Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:29 +0800 Subject: [PATCH 25/93] Inlined property --- server/src/server.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index f42fff0..fe370eb 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -70,7 +70,6 @@ export class LanguageServer implements ILanguageServer { export class LanguageServerConfiguration { - params: InitializeParams; capabilities: ServerCapabilities = { // Implemented documentSymbolProvider: true, @@ -111,9 +110,7 @@ export class LanguageServerConfiguration { experimental: undefined, }; - constructor(params: InitializeParams) { - this.params = params; - } + constructor(public params: InitializeParams) { } } From ddfb164a9b8359e1deaa0b00cf8a239f329a9713 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:52:53 +0800 Subject: [PATCH 26/93] Added method to Workspace interface --- server/src/injection/interface.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index 5e11959..220c3ba 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -2,7 +2,7 @@ import { LanguageServerConfiguration } from '../server'; import { BaseProjectDocument } from '../project/document'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { VbaFmtListener } from '../project/parser/vbaListener'; -import { CancellationToken } from 'vscode-languageserver'; +import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; export interface Logger { error(msg: string, lvl?: number): void; @@ -40,4 +40,5 @@ export interface IWorkspace { parseDocument(projectDocument: BaseProjectDocument): Promise; openDocument(document: TextDocument): void; closeDocument(document: TextDocument): void; + addWorkspaceFolder(params: WorkspaceFolder): void; } \ No newline at end of file From 6c35f4e1f15cd77e6e03a1ee7b954d836073b6fe Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:53:35 +0800 Subject: [PATCH 27/93] Code actions now properly async --- server/src/project/workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 14ab298..600092c 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -280,7 +280,7 @@ class WorkspaceEvents { connection.onHover(params => this.onHover(params)); connection.onDocumentFormatting(async (params, token) => await this.onDocumentFormatting(params, token)); connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); - connection.onCodeAction((params, token) => this.onCodeActionRequest(params, token)); + connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); if (hasWorkspaceConfigurationCapability(Services.server)) { connection.onFoldingRanges(async (params, token) => await cancellableOnFoldingRanges(params, token)); From 032915d7b36da4df2ed48b955fd839d1228b874f Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:56:38 +0800 Subject: [PATCH 28/93] Removed the concept of an "active" document --- server/src/project/workspace.ts | 36 +++++++++------------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 600092c..656db2b 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -56,10 +56,8 @@ export interface ExtensionConfiguration { @injectable() export class Workspace implements IWorkspace { private events?: WorkspaceEvents; - private documents: BaseProjectDocument[] = []; private parseCancellationTokenSource?: CancellationTokenSource; - private _activeDocument?: BaseProjectDocument; private readonly _hasConfigurationCapability: boolean; private _extensionConfiguration?: ExtensionConfiguration; @@ -82,10 +80,6 @@ export class Workspace implements IWorkspace { })(); } - get activeDocument() { - return this._activeDocument; - } - constructor( @inject("_Connection") public readonly connection: _Connection, @inject("ILanguageServer") private server: ILanguageServer) { @@ -101,27 +95,16 @@ export class Workspace implements IWorkspace { Services.registerProjectScope(projectScope); } - activateDocument(document?: BaseProjectDocument) { - if (document) { - this._activeDocument = document; - } - } - - async parseDocument(document?: BaseProjectDocument) { - this.activateDocument(document); + async parseDocument(document: BaseProjectDocument) { + // this.activateDocument(document); this.parseCancellationTokenSource?.cancel(); this.parseCancellationTokenSource = new CancellationTokenSource(); - if (!this.activeDocument) { - this.logger.error('No active document.'); - return; - } - // Exceptions thrown by the parser should be ignored. try { - await this.activeDocument.parseAsync(this.parseCancellationTokenSource.token); - this.logger.info(`Parsed ${this.activeDocument.name}`); - this.connection.sendDiagnostics(this.activeDocument.languageServerDiagnostics()); + await document.parse(this.parseCancellationTokenSource.token); + this.logger.info(`Parsed ${document.name}`); + this.connection.sendDiagnostics(document.languageServerDiagnostics()); } catch (e) { if (e instanceof ParseCancellationException) { // Swallow cancellation exceptions. They're good. We like these. @@ -135,14 +118,12 @@ export class Workspace implements IWorkspace { this.parseCancellationTokenSource = undefined; } - async formatParseDocument(document: TextDocument): Promise { - this.parseCancellationTokenSource?.cancel(); - this.parseCancellationTokenSource = new CancellationTokenSource(); - + async formatParseDocument(document: TextDocument, token: CancellationToken): Promise { // Exceptions thrown by the parser should be ignored. let result: VbaFmtListener | undefined; try { - result = await this.activeDocument?.formatParseAsync(this.parseCancellationTokenSource.token); + const projectDocument = this.projectDocuments.get(document.uri); + result = await projectDocument?.formatParse(token); this.logger.info(`Formatted ${document.uri}`); } catch (e) { @@ -163,6 +144,7 @@ export class Workspace implements IWorkspace { const projectDocument = this.projectDocuments.get(document.uri); if (document.version === projectDocument?.version) { projectDocument.open(); + this.parseDocument(projectDocument); this.connection.sendDiagnostics(projectDocument.languageServerDiagnostics()); } } From ca6790633763928e687e4514cee343e3fe3f24ca Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:57:20 +0800 Subject: [PATCH 29/93] Added support for parsing a whole workspace --- server/src/project/workspace.ts | 55 ++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 656db2b..b5218ea 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -20,13 +20,14 @@ import { SymbolInformation, TextDocuments, TextEdit, + WorkspaceFolder, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; import { BaseProjectDocument } from './document'; import { hasWorkspaceConfigurationCapability } from '../capabilities/workspaceFolder'; -import { sleep } from '../utils/helpers'; +import { sleep, walk } from '../utils/helpers'; import { ParseCancellationException } from 'antlr4ng'; import { getFormattingEdits } from './formatter'; import { VbaFmtListener } from './parser/vbaListener'; @@ -35,6 +36,7 @@ import { inject, injectable } from 'tsyringe'; import { Logger, ILanguageServer, IWorkspace } from '../injection/interface'; import { Services } from '../injection/services'; import { ItemType, ScopeItemCapability } from '../capabilities/capabilities'; +import { SyntaxParser } from './parser/vbaParser'; export interface ExtensionConfiguration { maxDocumentLines: number; @@ -95,6 +97,52 @@ export class Workspace implements IWorkspace { Services.registerProjectScope(projectScope); } + // Initially parse everything in the folder. + // ToDo: Handle removal of a workspace folder. + async addWorkspaceFolder(params: WorkspaceFolder): Promise { + this.logger.info(`Adding workspace: ${params.name}`); + const workspaceFiles = walk(params.uri, /\.(cls|bas|frm)$/i); + + // No need to continue if we have no files. + if (workspaceFiles.size === 0) { + return; + } + + // Set up parser and dummy token because we won't cancel this. + const parser = new SyntaxParser(this.logger); + const token = new CancellationTokenSource().token; + + // Handle each file in the workspace. + for (const [uri, file] of workspaceFiles) { + // Don't parse files that we're already tracking. + if (this.projectDocuments.has(uri)) { + this.logger.debug(`Skipping file: ${uri}`, 1); + continue; + } + + try { + // Read and parse the project document. + this.logger.debug(`Reading file: ${uri}`, 1); + const textDocument = TextDocument.create(`${uri}`, 'vba', 1, file); + const projectDocument = BaseProjectDocument.create(textDocument); + await parser.parse(token, projectDocument); + this.projectDocuments.set(uri, projectDocument); + this.logger.info(`Parsed ${projectDocument.name}`, 1); + } catch (e) { + // Log errors and anything else without failing. + this.logger.warn(`Failed to parse ${uri}`); + if (e instanceof Error) { + this.logger.stack(e); + } else { + this.logger.error(`Something went wrong: ${e}`); + } + } + } + + // Rebuild scopes from Project level. + Services.projectScope.build(); + } + async parseDocument(document: BaseProjectDocument) { // this.activateDocument(document); this.parseCancellationTokenSource?.cancel(); @@ -361,6 +409,11 @@ class WorkspaceEvents { ); connection.client.register(DidChangeConfigurationNotification.type, undefined); } + + // Read workspace folders if we have them. + Services.server.configuration?.params + .workspaceFolders?.forEach(folder => setImmediate(() => + Services.workspace.addWorkspaceFolder(folder))); } private async onDocumentFormatting(params: DocumentFormattingParams, token: CancellationToken): Promise { From a6ecba21b9e98ed54dd225ba7897f054751951d6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:07:59 +0800 Subject: [PATCH 30/93] Only modules declare public members in the project scope --- server/src/capabilities/capabilities.ts | 14 +++++++++----- server/src/project/elements/module.ts | 6 +++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 23b0839..93adaee 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -163,7 +163,9 @@ export enum ItemType { APPLICATION, /** The user's project. */ PROJECT, - /** Class/Module/Form. */ + /** Class/Form. */ + CLASS, + /** Module. */ MODULE, /** Function declaration. */ FUNCTION, @@ -444,7 +446,7 @@ export class ScopeItemCapability { /** Returns the module this scope item falls under */ get module(): ScopeItemCapability | undefined { - if (this.type == ItemType.MODULE) { + if (this.type === ItemType.MODULE || this.type === ItemType.CLASS) { return this; } return this.parent?.module; @@ -519,10 +521,12 @@ export class ScopeItemCapability { /** * Visibility on a method-scoped variable does nothing but isn't invalid. * These should declare as if they're private and raise a warning. + * + * Only MODULE scoped items are accessible implicitly in PROJECT scope and therefore + * only they should be 'escalated' to that scope. */ - const getParent = (item: ScopeItemCapability): ScopeItemCapability => - (item.isPublicScope ? this.project : this) ?? this; + (item.isPublicScope && this.type === ItemType.MODULE ? this.project : this) ?? this; // Method-scoped variables are always private. if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { @@ -594,7 +598,7 @@ export class ScopeItemCapability { } // Handle module registration - if (item.type === ItemType.MODULE) { + if (item.type === ItemType.MODULE || item.type === ItemType.CLASS) { item.parent.modules ??= new Map(); item.parent.addItem(item.parent.modules, item); return item; diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index c8bdc2f..f868bcb 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -30,6 +30,7 @@ abstract class BaseModuleElement extends BaseIdenti abstract attrubutes: ParserRuleContext[]; abstract diagnosticCapability: DiagnosticCapability; abstract hasOptionExplicit: boolean; + abstract scopeItemCapability: ScopeItemCapability; settings: DocumentSettings; symbolInformationCapability: SymbolInformationCapability; @@ -38,7 +39,6 @@ abstract class BaseModuleElement extends BaseIdenti super(ctx, doc); this.settings = documentSettings; this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); } // Helpers @@ -104,6 +104,7 @@ abstract class BaseModuleElement extends BaseIdenti export class ModuleElement extends BaseModuleElement { diagnosticCapability: DiagnosticCapability; identifierCapability: IdentifierCapability; + scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; hasOptionExplicit: boolean; @@ -112,6 +113,7 @@ export class ModuleElement extends BaseModuleElement { super(ctx, doc, documentSettings, SymbolKind.File); this.attrubutes = ctx.proceduralModuleHeader().proceduralModuleAttr(); this.diagnosticCapability = new DiagnosticCapability(this); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx .proceduralModuleBody() @@ -142,6 +144,7 @@ export class ModuleElement extends BaseModuleElement { export class ClassElement extends BaseModuleElement { diagnosticCapability: DiagnosticCapability; identifierCapability: IdentifierCapability; + scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; hasOptionExplicit: boolean; @@ -154,6 +157,7 @@ export class ClassElement extends BaseModuleElement { ctx.classModuleHeader().ignoredClassAttr() ].flat(); this.diagnosticCapability = new DiagnosticCapability(this); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.CLASS); this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx .classModuleBody() From ee3fa45264a8c22d3bcb26f19bee7face021876d Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:31:32 +0800 Subject: [PATCH 31/93] walk function now returns URIs if passed a URI rather than paths --- server/src/utils/helpers.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 28b6e0a..d1dee2f 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { Services } from '../injection/services'; -import { fileURLToPath } from 'url'; +import { fileURLToPath, pathToFileURL } from 'url'; export class Dictionary extends Map { private defaultFactory: (...args: any) => V; @@ -45,7 +45,8 @@ export function walk(uri: string, pattern?: RegExp, files?: Map) export function walk(dir: string, pattern?: RegExp, files?: Map): Map export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { Services.logger.debug(`Walking ${dirOrUri}`); - const dir = dirOrUri.startsWith('file://') ? fileURLToPath(dirOrUri) : dirOrUri; + const isUri = dirOrUri.startsWith('file://'); + const dir = isUri ? fileURLToPath(dirOrUri) : dirOrUri; for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name); @@ -53,7 +54,7 @@ export function walk(dirOrUri: string, pattern?: RegExp, files: Map Date: Tue, 22 Apr 2025 08:47:03 +0800 Subject: [PATCH 32/93] Can now log error stack as warning --- server/src/injection/interface.ts | 2 +- server/src/utils/logger.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index 220c3ba..ae15b1f 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -10,7 +10,7 @@ export interface Logger { info(msg: string, lvl?: number): void; log(msg: string, lvl?: number): void; debug(msg: string, lvl?: number): void; - stack(e: Error): void; + stack(e: Error, onlyWarn?: boolean): void; } export interface ClientConfiguration { diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index 2a79dd9..0608125 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -28,7 +28,7 @@ export class LspLogger implements Logger { info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl); log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl); debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl); - stack = (e: Error) => this.emit(LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); + stack = (e: Error, onlyWarn?: boolean) => this.emit(onlyWarn ? LogLevel.warn : LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); private emit(logLevel: LogLevel, msgText: string, msgLevel?: number): void { // Async get the configuration and then emit. From d23300cfe8b143d41954acbb657272ac7bac9c0e Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 08:47:59 +0800 Subject: [PATCH 33/93] Prevent async related errors on shut down --- server/src/server.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/server.ts b/server/src/server.ts index fe370eb..d4cb7a9 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -45,6 +45,10 @@ export class LanguageServer implements ILanguageServer { activateSemanticTokenProvider(result); return result; }); + // Register shutdown actions + this.connection.onShutdown(() => {}); + this.connection.onExit(() => process.exit(0)); + // Register for client configuration notification changes. this.connection.onInitialized(() => { this.connection.client.register(DidChangeConfigurationNotification.type, undefined); }); this.connection.onDidChangeConfiguration(() => this._clientConfiguration = undefined); From 6163de42dee0dae9784d8e556e782d2d4be560cb Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:10:12 +0800 Subject: [PATCH 34/93] Added error handling and uri conversion to walk function --- server/src/utils/helpers.ts | 49 +++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index d1dee2f..8d826c1 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -41,20 +41,53 @@ export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -export function walk(uri: string, pattern?: RegExp, files?: Map): Map -export function walk(dir: string, pattern?: RegExp, files?: Map): Map -export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { +/** + * Recursively walks a directory structure and returns a map of file path to string content. + * @param dirOrUri A directory as a path or 'file://' uri. + * @param pattern A predicate to filter results. + * @param files Used for internal recursive calls. + * @param wasUriConverted Used for internal recursive calls. + */ +export function walk(uri: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map +export function walk(dir: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map +export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map(), wasUriConverted?: boolean): Map { Services.logger.debug(`Walking ${dirOrUri}`); - const isUri = dirOrUri.startsWith('file://'); - const dir = isUri ? fileURLToPath(dirOrUri) : dirOrUri; + // Check whether the original directory was a dir or uri and convert as required. + const shouldConvertUri = wasUriConverted || (wasUriConverted === undefined && dirOrUri.startsWith('file://')); + const dir = (wasUriConverted === undefined && shouldConvertUri) + ? fileURLToPath(dirOrUri) + : dirOrUri; + + // Walk the contents of the directory. for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name); - if (fs.statSync(p).isDirectory()) { - walk(p, pattern, files); + + // Check if we have a directory. This can occasionally throw at an OS level. + let pIsDirectory: boolean | undefined; + try { pIsDirectory = fs.statSync(p).isDirectory(); } + catch (e) { + Services.logger.warn(`The OS threw an exception checking whether ${p} is a directory.`); + if (e instanceof Error) { + Services.logger.stack(e, true); + continue; + } + } + if (pIsDirectory) { + // Recursive call for directories. + walk(p, pattern, files, shouldConvertUri); } else if (pattern?.test(name) ?? true) { + // Track files that match the pattern. Services.logger.debug(`Found ${p}`, 1); - files.set(isUri ? pathToFileURL(p).href : p, fs.readFileSync(p, 'utf-8')); + let fileContent = ''; + try { fileContent = fs.readFileSync(p, 'utf-8'); } + catch (e) { + Services.logger.error(`The OS threw an exception reading ${p}.`); + if (e instanceof Error) { + Services.logger.stack(e); + } + } + files.set(shouldConvertUri ? pathToFileURL(p).href : p, fileContent); } } return files; From db89709ae0de51117ab4a9adeda12348da6cc35c Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:24:15 +0800 Subject: [PATCH 35/93] Better way of logging stack traces --- server/src/injection/interface.ts | 12 ++++++------ server/src/project/workspace.ts | 15 +++------------ server/src/utils/helpers.ts | 15 ++------------- server/src/utils/logger.ts | 19 ++++++++++++------- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index ae15b1f..8effea3 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -5,12 +5,12 @@ import { VbaFmtListener } from '../project/parser/vbaListener'; import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; export interface Logger { - error(msg: string, lvl?: number): void; - warn(msg: string, lvl?: number): void; - info(msg: string, lvl?: number): void; - log(msg: string, lvl?: number): void; - debug(msg: string, lvl?: number): void; - stack(e: Error, onlyWarn?: boolean): void; + error(msg: string, lvl?: number, e?: unknown): void; + warn(msg: string, lvl?: number, e?: unknown): void; + info(msg: string, lvl?: number, e?: unknown): void; + log(msg: string, lvl?: number, e?: unknown): void; + debug(msg: string, lvl?: number, e?: unknown): void; + stack(e: Error): void; } export interface ClientConfiguration { diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index b5218ea..16b68ef 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -130,12 +130,7 @@ export class Workspace implements IWorkspace { this.logger.info(`Parsed ${projectDocument.name}`, 1); } catch (e) { // Log errors and anything else without failing. - this.logger.warn(`Failed to parse ${uri}`); - if (e instanceof Error) { - this.logger.stack(e); - } else { - this.logger.error(`Something went wrong: ${e}`); - } + this.logger.error(`Failed to parse ${uri}`, 0, e); } } @@ -156,10 +151,8 @@ export class Workspace implements IWorkspace { } catch (e) { if (e instanceof ParseCancellationException) { // Swallow cancellation exceptions. They're good. We like these. - } else if (e instanceof Error) { - this.logger.stack(e); } else { - this.logger.error('Something went wrong.'); + this.logger.error('Something went wrong.', 0, e); } } @@ -177,10 +170,8 @@ export class Workspace implements IWorkspace { catch (e) { if (e instanceof ParseCancellationException) { this.logger.debug('Parse cancelled successfully.'); - } else if (e instanceof Error) { - this.logger.stack(e); } else { - this.logger.error(`Parse failed: ${e}`); + this.logger.error(`Parse failed.`, 0, e); } } diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 8d826c1..82b2982 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -66,13 +66,7 @@ export function walk(dirOrUri: string, pattern?: RegExp, files: Map this.emit(LogLevel.error, msg, lvl); - warn = (msg: string, lvl?: number) => this.emit(LogLevel.warn, msg, lvl); - info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl); - log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl); - debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl); - stack = (e: Error, onlyWarn?: boolean) => this.emit(onlyWarn ? LogLevel.warn : LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); + error = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.error, msg, lvl); + warn = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.warn, msg, lvl); + info = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.info, msg, lvl); + log = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.log, msg, lvl); + debug = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.debug, msg, lvl); + stack = (e: Error, logLevel?: LogLevel) => this.emit(logLevel ?? LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); - private emit(logLevel: LogLevel, msgText: string, msgLevel?: number): void { + private emit(logLevel: LogLevel, msgText: string, msgLevel?: number, e?: unknown): void { // Async get the configuration and then emit. this.server.clientConfiguration.then(config => { try { @@ -54,6 +54,11 @@ export class LspLogger implements Logger { message: msgText, level: msgLevel ?? 0 }); + + // If we have an error, then log stack trace too. + if (e instanceof Error) { + this.stack(e, logLevel); + } }); } From 12e4e40d7ec2b447219479bcdf963471a69cfdf6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:26:46 +0800 Subject: [PATCH 36/93] Refactor tests for class and module --- client/src/test/codeActions.test.ts | 2 +- client/src/test/diagnostics.test.ts | 48 ++++++++++++++------------- client/src/test/foldingRanges.test.ts | 2 +- client/src/test/formatting.test.ts | 4 ++- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/client/src/test/codeActions.test.ts b/client/src/test/codeActions.test.ts index 07c78b5..6412459 100644 --- a/client/src/test/codeActions.test.ts +++ b/client/src/test/codeActions.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get code actions', () => { +suite('Should get module code actions', () => { test('actions.module.missingOptionExplicitCodeAction', async () => { const docUri = getDocUri('EmptyModule.bas'); const edits = new vscode.WorkspaceEdit(); diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts index 18f1ebf..e72603c 100644 --- a/client/src/test/diagnostics.test.ts +++ b/client/src/test/diagnostics.test.ts @@ -8,18 +8,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get diagnostics', () => { - test('diagnostics.class.missingNameAttributeError', async () => { - await testDiagnostics(getDocUri('DiagnosticsMissingAttributeClass.cls'), [ - { - message: 'Module missing attribute VB_NAME.', - range: toRange(4, 3, 4, 3), - severity: vscode.DiagnosticSeverity.Error, - source: 'ex' - } - ]); - }); - +suite('Should get module diagnostics', () => { test('diagnostics.module.missingNameAttributeError', async () => { await testDiagnostics(getDocUri('DiagnosticsMissingAttributeModule.bas'), [ { @@ -31,17 +20,6 @@ suite('Should get diagnostics', () => { ]); }); - test('diagnostics.class.noOptionExplicitWarning', async () => { - await testDiagnostics(getDocUri('EmptyClass.cls'), [ - { - message: 'Option Explicit is missing from module header.', - range: toRange(11, 1, 11, 1), - severity: vscode.DiagnosticSeverity.Warning, - source: 'ex' - } - ]); - }); - test('diagnostics.module.noOptionExplicitWarning', async () => { await testDiagnostics(getDocUri('EmptyModule.bas'), [ { @@ -100,6 +78,30 @@ suite('Should get diagnostics', () => { } ]); }); +}); + +suite('Should get class diagnostics', () => { + test('diagnostics.class.missingNameAttributeError', async () => { + await testDiagnostics(getDocUri('DiagnosticsMissingAttributeClass.cls'), [ + { + message: 'Module missing attribute VB_NAME.', + range: toRange(4, 3, 4, 3), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } + ]); + }); + + test('diagnostics.class.noOptionExplicitWarning', async () => { + await testDiagnostics(getDocUri('EmptyClass.cls'), [ + { + message: 'Option Explicit is missing from module header.', + range: toRange(11, 1, 11, 1), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + } + ]); + }); test('diagnostics.class.generalDiagnostics', async () => { // Don't seem to be able to sort these. Good luck! diff --git a/client/src/test/foldingRanges.test.ts b/client/src/test/foldingRanges.test.ts index 6b80841..835841e 100644 --- a/client/src/test/foldingRanges.test.ts +++ b/client/src/test/foldingRanges.test.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; -suite('Should get folding ranges', () => { +suite('Should get class folding ranges', () => { test('formatting.class.template', async () => { const subFoo = {start: 23, end: 42}; const subBar = {start: 44, end: 58}; diff --git a/client/src/test/formatting.test.ts b/client/src/test/formatting.test.ts index ce539d1..40443d4 100644 --- a/client/src/test/formatting.test.ts +++ b/client/src/test/formatting.test.ts @@ -3,7 +3,7 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; -suite('Should get text edits', () => { +suite('Should get class text edits', () => { test('formatting.class.template', async () => { await testTextEdits(getDocUri('FormatTemplateClass.cls'), [ {range: toRange(3, 0, 3, 0), newText: ' '}, @@ -12,7 +12,9 @@ suite('Should get text edits', () => { {range: toRange(8, 0, 8, 8), newText: ''} ]); }); +}); +suite('Should get module text edits', () => { test('formatting.module.directives', async () => { await testTextEdits(getDocUri('FormatPrecompilerDirectives.bas'), [ // Staggered (half) indentation. From 2dc0ca0c7f6b821d821b099c6ac71a3875aae218 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:31:28 +0800 Subject: [PATCH 37/93] Improved auto closing pairs --- vba.language-configuration.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vba.language-configuration.json b/vba.language-configuration.json index 91918b0..8837871 100644 --- a/vba.language-configuration.json +++ b/vba.language-configuration.json @@ -8,8 +8,11 @@ ["(", ")"] ], "autoClosingPairs": [ - { "open": "\"", "close": "\""} + { "open": "\"", "close": "\""}, + { "open": "(", "close": ")"}, + { "open": "[", "close": "]"} ], + "autoCloseBefore": ": \n\r\t)}]\n", "surroundingPairs": [ [ "(", ")" ], [ "[", "]" ], From 508766d18760d19641077c8ac25ad229f4c5cb68 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:32:42 +0800 Subject: [PATCH 38/93] Placeholder auto indent patterns. --- vba.language-configuration.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vba.language-configuration.json b/vba.language-configuration.json index 8837871..9d5867f 100644 --- a/vba.language-configuration.json +++ b/vba.language-configuration.json @@ -19,4 +19,9 @@ [ "\"", "\"" ] ], "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", + // Placeholder - possibly won't use as it's a little awkward. Prefer to implement with LSP. + // "indentationRules": { + // "increaseIndentPattern": "^\\s*(?:Sub|Function|Property\\s+(?:Get|Let|Set)|Else|(Else)?If.*?Then|For|While|Do|Select\\s+Case|Type|With|Enum)\\b", + // "decreaseIndentPattern": "^\\s*(?:End\\s+(?:Sub|Function|Property|If|Select|Type|With|Enum)|Next|Loop|Else|ElseIf.*?Then)\\b" + // } } \ No newline at end of file From ed9525aa2c3a0b4de25ff0115af3601dd12079a6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:15:27 +0800 Subject: [PATCH 39/93] Spelling... --- server/src/antlr/vba.g4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index 7122bf2..4a735c6 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -861,7 +861,7 @@ identifierStatementLabel : ambiguousIdentifier ; -resetNumberLable +resetNumberLabel : MINUS INTEGERLITERAL ; @@ -1312,7 +1312,7 @@ onErrorStatement errorBehavior : RESUME wsc NEXT | GOTO wsc? statementLabel - | GOTO wsc? resetNumberLable + | GOTO wsc? resetNumberLabel ; // 5.4.4.2 Resume Statement From 9054e3408beba28c1389950148f9fbd5699a477a Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:30:04 +0800 Subject: [PATCH 40/93] Resolve timing conflict reading file first time --- server/src/project/workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 16b68ef..8e17f76 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -125,8 +125,8 @@ export class Workspace implements IWorkspace { this.logger.debug(`Reading file: ${uri}`, 1); const textDocument = TextDocument.create(`${uri}`, 'vba', 1, file); const projectDocument = BaseProjectDocument.create(textDocument); - await parser.parse(token, projectDocument); this.projectDocuments.set(uri, projectDocument); + await parser.parse(token, projectDocument); this.logger.info(`Parsed ${projectDocument.name}`, 1); } catch (e) { // Log errors and anything else without failing. From de981449aebb1c4380dd0a6f0c3a4bdc4ece0481 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:59 +0800 Subject: [PATCH 41/93] Added uri extension methods --- server/src/extensions/stringExtensions.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/server/src/extensions/stringExtensions.ts b/server/src/extensions/stringExtensions.ts index f8fb6fb..a48e809 100644 --- a/server/src/extensions/stringExtensions.ts +++ b/server/src/extensions/stringExtensions.ts @@ -1,9 +1,26 @@ -interface String { - stripQuotes(): string; -} +import { fileURLToPath, pathToFileURL } from "url"; +declare global { + interface String { + stripQuotes(): string; + toFilePath(): string; + toFileUri(): string; + } +} String.prototype.stripQuotes = function (): string { const exp = /^"?(.*?)"?$/; return exp.exec(this.toString())![1]; }; + +String.prototype.toFilePath = function (): string { + return this.startsWith('file://') + ? fileURLToPath(this.toString()) + : this.toString(); +}; + +String.prototype.toFileUri = function (): string { + return this.startsWith('file://') + ? pathToFileURL(this.toString()).href + : this.toString(); +}; From 1c9f976c6ee02d0d104d44cabad136729b70bb0f Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:31:28 +0800 Subject: [PATCH 42/93] Walk method no longer needs to handle URI conversion --- server/src/utils/helpers.ts | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 82b2982..79f5c78 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -46,38 +46,32 @@ export function sleep(ms: number): Promise { * @param dirOrUri A directory as a path or 'file://' uri. * @param pattern A predicate to filter results. * @param files Used for internal recursive calls. - * @param wasUriConverted Used for internal recursive calls. */ -export function walk(uri: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map -export function walk(dir: string, pattern?: RegExp, files?: Map, wasUriConverted?: boolean): Map -export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map(), wasUriConverted?: boolean): Map { - Services.logger.debug(`Walking ${dirOrUri}`); - - // Check whether the original directory was a dir or uri and convert as required. - const shouldConvertUri = wasUriConverted || (wasUriConverted === undefined && dirOrUri.startsWith('file://')); - const dir = (wasUriConverted === undefined && shouldConvertUri) - ? fileURLToPath(dirOrUri) - : dirOrUri; +export function walk(dirOrUri: string, pattern?: RegExp, files: Map = new Map()): Map { + const logger = Services.logger; + logger.debug(`Walking ${dirOrUri}`); // Walk the contents of the directory. + const dir = dirOrUri.toFilePath(); for (const name of fs.readdirSync(dir)) { const p = path.join(dir, name); // Check if we have a directory. This can occasionally throw at an OS level. let pIsDirectory: boolean | undefined; try { pIsDirectory = fs.statSync(p).isDirectory(); } - catch (e) { Services.logger.warn(`The OS threw an exception checking whether ${p} is a directory.`, 0, e); } + catch (e) { logger.warn(`The OS threw an exception checking whether ${p} is a directory.`, 0, e); } if (pIsDirectory) { // Recursive call for directories. - walk(p, pattern, files, shouldConvertUri); + walk(p, pattern, files); } else if (pattern?.test(name) ?? true) { // Track files that match the pattern. - Services.logger.debug(`Found ${p}`, 1); + logger.debug(`Found ${p}`, 1); + logger.debug(`href: ${pathToFileURL(p).href}`); let fileContent = ''; try { fileContent = fs.readFileSync(p, 'utf-8'); } - catch (e) { Services.logger.error(`The OS threw an exception reading ${p}.`, 0, e); } - files.set(shouldConvertUri ? pathToFileURL(p).href : p, fileContent); + catch (e) { logger.error(`The OS threw an exception reading ${p}.`, 0, e); } + files.set(p, fileContent); } } return files; -} \ No newline at end of file +} From b6648501f59464d6987673fe4e713bd6ef522ddb Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:32:10 +0800 Subject: [PATCH 43/93] Adding functionality to scope capability --- server/src/capabilities/capabilities.ts | 206 ++++++++++++++++++------ 1 file changed, 159 insertions(+), 47 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 93adaee..8656d2e 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -208,6 +208,7 @@ export class ScopeItemCapability { // Technical isDirty: boolean = true; + isInvalidated = false; private get isMethodScope(): boolean { return [ ItemType.SUBROUTINE, @@ -215,6 +216,22 @@ export class ScopeItemCapability { ItemType.PROPERTY, ].includes(this.type); } + get maps() { + const result: Map[] = []; + const addToResult = (map: Map | undefined) => { + if (map) result.push(map); + }; + addToResult(this.types); + addToResult(this.modules); + addToResult(this.functions); + addToResult(this.subroutines); + addToResult(this.properties?.getters); + addToResult(this.properties?.letters); + addToResult(this.properties?.setters); + addToResult(this.references); + + return result; + } // Item Properties explicitSetName?: string; @@ -232,6 +249,27 @@ export class ScopeItemCapability { * Recursively build from this node down. */ build(): void { + // Remove children that are invalidated. + this.maps.forEach(map => { + for (const [key, items] of map) { + const keep = items.filter(x => !x.isInvalidated); + if (keep.length === 0) map.delete(key); + else map.set(key, keep); + } + }); + + // Clean invalidated links. + if (this.link?.isInvalidated) this.link = undefined; + if (this.backLinks) { + const keep = this.backLinks.filter(x => !x.isInvalidated); + this.backLinks = keep.length > 0 ? keep : undefined; + } + + // Don't build self if invalidated. + if (this.isInvalidated) { + return; + } + if (this.type === ItemType.REFERENCE) { // Link to declaration if it exists. this.resolveLinks(); @@ -251,8 +289,9 @@ export class ScopeItemCapability { } } else { // Diagnostic checks on declarations. - this.resolveDuplicateDeclarations(); - this.resolveShadowedDeclarations(); + const ancestors = this.getParentChain(); + this.resolveDuplicateDeclarations(ancestors); + this.resolveShadowedDeclarations(ancestors); } // Call build on children. @@ -267,8 +306,26 @@ export class ScopeItemCapability { this.isDirty = false; } + /** Returns the chain of parents for bottom up name resolution. */ + getParentChain(items: ScopeItemCapability[] = []): ScopeItemCapability[] { + items.push(this); + return this.parent?.getParentChain(items) ?? items; + } + + /** Prints the hierarchy of scopes from this node down. */ + printToDebug(level: number = 0) { + const p = this.isPublicScope ? '[P] ' : ' '; + Services.logger.debug(`${p}${this.name}`, level); + + this.maps.forEach( + maps => maps.forEach( + items => items.forEach( + item => item.printToDebug(level + 1) + ))); + } + /** Resolves for the current scope, i.e., children of the current item. */ - private resolveDuplicateDeclarations() { + private resolveDuplicateDeclarations(ancestors: ScopeItemCapability[]) { // Reference types are never relevant. if (this.type == ItemType.REFERENCE) { return; @@ -349,39 +406,48 @@ export class ScopeItemCapability { } } - private resolveShadowedDeclarations() { - // Get the parent of the scope where this element is registered. - const parent = this.parent?.parent; - if (!parent) { + private resolveShadowedDeclarations(ancestors: ScopeItemCapability[]) { + // Don't check for shadowed declarations if we're above project level. + if (ancestors.length < 3) { return; } - // All declaration types check for modules. - this.resolveShadowedDeclaration(parent.findType(this.identifier)); - this.resolveShadowedDeclaration(parent.findModule(this.identifier)); - this.resolveShadowedDeclaration(parent.findFunction(this.identifier)); - this.resolveShadowedDeclaration(parent.findSubroutine(this.identifier)); - - // Properties care about everything except properties that - // aren't the same type. Everything else cares about everything. - - // ToDo: - // Variables are registered as props so should also squash their - // get/set/let diagnostics into one single diagnostic. + for (let i = 2; i < ancestors.length; i++) { + const ancestor = ancestors[i]; + const shadowing = ancestor.getAccessibleScopes(this.name); - // Check get properties. - if (this.assignmentType & AssignmentType.GET) { - this.resolveShadowedDeclaration(parent.findPropertyGetter(this.identifier)); - } - - // Check let properties. - if (this.assignmentType & AssignmentType.LET) { - this.resolveShadowedDeclaration(parent.findPropertyLetter(this.identifier)); - } + if (shadowing) { + const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic); + shadowing.forEach(item => this.addDiagnosticReference(diagnostic, item)); + } - // Check set properties. - if (this.assignmentType & AssignmentType.SET) { - this.resolveShadowedDeclaration(parent.findPropertySetter(this.identifier)); + // // All declaration types check for modules. + // this.resolveShadowedDeclaration(parent.findType(this.identifier)); + // this.resolveShadowedDeclaration(parent.findModule(this.identifier)); + // this.resolveShadowedDeclaration(parent.findFunction(this.identifier)); + // this.resolveShadowedDeclaration(parent.findSubroutine(this.identifier)); + + // // Properties care about everything except properties that + // // aren't the same type. Everything else cares about everything. + + // // ToDo: + // // Variables are registered as props so should also squash their + // // get/set/let diagnostics into one single diagnostic. + + // // Check get properties. + // if (this.assignmentType & AssignmentType.GET) { + // this.resolveShadowedDeclaration(parent.findPropertyGetter(this.identifier)); + // } + + // // Check let properties. + // if (this.assignmentType & AssignmentType.LET) { + // this.resolveShadowedDeclaration(parent.findPropertyLetter(this.identifier)); + // } + + // // Check set properties. + // if (this.assignmentType & AssignmentType.SET) { + // this.resolveShadowedDeclaration(parent.findPropertySetter(this.identifier)); + // } } } @@ -478,6 +544,33 @@ export class ScopeItemCapability { ?? this.parent?.findType(identifier); } + /** Get accessible declarations */ + getAccessibleScopes(identifier: string, results: ScopeItemCapability[] = []): ScopeItemCapability[] { + // Add any non-public items we find at this level. + this.maps.forEach(map => { + map.get(identifier)?.forEach(item => { + if (!item.isPublicScope) { + results.push(item); + } + }); + }); + + // Get all public scope types if we're at the project level. + if (this.type === ItemType.PROJECT) { + this.modules?.forEach(modules => modules.forEach( + module => module.maps.forEach(map => { + map.get(identifier)?.forEach(item => { + if (item.isPublicScope) { + results.push(item); + } + }); + }) + )); + } + + return this.parent?.getAccessibleScopes(identifier, results) ?? results; + } + findModule(identifier: string): ScopeItemCapability | undefined { return this.modules?.get(identifier)?.[0] ?? this.parent?.findModule(identifier); @@ -525,24 +618,29 @@ export class ScopeItemCapability { * Only MODULE scoped items are accessible implicitly in PROJECT scope and therefore * only they should be 'escalated' to that scope. */ - const getParent = (item: ScopeItemCapability): ScopeItemCapability => - (item.isPublicScope && this.type === ItemType.MODULE ? this.project : this) ?? this; - - // Method-scoped variables are always private. - if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { - item.isPublicScope = false; - if (item.visibilityModifierContext && item.element) { - const ctx = item.visibilityModifierContext; - const diagnostic = new MethodVariableIsPublicDiagnostic( - ctx.toRange(item.element.context.document), - ItemType[this.type] - ); - item.element?.diagnosticCapability?.diagnostics.push(diagnostic); - } + // const getParent = (item: ScopeItemCapability): ScopeItemCapability => + // (item.isPublicScope && this.type === ItemType.MODULE ? this.project : this) ?? this; + + // // Method-scoped variables are always private. + // if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { + // item.isPublicScope = false; + // if (item.visibilityModifierContext && item.element) { + // const ctx = item.visibilityModifierContext; + // const diagnostic = new MethodVariableIsPublicDiagnostic( + // ctx.toRange(item.element.context.document), + // ItemType[this.type] + // ); + // item.element?.diagnosticCapability?.diagnostics.push(diagnostic); + // } + // } + + // Immediately invalidate if we're an Unknown Module + if (item.type === ItemType.MODULE && item.name === 'Unknown Module') { + item.isInvalidated = true; } // Set the parent for the item. - item.parent = getParent(item); + item.parent = this; // getParent(item); item.parent.isDirty = true; // Get the scope level for logging. @@ -608,9 +706,23 @@ export class ScopeItemCapability { return item; } + invalidate(uri: string): void { + if (this.type !== ItemType.PROJECT) { + this.isInvalidated = true; + } + this.types?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.modules?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.functions?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.subroutines?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.getters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.letters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.setters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.references?.forEach(items => items.forEach(item => item.invalidate(uri))); + } + // Would be relatively simple to also do this via a "dirty" flag. /** Removes all elements with references to the document uri. */ - invalidate(uri: string): void { + invalidate2(uri: string): void { const unlink = (item: ScopeItemCapability): void => { // Remove backlink from linked item. item.link?.removeBacklink(item); From 3e5aa726619e9c31b5e14b3ab9d0dc6933c095e1 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:32:25 +0800 Subject: [PATCH 44/93] Print scope tree on build --- server/src/project/workspace.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 8e17f76..007db3c 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -111,7 +111,7 @@ export class Workspace implements IWorkspace { // Set up parser and dummy token because we won't cancel this. const parser = new SyntaxParser(this.logger); const token = new CancellationTokenSource().token; - + // Handle each file in the workspace. for (const [uri, file] of workspaceFiles) { // Don't parse files that we're already tracking. @@ -136,6 +136,7 @@ export class Workspace implements IWorkspace { // Rebuild scopes from Project level. Services.projectScope.build(); + Services.projectScope.printToDebug(); } async parseDocument(document: BaseProjectDocument) { @@ -468,10 +469,10 @@ class WorkspaceEvents { onDidOpen(document: TextDocument) { const logger = Services.logger; logger.debug('[event] onDidOpen'); - logger.debug(`uri: ${document.uri}`, 1); + logger.debug(`uri: ${document.uri.toFilePath()}`, 1); logger.debug(`languageId: ${document.languageId}`, 1); logger.debug(`version: ${document.version}`, 1); - const projectDocument = this.projectDocuments.get(document.uri); + const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); if (projectDocument) { Services.workspace.openDocument(document); } @@ -483,22 +484,25 @@ class WorkspaceEvents { */ onDidChangeContent(document: TextDocument): void { const logger = Services.logger; - logger.debug('[event] onDidChangeContentAsync'); - logger.debug(`uri: ${document.uri}`, 1); + logger.debug('[event] onDidChangeContent'); + logger.debug(`uri: ${document.uri.toFilePath()}`, 1); logger.debug(`languageId: ${document.languageId}`, 1); logger.debug(`version: ${document.version}`, 1); // If the event is fired for the same version of the document, don't reparse. - const existingDocument = this.projectDocuments.get(document.uri); - if ((existingDocument?.version ?? -1) >= document.version) { + const docPath = document.uri.toFilePath(); + const existingDocument = this.projectDocuments.get(docPath); + const existingVersion = existingDocument?.version ?? -1; + logger.debug(`existing: ${existingVersion}`, 1); + if (existingVersion >= document.version) { logger.debug('Document already parsed.'); return; } // The document is new or a new version that we should parse. const projectDocument = BaseProjectDocument.create(document); - this.projectDocuments.set(document.uri, projectDocument); - Services.projectScope.invalidate(document.uri); + this.projectDocuments.set(docPath, projectDocument); + Services.projectScope.invalidate(docPath); Services.workspace.parseDocument(projectDocument); } From a7e588304895552706e964adef10be073f9f9319 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:35:59 +0800 Subject: [PATCH 45/93] Fixed error logging issue --- server/src/capabilities/capabilities.ts | 6 +++++- server/src/project/workspace.ts | 2 +- server/src/utils/logger.ts | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 8656d2e..380a313 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -148,7 +148,11 @@ export class IdentifierCapability extends BaseCapability { this.range = this.nameContext.toRange(args.element.context.document); } else { // Use the defaults to set the values. - if (!args.defaultRange) throw new Error("Default range not optional where name context not found."); + if (!args.defaultRange) { + const msg = 'No default or name context for identifier at'; + const rng = JSON.stringify(args.element.context.range); + throw new Error(`${msg} ${rng}.`); + } this.name = (args.defaultName ?? "Unknown Element"); this.range = args.defaultRange ? args.defaultRange() : args.element.context.range; } diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 007db3c..2f00a80 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -153,7 +153,7 @@ export class Workspace implements IWorkspace { if (e instanceof ParseCancellationException) { // Swallow cancellation exceptions. They're good. We like these. } else { - this.logger.error('Something went wrong.', 0, e); + this.logger.debug('Parser did not cancel or complete.', 0, e); } } diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index 8fa0ad0..5c8d0b7 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -23,11 +23,11 @@ export class LspLogger implements Logger { @inject("_Connection") public connection: _Connection, @inject("ILanguageServer") private server: ILanguageServer) { } - error = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.error, msg, lvl); - warn = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.warn, msg, lvl); - info = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.info, msg, lvl); - log = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.log, msg, lvl); - debug = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.debug, msg, lvl); + error = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.error, msg, lvl, e); + warn = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.warn, msg, lvl, e); + info = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.info, msg, lvl, e); + log = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.log, msg, lvl, e); + debug = (msg: string, lvl?: number, e?: unknown) => this.emit(LogLevel.debug, msg, lvl, e); stack = (e: Error, logLevel?: LogLevel) => this.emit(logLevel ?? LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); private emit(logLevel: LogLevel, msgText: string, msgLevel?: number, e?: unknown): void { From 79ffc96eb8daa4668fc031fa8b0790a32ad89ccd Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:58:13 +0800 Subject: [PATCH 46/93] Only show output automatically when debugging --- client/src/extension.ts | 9 +++++++++ client/src/logger.ts | 24 +++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 62ff40c..ee6357a 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -71,3 +71,12 @@ export function deactivate(): Thenable | undefined { } return client.stop(); } + +export function isExtensionInDebugMode(): boolean { + // process.execArgv contains arguments specific to the Node.js executable itself. + // When debugging, VS Code launches the extension host with flags like + // '--inspect-brk' or '--inspect'. + return process.execArgv.some(arg => + arg.startsWith('--inspect') || arg.startsWith('--debug') // '--debug' is older, but good to include + ); +} diff --git a/client/src/logger.ts b/client/src/logger.ts index 01a261c..bd2e1e1 100644 --- a/client/src/logger.ts +++ b/client/src/logger.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { isExtensionInDebugMode } from './extension'; enum LogLevel { error = 1, @@ -15,23 +16,20 @@ export interface LogMessage { } export class VscodeLogger { - private static _outputChannel: vscode.OutputChannel; + private static _outputChannel: vscode.OutputChannel | undefined; private static get outputChannel(): vscode.OutputChannel { if (!VscodeLogger._outputChannel) { - VscodeLogger._outputChannel = vscode.window.createOutputChannel('VBAPro Output'); - VscodeLogger._outputChannel.show(); + VscodeLogger.initialiseOutputChannel(); } return VscodeLogger._outputChannel; } + private static get configuredLevel(): LogLevel { const config = vscode.workspace.getConfiguration('vbaLanguageServer'); const levelString = config.get('logLevel.outputChannel', 'warning'); return LogLevel[levelString as keyof typeof LogLevel]; } - static info = (msg: string, lvl?: number) => this.log(LogLevel.info, msg, lvl) - static debug = (msg: string, lvl?: number) => this.log(LogLevel.debug, msg, lvl) - static logMessage(params: LogMessage): void { this.log(params.type, params.message, params.level); } @@ -44,18 +42,26 @@ export class VscodeLogger { VscodeLogger.outputChannel.appendLine(`${t} [${LogLevel[type]}] ${i}${msg}`); } + private static initialiseOutputChannel(): void { + const channel = vscode.window.createOutputChannel('VBAPro Output'); + if (isExtensionInDebugMode) { + channel.show(); + } + VscodeLogger._outputChannel = channel; + } + private static getFormattedTimestamp(): string { const now = new Date(); - + const year = now.getFullYear(); const month = (now.getMonth() + 1).toString().padStart(2, "0"); const day = now.getDate().toString().padStart(2, "0"); - + const hours = now.getHours().toString().padStart(2, "0"); const minutes = now.getMinutes().toString().padStart(2, "0"); const seconds = now.getSeconds().toString().padStart(2, "0"); const milliseconds = now.getMilliseconds().toString().padStart(3, "0"); - + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`; } } From 7b13446c8adbf55f9c5a9c3538866e591aafae56 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:14:16 +0800 Subject: [PATCH 47/93] Better way of doing IdentifierCapability --- server/src/capabilities/capabilities.ts | 68 +++++++++++++----------- server/src/project/elements/module.ts | 49 +++++++++-------- server/src/project/elements/procedure.ts | 28 +++++----- server/src/project/elements/typing.ts | 26 ++++----- 4 files changed, 89 insertions(+), 82 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 380a313..383dce7 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -121,41 +121,49 @@ export class SymbolInformationCapability extends BaseCapability { } } -interface IdentifierArgs { - element: BaseRuleSyntaxElement, - getNameContext?: () => ParserRuleContext | TerminalNode | null | undefined, - formatName?: (name: string) => string, - defaultName?: string; - defaultRange?: () => Range; -} - export class IdentifierCapability extends BaseCapability { - nameContext: ParserRuleContext | TerminalNode; - range: Range; - name: string; - isDefaultMode: boolean; - - constructor(args: IdentifierArgs) { - super(args.element); - - this.nameContext = ((args.getNameContext ?? (() => args.element.context.rule))() ?? args.element.context.rule); - this.isDefaultMode = !(!!args.getNameContext && !!args.getNameContext()); + private get unformattedName(): string { + return this.nameContext?.getText() ?? this.defaultName ?? "Unknown Element"; + } - if (!this.isDefaultMode) { - // Use the context to set the values. - this.name = (args.formatName ?? ((name: string) => name))(this.nameContext.getText()); - this.range = this.nameContext.toRange(args.element.context.document); - } else { - // Use the defaults to set the values. - if (!args.defaultRange) { - const msg = 'No default or name context for identifier at'; - const rng = JSON.stringify(args.element.context.range); - throw new Error(`${msg} ${rng}.`); + get name(): string { + return this.formatName + ? this.formatName(this.unformattedName) + : this.unformattedName; + } + + get range(): Range { + if (this.getNameContext) { + const ctx = this.getNameContext(); + if (ctx) { + return ctx.toRange(this.element.context.document); + } else if (this.defaultRange) { + return this.defaultRange(); } - this.name = (args.defaultName ?? "Unknown Element"); - this.range = args.defaultRange ? args.defaultRange() : args.element.context.range; + Services.logger.warn(`Unable to get name context or default for ${this.name}`); } + return this.element.context.range; + } + + get nameContext(): ParserRuleContext | TerminalNode { + return this.getNameContext + ? this.getNameContext() ?? this.element.context.rule + : this.element.context.rule; + } + + get isDefaultMode(): boolean { + return !(!!this.getNameContext && !!this.getNameContext()); + } + + constructor( + readonly element: BaseRuleSyntaxElement, + private getNameContext?: () => ParserRuleContext | TerminalNode | null | undefined, + private formatName?: (name: string) => string, + private defaultName?: string, + private defaultRange?: () => Range + ) { + super(element); } } diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index f868bcb..834f185 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -120,18 +120,22 @@ export class ModuleElement extends BaseModuleElement { .proceduralModuleCode() .proceduralModuleCodeElement()); - this.identifierCapability = new IdentifierCapability({ - element: this, - formatName: (x: string) => x.stripQuotes(), - defaultName: 'Unknown Module', - defaultRange: () => Range.create(this.context.range.start, this.context.range.start), - getNameContext: () => ctx - .proceduralModuleHeader() - .proceduralModuleAttr() - .map(x => x.nameAttr()) - .filter(x => !!x)[0] - ?.STRINGLITERAL() - }); + const getIdentifierNameContext = () => this.context.rule + .proceduralModuleHeader() + .proceduralModuleAttr() + .map(x => x.nameAttr()) + .filter(x => !!x)[0] + ?.STRINGLITERAL(); + const getIdentifierFormattedName = (x: string) => x.stripQuotes(); + const getIdentifierDefaultRange = () => Range.create(this.context.range.start, this.context.range.start); + + this.identifierCapability = new IdentifierCapability( + this, + getIdentifierNameContext, + getIdentifierFormattedName, + 'Unknown Module', + getIdentifierDefaultRange + ); this.resolveConfiguration( this.diagnosticCapability.diagnostics, @@ -164,20 +168,23 @@ export class ClassElement extends BaseModuleElement { .classModuleCode() .classModuleCodeElement()); - let nameContextGetter; + let getIdentifierNameContext; if (ctx.classModuleHeader().nameAttr().length > 0) { - nameContextGetter = () => ctx + getIdentifierNameContext = () => ctx .classModuleHeader() .nameAttr()[0] .STRINGLITERAL(); } - this.identifierCapability = new IdentifierCapability({ - element: this, - formatName: (x: string) => x.stripQuotes(), - defaultName: 'Unknown Class', - defaultRange: () => Range.create(this.context.range.start, this.context.range.start), - getNameContext: nameContextGetter - }); + const getIdentifierFormattedName = (x: string) => x.stripQuotes(); + const getIdentifierDefaultRange = () => Range.create(this.context.range.start, this.context.range.start); + + this.identifierCapability = new IdentifierCapability( + this, + getIdentifierNameContext, + getIdentifierFormattedName, + 'Unknown Class', + getIdentifierDefaultRange + ); this.resolveConfiguration( this.diagnosticCapability.diagnostics, diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts index 769ebf4..c7f1d52 100644 --- a/server/src/project/elements/procedure.ts +++ b/server/src/project/elements/procedure.ts @@ -55,13 +55,9 @@ export class SubDeclarationElement extends BaseProcedureElement ctx.subroutineName()?.ambiguousIdentifier(), - // For some reason the IdentifierCapability throws if no default is given - // despite it not actually ever needing it. Most unusual. - defaultRange: () => this.context.range - }); + + const getIdentifierNameContext = () => ctx.subroutineName()?.ambiguousIdentifier(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); this.foldingRangeCapability.openWord = `Sub ${this.identifierCapability.name}`; this.foldingRangeCapability.closeWord = 'End Sub'; this.scopeItemCapability.type = ItemType.SUBROUTINE; @@ -74,10 +70,8 @@ export class FunctionDeclarationElement extends BaseProcedureElement ctx.functionName()?.ambiguousIdentifier(), - }); + const getIdentifierNameContext = () => ctx.functionName()?.ambiguousIdentifier(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); this.foldingRangeCapability.openWord = `Function ${this.identifierCapability.name}`; this.foldingRangeCapability.closeWord = 'End Function'; this.scopeItemCapability.type = ItemType.FUNCTION; @@ -117,11 +111,13 @@ abstract class BasePropertyDeclarationElement this.nameContext, - formatName: (x: string) => `${this.propertyType} ${x}` - }); + const getIdentifierNameContext = () => this.nameContext; + const getIdentifierFormattedName = (x: string) => `${this.propertyType} ${x}`; + this.identifierCapability = new IdentifierCapability( + this, + getIdentifierNameContext, + getIdentifierFormattedName + ); } } diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index 667af9f..3eeed98 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -61,10 +61,9 @@ export class EnumDeclarationElement extends BaseTypeDeclarationElement ctx.untypedName().ambiguousIdentifier() - }); + + const getIdentifierNameContext = () => ctx.untypedName().ambiguousIdentifier(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); if (isAfterProcedure) this.diagnosticCapability.diagnostics.push( new ElementOutOfPlaceDiagnostic(this.context.range, "Enum declaration") ); @@ -81,10 +80,8 @@ export class EnumMemberDeclarationElement extends BaseRuleSyntaxElement ctx.untypedName() - }); + const getIdentifierNameContext = () => ctx.untypedName(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE, AssignmentType.GET); } } @@ -99,10 +96,8 @@ export class TypeDeclarationElement extends BaseTypeDeclarationElement ctx.untypedName() - }); + const getIdentifierNameContext = () => ctx.untypedName(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); } } @@ -166,8 +161,9 @@ export class VariableDeclarationElement extends BaseRuleSyntaxElement ctx.ambiguousIdentifier() }); - + const getIdentifierNameContext = () => ctx.ambiguousIdentifier(); + this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); + // VariableDcl > TypedVariableDcl > TypedName > TypeSuffix // > UntypedVariableDcl > AsClause @@ -286,7 +282,7 @@ class VariableTypeInformation extends BaseRuleSyntaxElement Date: Thu, 1 May 2025 13:40:00 +0800 Subject: [PATCH 48/93] Added equality comparer for diagnostics --- server/src/capabilities/diagnostics.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 4dd28d8..9c97b78 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -32,6 +32,15 @@ export abstract class BaseDiagnostic implements Diagnostic { } this.relatedInformation.push(information); } + + equals(diagnostic: Diagnostic) { + return this.severity === diagnostic.severity + && this.message === diagnostic.message + && this.range.end.line === diagnostic.range.end.line + && this.range.start.line === diagnostic.range.start.line + && this.range.end.character === diagnostic.range.end.character + && this.range.start.character === diagnostic.range.start.character; + } } From dc2e9ae4eb718cef48fe7219b875d555a166e452 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 1 May 2025 13:41:03 +0800 Subject: [PATCH 49/93] Added context to variable diagnostics --- server/src/capabilities/diagnostics.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 9c97b78..525c618 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -89,26 +89,25 @@ export class DuplicateDeclarationDiagnostic extends BaseDiagnostic { // test export class ShadowDeclarationDiagnostic extends BaseDiagnostic { - message = "Declaration is shadowed in the local scope."; severity = DiagnosticSeverity.Warning; - constructor(range: Range) { + constructor(range: Range, message: string) { super(range); + this.message = `${message} is shadowed in the local scope.`; } } export class VariableNotDefinedDiagnostic extends BaseDiagnostic { - message = "Variable not defined."; - severity = DiagnosticSeverity.Error; - constructor(range: Range) { + constructor(range: Range, message: string, public severity: DiagnosticSeverity) { super(range); + this.message = `Variable ${message} not defined.`; } } export class SubOrFunctionNotDefinedDiagnostic extends BaseDiagnostic { - message = "Sub or Function not defined."; severity = DiagnosticSeverity.Error; - constructor(range: Range) { + constructor(range: Range, message: string) { super(range); + this.message = `Method ${message} not defined.`; } } From 24ea8b0f662d58d8e31a56aaf3b61d0ef6d77f63 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 1 May 2025 13:53:35 +0800 Subject: [PATCH 50/93] Awaiting never returns --- server/src/project/workspace.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 2f00a80..e294455 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -136,7 +136,7 @@ export class Workspace implements IWorkspace { // Rebuild scopes from Project level. Services.projectScope.build(); - Services.projectScope.printToDebug(); + // Services.projectScope.printToDebug(); } async parseDocument(document: BaseProjectDocument) { @@ -439,8 +439,6 @@ class WorkspaceEvents { } try { - // We don't actually need the document but await to ensure it's parsed. - await this.getParsedProjectDocument(params.textDocument.uri, 0, token); const uri = params.textDocument.uri; const result: (Command | CodeAction)[] = []; const codeActionRegistry = Services.codeActionsRegistry; From 33358f6bdce00df1f8037063dff5ce9462e2ce6d Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 1 May 2025 13:57:22 +0800 Subject: [PATCH 51/93] Improved diagnostics related to OptExp --- server/src/capabilities/capabilities.ts | 13 ++++++++----- server/src/capabilities/diagnostics.ts | 14 +++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 383dce7..e0b2316 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -1,6 +1,6 @@ // Core import { - Diagnostic, + DiagnosticSeverity, Range, SemanticTokenModifiers, SemanticTokenTypes, @@ -12,6 +12,7 @@ import { import { ParserRuleContext, TerminalNode } from 'antlr4ng'; // Project +import { BaseModuleElement } from '../project/elements/module'; import { SemanticToken } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, BaseSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; @@ -294,10 +295,12 @@ export class ScopeItemCapability { // References to function or sub calls should raise an error if they aren't declared. // -- Must always throw even when option explicit not present. // -- Nothing required on first reference as declaration may come later. - const diagnosticType = this.assignmentType & AssignmentType.CALL - ? SubOrFunctionNotDefinedDiagnostic - : VariableNotDefinedDiagnostic; - this.pushDiagnostic(diagnosticType); + const severity = (this.module?.element as BaseModuleElement).hasOptionExplicit + ? DiagnosticSeverity.Error + : DiagnosticSeverity.Warning; + const _ = this.assignmentType & AssignmentType.CALL + ? this.pushDiagnostic(SubOrFunctionNotDefinedDiagnostic, this, this.name) + : this.pushDiagnostic(VariableNotDefinedDiagnostic, this, this.name, severity); } } else { // Diagnostic checks on declarations. diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 525c618..8ba9c35 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -158,7 +158,7 @@ export class IgnoredAttributeDiagnostic extends BaseDiagnostic { export class MissingOptionExplicitDiagnostic extends BaseDiagnostic { message = "Option Explicit is missing from module header."; - severity = DiagnosticSeverity.Warning; + severity = DiagnosticSeverity.Hint; constructor(range: Range) { super(range); @@ -167,10 +167,14 @@ export class MissingOptionExplicitDiagnostic extends BaseDiagnostic { this.actionFactory = (diagnostic: Diagnostic, uri: string) => CodeAction.create( "Insert Option Explicit", - { changes: { [uri]: [{ - range: diagnostic.range, - newText: "\nOption Explicit" - }]}}, + { + changes: { + [uri]: [{ + range: diagnostic.range, + newText: "\nOption Explicit" + }] + } + }, CodeActionKind.QuickFix ); From 75bd9db7c6d344ed61b2059cb4d7c6f83b8cf799 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 1 May 2025 14:20:04 +0800 Subject: [PATCH 52/93] Removed console logging messages --- server/src/project/parser/vbaAntlr.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/server/src/project/parser/vbaAntlr.ts b/server/src/project/parser/vbaAntlr.ts index 0204663..ba58c13 100644 --- a/server/src/project/parser/vbaAntlr.ts +++ b/server/src/project/parser/vbaAntlr.ts @@ -97,7 +97,6 @@ export class VbaErrorHandler extends DefaultErrorStrategy { const inputStream = recognizer.inputStream; if (inputStream.LA(1) !== Token.EOF) { const intervalSet = this.getErrorRecoverySet(recognizer); - console.log(`recover consuming ${recognizer.getCurrentToken()}`); inputStream.consume(); // this.consumeUntil(recognizer, intervalSet); } @@ -108,12 +107,12 @@ export class VbaErrorHandler extends DefaultErrorStrategy { // Attempt to recover using token deletion. const matchedSymbol = this.singleTokenDeletion(recognizer); if (matchedSymbol) { - recognizer.consume(); - return matchedSymbol; + recognizer.consume(); + return matchedSymbol; } // Attempt to recover using token insertion. if (this.singleTokenInsertion(recognizer)) { - return this.getMissingSymbol(recognizer); + return this.getMissingSymbol(recognizer); } // When we don't know what could come next, invalidate the entire line. @@ -123,13 +122,11 @@ export class VbaErrorHandler extends DefaultErrorStrategy { const invalidTokens: Token[] = []; while (![vbaLexer.NEWLINE, vbaLexer.EOF].includes(stream.LA(1))) { invalidTokens.push(stream.LT(1)!); - console.log(`inline consuming ${recognizer.getCurrentToken()}`); recognizer.consume(); } if (invalidTokens.length > 0) { const missingToken = this.createErrorToken(recognizer, invalidTokens); this.reportMatch(recognizer); - console.log(`inline consuming ${recognizer.getCurrentToken()}`); recognizer.consume(); return missingToken; } From 9e4532ad043b62b6a8e0037144dfb42177ca3356 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 07:58:13 +0800 Subject: [PATCH 53/93] Allow diagnostic comparison --- server/src/capabilities/capabilities.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index e0b2316..d2c9ce1 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -56,10 +56,10 @@ export class FoldingRangeCapability extends BaseCapability { export class DiagnosticCapability extends BaseCapability { - diagnostics: Diagnostic[] = []; - evaluate: (...args: any[]) => Diagnostic[]; + diagnostics: BaseDiagnostic[] = []; + evaluate: (...args: any[]) => BaseDiagnostic[]; - constructor(element: BaseSyntaxElement, evaluate?: (...args: any[]) => Diagnostic[]) { + constructor(element: BaseSyntaxElement, evaluate?: (...args: any[]) => BaseDiagnostic[]) { super(element); this.evaluate = evaluate ?? (() => this.diagnostics); } From 2e9a5dc7481ec16f794a1dab6798fc37d623339d Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 07:59:23 +0800 Subject: [PATCH 54/93] Scope references now build --- server/src/capabilities/capabilities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index d2c9ce1..727a452 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -317,6 +317,7 @@ export class ScopeItemCapability { this.properties?.getters?.forEach(items => items.forEach(item => item.build())); this.properties?.letters?.forEach(items => items.forEach(item => item.build())); this.properties?.setters?.forEach(items => items.forEach(item => item.build())); + this.references?.forEach(items => items.forEach(item => item.build())); this.isDirty = false; } From be20672b3a22ea6a5cb71f5e09966e0a375d2819 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 08:07:57 +0800 Subject: [PATCH 55/93] Simplify scope invalidation --- server/src/capabilities/capabilities.ts | 66 ++----------------------- 1 file changed, 3 insertions(+), 63 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 727a452..fb695e6 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -726,69 +726,9 @@ export class ScopeItemCapability { if (this.type !== ItemType.PROJECT) { this.isInvalidated = true; } - this.types?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.modules?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.functions?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.subroutines?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.getters?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.letters?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.setters?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.references?.forEach(items => items.forEach(item => item.invalidate(uri))); - } - - // Would be relatively simple to also do this via a "dirty" flag. - /** Removes all elements with references to the document uri. */ - invalidate2(uri: string): void { - const unlink = (item: ScopeItemCapability): void => { - // Remove backlink from linked item. - item.link?.removeBacklink(item); - // Remove link from any backlinked items. - item.backLinks?.forEach(node => node.link = undefined); - }; - - const scan = (map: Map | undefined, uri: string) => { - if (map === undefined) { - return; - } - - const keys = Array.from(map.keys()); - keys.forEach(key => { - const items = map.get(key)!; - const keep = items.filter(item => item.element?.context.document.uri !== uri); - const remove = items.filter(item => item.element?.context.document.uri === uri); - - // Sever links for items to be removed. - remove.forEach(x => unlink(x)); - - // Update the map. - if (keep.length === 0) { - map.delete(key); - } else { - map.set(key, keep); - } - }); - }; - - // Invalidate and unlink items. - scan(this.types, uri); - scan(this.modules, uri); - scan(this.functions, uri); - scan(this.subroutines, uri); - if (this.properties !== undefined) { - scan(this.properties.getters, uri); - scan(this.properties.letters, uri); - scan(this.properties.setters, uri); - } - scan(this.references, uri); - - // Call invalidate on children. - this.types?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.modules?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.functions?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.subroutines?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.getters?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.letters?.forEach(items => items.forEach(item => item.invalidate(uri))); - this.properties?.setters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.maps.forEach(map => map.forEach(items => + items.forEach(item => item.invalidate(uri)) + )); } private addItem(target: Map, item: ScopeItemCapability): void { From 099c80c383ea73de3168e86419736e13fb20047d Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 08:09:55 +0800 Subject: [PATCH 56/93] Avoid duplicate diagnostics --- server/src/capabilities/capabilities.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index fb695e6..df6e8cd 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -737,9 +737,23 @@ export class ScopeItemCapability { target.set(item.identifier, items); } + private hasDiagnostic(diagnostic: BaseDiagnostic): boolean { + const diagnostics = this.element?.diagnosticCapability?.diagnostics; + if (!diagnostics || diagnostics.length === 0) { + return false; + } + + for (const pushedDiagnostic of diagnostics) { + if (diagnostic.equals(pushedDiagnostic)) { + return true; + } + } + return false; + } + /** * Generates and pushes a diagnostic to the underlying element on the scope item. - * No diagnostic is created unless we have both a range and the capability on the element. + * No diagnostic is created unless we have a range, the capability on the element, and no duplicate exists. * Range is automatically injected into the constructor but arguments can't be verified at compile time, so it's on you to check. * @param ctor The diagnostic we're creating. * @param item The scope item to get the range from. @@ -751,6 +765,9 @@ export class ScopeItemCapability { const diagnostics = (item ?? this).element?.diagnosticCapability?.diagnostics; if (range && diagnostics) { const diagnostic = new ctor(...[range, args].flat()); + if (this.hasDiagnostic(diagnostic)) { + return; + } diagnostics.push(diagnostic); return diagnostic; } From 61658a8f722f59573c339c062dd770f3d1621a9a Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 08:11:00 +0800 Subject: [PATCH 57/93] Filter duplicate diagnostics --- server/src/project/document.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/server/src/project/document.ts b/server/src/project/document.ts index d50022c..f571f8e 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -13,7 +13,6 @@ import { SemanticTokensManager } from '../capabilities/semanticTokens'; import { BaseRuleSyntaxElement, BaseSyntaxElement, - DeclarableElement, HasDiagnosticCapability, HasFoldingRangeCapability, HasScopeItemCapability, @@ -31,6 +30,7 @@ import { VbaFmtListener } from './parser/vbaListener'; import { Services } from '../injection/services'; import { IWorkspace } from '../injection/interface'; import { ScopeItemCapability } from '../capabilities/capabilities'; +import { BaseDiagnostic } from '../capabilities/diagnostics'; // TODO --------------------------------------------- @@ -196,10 +196,18 @@ export abstract class BaseProjectDocument { buildScope.build(); // Evaluate the diagnostics. - this.diagnostics = this.hasDiagnosticElements + const diagnostics = this.hasDiagnosticElements .map(e => e.diagnosticCapability.evaluate()) .flat(); + // Ensure diagnostics aren't reported twice. + // TODO: Redesign diagnostics so this isn't required. + diagnostics.forEach(diagnostic => { + if (!this.hasDiagnostic(diagnostic)) { + this.diagnostics.push(diagnostic); + } + }); + this._isBusy = false; }; @@ -316,6 +324,15 @@ export abstract class BaseProjectDocument { return result.join('\r\n'); } } + + private hasDiagnostic(diagnostic: BaseDiagnostic): boolean { + for (const pushedDiagnostic of this.diagnostics) { + if (diagnostic.equals(pushedDiagnostic)) { + return true; + } + } + return false; + } } From 0fe9f69c2bcb721dfb2a880834a32ce550325b30 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 9 May 2025 08:18:06 +0800 Subject: [PATCH 58/93] Switch to Module from BaseModule to avoid export --- server/src/capabilities/capabilities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index df6e8cd..b404cb8 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -12,7 +12,7 @@ import { import { ParserRuleContext, TerminalNode } from 'antlr4ng'; // Project -import { BaseModuleElement } from '../project/elements/module'; +import { ModuleElement } from '../project/elements/module'; import { SemanticToken } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, BaseSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; @@ -295,7 +295,7 @@ export class ScopeItemCapability { // References to function or sub calls should raise an error if they aren't declared. // -- Must always throw even when option explicit not present. // -- Nothing required on first reference as declaration may come later. - const severity = (this.module?.element as BaseModuleElement).hasOptionExplicit + const severity = (this.module?.element as ModuleElement).hasOptionExplicit ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; const _ = this.assignmentType & AssignmentType.CALL From 79148644df6497eb43f049c5b801d07c323273e1 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 15 May 2025 00:13:21 +0800 Subject: [PATCH 59/93] Added support for rename requests --- server/src/capabilities/capabilities.ts | 56 +++++++++++++++++++++++++ server/src/project/workspace.ts | 49 ++++++++++++++++++++-- server/src/server.ts | 4 +- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index b404cb8..8454fd8 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -731,6 +731,62 @@ export class ScopeItemCapability { )); } + getRenameItems(uri: string, position: { line: number, character: number }): ScopeItemCapability[] { + const moduleName = uri.split('/').at(-1)?.split('.').slice(0, -1).join('.'); + if (!moduleName) { + Services.logger.error(`Bad URI or name: ${moduleName} from ${uri}`); + return []; + } + + const modules = this.modules?.get(moduleName); + if (!modules) { + Services.logger.error(`No such module: ${moduleName}`); + return []; + } + + if (modules.length > 1) { + Services.logger.error(`Module name ambiguity: ${modules.length} found.`); + return []; + } + + const module = modules[0]; + const itemsAtPosition: ScopeItemCapability[] = []; + module.maps.map(m => m.forEach(items => items.forEach(item => { + if (item.isAtPosition(position)) { + itemsAtPosition.push(item); + } + }))); + if (itemsAtPosition.length === 0) { + Services.logger.warn(`Nothing to rename.`); + return []; + } + + if (itemsAtPosition.length > 1) { + Services.logger.warn(`Ambiguity detected: ${itemsAtPosition.length} overlapping names.`); + return []; + } + + const declarationItem = itemsAtPosition[0].link ? itemsAtPosition[0].link : itemsAtPosition[0]; + const result = [declarationItem, ...declarationItem.backLinks ?? []]; + + return result; + } + + isAtPosition(position: { line: number, character: number }): boolean { + const range = this.element?.context.range; + if (!range) { + return false; + } + + if (range.start.line !== range.end.line) { + return position.line >= range.start.line + && position.line <= range.end.line; + } + + return position.character >= range.start.character + && position.character <= range.end.character; + } + private addItem(target: Map, item: ScopeItemCapability): void { const items = target.get(item.identifier) ?? []; items.push(item); diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index e294455..63e7c40 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -16,10 +16,12 @@ import { FoldingRangeParams, Hover, HoverParams, + RenameParams, SemanticTokensRangeParams, SymbolInformation, TextDocuments, TextEdit, + WorkspaceEdit, WorkspaceFolder, WorkspaceFoldersChangeEvent, _Connection @@ -293,16 +295,17 @@ class WorkspaceEvents { const cancellableOnFoldingRanges = returnDefaultOnCancelClientRequest( (p: FoldingRangeParams, t) => this.onFoldingRangesAsync(p, t), [], 'Folding Range'); - connection.onInitialized(() => this.onInitialized()); + connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); connection.onCompletion(params => this.onCompletion(params)); connection.onCompletionResolve(item => this.onCompletionResolve(item)); connection.onDidChangeConfiguration(() => Services.workspace.clearDocumentsConfiguration()); connection.onDidChangeWatchedFiles(params => this.onDidChangeWatchedFiles(params)); + connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); + connection.onDocumentFormatting(async (params, token) => await this.onDocumentFormatting(params, token)); connection.onDocumentSymbol(async (params, token) => await cancellableOnDocSymbol(params, token)); connection.onHover(params => this.onHover(params)); - connection.onDocumentFormatting(async (params, token) => await this.onDocumentFormatting(params, token)); - connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); - connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); + connection.onInitialized(() => this.onInitialized()); + connection.onRenameRequest((params, token) => this.onRenameRequest(params, token)); if (hasWorkspaceConfigurationCapability(Services.server)) { connection.onFoldingRanges(async (params, token) => await cancellableOnFoldingRanges(params, token)); @@ -458,6 +461,44 @@ class WorkspaceEvents { } } + private async onRenameRequest(params: RenameParams, token: CancellationToken): Promise { + Services.logger.debug(`[event] onRenameRequest: ${JSON.stringify(params)}`); + if (token.isCancellationRequested) { + Services.logger.debug(`onRenameRequest cancelled before start.`); + return; + } + + const renameItems = Services.projectScope.getRenameItems(params.textDocument.uri, params.position); + const workspaceEdit: { changes: { [uri: string]: TextEdit[] }; } = { changes: {} }; + + for (const renameItem of renameItems) { + const uri = renameItem.element?.context.document.uri; + if (!uri) { + Services.logger.warn('Scope item has no element to rename'); + continue; + } + + const range = renameItem.element.identifierCapability?.range; + if (!range) { + Services.logger.warn('Scope item has no identifier to rename'); + continue; + } + + workspaceEdit.changes[uri] ??= []; + workspaceEdit.changes[uri].push(TextEdit.replace(range, params.newName)); + } + + Services.logger.debug(`resolved onRenameRequest: returning\n${JSON.stringify(workspaceEdit)}`); + + // Allow a cancellation to be processed if we have one. + await new Promise(resolve => setTimeout(resolve, 0)); + if (token.isCancellationRequested) { + Services.logger.debug(`onRenameRequest cancelled during run.`); + return; + } + return workspaceEdit; + } + /** Documents event handlers */ /** diff --git a/server/src/server.ts b/server/src/server.ts index d4cb7a9..8beb387 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -46,7 +46,7 @@ export class LanguageServer implements ILanguageServer { return result; }); // Register shutdown actions - this.connection.onShutdown(() => {}); + this.connection.onShutdown(() => { }); this.connection.onExit(() => process.exit(0)); // Register for client configuration notification changes. @@ -104,7 +104,7 @@ export class LanguageServerConfiguration { documentFormattingProvider: true, documentRangeFormattingProvider: false, documentOnTypeFormattingProvider: undefined, - renameProvider: false, + renameProvider: true, selectionRangeProvider: false, executeCommandProvider: undefined, callHierarchyProvider: false, From 690658853a0cdae22e670d2f407ce03b0b743771 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 16 May 2025 10:54:40 +0800 Subject: [PATCH 60/93] New helper to check if a position is in a range. --- server/src/utils/helpers.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 79f5c78..c031759 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; import { Services } from '../injection/services'; -import { fileURLToPath, pathToFileURL } from 'url'; +import { pathToFileURL } from 'url'; +import { Position, Range } from 'vscode-languageserver'; export class Dictionary extends Map { private defaultFactory: (...args: any) => V; @@ -75,3 +76,14 @@ export function walk(dirOrUri: string, pattern?: RegExp, files: Map= range.start.line + && position.line <= range.end.line; + } + + return position.line === range.start.line + && position.character >= range.start.character + && position.character <= range.end.character; +} \ No newline at end of file From caad0138438898b295253646a7042415532718ee Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 16 May 2025 13:49:35 +0800 Subject: [PATCH 61/93] Refactor antlr extensions --- server/src/extensions/antlrCoreExtensions.ts | 153 +++++++++ .../extensions/antlrVbaParserExtensions.ts | 116 +++++++ .../extensions/antlrVbaPreParserExtensions.ts | 17 + server/src/extensions/parserExtensions.ts | 301 ------------------ server/src/server.ts | 4 +- 5 files changed, 289 insertions(+), 302 deletions(-) create mode 100644 server/src/extensions/antlrCoreExtensions.ts create mode 100644 server/src/extensions/antlrVbaParserExtensions.ts create mode 100644 server/src/extensions/antlrVbaPreParserExtensions.ts delete mode 100644 server/src/extensions/parserExtensions.ts diff --git a/server/src/extensions/antlrCoreExtensions.ts b/server/src/extensions/antlrCoreExtensions.ts new file mode 100644 index 0000000..d067897 --- /dev/null +++ b/server/src/extensions/antlrCoreExtensions.ts @@ -0,0 +1,153 @@ +// Core +import { Range } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; + +// Antlr +import { ParserRuleContext, TerminalNode } from 'antlr4ng'; + +// Project +import { + EndOfStatementContext, + EndOfStatementNoWsContext, + ProcedureTailContext +} from '../antlr/out/vbaParser'; +import { LineEndingContext } from '../antlr/out/vbafmtParser'; + + +declare module 'antlr4ng' { + interface ParserRuleContext { + /** Convert the node to a range. */ + toRange(doc: TextDocument): Range; + startIndex(): number; + stopIndex(): number; + hasPositionOf(ctx: ParserRuleContext): boolean; + endsWithLineEnding: boolean; + countTrailingLineEndings(): number; + } + + interface TerminalNode { + /** Convert the node to a range. */ + toRange(doc: TextDocument): Range; + startIndex(): number; + stopIndex(): number; + } +} + +ParserRuleContext.prototype.toRange = function (doc: TextDocument): Range { + const startIndex = this.start?.start ?? 0; + const stopIndex = this.stop?.stop ?? startIndex; + return Range.create( + doc.positionAt(startIndex), + doc.positionAt(stopIndex + 1) + ); +}; + +ParserRuleContext.prototype.startIndex = function (): number { + return this.start?.start ?? 0; +}; + +ParserRuleContext.prototype.stopIndex = function (): number { + return this.stop?.stop ?? this.startIndex(); +}; + +ParserRuleContext.prototype.hasPositionOf = function (ctx: ParserRuleContext): boolean { + return this.startIndex() === ctx.startIndex() && this.stopIndex() === ctx.stopIndex(); +}; + +Object.defineProperty(ParserRuleContext.prototype, 'endsWithLineEnding', { + get: function endsWithLineEnding() { + // Ensure we have a context. + if (!(this instanceof ParserRuleContext)) + return false; + + // Check last child is a line ending. + const child = this.children.at(-1); + if (!child) + return false; + + // Check the various line ending contexts. + if (child instanceof LineEndingContext) + return true; + if (child instanceof EndOfStatementContext) + return true; + if (child instanceof EndOfStatementNoWsContext) + return true; + if (child instanceof ProcedureTailContext) + return true; + + // Run it again! + if (child.getChildCount() > 0) + return (child as ParserRuleContext).endsWithLineEnding; + + // Not a line ending and no more children. + return false; + } +}); + +interface LineEndingParserRuleContext { + NEWLINE(): TerminalNode | null; +} + +function isLineEndingParserRuleContext(ctx: unknown): ctx is LineEndingParserRuleContext { + return typeof ctx === 'object' + && ctx !== null + && typeof (ctx as any).NEWLINE === 'function'; +} + +function countTrailingLineEndings(ctx: ParserRuleContext): number { + // This function recursively loops through last child of + // the context to find one that has a NEWLINE terminal node. + + // Check if we have a NEWLINE node. + if (isLineEndingParserRuleContext(ctx)) { + const lines = ctx.NEWLINE()?.getText(); + if (!lines) { + return 0; + } + + let i = 0; + let result = 0; + while (i < lines.length) { + const char = lines[i]; + + if (char === '\r') { + result++; + i += lines[i + 1] === '\n' ? 2 : 1; + } else if (char === '\n') { + result++; + i++; + } + } + + return result; + } + + // Recursive call on last child. + const lastChild = ctx.children.at(-1); + if (lastChild instanceof ParserRuleContext) { + return countTrailingLineEndings(lastChild); + } + + // If we get here, we have no trailing lines. + return 0; +} + +ParserRuleContext.prototype.countTrailingLineEndings = function (): number { + return countTrailingLineEndings(this); +}; + + +TerminalNode.prototype.toRange = function (doc: TextDocument): Range { + return Range.create( + doc.positionAt(this.startIndex()), + doc.positionAt(this.stopIndex() + 1) + ); +}; + +TerminalNode.prototype.startIndex = function (): number { + return this.getPayload()?.start ?? 0; +}; + +TerminalNode.prototype.stopIndex = function (): number { + return this.getPayload()?.stop ?? this.startIndex(); +}; \ No newline at end of file diff --git a/server/src/extensions/antlrVbaParserExtensions.ts b/server/src/extensions/antlrVbaParserExtensions.ts new file mode 100644 index 0000000..e0fb336 --- /dev/null +++ b/server/src/extensions/antlrVbaParserExtensions.ts @@ -0,0 +1,116 @@ +// Core +import { SymbolKind } from 'vscode-languageserver'; + +// Project +import { + AmbiguousIdentifierContext, + BuiltinTypeContext, + ClassTypeNameContext, + ConstItemContext, + LExpressionContext, + TypeSpecContext, + TypeSuffixContext, + VariableDclContext, + WitheventsVariableDclContext +} from '../antlr/out/vbaParser'; + + +declare module '../antlr/out/vbaParser' { + + interface ConstItemContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): BuiltinTypeContext | TypeSuffixContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface VariableDclContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface WitheventsVariableDclContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface LExpressionContext { + /** Recursive search for LPAREN terminal node. */ + hasParenthesis(): boolean; + } +} + +LExpressionContext.prototype.hasParenthesis = function (): boolean { + return !!this.LPAREN() || (this.lExpression()?.hasParenthesis() ?? false); +}; + +VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { + // A variable will always be typed or untyped. + return this.typedVariableDcl()?.typedName().ambiguousIdentifier() + ?? this.untypedVariableDcl()!.ambiguousIdentifier(); +}; + + +VariableDclContext.prototype.typeContext = function (): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined { + return this.typedVariableDcl()?.typedName().typeSuffix() + ?? this.untypedVariableDcl()?.asClause()?.asType()?.typeSpec() + ?? this.untypedVariableDcl()?.asClause()?.asAutoObject()?.classTypeName(); +}; + + +// SymbolKind + +ConstItemContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +WitheventsVariableDclContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +VariableDclContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +function toSymbolKind(context: BuiltinTypeContext | TypeSuffixContext | TypeSpecContext | ClassTypeNameContext | undefined): SymbolKind { + switch (context?.getText().toLocaleLowerCase()) { + case undefined: + return SymbolKind.Class; + case 'boolean': + return SymbolKind.Boolean; + case '$': // string + case 'byte': + case 'string': + return SymbolKind.String; + case '%': // integer + case '&': // long + case '^': // longlong + case '@': // decimal + case '!': // single + case '#': // double + case 'double': + case 'currency': + case 'integer': + case 'long': + case 'longPtr': + case 'longLong': + return SymbolKind.Number; + case 'object': + return SymbolKind.Object; + default: + return SymbolKind.Class; + } +} diff --git a/server/src/extensions/antlrVbaPreParserExtensions.ts b/server/src/extensions/antlrVbaPreParserExtensions.ts new file mode 100644 index 0000000..992afbb --- /dev/null +++ b/server/src/extensions/antlrVbaPreParserExtensions.ts @@ -0,0 +1,17 @@ +// Project +import { CompilerConditionalStatementContext } from '../antlr/out/vbapreParser'; + + +declare module '../antlr/out/vbapreParser' { + interface CompilerConditionalStatementContext { + vbaExpression(): string; + } +} + + +CompilerConditionalStatementContext.prototype.vbaExpression = function (): string { + return (this.compilerIfStatement() ?? this.compilerElseIfStatement())! + .booleanExpression() + .getText() + .toLowerCase(); +}; diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts deleted file mode 100644 index 482da9e..0000000 --- a/server/src/extensions/parserExtensions.ts +++ /dev/null @@ -1,301 +0,0 @@ -// Core -import { Range, SymbolKind } from 'vscode-languageserver'; -import { TextDocument } from 'vscode-languageserver-textdocument'; - -// Antlr -import { ParserRuleContext, TerminalNode } from 'antlr4ng'; - -// Project -import { CompilerConditionalStatementContext } from '../antlr/out/vbapreParser'; -import { - AmbiguousIdentifierContext, - BuiltinTypeContext, - ClassTypeNameContext, - ConstItemContext, - EndOfStatementContext, - EndOfStatementNoWsContext, - ProcedureTailContext, - TypeSpecContext, - TypeSuffixContext, - VariableDclContext, - WitheventsVariableDclContext -} from '../antlr/out/vbaParser'; -import { LineEndingContext } from '../antlr/out/vbafmtParser'; - - -declare module 'antlr4ng' { - interface ParserRuleContext { - /** Convert the node to a range. */ - toRange(doc: TextDocument): Range; - startIndex(): number; - stopIndex(): number; - hasPositionOf(ctx: ParserRuleContext): boolean; - endsWithLineEnding: boolean; - countTrailingLineEndings(): number; - } - - interface TerminalNode { - /** Convert the node to a range. */ - toRange(doc: TextDocument): Range; - startIndex(): number; - stopIndex(): number; - } -} - - -declare module '../antlr/out/vbapreParser' { - interface CompilerConditionalStatementContext { - vbaExpression(): string; - } -} - - -declare module '../antlr/out/vbaParser' { - - interface ConstItemContext { - /** Shortcut the identifier as we know it will always exist. */ - ambiguousIdentifier(): AmbiguousIdentifierContext; - /** Shortcut to get the type context */ - typeContext(): BuiltinTypeContext | TypeSuffixContext | undefined; - /** Extension method to get the symbol kind. */ - toSymbolKind(): SymbolKind; - } - - interface VariableDclContext { - /** Shortcut the identifier as we know it will always exist. */ - ambiguousIdentifier(): AmbiguousIdentifierContext; - /** Shortcut to get the type context */ - typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; - /** Extension method to get the symbol kind. */ - toSymbolKind(): SymbolKind; - } - - interface WitheventsVariableDclContext { - /** Shortcut the identifier as we know it will always exist. */ - ambiguousIdentifier(): AmbiguousIdentifierContext; - /** Shortcut to get the type context */ - typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; - /** Extension method to get the symbol kind. */ - toSymbolKind(): SymbolKind; - } -} - - -ParserRuleContext.prototype.toRange = function (doc: TextDocument): Range { - const startIndex = this.start?.start ?? 0; - const stopIndex = this.stop?.stop ?? startIndex; - return Range.create( - doc.positionAt(startIndex), - doc.positionAt(stopIndex + 1) - ); -}; - -ParserRuleContext.prototype.startIndex = function (): number { - return this.start?.start ?? 0; -}; - -ParserRuleContext.prototype.stopIndex = function (): number { - return this.stop?.stop ?? this.startIndex(); -}; - -ParserRuleContext.prototype.hasPositionOf = function (ctx: ParserRuleContext): boolean { - return this.startIndex() === ctx.startIndex() && this.stopIndex() === ctx.stopIndex(); -}; - -Object.defineProperty(ParserRuleContext.prototype, 'endsWithLineEnding', { - get: function endsWithLineEnding() { - // Ensure we have a context. - if (!(this instanceof ParserRuleContext)) - return false; - - // Check last child is a line ending. - const child = this.children.at(-1); - if (!child) - return false; - - // Check the various line ending contexts. - if (child instanceof LineEndingContext) - return true; - if (child instanceof EndOfStatementContext) - return true; - if (child instanceof EndOfStatementNoWsContext) - return true; - if (child instanceof ProcedureTailContext) - return true; - - // Run it again! - if (child.getChildCount() > 0) - return (child as ParserRuleContext).endsWithLineEnding; - - // Not a line ending and no more children. - return false; - } -}); - -interface LineEndingParserRuleContext { - NEWLINE(): TerminalNode | null; -} - -function isLineEndingParserRuleContext(ctx: unknown): ctx is LineEndingParserRuleContext { - return typeof ctx === 'object' - && ctx !== null - && typeof (ctx as any).NEWLINE === 'function'; -} - -function countTrailingLineEndings(ctx: ParserRuleContext): number { - // This function recursively loops through last child of - // the context to find one that has a NEWLINE terminal node. - - // Check if we have a NEWLINE node. - if (isLineEndingParserRuleContext(ctx)) { - const lines = ctx.NEWLINE()?.getText(); - if (!lines) { - return 0; - } - - let i = 0; - let result = 0; - while (i < lines.length) { - const char = lines[i]; - - if (char === '\r') { - result++; - i += lines[i + 1] === '\n' ? 2 : 1; - } else if (char === '\n') { - result++; - i++; - } - } - - return result; - } - - // Recursive call on last child. - const lastChild = ctx.children.at(-1); - if (lastChild instanceof ParserRuleContext) { - return countTrailingLineEndings(lastChild); - } - - // If we get here, we have no trailing lines. - return 0; -} - -ParserRuleContext.prototype.countTrailingLineEndings = function (): number { - return countTrailingLineEndings(this); -}; - - -TerminalNode.prototype.toRange = function (doc: TextDocument): Range { - return Range.create( - doc.positionAt(this.startIndex()), - doc.positionAt(this.stopIndex() + 1) - ); -}; - -TerminalNode.prototype.startIndex = function (): number { - return this.getPayload()?.start ?? 0; -}; - -TerminalNode.prototype.stopIndex = function (): number { - return this.getPayload()?.stop ?? this.startIndex(); -}; - - -CompilerConditionalStatementContext.prototype.vbaExpression = function (): string { - return (this.compilerIfStatement() ?? this.compilerElseIfStatement())! - .booleanExpression() - .getText() - .toLowerCase(); -}; - - - -VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { - // A variable will always be typed or untyped. - return this.typedVariableDcl()?.typedName().ambiguousIdentifier() - ?? this.untypedVariableDcl()!.ambiguousIdentifier(); -}; - - -VariableDclContext.prototype.typeContext = function (): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined { - return this.typedVariableDcl()?.typedName().typeSuffix() - ?? this.untypedVariableDcl()?.asClause()?.asType()?.typeSpec() - ?? this.untypedVariableDcl()?.asClause()?.asAutoObject()?.classTypeName(); -}; - - -// SymbolKind - -ConstItemContext.prototype.toSymbolKind = function (): SymbolKind { - return toSymbolKind(this.typeContext()); -}; - - -WitheventsVariableDclContext.prototype.toSymbolKind = function (): SymbolKind { - return toSymbolKind(this.typeContext()); -}; - - -VariableDclContext.prototype.toSymbolKind = function (): SymbolKind { - return toSymbolKind(this.typeContext()); -}; - - -function toSymbolKind(context: BuiltinTypeContext | TypeSuffixContext | TypeSpecContext | ClassTypeNameContext | undefined): SymbolKind { - switch (context?.getText().toLocaleLowerCase()) { - case undefined: - return SymbolKind.Class; - case 'boolean': - return SymbolKind.Boolean; - case '$': // string - case 'byte': - case 'string': - return SymbolKind.String; - case '%': // integer - case '&': // long - case '^': // longlong - case '@': // decimal - case '!': // single - case '#': // double - case 'double': - case 'currency': - case 'integer': - case 'long': - case 'longPtr': - case 'longLong': - return SymbolKind.Number; - case 'object': - return SymbolKind.Object; - default: - return SymbolKind.Class; - } -} - -/** - * const File: 1; - const Module: 2; - const Namespace: 3; - const Package: 4; - const Class: 5; - const Method: 6; - const Property: 7; - const Field: 8; - const Constructor: 9; - const Enum: 10; - const Interface: 11; - const Function: 12; - const Variable: 13; - const Constant: 14; - const String: 15; - const Number: 16; - const Boolean: 17; - const Array: 18; - const Object: 19; - const Key: 20; - const Null: 21; - const EnumMember: 22; - const Struct: 23; - const Event: 24; - const Operator: 25; - const TypeParameter: 26; - */ \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index 8beb387..78face8 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -16,8 +16,10 @@ import { Services } from './injection/services'; // Ensures globally available type extensions. import './extensions/stringExtensions'; -import './extensions/parserExtensions'; import './extensions/numberExtensions'; +import './extensions/antlrCoreExtensions'; +import './extensions/antlrVbaParserExtensions'; +import './extensions/antlrVbaPreParserExtensions'; import { Workspace } from './project/workspace'; import { activateSemanticTokenProvider } from './capabilities/semanticTokens'; import { activateWorkspaceFolderCapability } from './capabilities/workspaceFolder'; From 5d43bff0763896e308b021f727268b5b252cb945 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 16 May 2025 13:51:56 +0800 Subject: [PATCH 62/93] Extension method deprecated --- server/src/extensions/antlrVbaParserExtensions.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server/src/extensions/antlrVbaParserExtensions.ts b/server/src/extensions/antlrVbaParserExtensions.ts index e0fb336..5e20bec 100644 --- a/server/src/extensions/antlrVbaParserExtensions.ts +++ b/server/src/extensions/antlrVbaParserExtensions.ts @@ -7,7 +7,6 @@ import { BuiltinTypeContext, ClassTypeNameContext, ConstItemContext, - LExpressionContext, TypeSpecContext, TypeSuffixContext, VariableDclContext, @@ -43,17 +42,8 @@ declare module '../antlr/out/vbaParser' { /** Extension method to get the symbol kind. */ toSymbolKind(): SymbolKind; } - - interface LExpressionContext { - /** Recursive search for LPAREN terminal node. */ - hasParenthesis(): boolean; - } } -LExpressionContext.prototype.hasParenthesis = function (): boolean { - return !!this.LPAREN() || (this.lExpression()?.hasParenthesis() ?? false); -}; - VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { // A variable will always be typed or untyped. return this.typedVariableDcl()?.typedName().ambiguousIdentifier() From 1348c8ad61ffc66c6f4d387888c57525eb1c3f02 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sat, 17 May 2025 21:36:15 +0800 Subject: [PATCH 63/93] Added new context extensions --- .../extensions/antlrVbaParserExtensions.ts | 178 +++++++++++++++++- 1 file changed, 168 insertions(+), 10 deletions(-) diff --git a/server/src/extensions/antlrVbaParserExtensions.ts b/server/src/extensions/antlrVbaParserExtensions.ts index 5e20bec..15297d5 100644 --- a/server/src/extensions/antlrVbaParserExtensions.ts +++ b/server/src/extensions/antlrVbaParserExtensions.ts @@ -4,23 +4,31 @@ import { SymbolKind } from 'vscode-languageserver'; // Project import { AmbiguousIdentifierContext, - BuiltinTypeContext, - ClassTypeNameContext, + ArrayClauseContext, + ArrayDesignatorContext, + ArrayDimContext, + AsClauseContext, ConstItemContext, - TypeSpecContext, + LowerBoundContext, + PositionalParamContext, + TypeExpressionContext, TypeSuffixContext, + UpperBoundContext, VariableDclContext, WitheventsVariableDclContext } from '../antlr/out/vbaParser'; +import { ParserRuleContext } from 'antlr4ng'; +export type ArrayDimension = { + lowerBound: number; + upperBound: number; +} declare module '../antlr/out/vbaParser' { interface ConstItemContext { - /** Shortcut the identifier as we know it will always exist. */ - ambiguousIdentifier(): AmbiguousIdentifierContext; /** Shortcut to get the type context */ - typeContext(): BuiltinTypeContext | TypeSuffixContext | undefined; + typeContext(): ParserRuleContext | undefined; /** Extension method to get the symbol kind. */ toSymbolKind(): SymbolKind; } @@ -29,7 +37,7 @@ declare module '../antlr/out/vbaParser' { /** Shortcut the identifier as we know it will always exist. */ ambiguousIdentifier(): AmbiguousIdentifierContext; /** Shortcut to get the type context */ - typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + typeContext(): ParserRuleContext | undefined; /** Extension method to get the symbol kind. */ toSymbolKind(): SymbolKind; } @@ -38,12 +46,54 @@ declare module '../antlr/out/vbaParser' { /** Shortcut the identifier as we know it will always exist. */ ambiguousIdentifier(): AmbiguousIdentifierContext; /** Shortcut to get the type context */ - typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + typeContext(): ParserRuleContext | undefined; /** Extension method to get the symbol kind. */ toSymbolKind(): SymbolKind; } + + interface PositionalParamContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): ParserRuleContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface AsClauseContext { + isObject: boolean; + isVariant: boolean; + isPrimative: boolean; + } + + interface TypeExpressionContext { + isObject: boolean; + isVariant: boolean; + isPrimative: boolean; + } + + interface TypeSuffixContext { + isObject: boolean; + isVariant: boolean; + isPrimative: boolean; + } + + interface ArrayDesignatorContext { + isResizable: boolean; + getDimensions(): ArrayDimension[] | undefined; + } + + interface ArrayDimContext { + isResizable: boolean; + getDimensions(): ArrayDimension[] | undefined; + } } +ConstItemContext.prototype.typeContext = function (): ParserRuleContext | undefined { + return this.typeSuffix() + ?? this.constAsClause()?.builtinType(); +}; + VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { // A variable will always be typed or untyped. return this.typedVariableDcl()?.typedName().ambiguousIdentifier() @@ -51,12 +101,116 @@ VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentif }; -VariableDclContext.prototype.typeContext = function (): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined { +VariableDclContext.prototype.typeContext = function (): ParserRuleContext | undefined { return this.typedVariableDcl()?.typedName().typeSuffix() ?? this.untypedVariableDcl()?.asClause()?.asType()?.typeSpec() ?? this.untypedVariableDcl()?.asClause()?.asAutoObject()?.classTypeName(); }; +PositionalParamContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { + return this.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() + ?? this.paramDcl().typedNameParamDcl()!.typedName().ambiguousIdentifier()!; +}; + +PositionalParamContext.prototype.typeContext = function (): ParserRuleContext | undefined { + return this.paramDcl().untypedNameParamDcl()?.parameterType()?.typeExpression() + ?? this.paramDcl().typedNameParamDcl()?.typedName().typeSuffix(); +}; + + +// Type Properties + +Object.defineProperty(TypeExpressionContext.prototype, "isPrimative", { + get: function (this: TypeExpressionContext): boolean { + return !!this.builtinType() && !this.isVariant; + } +}); + +Object.defineProperty(TypeExpressionContext.prototype, "isVariant", { + get: function (this: TypeExpressionContext): boolean { + return !!this.builtinType()?.reservedTypeIdentifier()?.VARIANT() + || !!this.builtinType()?.reservedTypeIdentifierB()?.VARIANT_B(); + } +}); + +Object.defineProperty(TypeExpressionContext.prototype, "isObject", { + get: function (this: TypeExpressionContext): boolean { + return !this.isPrimative && !this.isVariant; + } +}); + +Object.defineProperty(TypeSuffixContext.prototype, "isPrimative", { + get: function (this: TypeSuffixContext): boolean { + return true; + } +}); + +Object.defineProperty(TypeSuffixContext.prototype, "isVariant", { + get: function (this: TypeSuffixContext): boolean { + return false; + } +}); + +Object.defineProperty(TypeSuffixContext.prototype, "isObject", { + get: function (this: TypeSuffixContext): boolean { + return false; + } +}); + +Object.defineProperty(AsClauseContext.prototype, "isPrimative", { + get: function (this: AsClauseContext): boolean { + const typeSpecContext = this.asType()?.typeSpec(); + return !!(typeSpecContext + && (typeSpecContext.fixedLengthStringSpec() || typeSpecContext.typeExpression()?.isPrimative) + ); + } +}); + +Object.defineProperty(AsClauseContext.prototype, "isVariant", { + get: function (this: AsClauseContext): boolean { + return this.asType()?.typeSpec().typeExpression()?.isVariant ?? false; + } +}); + +Object.defineProperty(AsClauseContext.prototype, "isObject", { + get: function (this: AsClauseContext): boolean { + const isAutoObject = !!this.asAutoObject(); + const isTypeExprObject = !!(this.asType()?.typeSpec().typeExpression() ?? false); + return isAutoObject || isTypeExprObject; + } +}); + +Object.defineProperty(ArrayDesignatorContext.prototype, "isResizable", { + get: function (this: ArrayDesignatorContext): boolean { + return false; + } +}); + +ArrayDesignatorContext.prototype.getDimensions = function (): ArrayDimension[] | undefined { + return undefined; +}; + +Object.defineProperty(ArrayClauseContext.prototype, "isResizable", { + get: function (this: ArrayClauseContext): boolean { + return !!this.arrayDim().boundsList(); + } +}); + +ArrayDimContext.prototype.getDimensions = function (): ArrayDimension[] | undefined { + const boundsListCtx = this.boundsList(); + const getNum = (boundCtx: LowerBoundContext | UpperBoundContext | null): number => { + const FIXME_DEFAULT = 0; + const n = boundCtx?.constantExpression().expression().literalExpression()?.INTEGERLITERAL()?.getText(); + return n === undefined ? FIXME_DEFAULT : Number.parseInt(n); + }; + return !boundsListCtx ? undefined : boundsListCtx.dimSpec().map(dimensionCtx => { + return { + lowerBound: getNum(dimensionCtx.lowerBound()), + upperBound: getNum(dimensionCtx.upperBound()) + }; + }); +}; + // SymbolKind @@ -74,8 +228,12 @@ VariableDclContext.prototype.toSymbolKind = function (): SymbolKind { return toSymbolKind(this.typeContext()); }; +PositionalParamContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + -function toSymbolKind(context: BuiltinTypeContext | TypeSuffixContext | TypeSpecContext | ClassTypeNameContext | undefined): SymbolKind { +function toSymbolKind(context: ParserRuleContext | undefined): SymbolKind { switch (context?.getText().toLocaleLowerCase()) { case undefined: return SymbolKind.Class; From 11f4107576e84d7787697cfa5599306235c262c4 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sat, 17 May 2025 21:37:56 +0800 Subject: [PATCH 64/93] Removed typeableReservedName --- server/src/antlr/vba.g4 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index 4a735c6..399378c 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -2062,11 +2062,6 @@ reservedTypeIdentifierB | VARIANT_B ; -typeableReservedName - : DATE - | STRING - ; - literalIdentifier : booleanLiteralIdentifier | objectLiteralIdentifier @@ -2135,7 +2130,6 @@ builtinType // This probably could be turned into a token typedName : ambiguousIdentifier typeSuffix - | typeableReservedName typeSuffix ; typeSuffix From fbbb32be90c3392bac93a0004590041591bb21ea Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 21 May 2025 22:45:05 +0800 Subject: [PATCH 65/93] Fixed bug causing documents not to send diagnostics on open/close events. --- server/src/project/elements/naming.ts | 77 +++++++++++++++++++++++++++ server/src/project/workspace.ts | 10 ++-- 2 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 server/src/project/elements/naming.ts diff --git a/server/src/project/elements/naming.ts b/server/src/project/elements/naming.ts new file mode 100644 index 0000000..46cbdbc --- /dev/null +++ b/server/src/project/elements/naming.ts @@ -0,0 +1,77 @@ +// Core +import { TextDocument } from "vscode-languageserver-textdocument"; + +// Antlr +import { ParserRuleContext } from "antlr4ng"; +import { + AmbiguousIdentifierContext, + DictionaryAccessExpressionContext, + IndexExpressionContext, + LExpressionContext, + MemberAccessExpressionContext, + PositionalParamContext, + SimpleNameExpressionContext, + UnrestrictedNameContext, + WithMemberAccessExpressionContext, + WithStatementContext +} from "../../antlr/out/vbaParser"; + +// Project +import { BaseRuleSyntaxElement } from "./base"; +import { AssignmentType, IdentifierCapability, ItemType, ScopeItemCapability } from "../../capabilities/capabilities"; + + +export class WithStatementElement extends BaseRuleSyntaxElement { + nameExpressionElement?: NameExpressionElement; + + get nameStack(): ParserRuleContext[] { + return this.nameExpressionElement?.nameStack ?? []; + } + + constructor(ctx: WithStatementContext, doc: TextDocument) { + super(ctx, doc); + } +} + + +export type NameExpressionContext = LExpressionContext + | SimpleNameExpressionContext + | MemberAccessExpressionContext + | PositionalParamContext + | IndexExpressionContext + | WithMemberAccessExpressionContext + | DictionaryAccessExpressionContext; + +export class NameExpressionElement extends BaseRuleSyntaxElement { + scopeItemCapability: ScopeItemCapability; + identifierCapability: IdentifierCapability; + + nameContexts: (SimpleNameExpressionContext | UnrestrictedNameContext | AmbiguousIdentifierContext)[] = []; + withStatementElement?: WithStatementElement; + + get hasNames(): boolean { + return this.nameContexts.length > 0; + } + + get nameStack(): ParserRuleContext[] { + return [(this.withStatementElement?.nameStack ?? []), this.nameContexts].flat(); + } + + get fqName(): string { + return this.nameStack.map(x => x.getText()).join('.'); + } + + constructor(ctx: NameExpressionContext, doc: TextDocument) { + super(ctx, doc); + this.identifierCapability = new IdentifierCapability(this, () => this.nameStack.at(-1)); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.REFERENCE, AssignmentType.GET); + } + + setAsCallType = () => this.scopeItemCapability.assignmentType = AssignmentType.CALL; + setAsLetType = () => this.scopeItemCapability.assignmentType = AssignmentType.LET; + setAsSetType = () => this.scopeItemCapability.assignmentType = AssignmentType.SET; + + addName = (ctx: SimpleNameExpressionContext | UnrestrictedNameContext | AmbiguousIdentifierContext) => { + this.nameContexts.push(ctx); + }; +} \ No newline at end of file diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 63e7c40..43cef22 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -183,7 +183,7 @@ export class Workspace implements IWorkspace { } openDocument(document: TextDocument): void { - const projectDocument = this.projectDocuments.get(document.uri); + const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); if (document.version === projectDocument?.version) { projectDocument.open(); this.parseDocument(projectDocument); @@ -192,7 +192,7 @@ export class Workspace implements IWorkspace { } closeDocument(document: TextDocument): void { - const projectDocument = this.projectDocuments.get(document.uri); + const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); if (!projectDocument) { Services.logger.warn(`Failed to get document to close: ${document.uri}`); return; @@ -511,8 +511,7 @@ class WorkspaceEvents { logger.debug(`uri: ${document.uri.toFilePath()}`, 1); logger.debug(`languageId: ${document.languageId}`, 1); logger.debug(`version: ${document.version}`, 1); - const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); - if (projectDocument) { + if (this.projectDocuments.has(document.uri.toFilePath())) { Services.workspace.openDocument(document); } } @@ -555,8 +554,7 @@ class WorkspaceEvents { logger.debug(`uri: ${document.uri}`, 1); logger.debug(`languageId: ${document.languageId}`, 1); logger.debug(`version: ${document.version}`, 1); - const projectDocument = this.projectDocuments.get(document.uri); - if (projectDocument) { + if (this.projectDocuments.has(document.uri.toFilePath())) { Services.workspace.closeDocument(document); } } From 1f70b499475f9dcb37365392867bf4e05d277503 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 08:34:01 +0800 Subject: [PATCH 66/93] Moved OptExp to be a property on scope that inherits --- server/src/capabilities/capabilities.ts | 11 +++++++++-- server/src/project/elements/module.ts | 9 +++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 8454fd8..c6d31fa 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -250,6 +250,8 @@ export class ScopeItemCapability { explicitSetName?: string; isPublicScope = false; visibilityModifierContext?: ParserRuleContext; + qualififications?: BaseIdentifyableSyntaxElement[]; + isOptionExplicitScope = false; constructor( readonly element?: BaseRuleSyntaxElement, @@ -295,7 +297,7 @@ export class ScopeItemCapability { // References to function or sub calls should raise an error if they aren't declared. // -- Must always throw even when option explicit not present. // -- Nothing required on first reference as declaration may come later. - const severity = (this.module?.element as ModuleElement).hasOptionExplicit + const severity = this.isOptionExplicitScope ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; const _ = this.assignmentType & AssignmentType.CALL @@ -663,7 +665,12 @@ export class ScopeItemCapability { const getAncestorLevel = (item: ScopeItemCapability, level: number): number => item.parent ? getAncestorLevel(item.parent, level + 1) : level; const ancestorLevel = getAncestorLevel(this, 0); - Services.logger.debug(`Registering [${item.isPublicScope ? 'public' : 'private'} ${ItemType[item.type]}] ${item.name}`, ancestorLevel); + Services.logger.debug(`Registering [${visibility} ${ItemType[item.type]} ${assignment}] ${item.name}`, ancestorLevel); + + // Inherit option explicit property. + if (item.parent.isOptionExplicitScope) { + this.isOptionExplicitScope = true; + } // Reference types are not declarations. if (item.type === ItemType.REFERENCE) { diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index 834f185..7de328d 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -29,7 +29,6 @@ interface DocumentSettings { abstract class BaseModuleElement extends BaseIdentifyableSyntaxElement { abstract attrubutes: ParserRuleContext[]; abstract diagnosticCapability: DiagnosticCapability; - abstract hasOptionExplicit: boolean; abstract scopeItemCapability: ScopeItemCapability; settings: DocumentSettings; @@ -60,7 +59,7 @@ abstract class BaseModuleElement extends BaseIdenti } protected addOptionExplicitMissingDiagnostic(diagnostics: Diagnostic[], header: ClassModuleHeaderContext | ProceduralModuleHeaderContext): void { - if (this.settings.doWarnOptionExplicitMissing && !this.hasOptionExplicit) { + if (this.settings.doWarnOptionExplicitMissing && !this.scopeItemCapability.isOptionExplicitScope) { const startLine = header.stop?.line ?? 0 + 1; diagnostics.push(new MissingOptionExplicitDiagnostic( Range.create(startLine, 1, startLine, 1) @@ -107,7 +106,6 @@ export class ModuleElement extends BaseModuleElement { scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; - hasOptionExplicit: boolean; constructor(ctx: ProceduralModuleContext, doc: TextDocument, documentSettings: DocumentSettings) { super(ctx, doc, documentSettings, SymbolKind.File); @@ -115,7 +113,7 @@ export class ModuleElement extends BaseModuleElement { this.diagnosticCapability = new DiagnosticCapability(this); this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); - this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx + this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .proceduralModuleBody() .proceduralModuleCode() .proceduralModuleCodeElement()); @@ -151,7 +149,6 @@ export class ClassElement extends BaseModuleElement { scopeItemCapability: ScopeItemCapability; attrubutes: ParserRuleContext[]; - hasOptionExplicit: boolean; constructor(ctx: ClassModuleContext, doc: TextDocument, documentSettings: DocumentSettings) { super(ctx, doc, documentSettings, SymbolKind.File); @@ -163,7 +160,7 @@ export class ClassElement extends BaseModuleElement { this.diagnosticCapability = new DiagnosticCapability(this); this.scopeItemCapability = new ScopeItemCapability(this, ItemType.CLASS); - this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx + this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .classModuleBody() .classModuleCode() .classModuleCodeElement()); From ddc2d5498b9206436cdcef3cac10a168125cf463 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 09:00:53 +0800 Subject: [PATCH 67/93] Downgraded missing OptExp to hint --- server/src/capabilities/capabilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index c6d31fa..99d571c 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -299,7 +299,7 @@ export class ScopeItemCapability { // -- Nothing required on first reference as declaration may come later. const severity = this.isOptionExplicitScope ? DiagnosticSeverity.Error - : DiagnosticSeverity.Warning; + : DiagnosticSeverity.Hint; const _ = this.assignmentType & AssignmentType.CALL ? this.pushDiagnostic(SubOrFunctionNotDefinedDiagnostic, this, this.name) : this.pushDiagnostic(VariableNotDefinedDiagnostic, this, this.name, severity); From 348bba02949b4bcb9a45e8f387bdeb2a22277bfd Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 12:21:47 +0800 Subject: [PATCH 68/93] Updated unknown attribute diagnostic to Error --- server/src/capabilities/diagnostics.ts | 4 ++-- server/src/project/elements/module.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 8ba9c35..4896c4d 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -148,8 +148,8 @@ export class UnreachableCodeDiagnostic extends BaseDiagnostic { } -export class IgnoredAttributeDiagnostic extends BaseDiagnostic { - severity = DiagnosticSeverity.Warning; +export class UnknownAttributeDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Error; constructor(range: Range, attributeName: string) { super(range, `Unknown attribute '${attributeName}' will be ignored.`); } diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index 7de328d..6e0023a 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -18,7 +18,7 @@ import { // Project import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base'; import { DiagnosticCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; -import { DuplicateAttributeDiagnostic, IgnoredAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; +import { DuplicateAttributeDiagnostic, UnknownAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; interface DocumentSettings { @@ -197,7 +197,7 @@ export class ModuleIgnoredAttributeElement extends BaseRuleSyntaxElement { - this.diagnosticCapability.diagnostics.push(new IgnoredAttributeDiagnostic( + this.diagnosticCapability.diagnostics.push(new UnknownAttributeDiagnostic( this.context.range, this.context.text.split(' ')[1] )); return this.diagnosticCapability.diagnostics; From ba02497f5227ebc49277d693c56f74b75b9beccd Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 12:22:55 +0800 Subject: [PATCH 69/93] Added feature to diagnose unused elements --- server/src/capabilities/capabilities.ts | 33 +++++++++++++++++++++++++ server/src/capabilities/diagnostics.ts | 9 +++++++ server/src/project/document.ts | 1 + 3 files changed, 43 insertions(+) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 99d571c..34a1218 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -324,6 +324,39 @@ export class ScopeItemCapability { this.isDirty = false; } + resolveUnused(): void { + // Don't diagnose projects, classes or modules. + // Don't diagnose publically declared items. + // Don't diagnose if we have backlinks. + const isUsed: boolean = this.type === ItemType.CLASS + || this.type === ItemType.MODULE + || this.isPublicScope + || (!!this.backlinks && this.backlinks.length > 0) + || !this.element + || !this.element.identifierCapability; + + if (!isUsed) { + const identifier = this.element?.identifierCapability; + const diagnostics = this.element?.diagnosticCapability?.diagnostics; + if (identifier && diagnostics) { + diagnostics.push(new UnusedDiagnostic(identifier.range)); + } + } + + if (!this.hasScopeBody) { + return; + } + + // Recursively call this method on child declarations. + this.types?.forEach(items => items.forEach(item => item.resolveUnused())); + this.modules?.forEach(items => items.forEach(item => item.resolveUnused())); + this.functions?.forEach(items => items.forEach(item => item.resolveUnused())); + this.subroutines?.forEach(items => items.forEach(item => item.resolveUnused())); + this.properties?.getters?.forEach(items => items.forEach(item => item.resolveUnused())); + this.properties?.letters?.forEach(items => items.forEach(item => item.resolveUnused())); + this.properties?.setters?.forEach(items => items.forEach(item => item.resolveUnused())); + } + /** Returns the chain of parents for bottom up name resolution. */ getParentChain(items: ScopeItemCapability[] = []): ScopeItemCapability[] { items.push(this); diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 4896c4d..c87ebe4 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -147,6 +147,15 @@ export class UnreachableCodeDiagnostic extends BaseDiagnostic { } } +// test +export class UnusedDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Hint; + tags = [DiagnosticTag.Unnecessary]; + constructor(range: Range) { + super(range); + } +} + export class UnknownAttributeDiagnostic extends BaseDiagnostic { severity = DiagnosticSeverity.Error; diff --git a/server/src/project/document.ts b/server/src/project/document.ts index f571f8e..15eef8c 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -194,6 +194,7 @@ export abstract class BaseProjectDocument { const projectScope = this.currentScope.project; const buildScope = projectScope?.isDirty ? projectScope : this.currentScope; buildScope.build(); + buildScope.resolveUnused(); // Evaluate the diagnostics. const diagnostics = this.hasDiagnosticElements From 11b76d3d643a95e49213f42965f3a0fd2d11d6de Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 23:38:11 +0800 Subject: [PATCH 70/93] Define own tokens --- server/src/capabilities/semanticTokens.ts | 58 ++++++++++++++++++----- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/server/src/capabilities/semanticTokens.ts b/server/src/capabilities/semanticTokens.ts index c6ddc8f..1f2abe4 100644 --- a/server/src/capabilities/semanticTokens.ts +++ b/server/src/capabilities/semanticTokens.ts @@ -2,8 +2,6 @@ import { InitializeResult, Range, - SemanticTokenModifiers, - SemanticTokenTypes, uinteger, SemanticTokens } from 'vscode-languageserver'; @@ -14,6 +12,45 @@ import { ParserRuleContext } from 'antlr4ng/dist/ParserRuleContext'; // Project import { BaseRuleSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base'; +export enum SemanticTokenTypes { + namespace = "namespace", + type = "type", + class = "class", + enum = "enum", + interface = "interface", + struct = "struct", + typeParameter = "typeParameter", + parameter = "parameter", + variable = "variable", + property = "property", + enumMember = "enumMember", + event = "event", + function = "function", + method = "method", + macro = "macro", + keyword = "keyword", + modifier = "modifier", + comment = "comment", + string = "string", + number = "number", + regexp = "regexp", + operator = "operator", + decorator = "decorator" +} + +export enum SemanticTokenModifiers { + declaration = "declaration", + definition = "definition", + readonly = "readonly", + static = "static", + deprecated = "deprecated", + abstract = "abstract", + async = "async", + modification = "modification", + documentation = "documentation", + defaultLibrary = "defaultLibrary" +} + const registeredTokenTypes = new Map((Object.keys(SemanticTokenTypes) as (keyof typeof SemanticTokenTypes)[]).map((k, i) => ([k, i]))); const registeredTokenModifiers = new Map((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2 ** i]))); @@ -33,18 +70,17 @@ type SemanticElementType = HasSemanticTokenCapability & BaseRuleSyntaxElement; export class SemanticToken { - line: uinteger; - char: uinteger; - length: uinteger; tokenType: uinteger; tokenModifiers: uinteger = 0; - element: SemanticElementType; - constructor(element: SemanticElementType, line: uinteger, startChar: uinteger, length: uinteger, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[]) { - this.element = element; - this.line = line; - this.char = startChar; - this.length = length; + constructor( + public element: SemanticElementType, + public line: uinteger, + public char: uinteger, + public length: uinteger, + tokenType: SemanticTokenTypes, + tokenModifiers: SemanticTokenModifiers[] + ) { this.tokenType = registeredTokenTypes.get(tokenType)!; tokenModifiers.forEach((x) => this.tokenModifiers += registeredTokenModifiers.get(x) ?? 0); } From c8abad09301c11c28cbce58dafd6d2230c757fb9 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 23 May 2025 23:39:50 +0800 Subject: [PATCH 71/93] Updated Launch for project testing --- .vscode/launch.json | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c7691e1..02afd39 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "runtimeExecutable": "${execPath}", "args": [ "--extensionDevelopmentPath=${workspaceRoot}", - "${workspaceFolder}/sample" + "${workspaceFolder}/sample/project" ], "preLaunchTask": { "type": "npm", @@ -33,7 +33,9 @@ "--extensionTestsPath=${workspaceRoot}/dist/client/out/test/index", "${workspaceRoot}/test/fixtures" ], - "outFiles": ["${workspaceRoot}/dist/client/out/test/**/*.js"] + "outFiles": [ + "${workspaceRoot}/dist/client/out/test/**/*.js" + ] }, { "name": "Run Web Extension in VS Code", @@ -84,16 +86,21 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/client/out/test/index" + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/dist/client/out/test/index" ], - "outFiles": ["${workspaceFolder}/dist/client/out/test/**/*.test.js"] + "outFiles": [ + "${workspaceFolder}/dist/client/out/test/**/*.test.js" + ] } ], "compounds": [ { "name": "Client + Server", - "configurations": ["Launch Client", "Attach to Server"] + "configurations": [ + "Launch Client", + "Attach to Server" + ] } ] -} +} \ No newline at end of file From 8461e1351d68384e9dbadb0f63f4a1d6a2f23f8f Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sat, 24 May 2025 01:10:23 +0800 Subject: [PATCH 72/93] Reimplement scopes and add definitionProvider server capability --- server/src/capabilities/capabilities.ts | 400 +++++++++++++++------- server/src/project/elements/module.ts | 9 +- server/src/project/elements/naming.ts | 11 +- server/src/project/elements/procedure.ts | 12 +- server/src/project/elements/typing.ts | 134 ++++---- server/src/project/parser/vbaListener.ts | 419 ++++++++++++++++++++++- server/src/project/workspace.ts | 31 +- server/src/server.ts | 2 +- 8 files changed, 797 insertions(+), 221 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 34a1218..b87230c 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -1,9 +1,9 @@ // Core import { DiagnosticSeverity, + LocationLink, + Position, Range, - SemanticTokenModifiers, - SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; @@ -12,12 +12,12 @@ import { import { ParserRuleContext, TerminalNode } from 'antlr4ng'; // Project -import { ModuleElement } from '../project/elements/module'; -import { SemanticToken } from '../capabilities/semanticTokens'; +import { SemanticToken, SemanticTokenModifiers, SemanticTokenTypes } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, BaseSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; -import { BaseDiagnostic, DuplicateDeclarationDiagnostic, MethodVariableIsPublicDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; +import { BaseDiagnostic, DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, UnusedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; import { Services } from '../injection/services'; +import { isPositionInsideRange } from '../utils/helpers'; abstract class BaseCapability { @@ -67,11 +67,6 @@ export class DiagnosticCapability extends BaseCapability { export class SemanticTokenCapability extends BaseCapability { - private tokenType: SemanticTokenTypes; - private tokenModifiers: SemanticTokenModifiers[]; - private overrideRange?: Range; - private overrideLength?: number; - get semanticToken(): SemanticToken { const element = this.element as BaseRuleSyntaxElement & HasSemanticTokenCapability; const context = element.identifierCapability @@ -93,19 +88,17 @@ export class SemanticTokenCapability extends BaseCapability { ); } - constructor(element: BaseRuleSyntaxElement & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], overrideRange?: Range, overrideLength?: number) { + constructor(element: BaseRuleSyntaxElement, + private tokenType: SemanticTokenTypes, + private tokenModifiers: SemanticTokenModifiers[], + private overrideRange?: Range, + private overrideLength?: number) { super(element); - this.tokenType = tokenType; - this.tokenModifiers = tokenModifiers; - this.overrideRange = overrideRange; - this.overrideLength = overrideLength; } } export class SymbolInformationCapability extends BaseCapability { - private symbolKind: SymbolKind; - get SymbolInformation(): SymbolInformation { const element = this.element as BaseIdentifyableSyntaxElement; return SymbolInformation.create( @@ -116,9 +109,8 @@ export class SymbolInformationCapability extends BaseCapability { ); } - constructor(element: BaseIdentifyableSyntaxElement, symbolKind: SymbolKind) { + constructor(element: BaseIdentifyableSyntaxElement, private symbolKind: SymbolKind) { super(element); - this.symbolKind = symbolKind; } } @@ -169,7 +161,7 @@ export class IdentifierCapability extends BaseCapability { } -export enum ItemType { +export enum ScopeType { /** Base language. */ VBA, /** Application model. */ @@ -190,6 +182,8 @@ export enum ItemType { TYPE, /** Variable declaration. */ VARIABLE, + /** A variable declaration in a signature */ + PARAMETER, /** Any reference type that isn't a declaration. */ REFERENCE } @@ -199,7 +193,8 @@ export enum AssignmentType { GET = 1 << 0, LET = 1 << 1, SET = 1 << 2, - CALL = 1 << 3 + CALL = 1 << 3, + CONST = 1 << 4 } export class ScopeItemCapability { @@ -213,22 +208,20 @@ export class ScopeItemCapability { setters?: Map, letters?: Map }; + parameters?: Map; references?: Map; + // Special scope references for easier resolution of names. + implicitDeclarations?: Map; + // Links link?: ScopeItemCapability; - backLinks?: ScopeItemCapability[]; + backlinks?: ScopeItemCapability[]; // Technical isDirty: boolean = true; isInvalidated = false; - private get isMethodScope(): boolean { - return [ - ItemType.SUBROUTINE, - ItemType.FUNCTION, - ItemType.PROPERTY, - ].includes(this.type); - } + get maps() { const result: Map[] = []; const addToResult = (map: Map | undefined) => { @@ -246,16 +239,45 @@ export class ScopeItemCapability { return result; } + get explicitDeclarations() { + const result: Map[] = []; + const addToResult = (map: Map | undefined) => { + if (map) result.push(map); + }; + addToResult(this.types); + addToResult(this.modules); + addToResult(this.functions); + addToResult(this.subroutines); + addToResult(this.properties?.getters); + addToResult(this.properties?.letters); + addToResult(this.properties?.setters); + + return result; + } + + get hasScopeBody(): boolean { + return this.type !== ScopeType.VARIABLE + && this.type !== ScopeType.REFERENCE + && this.type !== ScopeType.PARAMETER; + } + + get assignmentTypeText(): string { + const enumNamesAndValues = Object.values(AssignmentType); + const enumValues = enumNamesAndValues.slice(enumNamesAndValues.length / 2 + 1); + const result = `${enumValues.map(x => AssignmentType[this.assignmentType & x as number]).filter(x => x !== 'NONE').join('|')}`; + + return result === '' ? 'NONE' : result; + } + // Item Properties - explicitSetName?: string; - isPublicScope = false; - visibilityModifierContext?: ParserRuleContext; - qualififications?: BaseIdentifyableSyntaxElement[]; + locationUri?: string; + isPublicScope?: boolean; + accessMembers?: string[]; isOptionExplicitScope = false; constructor( readonly element?: BaseRuleSyntaxElement, - public type: ItemType = ItemType.REFERENCE, + public type: ScopeType = ScopeType.REFERENCE, public assignmentType: AssignmentType = AssignmentType.NONE, public parent?: ScopeItemCapability, ) { } @@ -275,9 +297,9 @@ export class ScopeItemCapability { // Clean invalidated links. if (this.link?.isInvalidated) this.link = undefined; - if (this.backLinks) { - const keep = this.backLinks.filter(x => !x.isInvalidated); - this.backLinks = keep.length > 0 ? keep : undefined; + if (this.backlinks) { + const keep = this.backlinks.filter(x => !x.isInvalidated); + this.backlinks = keep.length > 0 ? keep : undefined; } // Don't build self if invalidated. @@ -285,15 +307,18 @@ export class ScopeItemCapability { return; } - if (this.type === ItemType.REFERENCE) { + if (this.type === ScopeType.REFERENCE) { // Link to declaration if it exists. this.resolveLinks(); + const abc = 0; if (!this.link) { // TODO: // References to variables should get a diagnostic if they aren't declared. - // -- No option explicit: gets a hint with code action to declare. - // -- Option explicit: gets an error with code action to declare. + // -- No option explicit: Hint with code action to declare. + // GET before declared gets a warning. + // -- Option explicit: Error with code action to declare. // -- Subsequent explicit declaration should raise duplicate declaration (current bahaviour). + // -- All declarations with no GET references get a warning. // References to function or sub calls should raise an error if they aren't declared. // -- Must always throw even when option explicit not present. // -- Nothing required on first reference as declaration may come later. @@ -328,8 +353,8 @@ export class ScopeItemCapability { // Don't diagnose projects, classes or modules. // Don't diagnose publically declared items. // Don't diagnose if we have backlinks. - const isUsed: boolean = this.type === ItemType.CLASS - || this.type === ItemType.MODULE + const isUsed: boolean = this.type === ScopeType.CLASS + || this.type === ScopeType.MODULE || this.isPublicScope || (!!this.backlinks && this.backlinks.length > 0) || !this.element @@ -378,7 +403,7 @@ export class ScopeItemCapability { /** Resolves for the current scope, i.e., children of the current item. */ private resolveDuplicateDeclarations(ancestors: ScopeItemCapability[]) { // Reference types are never relevant. - if (this.type == ItemType.REFERENCE) { + if (this.type == ScopeType.REFERENCE) { return; } @@ -452,7 +477,7 @@ export class ScopeItemCapability { private resolveShadowedDeclaration(item: ScopeItemCapability | undefined): void { if (item) { - const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic); + const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic, this, this.name); this.addDiagnosticReference(diagnostic, item); } } @@ -465,10 +490,15 @@ export class ScopeItemCapability { for (let i = 2; i < ancestors.length; i++) { const ancestor = ancestors[i]; - const shadowing = ancestor.getAccessibleScopes(this.name); - - if (shadowing) { - const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic); + const shadowing = ancestor + .getAccessibleScopes(this.name) + .filter(item => item.parent?.name !== this.parent?.name); + + if (shadowing.length > 0) { + const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic, this, this.name); + if (diagnostic === undefined) { + continue; + } shadowing.forEach(item => this.addDiagnosticReference(diagnostic, item)); } @@ -503,42 +533,9 @@ export class ScopeItemCapability { } private resolveLinks() { - /** - * Call Foo(bar) - * ^^^ NONE - * ^^^ GET - * - * Let foo = bar - * ^^^ LET - * ^^^ GET - * - * Set foo = bar - * ^^^ SET - * ^^^ GET - */ - - // Handle calls that aren't assignments. - if (this.assignmentType === AssignmentType.CALL) { - this.linkThisToItem(this.findFunction(this.identifier)); - this.linkThisToItem(this.findSubroutine(this.identifier)); - return; - } - - // Handle get/set/let relationships. - if (this.assignmentType & AssignmentType.GET) { - this.linkThisToItem(this.findFunction(this.identifier)); - this.linkThisToItem(this.findPropertyGetter(this.identifier)); - return; - } - - if (this.assignmentType & AssignmentType.LET) { - this.linkThisToItem(this.findPropertyLetter(this.identifier)); - return; - } - - if (this.assignmentType & AssignmentType.SET) { - this.linkThisToItem(this.findPropertySetter(this.identifier)); - return; + const declarations = this.findDeclarations(this.identifier); + if (declarations) { + this.linkThisToItem(declarations[0]); } } @@ -548,22 +545,22 @@ export class ScopeItemCapability { } this.link = linkItem; - linkItem.backLinks ??= []; - linkItem.backLinks.push(this); + linkItem.backlinks ??= []; + linkItem.backlinks.push(this); } private removeBacklink(backlinkedItem: ScopeItemCapability): void { - if (!this.backLinks) { + if (!this.backlinks) { return; } - const keep = this.backLinks.filter(x => x !== backlinkedItem); - this.backLinks = keep.length === 0 ? undefined : keep; + const keep = this.backlinks.filter(x => x !== backlinkedItem); + this.backlinks = keep.length === 0 ? undefined : keep; } /** Returns the module this scope item falls under */ get module(): ScopeItemCapability | undefined { - if (this.type === ItemType.MODULE || this.type === ItemType.CLASS) { + if (this.type === ScopeType.MODULE || this.type === ScopeType.CLASS) { return this; } return this.parent?.module; @@ -571,30 +568,69 @@ export class ScopeItemCapability { /** Returns the project this scope item falls under */ get project(): ScopeItemCapability | undefined { - if (this.type == ItemType.PROJECT) { + if (this.type == ScopeType.PROJECT) { return this; } return this.parent?.project; } get identifier(): string { - if (this.type === ItemType.PROPERTY) { + if (this.type === ScopeType.PROPERTY) { return this.name.split(' ')[1]; } return this.name; } get name(): string { - return this.explicitSetName - ?? this.element?.identifierCapability?.name - ?? 'Unknown'; + return this.element?.identifierCapability?.name ?? 'Unknown'; } - findType(identifier: string): ScopeItemCapability | undefined { - return this.types?.get(identifier)?.[0] - ?? this.parent?.findType(identifier); + has(identifier: string): boolean { + for (const map of this.maps) { + if (map.has(identifier)) { + return true; + } + } + return false; } + // findDeclarations(name: string, section?: string): ScopeItemCapability[] { + // const identifier = section ?? name.split('.')[0]; + // if (this.has(identifier)) { + // return this.maps.map(x => x.get(identifier) ?? []).flat(); + // } + + // // FIXME: Handle publicly accessible names in project scope. + // return this.parent?.findDeclarations(name, section) ?? []; + // } + + // resolvePropertyChain(chain: string) { + // // Search UP for foo in foo.xx.xx.xx.... + // const declarations = this.findDeclarations(chain); + + // // Not found. + // if (declarations.length === 0) { + // return { status: 404, scope: undefined }; + // } + + // // Conflicting names FIXME: may need to add logic to handle get/set/let. + // if (declarations.length > 1) { + // return { status: 409, scope: undefined }; + // } + + // // We have exactly one name. Search DOWN the chain. + // const chainLinks = chain.split('.').slice(1); + // let declaration = declarations[0]; + // for (const chainLink of chainLinks) { + // // Assume we're only going to get one result here. + // // If we get more, we can handle diagnostics elsewhere. + // declaration = declaration.maps.map( + // x => x.get(chainLink) ?? [] + // ).flat()[0]; + // } + // return { status: 200, scope: declaration }; + // } + /** Get accessible declarations */ getAccessibleScopes(identifier: string, results: ScopeItemCapability[] = []): ScopeItemCapability[] { // Add any non-public items we find at this level. @@ -607,7 +643,7 @@ export class ScopeItemCapability { }); // Get all public scope types if we're at the project level. - if (this.type === ItemType.PROJECT) { + if (this.type === ScopeType.PROJECT) { this.modules?.forEach(modules => modules.forEach( module => module.maps.forEach(map => { map.get(identifier)?.forEach(item => { @@ -622,6 +658,11 @@ export class ScopeItemCapability { return this.parent?.getAccessibleScopes(identifier, results) ?? results; } + findType(identifier: string): ScopeItemCapability | undefined { + return this.types?.get(identifier)?.[0] + ?? this.parent?.findType(identifier); + } + findModule(identifier: string): ScopeItemCapability | undefined { return this.modules?.get(identifier)?.[0] ?? this.parent?.findModule(identifier); @@ -652,6 +693,26 @@ export class ScopeItemCapability { ?? this.parent?.findPropertySetter(identifier); } + findDeclarations(identifier: string): ScopeItemCapability[] | undefined { + const explicitResult = this.explicitDeclarations + .map(x => x.get(identifier)) + .filter(x => !!x) + .flat(); + + if (explicitResult.length > 0) { + return explicitResult; + } + + const implicitResult = this.implicitDeclarations + ?.get(identifier); + + if (implicitResult && implicitResult.length > 0) { + return implicitResult; + } + + return this.parent?.findDeclarations(identifier); + } + /** * Registers a scope and returns the new current scope. * @param item The scope item to register. @@ -686,7 +747,7 @@ export class ScopeItemCapability { // } // Immediately invalidate if we're an Unknown Module - if (item.type === ItemType.MODULE && item.name === 'Unknown Module') { + if (item.type === ScopeType.MODULE && item.name === 'Unknown Module') { item.isInvalidated = true; } @@ -694,11 +755,21 @@ export class ScopeItemCapability { item.parent = this; // getParent(item); item.parent.isDirty = true; + // Set the URI from the parent if we don't have one. + if (item.locationUri === undefined) { + item.locationUri = item.parent.locationUri; + } + // Get the scope level for logging. const getAncestorLevel = (item: ScopeItemCapability, level: number): number => item.parent ? getAncestorLevel(item.parent, level + 1) : level; + + + item.isPublicScope = this.getVisibility(item); + const visibility = item.isPublicScope ? 'public' : 'private'; + const assignment = item.assignmentTypeText; const ancestorLevel = getAncestorLevel(this, 0); - Services.logger.debug(`Registering [${visibility} ${ItemType[item.type]} ${assignment}] ${item.name}`, ancestorLevel); + Services.logger.debug(`Registering [${visibility} ${ScopeType[item.type]} ${assignment}] ${item.name}`, ancestorLevel); // Inherit option explicit property. if (item.parent.isOptionExplicitScope) { @@ -706,35 +777,50 @@ export class ScopeItemCapability { } // Reference types are not declarations. - if (item.type === ItemType.REFERENCE) { + if (item.type === ScopeType.REFERENCE) { item.parent.references ??= new Map(); item.parent.addItem(item.parent.references, item); return this; } + // Add implicitly accessible names to the project scope. + if (item.isPublicScope && this.project) { + this.project.implicitDeclarations ??= new Map(); + this.addItem(this.project.implicitDeclarations, item, item.name); + } + // Register functions. - if (item.type === ItemType.FUNCTION) { + if (item.type === ScopeType.FUNCTION) { item.parent.functions ??= new Map(); item.parent.addItem(item.parent.functions, item); return item; } // Register subroutine. - if (item.type === ItemType.SUBROUTINE) { + if (item.type === ScopeType.SUBROUTINE) { item.parent.subroutines ??= new Map(); item.parent.addItem(item.parent.subroutines, item); return item; } // Register enum or type. - if (item.type === ItemType.TYPE) { + if (item.type === ScopeType.TYPE) { item.parent.types ??= new Map(); item.parent.addItem(item.parent.types, item); return item; } + // Register parameters. + if (item.type === ScopeType.PARAMETER) { + item.parent.parameters ??= new Map(); + item.parent.addItem(item.parent.parameters, item); + } + // Register properties and variables. - if (item.type === ItemType.PROPERTY || item.type === ItemType.VARIABLE) { + const isGetSetLetType = item.type === ScopeType.PROPERTY + || item.type === ScopeType.VARIABLE + || item.type === ScopeType.PARAMETER; + if (isGetSetLetType) { item.parent.properties ??= {}; if (item.assignmentType & AssignmentType.GET) { item.parent.properties.getters ??= new Map(); @@ -748,11 +834,11 @@ export class ScopeItemCapability { item.parent.properties.setters ??= new Map(); item.parent.addItem(item.parent.properties.setters, item); } - return item.type === ItemType.PROPERTY ? item : this; + return item.type === ScopeType.PROPERTY ? item : this; } // Handle module registration - if (item.type === ItemType.MODULE || item.type === ItemType.CLASS) { + if (item.type === ScopeType.MODULE || item.type === ScopeType.CLASS) { item.parent.modules ??= new Map(); item.parent.addItem(item.parent.modules, item); return item; @@ -763,7 +849,7 @@ export class ScopeItemCapability { } invalidate(uri: string): void { - if (this.type !== ItemType.PROJECT) { + if (this.type !== ScopeType.PROJECT) { this.isInvalidated = true; } this.maps.forEach(map => map.forEach(items => @@ -771,31 +857,74 @@ export class ScopeItemCapability { )); } - getRenameItems(uri: string, position: { line: number, character: number }): ScopeItemCapability[] { + /** Returns true for public and false for private */ + private getVisibility(item: ScopeItemCapability): boolean { + // Classes and modules are always public. + if (item.parent?.type === ScopeType.PROJECT) { + return true; + } + + // Module members can explicitly set their access or default + // to private. TODO: multi-project requires another scope layer + // to control access between projects. + // Public members of Option Private Modules are scoped to project. + if (item.parent?.type === ScopeType.MODULE) { + // Variables default to private, everything else deafults public. + return item.isPublicScope ?? item.type !== ScopeType.VARIABLE; + } + + // Everything else is private. + return false; + } + + private findModuleByUri(uri: string): ScopeItemCapability | undefined { const moduleName = uri.split('/').at(-1)?.split('.').slice(0, -1).join('.'); if (!moduleName) { Services.logger.error(`Bad URI or name: ${moduleName} from ${uri}`); - return []; + return; } const modules = this.modules?.get(moduleName); if (!modules) { Services.logger.error(`No such module: ${moduleName}`); - return []; + return; } if (modules.length > 1) { Services.logger.error(`Module name ambiguity: ${modules.length} found.`); + return; + } + + return modules[0]; + } + + private getItemsIdentifiedAtPosition(position: Position, results: ScopeItemCapability[] = [], searchItems: ScopeItemCapability[] = []): void { + while (searchItems.length > 0) { + const scope = searchItems.pop(); + + // Check all items for whether they have a name overlap or a scope overlap. + scope?.maps.forEach(map => map.forEach(items => items.forEach(item => { + const elementRange = item.element?.context.range; + const identifierRange = item.element?.identifierCapability?.range; + if (identifierRange && isPositionInsideRange(position, identifierRange)) { + // Position is inside the identifier, push to results. + results.push(item); + } else if (elementRange && isPositionInsideRange(position, elementRange)) { + // Position is inside element, queue to be searched. + searchItems.push(item); + } + }))); + } + } + + getRenameItems(uri: string, position: Position): ScopeItemCapability[] { + const module = this.findModuleByUri(uri); + if (!module) { return []; } - const module = modules[0]; const itemsAtPosition: ScopeItemCapability[] = []; - module.maps.map(m => m.forEach(items => items.forEach(item => { - if (item.isAtPosition(position)) { - itemsAtPosition.push(item); - } - }))); + this.getItemsIdentifiedAtPosition(position, itemsAtPosition, [module]); if (itemsAtPosition.length === 0) { Services.logger.warn(`Nothing to rename.`); return []; @@ -807,30 +936,41 @@ export class ScopeItemCapability { } const declarationItem = itemsAtPosition[0].link ? itemsAtPosition[0].link : itemsAtPosition[0]; - const result = [declarationItem, ...declarationItem.backLinks ?? []]; + const result = [declarationItem, ...declarationItem.backlinks ?? []]; return result; } - isAtPosition(position: { line: number, character: number }): boolean { - const range = this.element?.context.range; - if (!range) { - return false; + getDeclarationLocation(uri: string, position: Position): LocationLink[] | undefined { + const module = this.findModuleByUri(uri); + if (!module) { + return; } - if (range.start.line !== range.end.line) { - return position.line >= range.start.line - && position.line <= range.end.line; + const itemsAtPosition: ScopeItemCapability[] = []; + this.getItemsIdentifiedAtPosition(position, itemsAtPosition, [module]); + return itemsAtPosition.map(x => x.toLocationLink()).filter(x => !!x); + } + + toLocationLink(): LocationLink | undefined { + const link = this.link; + + if (!link || !link.locationUri || !link.element || !link.element.identifierCapability) { + return; } - return position.character >= range.start.character - && position.character <= range.end.character; + return LocationLink.create( + link.locationUri, + link.element.context.range, + link.element.identifierCapability.range, + this.element?.context.range + ); } - private addItem(target: Map, item: ScopeItemCapability): void { + private addItem(target: Map, item: ScopeItemCapability, name?: string): void { const items = target.get(item.identifier) ?? []; items.push(item); - target.set(item.identifier, items); + target.set(name ?? item.identifier, items); } private hasDiagnostic(diagnostic: BaseDiagnostic): boolean { diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index 6e0023a..20dcaee 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -1,4 +1,5 @@ // Core +import * as url from 'url'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { Diagnostic, Range, SymbolKind } from 'vscode-languageserver'; @@ -17,7 +18,7 @@ import { // Project import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base'; -import { DiagnosticCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +import { DiagnosticCapability, IdentifierCapability, ScopeType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; import { DuplicateAttributeDiagnostic, UnknownAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; @@ -111,7 +112,8 @@ export class ModuleElement extends BaseModuleElement { super(ctx, doc, documentSettings, SymbolKind.File); this.attrubutes = ctx.proceduralModuleHeader().proceduralModuleAttr(); this.diagnosticCapability = new DiagnosticCapability(this); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.MODULE); + this.scopeItemCapability.locationUri = url.pathToFileURL(doc.uri).toString(); this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .proceduralModuleBody() @@ -158,7 +160,8 @@ export class ClassElement extends BaseModuleElement { ctx.classModuleHeader().ignoredClassAttr() ].flat(); this.diagnosticCapability = new DiagnosticCapability(this); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.CLASS); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.CLASS); + this.scopeItemCapability.locationUri = url.pathToFileURL(doc.uri).toString(); this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .classModuleBody() diff --git a/server/src/project/elements/naming.ts b/server/src/project/elements/naming.ts index 46cbdbc..5d6600f 100644 --- a/server/src/project/elements/naming.ts +++ b/server/src/project/elements/naming.ts @@ -18,7 +18,7 @@ import { // Project import { BaseRuleSyntaxElement } from "./base"; -import { AssignmentType, IdentifierCapability, ItemType, ScopeItemCapability } from "../../capabilities/capabilities"; +import { AssignmentType, IdentifierCapability, ScopeType, ScopeItemCapability } from "../../capabilities/capabilities"; export class WithStatementElement extends BaseRuleSyntaxElement { @@ -64,7 +64,7 @@ export class NameExpressionElement extends BaseRuleSyntaxElement this.nameStack.at(-1)); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.REFERENCE, AssignmentType.GET); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.REFERENCE, AssignmentType.GET); } setAsCallType = () => this.scopeItemCapability.assignmentType = AssignmentType.CALL; @@ -74,4 +74,11 @@ export class NameExpressionElement extends BaseRuleSyntaxElement { this.nameContexts.push(ctx); }; + + evaluateNameStack(): void { + const names = this.nameStack.map(x => x.getText()); + if (names.length > 0) { + this.scopeItemCapability.accessMembers = names; + } + } } \ No newline at end of file diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts index c7f1d52..a55473c 100644 --- a/server/src/project/elements/procedure.ts +++ b/server/src/project/elements/procedure.ts @@ -16,7 +16,7 @@ import { // Project import { BaseRuleSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base'; -import { AssignmentType, DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +import { AssignmentType, DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ScopeType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; interface HasProcedureScope { procedureScope(): ProcedureScopeContext | null @@ -60,7 +60,7 @@ export class SubDeclarationElement extends BaseProcedureElement extends BaseRuleSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability, HasSemanticTokenCapability { @@ -42,7 +46,7 @@ abstract class BaseTypeDeclarationElement extends B this.semanticTokenCapability = new SemanticTokenCapability(this, tokenType, tokenModifiers ?? []); // An enum is public unless explicitly set to private. - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.TYPE); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.TYPE); this.scopeItemCapability.isPublicScope = this.isPublicScope; } @@ -82,7 +86,7 @@ export class EnumMemberDeclarationElement extends BaseRuleSyntaxElement ctx.untypedName(); this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE, AssignmentType.GET); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.VARIABLE, AssignmentType.GET); } } @@ -124,7 +128,7 @@ export class VariableDeclarationStatementElement extends BaseRuleSyntaxElement ctx.ambiguousIdentifier(); - this.identifierCapability = new IdentifierCapability(this, getIdentifierNameContext); + this.identifierCapability = new IdentifierCapability(this, () => ctx.ambiguousIdentifier()); // VariableDcl > TypedVariableDcl > TypedName > TypeSuffix // > UntypedVariableDcl > AsClause @@ -182,29 +185,17 @@ export class VariableDeclarationElement extends BaseRuleSyntaxElement { +class VariableTypeInformation extends BaseRuleSyntaxElement { get isObjectType(): boolean { - // Type hints are never an object. - const ctx = this.context.rule; - if (ctx instanceof TypeSuffixContext) { - return false; - } + return this.context.rule.isObject; + } - // Check builtins for variant type. - const builtin = ctx.asType()?.typeSpec().typeExpression()?.builtinType(); - if (builtin?.reservedTypeIdentifier()?.VARIANT() || builtin?.reservedTypeIdentifierB()?.VARIANT_B()) { - return true; - } + get isPrimativeType(): boolean { + return this.context.rule.isPrimative; + } - // Don't trust anything else. Just check not a primative. - return !this.isPrimativeType; + get isVariantType(): boolean { + return this.context.rule.isVariant; } - get isPrimativeType(): boolean { - // Type hints are always primitive. - const ctx = this.context.rule; - if (ctx instanceof TypeSuffixContext) { - return true; - } + // TODO: + // - Variables in array bounds should validate they are constants or literals. + // - Build the capability to evaluate constant expressions. + get isResizable(): boolean { + return this.arrayCtx?.isResizable ?? false; + } - // A newed object is always an object. - if (ctx.asAutoObject()) { - return false; - } + constructor(ctx: TypeContext, doc: TextDocument, private readonly arrayCtx?: ArrayDimContext | ArrayDesignatorContext) { + super(ctx, doc); + } +} - // Fixed length strings are primative. - const typeSpec = ctx.asType()!.typeSpec(); - if (typeSpec.fixedLengthStringSpec()) { - return true; - } +export class PositionalParamElement extends BaseRuleSyntaxElement { + identifierCapability: IdentifierCapability; + diagnosticCapability: DiagnosticCapability; - // Built ins are primative (or can be in Variant's case) unless object. - const builtin = typeSpec.typeExpression()?.builtinType(); - if (builtin?.reservedTypeIdentifier() || builtin?.reservedTypeIdentifierB()) { - return true; - } else if (builtin?.OBJECT() || builtin?.OBJECT_B()) { - return false; - } + private variableTypeInformation?: VariableTypeInformation; + + constructor(ctx: PositionalParamContext, doc: TextDocument) { + super(ctx, doc); + + const typeCtx = ctx.paramDcl().untypedNameParamDcl()?.parameterType()?.typeExpression() + ?? ctx.paramDcl().typedNameParamDcl()?.typedName().typeSuffix(); + + const arrayCtx = ctx.paramDcl().untypedNameParamDcl()?.parameterType()?.arrayDesignator() + ?? ctx.paramDcl().typedNameParamDcl()?.arrayDesignator() + ?? undefined; - // Defined names can be all sorts of things but if we got here, we're an object. - const definedType = typeSpec.typeExpression()?.definedTypeExpression(); - if (definedType?.simpleNameExpression()) { - return false; + if (typeCtx) { + this.variableTypeInformation = new VariableTypeInformation(typeCtx, doc, arrayCtx); } - // If we have a member accessed type, we need to do more digging... - const memberAccessed = definedType?.memberAccessExpression()?.unrestrictedName(); - const isPrimativeMember = (ctx: UnrestrictedNameContext | undefined): boolean => !!memberAccessed?.reservedIdentifier()?.reservedTypeIdentifier(); - const isTypeSuffixMember = (ctx: UnrestrictedNameContext | undefined): boolean => !!memberAccessed?.name()?.typedName(); - return isPrimativeMember(memberAccessed) || isTypeSuffixMember(memberAccessed); + const identifierCtx = ctx.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() + ?? ctx.paramDcl().typedNameParamDcl()?.typedName().ambiguousIdentifier(); + this.identifierCapability = new IdentifierCapability(this, () => identifierCtx); + this.diagnosticCapability = new DiagnosticCapability(this); + this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.PARAMETER,); + this.scopeItemCapability.assignmentType = AssignmentType.GET + | (this.hasLetAccessor ? AssignmentType.LET : AssignmentType.NONE) + | (this.hasSetAccessor ? AssignmentType.SET : AssignmentType.NONE); } - get isFixedArrayType(): boolean { - return !this.arrayCtx?.boundsList(); + get hasLetAccessor(): boolean { + return this.variableTypeInformation?.isPrimativeType ?? true; } - constructor(ctx: TypeSuffixContext | AsClauseContext, doc: TextDocument, private readonly arrayCtx?: ArrayDimContext) { - super(ctx, doc); + get hasSetAccessor(): boolean { + return this.variableTypeInformation?.isObjectType ?? true; } } diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index 78d861a..424010d 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -8,24 +8,42 @@ import { vbapreListener } from '../../antlr/out/vbapreListener'; import { vbafmtListener } from '../../antlr/out/vbafmtListener'; import { CompilerIfBlockContext } from '../../antlr/out/vbapreParser'; import { + AmbiguousIdentifierContext, AnyOperatorContext, + ArgumentListContext, + CallStatementContext, ClassModuleContext, + DictionaryAccessExpressionContext, EnumDeclarationContext, EnumMemberContext, FunctionDeclarationContext, IfStatementContext, IgnoredClassAttrContext, IgnoredProceduralAttrContext, + IndexExpressionContext, + LetStatementContext, + LExpressionContext, + MemberAccessExpressionContext, + OptionalParamContext, + ParamArrayContext, + PositionalParamContext, ProceduralModuleContext, ProcedureDeclarationContext, PropertyGetDeclarationContext, PropertySetDeclarationContext, + SetStatementContext, + SimpleNameExpressionContext, SubroutineDeclarationContext, + TypeExpressionContext, TypeSuffixContext, UdtDeclarationContext, UnexpectedEndOfLineContext, + UnrestrictedNameContext, VariableDeclarationContext, - WhileStatementContext + WhileStatementContext, + WithExpressionContext, + WithMemberAccessExpressionContext, + WithStatementContext } from '../../antlr/out/vbaParser'; import { AttributeStatementContext, @@ -53,11 +71,23 @@ import { UnexpectedEndOfLineElement } from '../elements/utils'; import { DuplicateOperatorElement, IfElseBlock as IfStatementElement, WhileLoopElement } from '../elements/flow'; import { VbaClassDocument, VbaModuleDocument } from '../document'; import { ClassElement, ModuleElement, ModuleIgnoredAttributeElement } from '../elements/module'; -import { VariableDeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; +import { VariableDeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement, PositionalParamElement } from '../elements/typing'; import { FunctionDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement, SubDeclarationElement } from '../elements/procedure'; import { ExtensionConfiguration } from '../workspace'; import { Services } from '../../injection/services'; import { ErrorRuleElement } from '../elements/generic'; +import { NameExpressionContext, NameExpressionElement, WithStatementElement } from '../elements/naming'; +import { FunctionOrArray } from '../elements/expression'; +import { AssignmentType } from '../../capabilities/capabilities'; + + +enum ParserAssignmentState { + NONE, + LET, + SET, + CALL +} + export class CommonParserCapability { document: VbaClassDocument | VbaModuleDocument; @@ -80,14 +110,47 @@ export class CommonParserCapability { } } +type ParserState = { + inCallExp: boolean + isWithExp: boolean; + isWithStmt: boolean; + isDictAccess: boolean; + assignment: ParserAssignmentState; + nameElements: NameExpressionElement[]; + callMembers?: number; +}; export class VbaListener extends vbaListener { document: VbaClassDocument | VbaModuleDocument; protected documentSettings?: ExtensionConfiguration; protected isAfterMethodDeclaration = false; + private withStatementStack: WithStatementElement[] = []; + private functionOrArrayStack: FunctionOrArray[] = []; + private parserStateStack: ParserState[] = []; + + private readonly verbose = false; + + private get parserState(): ParserState { + if (this.parserStateStack.length === 0) { + Services.logger.error('State stack is empty.'); + this.pushNewState(); + } + return this.parserStateStack.at(-1)!; + } + + private pushNewState = () => + this.parserStateStack.push({ + inCallExp: false, + isWithExp: false, + isWithStmt: false, + isDictAccess: false, + nameElements: [], + assignment: ParserAssignmentState.NONE + }); constructor(document: VbaClassDocument | VbaModuleDocument) { super(); + this.pushNewState(); this.document = document; } @@ -171,6 +234,358 @@ export class VbaListener extends vbaListener { this.document.registerElement(element); }; + enterArgumentList = (_: ArgumentListContext) => this.pushNewState(); + exitArgumentList = (_: ArgumentListContext) => this.parserStateStack.pop(); + + // enterLExpression = (ctx: LExpressionContext) => { + // // We're already dealing with an expression. + // if (this.parserState.nameElement) { + // return; + // } + + // // We have a function call or array. + // if (ctx.hasParenthesis()) { + // this.functionOrArrayStack.push(new FunctionOrArray(ctx, this.document.textDocument)); + // Services.logger.debug(`Push function or array (${this.functionOrArrayStack.length}): ${ctx.getText()}`); + // return; + // } + + // // All other cases, start an expression. + // Services.logger.debug(`Start expression: ${ctx.getText()}`); + // const element = new NameExpressionElement(ctx, this.document.textDocument); + // this.parserState.nameElement = element; + // const funcOrArray = this.functionOrArrayStack.at(-1); + // if (funcOrArray && !funcOrArray.nameExpressionElement) { + // funcOrArray.nameExpressionElement = element; + // } + + // // Handle assignment type + // if (this.parserState.assignment !== ParserAssignmentState.NONE) { + // if (this.parserState.assignment === ParserAssignmentState.LET) { + // element.setAsLetType(); + // } else { + // element.setAsSetType(); + // } + // this.parserState.assignment = ParserAssignmentState.NONE; + // } + // }; + + // exitLExpression = (ctx: LExpressionContext) => { + // if (this.functionOrArrayStack.at(-1)?.context.rule === ctx) { + // Services.logger.debug(`Pop function or array (${this.functionOrArrayStack.length}): ${ctx.getText()}`); + // this.functionOrArrayStack.pop(); + // return; + // } + // this.handleExitCallOrExpression(ctx); + // }; + + enterLetStatement = (_: LetStatementContext) => { + if (this.verbose) Services.logger.debug(`enterLetStatement`, this.parserStateStack.length); + this.parserState.assignment = ParserAssignmentState.LET; + }; + + enterSetStatement = (_: SetStatementContext) => { + if (this.verbose) Services.logger.debug(`enterSetStatement`, this.parserStateStack.length); + this.parserState.assignment = ParserAssignmentState.SET; + }; + + // enterCallStatement = (ctx: CallStatementContext) => { + // // We shouldn't be dealing with an expression. + // const state = this.parserState; + // if (state.nameElement) { + // Services.logger.error('Call statement in expression?'); + // } + // Services.logger.debug(`Start call expression: ${ctx.getText()}`); + // state.nameElement = new NameExpressionElement(ctx, this.document.textDocument); + // }; + + enterCallStatement = (ctx: CallStatementContext) => { + if (this.verbose) Services.logger.debug(`enterCallStatement: ${ctx.getText()}`, this.parserStateStack.length); + this.parserState.inCallExp = true; + + // Sometimes a call statement does not have the normal trigger context to add a name expression. + const simpleNameCtx = ctx.simpleNameExpression(); + if (!ctx.memberAccessExpression() && !ctx.indexExpression() && simpleNameCtx) { + this.pushNameElement(simpleNameCtx); + } + }; + + exitCallStatement = (ctx: CallStatementContext) => { + if (this.verbose) Services.logger.debug(`exitCallStatement: ${ctx.getText()}`, this.parserStateStack.length); + + // Sometimes a call statement does not have the normal trigger context to exit a name expression. + const simpleNameCtx = ctx.simpleNameExpression(); + if (!ctx.memberAccessExpression() && !ctx.indexExpression() && simpleNameCtx) { + this.parserState.assignment = ParserAssignmentState.CALL; + this.registerNameElement(); + } + this.parserState.inCallExp = false; + }; + + enterMemberAccessExpression = (ctx: MemberAccessExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterMemberAccessExpression: ${ctx.getText()}`, this.parserStateStack.length); + this.pushNameElement(ctx); + }; + + enterDictionaryAccessExpression = (ctx: DictionaryAccessExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterDictionaryAccessExpression: ${ctx.getText()}`, this.parserStateStack.length); + this.parserState.isDictAccess = true; + this.pushNameElement(ctx); + }; + + exitDictionaryAccessExpression = (ctx: DictionaryAccessExpressionContext) => { + if (this.verbose) Services.logger.debug(`exitDictionaryAccessExpression`, this.parserStateStack.length); + this.parserState.isDictAccess = false; + }; + + enterWithMemberAccessExpression = (ctx: WithMemberAccessExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterWithMemberAccessExpression: ${ctx.getText()}`, this.parserStateStack.length); + this.pushNameElement(ctx); + }; + + exitMemberAccessExpression = (ctx: MemberAccessExpressionContext) => { + if (this.verbose) Services.logger.debug(`exitMemberAccessExpression: ${ctx.getText()}`, this.parserStateStack.length); + if (this.parserState.inCallExp) this.parserState.assignment = ParserAssignmentState.CALL; + this.registerNameElement(); + }; + + enterIndexExpression = (ctx: IndexExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterIndexExpression: ${ctx.getText()}`, this.parserStateStack.length); + this.pushNameElement(ctx); + }; + + exitIndexExpression = (ctx: IndexExpressionContext) => { + Services.logger.debug(`exitIndexExpression: ${ctx.getText()}`, this.parserStateStack.length); + if (this.parserState.inCallExp) this.parserState.assignment = ParserAssignmentState.CALL; + this.registerNameElement(); + }; + + enterLExpression = (ctx: LExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterLExpression: ${ctx.getText()}`, this.parserStateStack.length); + if (ctx.LPAREN()) { + // FIXME: Will need to track function calls properly when we have types... maybe. + return; + } + + // Will be handled by the WithExpression. + if (ctx.withExpression()) { + return; + } + this.pushNameElement(ctx); + }; + + exitLExpression = (ctx: LExpressionContext) => { + if (this.verbose) Services.logger.debug(`exitLExpression: ${ctx.getText()}`, this.parserStateStack.length); + if (ctx.LPAREN()) { + return; + } + // Was handled by the WithExpression. + if (ctx.withExpression()) { + return; + } + this.registerNameElement(); + }; + + enterPositionalParam = (ctx: PositionalParamContext) => { + if (this.verbose) Services.logger.debug(`enterPositionalParam: ${ctx.getText()}`, this.parserStateStack.length); + const element = new PositionalParamElement(ctx, this.document.textDocument); + this.document.registerElement(element); + // this.pushNameElement(ctx); + + // const identifierCtx = ctx.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() + // ?? ctx.paramDcl().typedNameParamDcl()?.typedName().ambiguousIdentifier(); + + // if (identifierCtx) { + // this.addNameElementContext(identifierCtx, 'ambigiousNameContext'); + // } + }; + + // enterTypeExpression = (ctx: TypeExpressionContext) => + // Services.logger.log(`TypeExpressionContext ${ctx.getText()} is primative: ${ctx.isPrimative}`); + + // exitPositionalParam = (ctx: PositionalParamContext) => { + // if (this.verbose) Services.logger.debug(`exitPositionalParam: ${ctx.getText()}`, this.parserStateStack.length); + // this.registerNameElement(); + // }; + + enterOptionalParam = (ctx: OptionalParamContext) => { + if (this.verbose) Services.logger.debug(`enterOptionalParam: ${ctx.getText()}`, this.parserStateStack.length); + const identifierCtx = ctx.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() + ?? ctx.paramDcl().typedNameParamDcl()?.typedName().ambiguousIdentifier(); + + if (identifierCtx) { + this.addNameElementContext(identifierCtx, 'ambigiousNameContext'); + } + }; + + exitOptionalParam = (ctx: OptionalParamContext) => { + if (this.verbose) Services.logger.debug(`exitOptionalParam: ${ctx.getText()}`, this.parserStateStack.length); + this.registerNameElement(); + }; + + enterParamArray = (ctx: ParamArrayContext) => { + if (this.verbose) Services.logger.debug(`enterParamArray: ${ctx.getText()}`, this.parserStateStack.length); + this.addNameElementContext(ctx.ambiguousIdentifier(), 'ambigiousNameContext'); + }; + + exitParamArray = (ctx: ParamArrayContext) => { + if (this.verbose) Services.logger.debug(`exitParamArray: ${ctx.getText()}`, this.parserStateStack.length); + this.registerNameElement(); + }; + + enterUnrestrictedName = (ctx: UnrestrictedNameContext) => this.addNameElementContext(ctx, 'enterUnrestrictedName'); + enterSimpleNameExpression = (ctx: SimpleNameExpressionContext) => this.addNameElementContext(ctx, 'enterSimpleNameExpression'); + + private addNameElementContext(ctx: UnrestrictedNameContext | SimpleNameExpressionContext | AmbiguousIdentifierContext, source: string) { + if (this.verbose) Services.logger.debug(`${source}: ${ctx.getText()}`, this.parserStateStack.length); + const nameElement = this.parserState.nameElements.at(-1); + if (!nameElement) { + Services.logger.error(`Cannot add name ${ctx.getText()}`, this.parserStateStack.length); + return; + } + + nameElement.addName(ctx); + } + + private pushNameElement(ctx: NameExpressionContext): void { + if (this.verbose) Services.logger.debug('Pushing name', this.parserStateStack.length); + const element = new NameExpressionElement(ctx, this.document.textDocument); + + // Push the name element to the stack. + this.parserState.nameElements.push(element); + + // Link to WithStatement if we have one. + const withStatement = this.withStatementStack.at(-1); + if (withStatement && !withStatement.nameExpressionElement) { + withStatement.nameExpressionElement = element; + } + } + + private registerNameElement(): void { + // Pop the current element and return if we don't have one. + const nameElement = this.parserState.nameElements.pop(); + if (!nameElement) { + return; + } + + // Add with names if we're in a WithExpression + if (this.parserState.isWithExp) { + nameElement.withStatementElement = this.withStatementStack.at(-1); + this.parserState.isWithExp = false; + } + + // Set the type based on parser state. + switch (this.parserState.assignment) { + case ParserAssignmentState.SET: + nameElement.setAsSetType(); + break; + case ParserAssignmentState.LET: + nameElement.setAsLetType(); + break; + case ParserAssignmentState.CALL: + nameElement.setAsCallType(); + break; + } + this.parserState.assignment = ParserAssignmentState.NONE; + + // Register this name element. + this.document.registerElement(nameElement); + if (this.verbose) Services.logger.debug(`Registered ${nameElement.fqName} as ${nameElement.identifierCapability.name}`, this.parserStateStack.length); + + // Add the name(s) to the next element down. + const nextElement = this.parserState.nameElements.at(-1); + if (nextElement) { + nextElement.nameContexts = [ + nameElement.nameContexts, + nextElement.nameContexts + ].flat(); + } + } + + // exitCallStatement = (ctx: CallStatementContext) => { + // if (!this.parserState.nameElement) { + // Services.logger.error('No call expression to exit.'); + // return; + // } + // this.parserState.nameElement.setAsCallType(); + // this.handleExitCallOrExpression(ctx); + // }; + + // private handleExitCallOrExpression(ctx: LExpressionContext | CallStatementContext) { + // // This isn't the expression we're dealing with. + // const nameExpressionElement = this.parserState.nameElement; + // if (!nameExpressionElement) { + // return; + // } + + // if (nameExpressionElement.context.rule !== ctx) { + // return; + // } + + // if (!nameExpressionElement.hasNames) { + // Services.logger.debug(`End expression 0 names: ${ctx.getText()}`); + // this.parserState.nameElement = undefined; + // return; + // } + + // // Handle when this name is a With statement. + // const withStatement = this.withStatementStack.at(-1); + // if (withStatement && !withStatement.nameExpressionElement) { + // withStatement.nameExpressionElement = nameExpressionElement; + // if (this.isWithExpression) { + // // Attach to the parent when this also uses a With expression. + // const withStatementParent = this.withStatementStack.at(-2); + // if (!withStatementParent) { + // Services.logger.error(`Not enough ancestors.`); + // } else { + // nameExpressionElement.withStatementElement = withStatementParent; + // } + // this.isWithExpression = false; + // } + // this.parserState.nameElement = undefined; + // return; + // } + + // // Attach With statement if we're in a With expression. + // if (this.isWithExpression && withStatement) { + // nameExpressionElement.withStatementElement = withStatement; + // this.isWithExpression = false; + // } + + // Services.logger.debug(`Registering name: ${nameExpressionElement.fqName}`); + // this.document.registerElement(nameExpressionElement); + // this.parserState.nameElement = undefined; + + // if (this.isWithExpression) { + // Services.logger.warn('Still within withExpression after exitLExpression'); + // } + // } + + enterWithStatement = (ctx: WithStatementContext) => { + if (this.verbose) Services.logger.debug(`enterWithStatement: ${ctx.getText().split('\n')[0]}...`, this.parserStateStack.length); + const element = new WithStatementElement(ctx, this.document.textDocument); + this.withStatementStack.push(element); + }; + + exitWithStatement = (ctx: WithStatementContext) => { + if (this.verbose) Services.logger.debug(`exitWithStatement`, this.parserStateStack.length); + if (this.withStatementStack.at(-1)?.context.rule === ctx) { + this.withStatementStack.pop(); + } else { + Services.logger.error(`Can't exit With`); + } + }; + + enterWithExpression = (_: WithExpressionContext) => { + if (this.verbose) Services.logger.debug(`enterWithExpression`, this.parserStateStack.length); + this.parserState.isWithExp = true; + }; + + exitWithExpression = (_: WithExpressionContext) => { + if (this.verbose) Services.logger.debug(`exitWithExpression`, this.parserStateStack.length); + this.registerNameElement(); + }; + enterTypeSuffix = (ctx: TypeSuffixContext) => this.document.registerElement(new TypeSuffixElement(ctx, this.document.textDocument)); diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 43cef22..5fc11e2 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -8,6 +8,7 @@ import { Command, CompletionItem, CompletionParams, + DefinitionParams, DidChangeConfigurationNotification, DidChangeWatchedFilesParams, DocumentFormattingParams, @@ -16,6 +17,7 @@ import { FoldingRangeParams, Hover, HoverParams, + LocationLink, RenameParams, SemanticTokensRangeParams, SymbolInformation, @@ -37,7 +39,7 @@ import { returnDefaultOnCancelClientRequest } from '../utils/wrappers'; import { inject, injectable } from 'tsyringe'; import { Logger, ILanguageServer, IWorkspace } from '../injection/interface'; import { Services } from '../injection/services'; -import { ItemType, ScopeItemCapability } from '../capabilities/capabilities'; +import { ScopeType, ScopeItemCapability } from '../capabilities/capabilities'; import { SyntaxParser } from './parser/vbaParser'; export interface ExtensionConfiguration { @@ -93,9 +95,9 @@ export class Workspace implements IWorkspace { this._hasConfigurationCapability = hasWorkspaceConfigurationCapability(this.server); // Configure scopes - const languageScope = new ScopeItemCapability(undefined, ItemType.VBA); - const applicationScope = new ScopeItemCapability(undefined, ItemType.APPLICATION, undefined, languageScope); - const projectScope = new ScopeItemCapability(undefined, ItemType.PROJECT, undefined, applicationScope); + const languageScope = new ScopeItemCapability(undefined, ScopeType.VBA); + const applicationScope = new ScopeItemCapability(undefined, ScopeType.APPLICATION, undefined, languageScope); + const projectScope = new ScopeItemCapability(undefined, ScopeType.PROJECT, undefined, applicationScope); Services.registerProjectScope(projectScope); } @@ -298,6 +300,7 @@ class WorkspaceEvents { connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); connection.onCompletion(params => this.onCompletion(params)); connection.onCompletionResolve(item => this.onCompletionResolve(item)); + connection.onDefinition(async (params, token) => await this.onDefinition(params, token)); connection.onDidChangeConfiguration(() => Services.workspace.clearDocumentsConfiguration()); connection.onDidChangeWatchedFiles(params => this.onDidChangeWatchedFiles(params)); connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); @@ -314,12 +317,13 @@ class WorkspaceEvents { connection.onRequest((method: string, params: object | object[] | any) => { switch (method) { case 'textDocument/semanticTokens/full': { - const uri: string = params.textDocument.uri; - return this.activeDocument?.languageServerSemanticTokens(); + const uri: string = params.textDocument.uri.toFilePath(); + return this.projectDocuments.get(uri)?.languageServerSemanticTokens(); } case 'textDocument/semanticTokens/range': { const rangeParams = params as SemanticTokensRangeParams; - return this.activeDocument?.languageServerSemanticTokens(rangeParams.range); + const uri: string = params.textDocument.uri.toFilePath(); + return this.projectDocuments.get(uri)?.languageServerSemanticTokens(rangeParams.range); } default: Services.logger.error(`Unresolved request path: ${method}`); @@ -348,6 +352,19 @@ class WorkspaceEvents { return item; } + private async onDefinition(params: DefinitionParams, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return; + } + + const results = Services.projectScope.getDeclarationLocation(params.textDocument.uri, params.position); + if (results === undefined) { + return null; + } else { + return results; + } + } + private onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { Services.logger.debug('[event] onDidChangeWatchedFiles'); Services.logger.debug(JSON.stringify(params), 1); diff --git a/server/src/server.ts b/server/src/server.ts index 78face8..5ba89f3 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -90,11 +90,11 @@ export class LanguageServerConfiguration { codeActionProvider: true, completionProvider: undefined, hoverProvider: false, + definitionProvider: true, // Not implemented. signatureHelpProvider: undefined, declarationProvider: false, - definitionProvider: false, typeDefinitionProvider: false, implementationProvider: false, referencesProvider: false, From b5abd7f683ef35d7bd7dafa6f848392b5b6393b9 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:25:16 +0800 Subject: [PATCH 73/93] Fix bug with toFileUri --- server/src/extensions/stringExtensions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/extensions/stringExtensions.ts b/server/src/extensions/stringExtensions.ts index a48e809..fa33bf3 100644 --- a/server/src/extensions/stringExtensions.ts +++ b/server/src/extensions/stringExtensions.ts @@ -20,7 +20,7 @@ String.prototype.toFilePath = function (): string { }; String.prototype.toFileUri = function (): string { - return this.startsWith('file://') + return !this.startsWith('file://') ? pathToFileURL(this.toString()).href : this.toString(); }; From 33a5a6b6606ed1f5e832e484be9b1daad3d335a9 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:26:49 +0800 Subject: [PATCH 74/93] findDeclaration finds by type --- server/src/capabilities/capabilities.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index b87230c..b02adcc 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -693,12 +693,30 @@ export class ScopeItemCapability { ?? this.parent?.findPropertySetter(identifier); } - findDeclarations(identifier: string): ScopeItemCapability[] | undefined { + findDeclarations(identifier: string, assignmentType: AssignmentType): ScopeItemCapability[] | undefined { const explicitResult = this.explicitDeclarations .map(x => x.get(identifier)) .filter(x => !!x) .flat(); + if (assignmentType & AssignmentType.GET) { + this.properties?.getters?.get(identifier)?.forEach(x => + explicitResult.push(x) + ); + } + + if (assignmentType & AssignmentType.SET) { + this.properties?.setters?.get(identifier)?.forEach(x => + explicitResult.push(x) + ); + } + + if (assignmentType & AssignmentType.LET) { + this.properties?.letters?.get(identifier)?.forEach(x => + explicitResult.push(x) + ); + } + if (explicitResult.length > 0) { return explicitResult; } @@ -710,7 +728,7 @@ export class ScopeItemCapability { return implicitResult; } - return this.parent?.findDeclarations(identifier); + return this.parent?.findDeclarations(identifier, assignmentType); } /** From e811563a94a4f27eaf35d294ac43c1f491866e66 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:29:17 +0800 Subject: [PATCH 75/93] explicitDeclarations now excludes properties --- server/src/capabilities/capabilities.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index b02adcc..dbb6dbd 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -248,9 +248,6 @@ export class ScopeItemCapability { addToResult(this.modules); addToResult(this.functions); addToResult(this.subroutines); - addToResult(this.properties?.getters); - addToResult(this.properties?.letters); - addToResult(this.properties?.setters); return result; } From 178a4ac47b4d242aa1848fe2964f41fd1a0ece88 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:31:15 +0800 Subject: [PATCH 76/93] Variables now pass their class type if they have one --- server/src/capabilities/capabilities.ts | 1 + .../extensions/antlrVbaParserExtensions.ts | 24 +++++++++++++++++++ server/src/project/elements/typing.ts | 5 ++++ 3 files changed, 30 insertions(+) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index dbb6dbd..d37e83d 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -271,6 +271,7 @@ export class ScopeItemCapability { isPublicScope?: boolean; accessMembers?: string[]; isOptionExplicitScope = false; + classTypeName?: string; constructor( readonly element?: BaseRuleSyntaxElement, diff --git a/server/src/extensions/antlrVbaParserExtensions.ts b/server/src/extensions/antlrVbaParserExtensions.ts index 15297d5..f4c37b7 100644 --- a/server/src/extensions/antlrVbaParserExtensions.ts +++ b/server/src/extensions/antlrVbaParserExtensions.ts @@ -64,18 +64,21 @@ declare module '../antlr/out/vbaParser' { isObject: boolean; isVariant: boolean; isPrimative: boolean; + classTypeName: string | undefined; } interface TypeExpressionContext { isObject: boolean; isVariant: boolean; isPrimative: boolean; + classTypeName: string | undefined; } interface TypeSuffixContext { isObject: boolean; isVariant: boolean; isPrimative: boolean; + classTypeName: string | undefined; } interface ArrayDesignatorContext { @@ -139,6 +142,12 @@ Object.defineProperty(TypeExpressionContext.prototype, "isObject", { } }); +Object.defineProperty(TypeExpressionContext.prototype, "classTypeName", { + get: function (this: TypeExpressionContext): string | undefined { + return this.definedTypeExpression()?.getText(); + } +}); + Object.defineProperty(TypeSuffixContext.prototype, "isPrimative", { get: function (this: TypeSuffixContext): boolean { return true; @@ -157,6 +166,12 @@ Object.defineProperty(TypeSuffixContext.prototype, "isObject", { } }); +Object.defineProperty(TypeSuffixContext.prototype, "classTypeName", { + get: function (this: TypeSuffixContext): string | undefined { + return undefined; + } +}); + Object.defineProperty(AsClauseContext.prototype, "isPrimative", { get: function (this: AsClauseContext): boolean { const typeSpecContext = this.asType()?.typeSpec(); @@ -180,6 +195,15 @@ Object.defineProperty(AsClauseContext.prototype, "isObject", { } }); +Object.defineProperty(AsClauseContext.prototype, "classTypeName", { + get: function (this: AsClauseContext): string | undefined { + return this.isObject + ? this.asType()?.typeSpec().getText() + ?? this.asAutoObject()?.classTypeName().getText() + : undefined; + } +}); + Object.defineProperty(ArrayDesignatorContext.prototype, "isResizable", { get: function (this: ArrayDesignatorContext): boolean { return false; diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index b49d01f..ebb6a5e 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -189,6 +189,7 @@ export class VariableDeclarationElement extends BaseRuleSyntaxElement { return this.arrayCtx?.isResizable ?? false; } + get classTypeName(): string | undefined { + return this.context.rule.classTypeName; + } + constructor(ctx: TypeContext, doc: TextDocument, private readonly arrayCtx?: ArrayDimContext | ArrayDesignatorContext) { super(ctx, doc); } From 39d51ad86d2b71109df8dbcc7f42897637e8d1ec Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:32:21 +0800 Subject: [PATCH 77/93] Fixed locationUri --- server/src/project/elements/module.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index 20dcaee..f3b819b 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -1,5 +1,4 @@ // Core -import * as url from 'url'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { Diagnostic, Range, SymbolKind } from 'vscode-languageserver'; @@ -113,7 +112,7 @@ export class ModuleElement extends BaseModuleElement { this.attrubutes = ctx.proceduralModuleHeader().proceduralModuleAttr(); this.diagnosticCapability = new DiagnosticCapability(this); this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.MODULE); - this.scopeItemCapability.locationUri = url.pathToFileURL(doc.uri).toString(); + this.scopeItemCapability.locationUri = doc.uri.toFileUri(); this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .proceduralModuleBody() @@ -161,7 +160,7 @@ export class ClassElement extends BaseModuleElement { ].flat(); this.diagnosticCapability = new DiagnosticCapability(this); this.scopeItemCapability = new ScopeItemCapability(this, ScopeType.CLASS); - this.scopeItemCapability.locationUri = url.pathToFileURL(doc.uri).toString(); + this.scopeItemCapability.locationUri = doc.uri.toFileUri(); this.scopeItemCapability.isOptionExplicitScope = this.evaluateHasOptionExplicit(ctx .classModuleBody() From 65012d9591e6e67c18624a217e6008380060c151 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:32:50 +0800 Subject: [PATCH 78/93] Logging for onDefinition --- server/src/project/workspace.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 5fc11e2..9a350fa 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -353,11 +353,15 @@ class WorkspaceEvents { } private async onDefinition(params: DefinitionParams, token: CancellationToken): Promise { + Services.logger.debug('[event] onDefinition'); + Services.logger.debug(JSON.stringify(params), 1); if (token.isCancellationRequested) { return; } const results = Services.projectScope.getDeclarationLocation(params.textDocument.uri, params.position); + Services.logger.debug(`Processed onDefinition: returning ${JSON.stringify(results)}`); + if (results === undefined) { return null; } else { From 370c981654d59dcd0ae5a1d7ec9db869362e7be5 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:38:41 +0800 Subject: [PATCH 79/93] Changed how the name stack works --- server/src/project/elements/naming.ts | 12 ++++++------ server/src/project/parser/vbaListener.ts | 9 ++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/server/src/project/elements/naming.ts b/server/src/project/elements/naming.ts index 5d6600f..81f7fc3 100644 --- a/server/src/project/elements/naming.ts +++ b/server/src/project/elements/naming.ts @@ -57,8 +57,11 @@ export class NameExpressionElement extends BaseRuleSyntaxElement x.getText()).join('.'); + get accessMembers(): ParserRuleContext[] | undefined { + const names = this.nameStack; + if (names.length > 1) { + return names; + } } constructor(ctx: NameExpressionContext, doc: TextDocument) { @@ -76,9 +79,6 @@ export class NameExpressionElement extends BaseRuleSyntaxElement x.getText()); - if (names.length > 0) { - this.scopeItemCapability.accessMembers = names; - } + this.scopeItemCapability.accessMembers = this.accessMembers; } } \ No newline at end of file diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index 424010d..1a18f51 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -474,6 +474,9 @@ export class VbaListener extends vbaListener { this.parserState.isWithExp = false; } + // Resolve the access members if we have them. + nameElement.evaluateNameStack(); + // Set the type based on parser state. switch (this.parserState.assignment) { case ParserAssignmentState.SET: @@ -490,7 +493,11 @@ export class VbaListener extends vbaListener { // Register this name element. this.document.registerElement(nameElement); - if (this.verbose) Services.logger.debug(`Registered ${nameElement.fqName} as ${nameElement.identifierCapability.name}`, this.parserStateStack.length); + if (this.verbose) { + const name = nameElement.identifierCapability.name; + const fqName = nameElement.accessMembers?.map(x => x.getText).join('.'); + Services.logger.debug(`Registered ${fqName} as ${name}`, this.parserStateStack.length); + } // Add the name(s) to the next element down. const nextElement = this.parserState.nameElements.at(-1); From d526d02fbb5a9e6cdeece9c6bc652c3bd8bafcc5 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 13:40:52 +0800 Subject: [PATCH 80/93] Added ambiguousName diag and fixed link resolution --- server/src/capabilities/capabilities.ts | 92 ++++++++++++++++++++++++- server/src/capabilities/diagnostics.ts | 10 +++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index d37e83d..a99571c 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -15,7 +15,7 @@ import { ParserRuleContext, TerminalNode } from 'antlr4ng'; import { SemanticToken, SemanticTokenModifiers, SemanticTokenTypes } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, BaseSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; -import { BaseDiagnostic, DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, UnusedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; +import { AmbiguousNameDiagnostic, BaseDiagnostic, DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, UnusedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; import { Services } from '../injection/services'; import { isPositionInsideRange } from '../utils/helpers'; @@ -531,10 +531,76 @@ export class ScopeItemCapability { } private resolveLinks() { - const declarations = this.findDeclarations(this.identifier); - if (declarations) { + + // Resolve where we have no member access names. + if (!this.accessMembers || this.accessMembers.length === 0) { + const declarations = this.findDeclarations(this.identifier, this.assignmentType); + if (declarations === undefined || declarations.length === 0) { + return; + } + + if (declarations.length > 1) { + const diagnostic = this.pushDiagnostic(AmbiguousNameDiagnostic, this, this.identifier); + this.addScopesAsRelatedInformation(diagnostic, declarations); + return; + } + this.linkThisToItem(declarations[0]); + return; + } + + // Resolve for member accessed names. + let foundDeclarations: ScopeItemCapability[] = []; + for (const [i, ctx] of this.accessMembers.entries()) { + // Get the scope item to search and exit if we don't have anything. + const searchScope = i === 0 ? this + : foundDeclarations.length === 0 + ? this.project + : foundDeclarations[0]; + + // Can't do anything more if we don't have a scope to search. + if (searchScope === undefined) { + return; + } + + // Get the details of what we're searching for. + const name = ctx.getText(); + const assignmentType = i < this.accessMembers.length - 1 + ? AssignmentType.GET + : this.assignmentType; + + // Search the immediate scope hierarchy if this is the first member. + foundDeclarations = searchScope.findDeclarations(name, assignmentType) ?? []; + + // If we didn't find anything, try searching the type. + if (foundDeclarations.length === 0 && searchScope.classTypeName !== undefined) { + foundDeclarations = this.project?.findDeclarations(searchScope.classTypeName, assignmentType) ?? []; + foundDeclarations = foundDeclarations[0]?.findDeclarations(name, assignmentType) ?? []; + } + + // Exactly one means we found something. + if (foundDeclarations.length === 1) { + continue; + } + + // Nothing found means we can't continue. + if (foundDeclarations.length === 0) { + return; + } + + // More than one declaration is ambiguous. + if (foundDeclarations.length > 1) { + const document = this.element?.context.document; + if (document) { + const diagnostic = new AmbiguousNameDiagnostic(ctx.toRange(document), ''); + this.addScopesAsRelatedInformation(diagnostic, foundDeclarations); + } + return; + } } + + // If we get here, we have resolved the member access name. + this.linkThisToItem(foundDeclarations[0]); } private linkThisToItem(linkItem?: ScopeItemCapability): void { @@ -1024,4 +1090,24 @@ export class ScopeItemCapability { return diagnostic; } } + + private addScopesAsRelatedInformation(diagnostic: BaseDiagnostic | undefined, items: ScopeItemCapability[]): void { + if (diagnostic === undefined) { + return; + } + + items.forEach(item => { + const ctx = item.element?.context; + if (ctx === undefined) { + return; + } + diagnostic.addRelatedInformation({ + message: "Related Information", + location: { + uri: ctx.document.uri, + range: ctx.range + } + }); + }); + } } \ No newline at end of file diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index c87ebe4..4bb70a2 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -87,6 +87,16 @@ export class DuplicateDeclarationDiagnostic extends BaseDiagnostic { } +// test +export class AmbiguousNameDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Error; + constructor(range: Range, message: string) { + super(range); + this.message = `Ambiguous name detected: '${message}'.`; + } +} + + // test export class ShadowDeclarationDiagnostic extends BaseDiagnostic { severity = DiagnosticSeverity.Warning; From 4434ad80295d35b4fe3d6c0e9ec944d766bd1799 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 14:11:28 +0800 Subject: [PATCH 81/93] Removed unused code and tidied imports --- server/src/capabilities/capabilities.ts | 94 +----------- server/src/capabilities/codeActions.ts | 27 ++-- server/src/capabilities/folding.ts | 1 + server/src/extensions/stringExtensions.ts | 1 + server/src/injection/interface.ts | 8 +- server/src/injection/services.ts | 9 +- server/src/project/document.ts | 40 +---- server/src/project/elements/base.ts | 4 +- server/src/project/elements/flow.ts | 2 +- server/src/project/elements/generic.ts | 4 +- server/src/project/formatter.ts | 2 + server/src/project/parser/vbaListener.ts | 170 +++------------------- server/src/project/workspace.ts | 17 ++- server/src/server.ts | 12 +- server/src/utils/helpers.ts | 1 + server/src/utils/logger.ts | 5 +- server/src/utils/wrappers.ts | 31 +--- 17 files changed, 84 insertions(+), 344 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index a99571c..71c1630 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -269,7 +269,7 @@ export class ScopeItemCapability { // Item Properties locationUri?: string; isPublicScope?: boolean; - accessMembers?: string[]; + accessMembers?: ParserRuleContext[]; isOptionExplicitScope = false; classTypeName?: string; @@ -499,34 +499,6 @@ export class ScopeItemCapability { } shadowing.forEach(item => this.addDiagnosticReference(diagnostic, item)); } - - // // All declaration types check for modules. - // this.resolveShadowedDeclaration(parent.findType(this.identifier)); - // this.resolveShadowedDeclaration(parent.findModule(this.identifier)); - // this.resolveShadowedDeclaration(parent.findFunction(this.identifier)); - // this.resolveShadowedDeclaration(parent.findSubroutine(this.identifier)); - - // // Properties care about everything except properties that - // // aren't the same type. Everything else cares about everything. - - // // ToDo: - // // Variables are registered as props so should also squash their - // // get/set/let diagnostics into one single diagnostic. - - // // Check get properties. - // if (this.assignmentType & AssignmentType.GET) { - // this.resolveShadowedDeclaration(parent.findPropertyGetter(this.identifier)); - // } - - // // Check let properties. - // if (this.assignmentType & AssignmentType.LET) { - // this.resolveShadowedDeclaration(parent.findPropertyLetter(this.identifier)); - // } - - // // Check set properties. - // if (this.assignmentType & AssignmentType.SET) { - // this.resolveShadowedDeclaration(parent.findPropertySetter(this.identifier)); - // } } } @@ -658,43 +630,6 @@ export class ScopeItemCapability { return false; } - // findDeclarations(name: string, section?: string): ScopeItemCapability[] { - // const identifier = section ?? name.split('.')[0]; - // if (this.has(identifier)) { - // return this.maps.map(x => x.get(identifier) ?? []).flat(); - // } - - // // FIXME: Handle publicly accessible names in project scope. - // return this.parent?.findDeclarations(name, section) ?? []; - // } - - // resolvePropertyChain(chain: string) { - // // Search UP for foo in foo.xx.xx.xx.... - // const declarations = this.findDeclarations(chain); - - // // Not found. - // if (declarations.length === 0) { - // return { status: 404, scope: undefined }; - // } - - // // Conflicting names FIXME: may need to add logic to handle get/set/let. - // if (declarations.length > 1) { - // return { status: 409, scope: undefined }; - // } - - // // We have exactly one name. Search DOWN the chain. - // const chainLinks = chain.split('.').slice(1); - // let declaration = declarations[0]; - // for (const chainLink of chainLinks) { - // // Assume we're only going to get one result here. - // // If we get more, we can handle diagnostics elsewhere. - // declaration = declaration.maps.map( - // x => x.get(chainLink) ?? [] - // ).flat()[0]; - // } - // return { status: 200, scope: declaration }; - // } - /** Get accessible declarations */ getAccessibleScopes(identifier: string, results: ScopeItemCapability[] = []): ScopeItemCapability[] { // Add any non-public items we find at this level. @@ -801,33 +736,6 @@ export class ScopeItemCapability { * @returns The current scope. */ registerScopeItem(item: ScopeItemCapability): ScopeItemCapability { - // ToDo: Get the parent based on visibility. - // Public scoped elements should get the project. - // Check pub/priv declares in same document treated as duplicate instead of shadowed. - - /** - * Visibility on a method-scoped variable does nothing but isn't invalid. - * These should declare as if they're private and raise a warning. - * - * Only MODULE scoped items are accessible implicitly in PROJECT scope and therefore - * only they should be 'escalated' to that scope. - */ - // const getParent = (item: ScopeItemCapability): ScopeItemCapability => - // (item.isPublicScope && this.type === ItemType.MODULE ? this.project : this) ?? this; - - // // Method-scoped variables are always private. - // if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { - // item.isPublicScope = false; - // if (item.visibilityModifierContext && item.element) { - // const ctx = item.visibilityModifierContext; - // const diagnostic = new MethodVariableIsPublicDiagnostic( - // ctx.toRange(item.element.context.document), - // ItemType[this.type] - // ); - // item.element?.diagnosticCapability?.diagnostics.push(diagnostic); - // } - // } - // Immediately invalidate if we're an Unknown Module if (item.type === ScopeType.MODULE && item.name === 'Unknown Module') { item.isInvalidated = true; diff --git a/server/src/capabilities/codeActions.ts b/server/src/capabilities/codeActions.ts index 3c3dde3..b668f89 100644 --- a/server/src/capabilities/codeActions.ts +++ b/server/src/capabilities/codeActions.ts @@ -1,4 +1,7 @@ +// Core import { CodeAction, Command, Diagnostic } from "vscode-languageserver"; + +// Project import { BaseDiagnostic } from "./diagnostics"; import { Services } from "../injection/services"; @@ -33,19 +36,19 @@ export class CodeActionsRegistry { // Example params that are related to a diagnostic. const onCodeActionParams = { - "textDocument":{ - "uri":"file:///c%3A/Repos/vba-LanguageServer/sample/b.bas" + "textDocument": { + "uri": "file:///c%3A/Repos/vba-LanguageServer/sample/b.bas" }, - "range":{"start":{"line":4,"character":1},"end":{"line":4,"character":1}}, - "context":{ - "diagnostics":[{ - "range":{"start":{"line":4,"character":1},"end":{"line":4,"character":1}}, - "message":"Option Explicit is missing from module header.", - "data":{"uri":"file:///c%3A/Repos/vba-LanguageServer/sample/b.bas"}, - "code":"W001", - "severity":2 + "range": { "start": { "line": 4, "character": 1 }, "end": { "line": 4, "character": 1 } }, + "context": { + "diagnostics": [{ + "range": { "start": { "line": 4, "character": 1 }, "end": { "line": 4, "character": 1 } }, + "message": "Option Explicit is missing from module header.", + "data": { "uri": "file:///c%3A/Repos/vba-LanguageServer/sample/b.bas" }, + "code": "W001", + "severity": 2 }], - "only":["quickfix"], - "triggerKind":1 + "only": ["quickfix"], + "triggerKind": 1 } }; diff --git a/server/src/capabilities/folding.ts b/server/src/capabilities/folding.ts index 2456032..922a1df 100644 --- a/server/src/capabilities/folding.ts +++ b/server/src/capabilities/folding.ts @@ -1,3 +1,4 @@ +// Core import { FoldingRange as VscFoldingRange, Range } from 'vscode-languageserver'; /** diff --git a/server/src/extensions/stringExtensions.ts b/server/src/extensions/stringExtensions.ts index fa33bf3..7f48100 100644 --- a/server/src/extensions/stringExtensions.ts +++ b/server/src/extensions/stringExtensions.ts @@ -1,3 +1,4 @@ +// Core import { fileURLToPath, pathToFileURL } from "url"; declare global { diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index 8effea3..3ff1ce3 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -1,8 +1,12 @@ +// Core +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; + +// Project import { LanguageServerConfiguration } from '../server'; import { BaseProjectDocument } from '../project/document'; -import { TextDocument } from 'vscode-languageserver-textdocument'; import { VbaFmtListener } from '../project/parser/vbaListener'; -import { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; + export interface Logger { error(msg: string, lvl?: number, e?: unknown): void; diff --git a/server/src/injection/services.ts b/server/src/injection/services.ts index 8cf22a0..39ecdd9 100644 --- a/server/src/injection/services.ts +++ b/server/src/injection/services.ts @@ -1,9 +1,12 @@ +// Core import { container, InjectionToken } from 'tsyringe'; -import { Logger, IWorkspace, ILanguageServer } from './interface'; -import { LspLogger } from '../utils/logger'; import { _Connection, createConnection, ProposedFeatures } from 'vscode-languageserver/node'; -import { ScopeItemCapability } from '../capabilities/capabilities'; + +// Project +import { LspLogger } from '../utils/logger'; +import { Logger, IWorkspace, ILanguageServer } from './interface'; import { CodeActionsRegistry } from '../capabilities/codeActions'; +import { ScopeItemCapability } from '../capabilities/capabilities'; export class Services { diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 15eef8c..b59520d 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -6,10 +6,15 @@ import { CancellationToken, Diagnostic, SymbolInformation, SymbolKind } from 'vs import { ParserRuleContext } from 'antlr4ng'; // Project +import { Services } from '../injection/services'; +import { IWorkspace } from '../injection/interface'; import { Dictionary } from '../utils/helpers'; import { SyntaxParser } from './parser/vbaParser'; import { FoldingRange } from '../capabilities/folding'; +import { VbaFmtListener } from './parser/vbaListener'; +import { BaseDiagnostic } from '../capabilities/diagnostics'; import { SemanticTokensManager } from '../capabilities/semanticTokens'; +import { ScopeItemCapability } from '../capabilities/capabilities'; import { BaseRuleSyntaxElement, BaseSyntaxElement, @@ -19,18 +24,12 @@ import { HasSemanticTokenCapability, HasSymbolInformationCapability } from './elements/base'; - import { PropertyDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement } from './elements/procedure'; -import { VbaFmtListener } from './parser/vbaListener'; -import { Services } from '../injection/services'; -import { IWorkspace } from '../injection/interface'; -import { ScopeItemCapability } from '../capabilities/capabilities'; -import { BaseDiagnostic } from '../capabilities/diagnostics'; // TODO --------------------------------------------- @@ -78,35 +77,6 @@ export abstract class BaseProjectDocument { return this.subtractTextFromRanges(this.redactedElements.map(x => x.context.range)); } - // async getDocumentConfiguration(): Promise { - // // Get the stored configuration. - // if (this.documentConfiguration) { - // return this.documentConfiguration; - // } - - // // Get the configuration from the client. - // if (this.workspace.hasConfigurationCapability) { - // this.documentConfiguration = await this.workspace.requestDocumentSettings(this.textDocument.uri); - // if (this.documentConfiguration) { - // return this.documentConfiguration; - // } - // } - - // // Use the defaults. - // this.documentConfiguration = { - // maxDocumentLines: 1500, - // maxNumberOfProblems: 100, - // doWarnOptionExplicitMissing: true, - // environment: { - // os: "Win64", - // version: "Vba7" - // } - // }; - // return this.documentConfiguration; - // } - - // clearDocumentConfiguration = () => this.documentConfiguration = undefined; - constructor(name: string, document: TextDocument) { this.textDocument = document; this.workspace = Services.workspace; diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index 1026e0f..0a053db 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -3,7 +3,7 @@ import { Position, Range } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; // Antlr -import { Parser, ParserRuleContext, TerminalNode } from 'antlr4ng'; +import { ParserRuleContext, TerminalNode } from 'antlr4ng'; // Project import { @@ -33,7 +33,7 @@ export abstract class BaseSyntaxElement { */ constructor() { let x: TerminalNode | ParserRuleContext; - + } } diff --git a/server/src/project/elements/flow.ts b/server/src/project/elements/flow.ts index 1228cc1..4d00190 100644 --- a/server/src/project/elements/flow.ts +++ b/server/src/project/elements/flow.ts @@ -9,7 +9,7 @@ import { DiagnosticCapability, FoldingRangeCapability } from '../../capabilities import { BaseRuleSyntaxElement, HasDiagnosticCapability } from './base'; import { MultipleOperatorsDiagnostic, WhileWendDeprecatedDiagnostic } from '../../capabilities/diagnostics'; -export class IfElseBlock extends BaseRuleSyntaxElement { +export class IfElseBlockElement extends BaseRuleSyntaxElement { constructor(context: IfStatementContext, document: TextDocument) { super(context, document); this.foldingRangeCapability = new FoldingRangeCapability(this); diff --git a/server/src/project/elements/generic.ts b/server/src/project/elements/generic.ts index 57e9b5d..3e66dab 100644 --- a/server/src/project/elements/generic.ts +++ b/server/src/project/elements/generic.ts @@ -2,10 +2,10 @@ import { TextDocument } from 'vscode-languageserver-textdocument'; // Antlr -import { ErrorNode, ParserRuleContext, TerminalNode } from 'antlr4ng'; +import { ErrorNode, TerminalNode } from 'antlr4ng'; // Project -import { BaseRuleSyntaxElement, BaseSyntaxElement, Context } from './base'; +import { BaseSyntaxElement, Context } from './base'; import { DiagnosticCapability } from '../../capabilities/capabilities'; import { ParserErrorDiagnostic } from '../../capabilities/diagnostics'; diff --git a/server/src/project/formatter.ts b/server/src/project/formatter.ts index 9e6b564..132f089 100644 --- a/server/src/project/formatter.ts +++ b/server/src/project/formatter.ts @@ -1,6 +1,8 @@ // Core import { Range, TextEdit } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; + +// Project import { VbaFmtListener } from './parser/vbaListener'; diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index 1a18f51..479e4a4 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -20,7 +20,6 @@ import { IfStatementContext, IgnoredClassAttrContext, IgnoredProceduralAttrContext, - IndexExpressionContext, LetStatementContext, LExpressionContext, MemberAccessExpressionContext, @@ -34,7 +33,6 @@ import { SetStatementContext, SimpleNameExpressionContext, SubroutineDeclarationContext, - TypeExpressionContext, TypeSuffixContext, UdtDeclarationContext, UnexpectedEndOfLineContext, @@ -66,19 +64,30 @@ import { } from '../../antlr/out/vbafmtParser'; // Project +import { Services } from '../../injection/services'; +import { ExtensionConfiguration } from '../workspace'; +import { ErrorRuleElement } from '../elements/generic'; import { CompilerLogicalBlock } from '../elements/precompiled'; import { UnexpectedEndOfLineElement } from '../elements/utils'; -import { DuplicateOperatorElement, IfElseBlock as IfStatementElement, WhileLoopElement } from '../elements/flow'; import { VbaClassDocument, VbaModuleDocument } from '../document'; +import { DuplicateOperatorElement, IfElseBlockElement, WhileLoopElement } from '../elements/flow'; import { ClassElement, ModuleElement, ModuleIgnoredAttributeElement } from '../elements/module'; -import { VariableDeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement, PositionalParamElement } from '../elements/typing'; -import { FunctionDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement, SubDeclarationElement } from '../elements/procedure'; -import { ExtensionConfiguration } from '../workspace'; -import { Services } from '../../injection/services'; -import { ErrorRuleElement } from '../elements/generic'; import { NameExpressionContext, NameExpressionElement, WithStatementElement } from '../elements/naming'; -import { FunctionOrArray } from '../elements/expression'; -import { AssignmentType } from '../../capabilities/capabilities'; +import { + TypeSuffixElement, + EnumDeclarationElement, + TypeDeclarationElement, + PositionalParamElement, + EnumMemberDeclarationElement, + VariableDeclarationStatementElement, +} from '../elements/typing'; +import { + SubDeclarationElement, + FunctionDeclarationElement, + PropertyGetDeclarationElement, + PropertyLetDeclarationElement, + PropertySetDeclarationElement, +} from '../elements/procedure'; enum ParserAssignmentState { @@ -125,7 +134,6 @@ export class VbaListener extends vbaListener { protected documentSettings?: ExtensionConfiguration; protected isAfterMethodDeclaration = false; private withStatementStack: WithStatementElement[] = []; - private functionOrArrayStack: FunctionOrArray[] = []; private parserStateStack: ParserState[] = []; private readonly verbose = false; @@ -189,7 +197,7 @@ export class VbaListener extends vbaListener { }; enterIfStatement = (ctx: IfStatementContext) => - this.document.registerElement(new IfStatementElement(ctx, this.document.textDocument)); + this.document.registerElement(new IfElseBlockElement(ctx, this.document.textDocument)); enterIgnoredClassAttr = (ctx: IgnoredClassAttrContext) => this.registerIgnoredAttribute(ctx); enterIgnoredProceduralAttr = (ctx: IgnoredProceduralAttrContext) => this.registerIgnoredAttribute(ctx); @@ -237,48 +245,6 @@ export class VbaListener extends vbaListener { enterArgumentList = (_: ArgumentListContext) => this.pushNewState(); exitArgumentList = (_: ArgumentListContext) => this.parserStateStack.pop(); - // enterLExpression = (ctx: LExpressionContext) => { - // // We're already dealing with an expression. - // if (this.parserState.nameElement) { - // return; - // } - - // // We have a function call or array. - // if (ctx.hasParenthesis()) { - // this.functionOrArrayStack.push(new FunctionOrArray(ctx, this.document.textDocument)); - // Services.logger.debug(`Push function or array (${this.functionOrArrayStack.length}): ${ctx.getText()}`); - // return; - // } - - // // All other cases, start an expression. - // Services.logger.debug(`Start expression: ${ctx.getText()}`); - // const element = new NameExpressionElement(ctx, this.document.textDocument); - // this.parserState.nameElement = element; - // const funcOrArray = this.functionOrArrayStack.at(-1); - // if (funcOrArray && !funcOrArray.nameExpressionElement) { - // funcOrArray.nameExpressionElement = element; - // } - - // // Handle assignment type - // if (this.parserState.assignment !== ParserAssignmentState.NONE) { - // if (this.parserState.assignment === ParserAssignmentState.LET) { - // element.setAsLetType(); - // } else { - // element.setAsSetType(); - // } - // this.parserState.assignment = ParserAssignmentState.NONE; - // } - // }; - - // exitLExpression = (ctx: LExpressionContext) => { - // if (this.functionOrArrayStack.at(-1)?.context.rule === ctx) { - // Services.logger.debug(`Pop function or array (${this.functionOrArrayStack.length}): ${ctx.getText()}`); - // this.functionOrArrayStack.pop(); - // return; - // } - // this.handleExitCallOrExpression(ctx); - // }; - enterLetStatement = (_: LetStatementContext) => { if (this.verbose) Services.logger.debug(`enterLetStatement`, this.parserStateStack.length); this.parserState.assignment = ParserAssignmentState.LET; @@ -289,16 +255,6 @@ export class VbaListener extends vbaListener { this.parserState.assignment = ParserAssignmentState.SET; }; - // enterCallStatement = (ctx: CallStatementContext) => { - // // We shouldn't be dealing with an expression. - // const state = this.parserState; - // if (state.nameElement) { - // Services.logger.error('Call statement in expression?'); - // } - // Services.logger.debug(`Start call expression: ${ctx.getText()}`); - // state.nameElement = new NameExpressionElement(ctx, this.document.textDocument); - // }; - enterCallStatement = (ctx: CallStatementContext) => { if (this.verbose) Services.logger.debug(`enterCallStatement: ${ctx.getText()}`, this.parserStateStack.length); this.parserState.inCallExp = true; @@ -349,17 +305,6 @@ export class VbaListener extends vbaListener { this.registerNameElement(); }; - enterIndexExpression = (ctx: IndexExpressionContext) => { - if (this.verbose) Services.logger.debug(`enterIndexExpression: ${ctx.getText()}`, this.parserStateStack.length); - this.pushNameElement(ctx); - }; - - exitIndexExpression = (ctx: IndexExpressionContext) => { - Services.logger.debug(`exitIndexExpression: ${ctx.getText()}`, this.parserStateStack.length); - if (this.parserState.inCallExp) this.parserState.assignment = ParserAssignmentState.CALL; - this.registerNameElement(); - }; - enterLExpression = (ctx: LExpressionContext) => { if (this.verbose) Services.logger.debug(`enterLExpression: ${ctx.getText()}`, this.parserStateStack.length); if (ctx.LPAREN()) { @@ -390,24 +335,8 @@ export class VbaListener extends vbaListener { if (this.verbose) Services.logger.debug(`enterPositionalParam: ${ctx.getText()}`, this.parserStateStack.length); const element = new PositionalParamElement(ctx, this.document.textDocument); this.document.registerElement(element); - // this.pushNameElement(ctx); - - // const identifierCtx = ctx.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() - // ?? ctx.paramDcl().typedNameParamDcl()?.typedName().ambiguousIdentifier(); - - // if (identifierCtx) { - // this.addNameElementContext(identifierCtx, 'ambigiousNameContext'); - // } }; - // enterTypeExpression = (ctx: TypeExpressionContext) => - // Services.logger.log(`TypeExpressionContext ${ctx.getText()} is primative: ${ctx.isPrimative}`); - - // exitPositionalParam = (ctx: PositionalParamContext) => { - // if (this.verbose) Services.logger.debug(`exitPositionalParam: ${ctx.getText()}`, this.parserStateStack.length); - // this.registerNameElement(); - // }; - enterOptionalParam = (ctx: OptionalParamContext) => { if (this.verbose) Services.logger.debug(`enterOptionalParam: ${ctx.getText()}`, this.parserStateStack.length); const identifierCtx = ctx.paramDcl().untypedNameParamDcl()?.ambiguousIdentifier() @@ -509,65 +438,6 @@ export class VbaListener extends vbaListener { } } - // exitCallStatement = (ctx: CallStatementContext) => { - // if (!this.parserState.nameElement) { - // Services.logger.error('No call expression to exit.'); - // return; - // } - // this.parserState.nameElement.setAsCallType(); - // this.handleExitCallOrExpression(ctx); - // }; - - // private handleExitCallOrExpression(ctx: LExpressionContext | CallStatementContext) { - // // This isn't the expression we're dealing with. - // const nameExpressionElement = this.parserState.nameElement; - // if (!nameExpressionElement) { - // return; - // } - - // if (nameExpressionElement.context.rule !== ctx) { - // return; - // } - - // if (!nameExpressionElement.hasNames) { - // Services.logger.debug(`End expression 0 names: ${ctx.getText()}`); - // this.parserState.nameElement = undefined; - // return; - // } - - // // Handle when this name is a With statement. - // const withStatement = this.withStatementStack.at(-1); - // if (withStatement && !withStatement.nameExpressionElement) { - // withStatement.nameExpressionElement = nameExpressionElement; - // if (this.isWithExpression) { - // // Attach to the parent when this also uses a With expression. - // const withStatementParent = this.withStatementStack.at(-2); - // if (!withStatementParent) { - // Services.logger.error(`Not enough ancestors.`); - // } else { - // nameExpressionElement.withStatementElement = withStatementParent; - // } - // this.isWithExpression = false; - // } - // this.parserState.nameElement = undefined; - // return; - // } - - // // Attach With statement if we're in a With expression. - // if (this.isWithExpression && withStatement) { - // nameExpressionElement.withStatementElement = withStatement; - // this.isWithExpression = false; - // } - - // Services.logger.debug(`Registering name: ${nameExpressionElement.fqName}`); - // this.document.registerElement(nameExpressionElement); - // this.parserState.nameElement = undefined; - - // if (this.isWithExpression) { - // Services.logger.warn('Still within withExpression after exitLExpression'); - // } - // } - enterWithStatement = (ctx: WithStatementContext) => { if (this.verbose) Services.logger.debug(`enterWithStatement: ${ctx.getText().split('\n')[0]}...`, this.parserStateStack.length); const element = new WithStatementElement(ctx, this.document.textDocument); diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 9a350fa..171c83b 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -1,4 +1,5 @@ // Core +import { inject, injectable } from 'tsyringe'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { CancellationToken, @@ -29,18 +30,20 @@ import { _Connection } from 'vscode-languageserver'; -import { BaseProjectDocument } from './document'; -import { hasWorkspaceConfigurationCapability } from '../capabilities/workspaceFolder'; -import { sleep, walk } from '../utils/helpers'; +// Antlr import { ParseCancellationException } from 'antlr4ng'; + +// Project +import { sleep, walk } from '../utils/helpers'; +import { Services } from '../injection/services'; import { getFormattingEdits } from './formatter'; +import { BaseProjectDocument } from './document'; +import { SyntaxParser } from './parser/vbaParser'; import { VbaFmtListener } from './parser/vbaListener'; -import { returnDefaultOnCancelClientRequest } from '../utils/wrappers'; -import { inject, injectable } from 'tsyringe'; +import { hasWorkspaceConfigurationCapability } from '../capabilities/workspaceFolder'; import { Logger, ILanguageServer, IWorkspace } from '../injection/interface'; -import { Services } from '../injection/services'; +import { returnDefaultOnCancelClientRequest } from '../utils/wrappers'; import { ScopeType, ScopeItemCapability } from '../capabilities/capabilities'; -import { SyntaxParser } from './parser/vbaParser'; export interface ExtensionConfiguration { maxDocumentLines: number; diff --git a/server/src/server.ts b/server/src/server.ts index 5ba89f3..c527289 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -78,19 +78,15 @@ export class LanguageServer implements ILanguageServer { export class LanguageServerConfiguration { capabilities: ServerCapabilities = { // Implemented - documentSymbolProvider: true, + codeActionProvider: true, + definitionProvider: true, foldingRangeProvider: true, + documentSymbolProvider: true, textDocumentSync: TextDocumentSyncKind.Incremental, - // diagnosticProvider: { - // interFileDependencies: false, - // workspaceDiagnostics: false - // }, // Implement soon. - codeActionProvider: true, - completionProvider: undefined, hoverProvider: false, - definitionProvider: true, + completionProvider: undefined, // Not implemented. signatureHelpProvider: undefined, diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index c031759..40e69da 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,3 +1,4 @@ +// Core import * as fs from 'fs'; import * as path from 'path'; import { Services } from '../injection/services'; diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index 5c8d0b7..f7d4311 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -1,7 +1,10 @@ +// Core import { inject, injectable } from 'tsyringe'; -import { Logger, ILanguageServer } from '../injection/interface'; import { _Connection } from 'vscode-languageserver'; +// Project +import { Logger, ILanguageServer } from '../injection/interface'; + enum LogLevel { error = 1, diff --git a/server/src/utils/wrappers.ts b/server/src/utils/wrappers.ts index 2c04a32..3476144 100644 --- a/server/src/utils/wrappers.ts +++ b/server/src/utils/wrappers.ts @@ -1,36 +1,11 @@ +// Core import { CancellationToken } from 'vscode-languageserver'; -import { LspLogger } from './logger'; -import { Logger } from '../injection/interface'; + +// Project import { Services } from '../injection/services'; -type signature = (token: CancellationToken, ...args: A) => Promise; type paramsSignature = (params: P, token: CancellationToken) => Promise; -/** - * A wrapper to give cancellation token handling to an async function. - * @param fn An async function that requires cancellation token handling. - * @param defaultValue The value to return when cancelled. - */ -function returnDefaultOnCancel(fn: signature, logger?: Logger, name?: string, defaultValue?: T, cancelError?: Error): signature { - return async (token: CancellationToken, ...args: A): Promise => { - if (token.isCancellationRequested) { - if (logger) logger.debug(`Cancellation requested before start for ${name ?? 'unknown'}. Returning default.`); - if (cancelError) throw cancelError; - return defaultValue; - } - - return new Promise((resolve) => { - const onCancel = () => { - if (logger) logger.debug(`Cancellation requested during processing for ${name ?? 'unknown'}. Returning default.`); - if (cancelError) throw cancelError; - resolve(defaultValue); - }; - token.onCancellationRequested(onCancel); - fn(token, ...args).then(resolve).catch(() => resolve(defaultValue)); - }); - }; -} - /** * A wrapper to give cancellation token handling to an async client request. * @param fn An async function that requires cancellation token handling. From 55ba7dc894b81b62d64820180b3ee50bc1031375 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 14:53:25 +0800 Subject: [PATCH 82/93] Normalised file URIs with double convert --- server/src/project/workspace.ts | 75 ++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 171c83b..34fc5d8 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -121,23 +121,24 @@ export class Workspace implements IWorkspace { // Handle each file in the workspace. for (const [uri, file] of workspaceFiles) { + const normalisedUri = uri.toFilePath().toFileUri(); // Don't parse files that we're already tracking. - if (this.projectDocuments.has(uri)) { - this.logger.debug(`Skipping file: ${uri}`, 1); + if (this.projectDocuments.has(normalisedUri)) { + this.logger.debug(`Skipping file: ${normalisedUri}`, 1); continue; } try { // Read and parse the project document. - this.logger.debug(`Reading file: ${uri}`, 1); - const textDocument = TextDocument.create(`${uri}`, 'vba', 1, file); + this.logger.debug(`Reading file: ${normalisedUri}`, 1); + const textDocument = TextDocument.create(`${normalisedUri}`, 'vba', 1, file); const projectDocument = BaseProjectDocument.create(textDocument); - this.projectDocuments.set(uri, projectDocument); + this.projectDocuments.set(normalisedUri, projectDocument); await parser.parse(token, projectDocument); this.logger.info(`Parsed ${projectDocument.name}`, 1); } catch (e) { // Log errors and anything else without failing. - this.logger.error(`Failed to parse ${uri}`, 0, e); + this.logger.error(`Failed to parse ${normalisedUri}`, 0, e); } } @@ -188,16 +189,20 @@ export class Workspace implements IWorkspace { } openDocument(document: TextDocument): void { - const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); - if (document.version === projectDocument?.version) { + const normalisedUri = document.uri.toFilePath().toFileUri(); + const projectDocument = this.projectDocuments.get(normalisedUri); + if (projectDocument) { projectDocument.open(); - this.parseDocument(projectDocument); + if (document.version > projectDocument?.version) { + this.parseDocument(projectDocument); + } this.connection.sendDiagnostics(projectDocument.languageServerDiagnostics()); } } closeDocument(document: TextDocument): void { - const projectDocument = this.projectDocuments.get(document.uri.toFilePath()); + const normalisedUri = document.uri.toFilePath().toFileUri(); + const projectDocument = this.projectDocuments.get(normalisedUri); if (!projectDocument) { Services.logger.warn(`Failed to get document to close: ${document.uri}`); return; @@ -496,13 +501,13 @@ class WorkspaceEvents { const workspaceEdit: { changes: { [uri: string]: TextEdit[] }; } = { changes: {} }; for (const renameItem of renameItems) { - const uri = renameItem.element?.context.document.uri; + const uri = renameItem.locationUri; if (!uri) { Services.logger.warn('Scope item has no element to rename'); continue; } - const range = renameItem.element.identifierCapability?.range; + const range = renameItem.element?.identifierCapability?.range; if (!range) { Services.logger.warn('Scope item has no identifier to rename'); continue; @@ -530,12 +535,10 @@ class WorkspaceEvents { * @param document The document being opened. */ onDidOpen(document: TextDocument) { - const logger = Services.logger; - logger.debug('[event] onDidOpen'); - logger.debug(`uri: ${document.uri.toFilePath()}`, 1); - logger.debug(`languageId: ${document.languageId}`, 1); - logger.debug(`version: ${document.version}`, 1); - if (this.projectDocuments.has(document.uri.toFilePath())) { + Services.logger.debug('[event] onDidOpen'); + this.printDocumentInformation(document); + const normalisedUri = document.uri.toFilePath().toFileUri(); + if (this.projectDocuments.has(normalisedUri)) { Services.workspace.openDocument(document); } } @@ -545,26 +548,22 @@ class WorkspaceEvents { * @param document The document that was changed. */ onDidChangeContent(document: TextDocument): void { - const logger = Services.logger; - logger.debug('[event] onDidChangeContent'); - logger.debug(`uri: ${document.uri.toFilePath()}`, 1); - logger.debug(`languageId: ${document.languageId}`, 1); - logger.debug(`version: ${document.version}`, 1); + Services.logger.debug('[event] onDidChangeContent'); + this.printDocumentInformation(document); // If the event is fired for the same version of the document, don't reparse. - const docPath = document.uri.toFilePath(); - const existingDocument = this.projectDocuments.get(docPath); + const normalisedUri = document.uri.toFilePath().toFileUri(); + const existingDocument = this.projectDocuments.get(normalisedUri); const existingVersion = existingDocument?.version ?? -1; - logger.debug(`existing: ${existingVersion}`, 1); + Services.logger.debug(`existing: ${existingVersion}`, 1); if (existingVersion >= document.version) { - logger.debug('Document already parsed.'); return; } // The document is new or a new version that we should parse. const projectDocument = BaseProjectDocument.create(document); - this.projectDocuments.set(docPath, projectDocument); - Services.projectScope.invalidate(docPath); + this.projectDocuments.set(normalisedUri, projectDocument); + Services.projectScope.invalidate(normalisedUri); Services.workspace.parseDocument(projectDocument); } @@ -573,13 +572,21 @@ class WorkspaceEvents { * @param document The document being closed. */ onDidClose(document: TextDocument) { + Services.logger.debug('[event] onDidClose'); + this.printDocumentInformation(document); + + const normalisedUri = document.uri.toFilePath().toFileUri(); + if (this.projectDocuments.has(normalisedUri)) { + Services.workspace.closeDocument(document); + } + } + + private printDocumentInformation(document: TextDocument) { const logger = Services.logger; - logger.debug('[event] onDidClose'); - logger.debug(`uri: ${document.uri}`, 1); + const normalisedUri = document.uri.toFilePath().toFileUri(); + logger.debug(`doc uri: ${document.uri}`, 1); + logger.debug(`norm uri: ${normalisedUri}`, 1); logger.debug(`languageId: ${document.languageId}`, 1); logger.debug(`version: ${document.version}`, 1); - if (this.projectDocuments.has(document.uri.toFilePath())) { - Services.workspace.closeDocument(document); - } } } From d0d4d4529a3e40d78e49fd517e5c4b26f3fbfdc2 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 17:31:20 +0800 Subject: [PATCH 83/93] Fixed bug in scope invalidation --- server/src/capabilities/capabilities.ts | 125 ++++++++++++++++-------- server/src/project/workspace.ts | 2 +- 2 files changed, 85 insertions(+), 42 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 71c1630..d04ff2a 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -284,21 +284,8 @@ export class ScopeItemCapability { * Recursively build from this node down. */ build(): void { - // Remove children that are invalidated. - this.maps.forEach(map => { - for (const [key, items] of map) { - const keep = items.filter(x => !x.isInvalidated); - if (keep.length === 0) map.delete(key); - else map.set(key, keep); - } - }); - - // Clean invalidated links. - if (this.link?.isInvalidated) this.link = undefined; - if (this.backlinks) { - const keep = this.backlinks.filter(x => !x.isInvalidated); - this.backlinks = keep.length > 0 ? keep : undefined; - } + this.deleteInvalidatedScopes(); + this.cleanInvalidatedLinks(); // Don't build self if invalidated. if (this.isInvalidated) { @@ -473,13 +460,6 @@ export class ScopeItemCapability { }); } - private resolveShadowedDeclaration(item: ScopeItemCapability | undefined): void { - if (item) { - const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic, this, this.name); - this.addDiagnosticReference(diagnostic, item); - } - } - private resolveShadowedDeclarations(ancestors: ScopeItemCapability[]) { // Don't check for shadowed declarations if we're above project level. if (ancestors.length < 3) { @@ -585,13 +565,58 @@ export class ScopeItemCapability { linkItem.backlinks.push(this); } - private removeBacklink(backlinkedItem: ScopeItemCapability): void { - if (!this.backlinks) { - return; + private deleteInvalidatedScopes() { + const removeInvalidatedScopes = (map: Map | undefined) => { + if (!map) return; + + const result = new Map(); + for (const [name, scopes] of map) { + const filteredScopes = scopes.filter(x => !x.isInvalidated); + if (filteredScopes.length > 0) { + result.set(name, filteredScopes); + } + } + if (result.size !== 0) { + return result; + } + }; + + this.types = removeInvalidatedScopes(this.types); + this.modules = removeInvalidatedScopes(this.modules); + this.functions = removeInvalidatedScopes(this.functions); + this.subroutines = removeInvalidatedScopes(this.subroutines); + if (this.properties) { + this.properties.getters = removeInvalidatedScopes(this.properties?.getters); + this.properties.setters = removeInvalidatedScopes(this.properties?.setters); + this.properties.letters = removeInvalidatedScopes(this.properties?.letters); } + this.parameters = removeInvalidatedScopes(this.parameters); + this.references = removeInvalidatedScopes(this.references); + this.implicitDeclarations = removeInvalidatedScopes(this.implicitDeclarations); + } - const keep = this.backlinks.filter(x => x !== backlinkedItem); - this.backlinks = keep.length === 0 ? undefined : keep; + private cleanInvalidatedLinks() { + const removeLinks = (map: Map | undefined) => { + map?.forEach((scopes) => scopes.forEach(scope => { + if (scope.link && scope.link.isInvalidated) { + scope.link = undefined; + } + if (scope.backlinks) { + scope.backlinks = scope.backlinks.filter(link => !link.isInvalidated); + if (scope.backlinks.length === 0) scope.backlinks = undefined; + } + })); + }; + + removeLinks(this.types); + removeLinks(this.modules); + removeLinks(this.functions); + removeLinks(this.subroutines); + removeLinks(this.properties?.getters); + removeLinks(this.properties?.setters); + removeLinks(this.properties?.letters); + removeLinks(this.parameters); + removeLinks(this.references); } /** Returns the module this scope item falls under */ @@ -838,13 +863,18 @@ export class ScopeItemCapability { return item; } - invalidate(uri: string): void { - if (this.type !== ScopeType.PROJECT) { - this.isInvalidated = true; - } - this.maps.forEach(map => map.forEach(items => - items.forEach(item => item.invalidate(uri)) - )); + invalidateModule(uri: string): void { + const module = this.findModuleByUri(uri); + module?.invalidate(); + } + + invalidate(): void { + this.isInvalidated = true; + this.maps.forEach( + map => map.forEach( + scopes => scopes.forEach( + scope => scope.invalidate() + ))); } /** Returns true for public and false for private */ @@ -915,20 +945,22 @@ export class ScopeItemCapability { const itemsAtPosition: ScopeItemCapability[] = []; this.getItemsIdentifiedAtPosition(position, itemsAtPosition, [module]); - if (itemsAtPosition.length === 0) { + const uniqueItemsAtPosition = this.removeDuplicatesByRange(itemsAtPosition); + if (uniqueItemsAtPosition.length === 0) { Services.logger.warn(`Nothing to rename.`); return []; } - if (itemsAtPosition.length > 1) { - Services.logger.warn(`Ambiguity detected: ${itemsAtPosition.length} overlapping names.`); + if (uniqueItemsAtPosition.length > 1) { + Services.logger.warn(`Ambiguity detected: ${uniqueItemsAtPosition.length} overlapping names.`); return []; } - const declarationItem = itemsAtPosition[0].link ? itemsAtPosition[0].link : itemsAtPosition[0]; - const result = [declarationItem, ...declarationItem.backlinks ?? []]; - - return result; + // Use linked declaration to return all items, otherwise just return the item we have. + const declarationItem = uniqueItemsAtPosition[0].link + ? uniqueItemsAtPosition[0].link + : uniqueItemsAtPosition[0]; + return [declarationItem, ...declarationItem.backlinks ?? []]; } getDeclarationLocation(uri: string, position: Position): LocationLink[] | undefined { @@ -1018,4 +1050,15 @@ export class ScopeItemCapability { }); }); } + + private removeDuplicatesByRange(results: ScopeItemCapability[]): ScopeItemCapability[] { + const rangeString = (r: Range | undefined): string => { + if (!r) return 'null'; + return `${r.start.line}.${r.start.character}:${r.end.line}.${r.end.character}`; + }; + + const m = new Map(); + results.forEach(s => m.set(rangeString(s.element?.context.range), s)); + return Array.from(m.values()); + } } \ No newline at end of file diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 34fc5d8..abbf4d7 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -563,7 +563,7 @@ class WorkspaceEvents { // The document is new or a new version that we should parse. const projectDocument = BaseProjectDocument.create(document); this.projectDocuments.set(normalisedUri, projectDocument); - Services.projectScope.invalidate(normalisedUri); + Services.projectScope.invalidateModule(normalisedUri); Services.workspace.parseDocument(projectDocument); } From bc736505abeaf75dbbdc188a9215f705e2944e72 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 18:05:19 +0800 Subject: [PATCH 84/93] renameItems now gets all properties of the same name --- server/src/capabilities/capabilities.ts | 35 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index d04ff2a..aae4666 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -945,22 +945,33 @@ export class ScopeItemCapability { const itemsAtPosition: ScopeItemCapability[] = []; this.getItemsIdentifiedAtPosition(position, itemsAtPosition, [module]); - const uniqueItemsAtPosition = this.removeDuplicatesByRange(itemsAtPosition); - if (uniqueItemsAtPosition.length === 0) { + if (itemsAtPosition.length === 0) { Services.logger.warn(`Nothing to rename.`); return []; } - if (uniqueItemsAtPosition.length > 1) { - Services.logger.warn(`Ambiguity detected: ${uniqueItemsAtPosition.length} overlapping names.`); - return []; - } - - // Use linked declaration to return all items, otherwise just return the item we have. - const declarationItem = uniqueItemsAtPosition[0].link - ? uniqueItemsAtPosition[0].link - : uniqueItemsAtPosition[0]; - return [declarationItem, ...declarationItem.backlinks ?? []]; + // Switch to the linked declaration if we have one. + const swapRefsForDeclarations = itemsAtPosition.map( + item => item.link ? item.link : item); + + // Replace property items with all properties of same name. + const propertyIncludedItems = swapRefsForDeclarations.map(item => + item.type === ScopeType.PROPERTY && item.parent?.properties + ? [ + item.parent.properties.getters?.get(item.identifier), + item.parent.properties.setters?.get(item.identifier), + item.parent.properties.letters?.get(item.identifier) + ] + : item + ).flat().flat().flat().filter(x => !!x); + + // Add backlinks for each item. + const addedBacklinks = propertyIncludedItems.map(item => + item.backlinks ? [item, ...item.backlinks] : item + ).flat().flat(); + + const uniqueItemsAtPosition = this.removeDuplicatesByRange(addedBacklinks); + return uniqueItemsAtPosition; } getDeclarationLocation(uri: string, position: Position): LocationLink[] | undefined { From e1815d994106111e55725469201a23f0258ef963 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 20:01:26 +0800 Subject: [PATCH 85/93] Update readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 06efa67..535b248 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,23 @@ Provides Visual Basic for Applications (VBA) language support in Visual Studio C 1Currently full document `Shift+Alt+F` formatting only. +### *PREVIEW* Definition Provider + +This extension now supports definitions, i.e., following a name to a declaration. Pressing F12 will jump you to the relevant method or variable declaration. + +This is a preview feature and may not work 100% as expected. If bugs are encountered, please take the time to raise an issue on the repo. + +### *PREVIEW* Code Refactoring + +This extension now supports Rename requests, i.e., using F2 to rename an element. It will find the all instances of the name throughout the workspace and rename it. + +### Preview Limitations + +Known limitations: + - Method attributes (yet) aren't renamed when you rename a function or sub. + - Public methods still producing diagnostics when there's a similarly named method in another module. + - Can't yet rename classes with this functionality. + ### Web Support The VBA Pro Extension offers limited support for web environments (e.g. vscode-dev). @@ -75,6 +92,7 @@ End Property ## Coming Soon * Hovers +* Completion ## Installation From ce07076fecf7c8df1d013c8e4ffacf06628ab831 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 20:02:25 +0800 Subject: [PATCH 86/93] 1.6.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0feb940..a13e672 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.5.13", + "version": "1.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.5.13", + "version": "1.6.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 61c5b25..f9c6955 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "icon": "images/vba-lsp-icon.png", "author": "SSlinky", "license": "MIT", - "version": "1.5.13", + "version": "1.6.0", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From f8f0024033c48537b035c351c221cfb064448cd8 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 20:03:51 +0800 Subject: [PATCH 87/93] 1.7.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a13e672..55fe20a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.6.0", + "version": "1.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.6.0", + "version": "1.7.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f9c6955..517551a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "icon": "images/vba-lsp-icon.png", "author": "SSlinky", "license": "MIT", - "version": "1.6.0", + "version": "1.7.0", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From 0dc406830dcf96c4b6e28dd68b7a866640bdf897 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 22:24:10 +0800 Subject: [PATCH 88/93] Fixed test expected outcome --- client/src/test/diagnostics.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts index e72603c..26ed2e1 100644 --- a/client/src/test/diagnostics.test.ts +++ b/client/src/test/diagnostics.test.ts @@ -25,7 +25,7 @@ suite('Should get module diagnostics', () => { { message: 'Option Explicit is missing from module header.', range: toRange(2, 1, 2, 1), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Hint, source: 'ex' } ]); @@ -97,7 +97,7 @@ suite('Should get class diagnostics', () => { { message: 'Option Explicit is missing from module header.', range: toRange(11, 1, 11, 1), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Hint, source: 'ex' } ]); From d07b8b40aa567b1a141808b4b35b5a5ead0b047c Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 22:58:34 +0800 Subject: [PATCH 89/93] Fixed diagnostics and tests --- client/src/test/diagnostics.test.ts | 85 ++++++++++++++++++++++--- client/src/test/helper.ts | 6 +- server/src/capabilities/capabilities.ts | 5 +- server/src/capabilities/diagnostics.ts | 11 ++-- server/src/project/document.ts | 6 +- server/src/project/elements/module.ts | 4 +- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts index 26ed2e1..203cddb 100644 --- a/client/src/test/diagnostics.test.ts +++ b/client/src/test/diagnostics.test.ts @@ -8,6 +8,11 @@ import * as assert from 'assert'; import { getDocUri, activate, runOnActivate } from './helper'; import { toRange } from './util'; +enum DiagnosticTag { + Unnecessary = 1, + Deprecated = 2 +} + suite('Should get module diagnostics', () => { test('diagnostics.module.missingNameAttributeError', async () => { await testDiagnostics(getDocUri('DiagnosticsMissingAttributeModule.bas'), [ @@ -41,15 +46,15 @@ suite('Should get module diagnostics', () => { source: 'ex' }, { - message: 'Unknown attribute \'VB_Creatable\' will be ignored.', + message: 'Unknown attribute \'VB_Creatable\'.', range: toRange(3, 0, 3, 30), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Error, source: 'ex' }, { - message: 'Unknown attribute \'VB_Foo\' will be ignored.', + message: 'Unknown attribute \'VB_Foo\'.', range: toRange(4, 0, 4, 24), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Error, source: 'ex' }, { @@ -70,6 +75,27 @@ suite('Should get module diagnostics', () => { severity: vscode.DiagnosticSeverity.Error, source: 'ex' }, + { + message: 'Enum1 is declared but its value is never read.', + range: toRange(29, 4, 29, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, + { + message: 'Enum2 is declared but its value is never read.', + range: toRange(30, 4, 30, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, + { + message: 'Enum3 is declared but its value is never read.', + range: toRange(31, 4, 31, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, { message: 'Invalid syntax: InvalidSubCall(arg)', range: toRange(43, 4, 43, 23), @@ -119,15 +145,22 @@ suite('Should get class diagnostics', () => { source: 'ex' }, { - message: 'Unknown attribute \'VB_Exxposed\' will be ignored.', + message: 'Unknown attribute \'VB_Exxposed\'.', range: toRange(12, 0, 12, 29), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Error, source: 'ex' }, { - message: 'Unknown attribute \'VB_Exxposed\' will be ignored.', + message: 'Unknown attribute \'VB_Exxposed\'.', range: toRange(13, 0, 13, 29), - severity: vscode.DiagnosticSeverity.Warning, + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'SmkfeiondFoo is declared but its value is never read.', + range: toRange(17, 11, 17, 23), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], source: 'ex' }, { @@ -142,11 +175,46 @@ suite('Should get class diagnostics', () => { severity: vscode.DiagnosticSeverity.Error, source: 'ex' }, + { + message: 'GkiofseiFoo is declared but its value is never read.', + range: toRange(25, 4, 25, 15), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, { message: 'Enum declarations cannot appear below a Sub, Function, or Property declaration.', range: toRange(37, 7, 41, 8), severity: vscode.DiagnosticSeverity.Error, source: 'ex' + }, + { + message: 'PewmfoiawFoo is declared but its value is never read.', + range: toRange(37, 12, 37, 24), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, + { + message: 'Enum1 is declared but its value is never read.', + range: toRange(38, 4, 38, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, + { + message: 'Enum2 is declared but its value is never read.', + range: toRange(39, 4, 39, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' + }, + { + message: 'Enum3 is declared but its value is never read.', + range: toRange(40, 4, 40, 9), + severity: vscode.DiagnosticSeverity.Hint, + tags: [DiagnosticTag.Unnecessary], + source: 'ex' } ]); }); @@ -167,5 +235,6 @@ async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.D assert.equal(actualDiagnostic.message, expectedDiagnostic.message, `Message: expected '${expectedDiagnostic.message}' got '${actualDiagnostic.message}'.`); assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range, `Range: expected '${JSON.stringify(expectedDiagnostic.range)}' got '${JSON.stringify(actualDiagnostic.range)}'.`); assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity, `Severity: expected '${expectedDiagnostic.severity}' got '${actualDiagnostic.severity}'.`); + assert.deepEqual(actualDiagnostic.tags, expectedDiagnostic.tags, `Tags: expected '${JSON.stringify(expectedDiagnostic.tags)}' got '${JSON.stringify(actualDiagnostic.tags)}'.`); }); } \ No newline at end of file diff --git a/client/src/test/helper.ts b/client/src/test/helper.ts index 8168979..7fca652 100644 --- a/client/src/test/helper.ts +++ b/client/src/test/helper.ts @@ -10,7 +10,7 @@ export let doc: vscode.TextDocument; export let editor: vscode.TextEditor; export let documentEol: string; export let platformEol: string; -const TIMEOUTMS = 5000; +const TIMEOUTMS = 10000; /** * Activates the vscode.lsp-sample extension @@ -35,10 +35,10 @@ async function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } -export async function runOnActivate(action: () => T|Thenable, test: (result: T) => boolean): Promise { +export async function runOnActivate(action: () => T | Thenable, test: (result: T) => boolean): Promise { const timeout = getTimeout(); while (Date.now() < timeout) { - const result = await action(); + const result = await action(); if (test(result)) { return result; } diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index aae4666..dea7f41 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -117,7 +117,8 @@ export class SymbolInformationCapability extends BaseCapability { export class IdentifierCapability extends BaseCapability { private get unformattedName(): string { - return this.nameContext?.getText() ?? this.defaultName ?? "Unknown Element"; + const nameCtx = this.getNameContext ? this.getNameContext() : this.nameContext; + return nameCtx?.getText() ?? this.defaultName ?? "Unknown Element"; } get name(): string { @@ -349,7 +350,7 @@ export class ScopeItemCapability { const identifier = this.element?.identifierCapability; const diagnostics = this.element?.diagnosticCapability?.diagnostics; if (identifier && diagnostics) { - diagnostics.push(new UnusedDiagnostic(identifier.range)); + diagnostics.push(new UnusedDiagnostic(identifier.range, identifier.name)); } } diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 4bb70a2..309a70c 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -141,10 +141,9 @@ export class MethodVariableIsPublicDiagnostic extends BaseDiagnostic { // test export class UnexpectedLineEndingDiagnostic extends BaseDiagnostic { - message = "Unexpected line ending."; severity = DiagnosticSeverity.Error; constructor(range: Range) { - super(range); + super(range, 'Unexpected line ending.'); } } @@ -153,7 +152,7 @@ export class UnreachableCodeDiagnostic extends BaseDiagnostic { severity = DiagnosticSeverity.Hint; tags = [DiagnosticTag.Unnecessary]; constructor(range: Range) { - super(range); + super(range, 'Unreachable code detected.'); } } @@ -161,8 +160,8 @@ export class UnreachableCodeDiagnostic extends BaseDiagnostic { export class UnusedDiagnostic extends BaseDiagnostic { severity = DiagnosticSeverity.Hint; tags = [DiagnosticTag.Unnecessary]; - constructor(range: Range) { - super(range); + constructor(range: Range, message: string) { + super(range, `${message} is declared but its value is never read.`); } } @@ -170,7 +169,7 @@ export class UnusedDiagnostic extends BaseDiagnostic { export class UnknownAttributeDiagnostic extends BaseDiagnostic { severity = DiagnosticSeverity.Error; constructor(range: Range, attributeName: string) { - super(range, `Unknown attribute '${attributeName}' will be ignored.`); + super(range, `Unknown attribute '${attributeName}'.`); } } diff --git a/server/src/project/document.ts b/server/src/project/document.ts index b59520d..fb6a941 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -134,10 +134,14 @@ export abstract class BaseProjectDocument { // }; // } languageServerDiagnostics() { + const diagnostics = this.isClosed ? [] : this.diagnostics; + Services.logger.debug(`Sending diagnostics for ${this.textDocument.uri}`); + Services.logger.debug(JSON.stringify(diagnostics)); + return { uri: this.textDocument.uri, version: this.version, - diagnostics: this.isClosed ? [] : this.diagnostics + diagnostics: diagnostics }; } diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index f3b819b..6277b78 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -3,7 +3,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument'; import { Diagnostic, Range, SymbolKind } from 'vscode-languageserver'; // Antlr -import { ParserRuleContext } from 'antlr4ng'; +import { ParserRuleContext, TerminalNode } from 'antlr4ng'; import { ClassModuleCodeElementContext, ClassModuleContext, @@ -167,7 +167,7 @@ export class ClassElement extends BaseModuleElement { .classModuleCode() .classModuleCodeElement()); - let getIdentifierNameContext; + let getIdentifierNameContext = (): TerminalNode | undefined => undefined; if (ctx.classModuleHeader().nameAttr().length > 0) { getIdentifierNameContext = () => ctx .classModuleHeader() From b6585af47ba34b8c4950c3923fe124a4162d25c0 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 23:13:47 +0800 Subject: [PATCH 90/93] Squash folding ranges cancellation bug --- server/src/project/workspace.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index abbf4d7..d869239 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -302,9 +302,6 @@ class WorkspaceEvents { const cancellableOnDocSymbol = returnDefaultOnCancelClientRequest( (p: DocumentSymbolParams, t) => this.onDocumentSymbolAsync(p, t), [], 'Document Symbols'); - const cancellableOnFoldingRanges = returnDefaultOnCancelClientRequest( - (p: FoldingRangeParams, t) => this.onFoldingRangesAsync(p, t), [], 'Folding Range'); - connection.onCodeAction(async (params, token) => this.onCodeActionRequest(params, token)); connection.onCompletion(params => this.onCompletion(params)); connection.onCompletionResolve(item => this.onCompletionResolve(item)); @@ -319,7 +316,7 @@ class WorkspaceEvents { connection.onRenameRequest((params, token) => this.onRenameRequest(params, token)); if (hasWorkspaceConfigurationCapability(Services.server)) { - connection.onFoldingRanges(async (params, token) => await cancellableOnFoldingRanges(params, token)); + connection.onFoldingRanges(async (params, token) => await this.onFoldingRangesAsync(params, token)); } connection.onRequest((method: string, params: object | object[] | any) => { @@ -395,18 +392,33 @@ class WorkspaceEvents { return document?.languageServerSymbolInformation() ?? []; } - private async onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { + private async onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { const logger = Services.logger; logger.debug('[Event] onFoldingRanges'); + + // Don't do any work if we don't have to. + if (token.isCancellationRequested) { + logger.debug('Cancellation requested before start for Folding Ranges.'); + return; + } + let document: BaseProjectDocument | undefined; try { - document = await this.getParsedProjectDocument(params.textDocument.uri, 0, token); + const normalisedUri = params.textDocument.uri.toFilePath().toFileUri(); + document = await this.getParsedProjectDocument(normalisedUri, 0, token); } catch (error) { // Swallow parser cancellations and rethrow anything else. if (error instanceof ParseCancellationException) { throw error; } } + + // Check again if we're cancelled. + if (token.isCancellationRequested) { + logger.debug('Cancellation requested before start for Folding Ranges.'); + return; + } + const result = document?.languageServerFoldingRanges(); for (const foldingRange of result ?? []) { logger.debug(`${JSON.stringify(foldingRange.range)} '${foldingRange.openWord}..${foldingRange.closeWord}'`, 1); From ac7726f3a1afda033561c369008f24bc9fb08b82 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 23:22:19 +0800 Subject: [PATCH 91/93] Bug fixed document formatting --- server/src/project/workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index d869239..45fe2fe 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -172,7 +172,7 @@ export class Workspace implements IWorkspace { // Exceptions thrown by the parser should be ignored. let result: VbaFmtListener | undefined; try { - const projectDocument = this.projectDocuments.get(document.uri); + const projectDocument = this.projectDocuments.get(document.uri.toFilePath().toFileUri()); result = await projectDocument?.formatParse(token); this.logger.info(`Formatted ${document.uri}`); } From 2515178cf5430bdbd362d41ad3272d2087ef646c Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 23:22:44 +0800 Subject: [PATCH 92/93] 1.7.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55fe20a..5956dba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.7.0", + "version": "1.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.7.0", + "version": "1.7.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 517551a..1532a92 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "icon": "images/vba-lsp-icon.png", "author": "SSlinky", "license": "MIT", - "version": "1.7.0", + "version": "1.7.1", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From 0a53f0fdfffa3a9810c7f9d8eec69d7d49774167 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Sun, 25 May 2025 23:27:17 +0800 Subject: [PATCH 93/93] Update node packages --- client/package-lock.json | 279 ++++++-------- client/package.json | 4 +- package-lock.json | 780 +++++++++++++++++++-------------------- package.json | 14 +- 4 files changed, 488 insertions(+), 589 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 6691ff1..9d8b06d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,31 +12,31 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@types/vscode": "^1.99.0", - "@vscode/test-electron": "^2.4.1" + "@types/vscode": "^1.100.0", + "@vscode/test-electron": "^2.5.2" }, "engines": { "vscode": "^1.63.0" } }, "node_modules/@types/vscode": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.0.tgz", - "integrity": "sha512-30sjmas1hQ0gVbX68LAWlm/YYlEqUErunPJJKLpEl+xhK0mKn+jyzlCOpsdTwfkZfPy4U6CDkmygBLC3AB8W9Q==", + "version": "1.100.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.100.0.tgz", + "integrity": "sha512-4uNyvzHoraXEeCamR3+fzcBlh7Afs4Ifjs4epINyUX/jvdk0uzLnwiDY35UKDKnkCHP5Nu3dljl2H8lR6s+rQw==", "dev": true, "license": "MIT" }, "node_modules/@vscode/test-electron": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", - "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", "dev": true, "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "jszip": "^3.10.1", - "ora": "^7.0.1", + "ora": "^8.1.0", "semver": "^7.6.2" }, "engines": { @@ -72,54 +72,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -129,31 +81,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -168,16 +95,16 @@ } }, "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -204,9 +131,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -221,13 +148,6 @@ } } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -235,6 +155,19 @@ "dev": true, "license": "MIT" }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -263,27 +196,6 @@ "node": ">= 14" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -312,13 +224,13 @@ } }, "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -355,15 +267,28 @@ } }, "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -371,14 +296,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -401,40 +329,40 @@ "license": "MIT" }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -471,17 +399,17 @@ } }, "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -495,9 +423,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -514,23 +442,26 @@ "license": "MIT" }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, "license": "MIT", - "dependencies": { - "bl": "^5.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -547,18 +478,18 @@ } }, "node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/client/package.json b/client/package.json index 6d4c7c4..58575bb 100644 --- a/client/package.json +++ b/client/package.json @@ -16,7 +16,7 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@types/vscode": "^1.99.0", - "@vscode/test-electron": "^2.4.1" + "@types/vscode": "^1.100.0", + "@vscode/test-electron": "^2.5.2" } } diff --git a/package-lock.json b/package-lock.json index 5956dba..4cc0006 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,21 +12,21 @@ "dependencies": { "antlr4ng": "^3.0.16", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.9.1", - "typescript-eslint": "^8.29.1" + "tsyringe": "^4.10.0", + "typescript-eslint": "^8.32.1" }, "devDependencies": { "@types/mocha": "^10.0.10", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", "@vscode/test-cli": "^0.0.10", - "@vscode/test-electron": "^2.4.1", + "@vscode/test-electron": "^2.5.2", "antlr4ng-cli": "^2.0.0", - "esbuild": "^0.25.2", - "eslint": "^9.24.0", + "esbuild": "^0.25.4", + "eslint": "^9.27.0", "js-yaml": "^4.1.0", - "mocha": "^11.1.0", + "mocha": "^11.5.0", "npm-run-all": "^4.1.5", "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3" @@ -43,9 +43,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -60,9 +60,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -77,9 +77,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -94,9 +94,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -111,9 +111,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -128,9 +128,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -145,9 +145,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -162,9 +162,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -179,9 +179,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -196,9 +196,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -213,9 +213,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -230,9 +230,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -247,9 +247,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -264,9 +264,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -281,9 +281,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -298,9 +298,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -315,9 +315,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -332,9 +332,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -349,9 +349,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -366,9 +366,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -383,9 +383,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -400,9 +400,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -417,9 +417,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -434,9 +434,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -451,9 +451,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -468,9 +468,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -531,18 +531,18 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -584,6 +584,15 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -597,12 +606,15 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -615,30 +627,18 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -688,9 +688,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -829,9 +829,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -839,20 +839,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -868,15 +868,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -892,13 +892,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -909,15 +909,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -932,9 +932,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -945,19 +945,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -971,15 +971,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -994,12 +994,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1068,6 +1068,16 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/@vscode/test-cli/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/@vscode/test-cli/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1237,16 +1247,16 @@ } }, "node_modules/@vscode/test-electron": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", - "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", "dev": true, "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "jszip": "^3.10.1", - "ora": "^7.0.1", + "ora": "^8.1.0", "semver": "^7.6.2" }, "engines": { @@ -1446,27 +1456,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1480,33 +1469,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", @@ -1542,31 +1504,6 @@ "dev": true, "license": "ISC" }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/c8": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", @@ -1719,16 +1656,16 @@ } }, "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1942,9 +1879,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2014,9 +1951,9 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2077,9 +2014,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.23.10", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz", + "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==", "dev": true, "license": "MIT", "dependencies": { @@ -2087,18 +2024,18 @@ "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -2114,13 +2051,13 @@ "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", @@ -2133,7 +2070,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -2210,9 +2147,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2223,31 +2160,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/escalade": { @@ -2273,19 +2210,19 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2394,6 +2331,15 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2695,6 +2641,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2985,31 +2944,10 @@ "node": ">= 14" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", "license": "MIT", "engines": { "node": ">= 4" @@ -3803,14 +3741,17 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -3839,25 +3780,25 @@ } }, "node_modules/mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.5.0.tgz", + "integrity": "sha512-VKDjhy6LMTKm0WgNEdlY77YVsD49LZnPSXJAaPNL9NRYQADxvORsyG1DIQY6v53BKTnlNbEE2MbVCDbnxr4K3w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", @@ -3874,17 +3815,34 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/mocha/node_modules/supports-color": { @@ -4211,16 +4169,16 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4244,24 +4202,24 @@ } }, "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4288,28 +4246,41 @@ "license": "MIT" }, "node_modules/ora/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ora/node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4318,18 +4289,18 @@ } }, "node_modules/ora/node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4488,6 +4459,13 @@ "node": ">=4" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -4723,29 +4701,22 @@ } }, "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -4856,9 +4827,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5093,16 +5064,13 @@ "license": "CC0-1.0" }, "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, "license": "MIT", - "dependencies": { - "bl": "^5.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5349,9 +5317,9 @@ } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { @@ -5450,9 +5418,9 @@ "license": "0BSD" }, "node_modules/tsyringe": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.9.1.tgz", - "integrity": "sha512-dJCWk0RolAnGk0j839M0lcuS/PtNUPaMsnBosn+wg5N16xy0tofcVuvsidMs0JuRbaJ0wVIT7RsuHWbVIZ5Rcg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", "license": "MIT", "dependencies": { "tslib": "^1.9.3" @@ -5565,14 +5533,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", - "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@typescript-eslint/utils": "8.29.1" + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 1532a92..3d929c6 100644 --- a/package.json +++ b/package.json @@ -170,21 +170,21 @@ "dependencies": { "antlr4ng": "^3.0.16", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.9.1", - "typescript-eslint": "^8.29.1" + "tsyringe": "^4.10.0", + "typescript-eslint": "^8.32.1" }, "devDependencies": { "@types/mocha": "^10.0.10", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", "@vscode/test-cli": "^0.0.10", - "@vscode/test-electron": "^2.4.1", + "@vscode/test-electron": "^2.5.2", "antlr4ng-cli": "^2.0.0", - "esbuild": "^0.25.2", - "eslint": "^9.24.0", + "esbuild": "^0.25.4", + "eslint": "^9.27.0", "js-yaml": "^4.1.0", - "mocha": "^11.1.0", + "mocha": "^11.5.0", "npm-run-all": "^4.1.5", "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3"