├── .gitignore ├── .npmignore ├── Readme.md ├── esbuild.js ├── history.md ├── package.json ├── schemas └── package.schema.json ├── server ├── cssServer.ts ├── customData.ts ├── languageModelCache.ts ├── node │ ├── cssServerMain.ts │ └── nodeFs.ts ├── requests.ts └── utils │ ├── documentContext.ts │ ├── runner.ts │ ├── strings.ts │ └── validation.ts ├── src ├── customData.ts ├── index.ts ├── nodeFs.ts └── requests.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | tsconfig.json 3 | tslint.json 4 | *.map 5 | server 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # coc-css 2 | 3 | Css language server extension for [coc.nvim](https://github.com/neoclide/coc.nvim). 4 | 5 | Uses [vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) inside. 6 | 7 | **Note**: words are exetracted by `iskeyword` option of buffer, you may want to 8 | adjust it by command like: 9 | 10 | autocmd FileType css setl iskeyword+=- 11 | 12 | **Note** configuration `css.enable` and wxss support removed from 2.0.0. 13 | 14 | ## Install 15 | 16 | In your vim/neovim, run the command: 17 | 18 | ``` 19 | :CocInstall coc-css 20 | ``` 21 | 22 | For scss files, you may need use: 23 | 24 | ```vim 25 | autocmd FileType scss setl iskeyword+=@-@ 26 | ``` 27 | 28 | in your vimrc for add `@` to iskeyword option. 29 | 30 | ## Features 31 | 32 | All features that [vscode-css-languageservice](https://www.npmjs.com/package/vscode-css-languageservice) provide should work. 33 | 34 | - `doValidation` analyzes an input string and returns syntax and lint errros. 35 | - `doComplete` provides completion proposals for a given location. 36 | - `doHover` provides a hover text for a given location. 37 | - `findDefinition` finds the definition of the symbol at the given location. 38 | - `findReferences` finds all references to the symbol at the given location. 39 | - `findDocumentHighlights` finds all symbols connected to the given location. 40 | - `findDocumentSymbols` provides all symbols in the given document 41 | - `doCodeActions` evaluates code actions for the given location, typically to fix a problem. 42 | - `findColorSymbols` evaluates all color symbols in the given document 43 | - `doRename` renames all symbols connected to the given location. 44 | - `getFoldingRanges` returns folding ranges in the given document. 45 | - `format` format css/scss/less files. 46 | 47 | ## Configuration options 48 | 49 | Checkout `:h coc-configuration` in your vim for guide of coc.nvim's configuration. 50 | 51 | - `css.execArgv`: Extra arguments for node which start language server. default: `[]` 52 | - `css.customData`: default: `[]` 53 | - `css.completion.triggerPropertyValueCompletion`: By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. default: `true` 54 | - `css.completion.completePropertyWithSemicolon`: Insert semicolon at end of line when completing CSS properties. default: `true` 55 | - `css.validate`: Enables or disables all validations. default: `true` 56 | - `css.hover.documentation`: Show tag and attribute documentation in CSS hovers. default: `true` 57 | - `css.hover.references`: Show references to MDN in CSS hovers. default: `true` 58 | - `css.lint.compatibleVendorPrefixes`: When using a vendor-specific prefix make sure to also include all other vendor-specific properties. default: `"ignore"` 59 | Valid options: ["ignore","warning","error"] 60 | - `css.lint.vendorPrefix`: When using a vendor-specific prefix, also include the standard property. default: `"warning"` 61 | Valid options: ["ignore","warning","error"] 62 | - `css.lint.duplicateProperties`: Do not use duplicate style definitions. default: `"ignore"` 63 | Valid options: ["ignore","warning","error"] 64 | - `css.lint.emptyRules`: Do not use empty rulesets. default: `"warning"` 65 | Valid options: ["ignore","warning","error"] 66 | - `css.lint.importStatement`: Import statements do not load in parallel. default: `"ignore"` 67 | Valid options: ["ignore","warning","error"] 68 | - `css.lint.boxModel`: default: `"ignore"` 69 | Valid options: ["ignore","warning","error"] 70 | - `css.lint.universalSelector`: default: `"ignore"` 71 | Valid options: ["ignore","warning","error"] 72 | - `css.lint.zeroUnits`: No unit for zero needed. default: `"ignore"` 73 | Valid options: ["ignore","warning","error"] 74 | - `css.lint.fontFaceProperties`: default: `"warning"` 75 | Valid options: ["ignore","warning","error"] 76 | - `css.lint.hexColorLength`: Hex colors must consist of three or six hex numbers. default: `"error"` 77 | Valid options: ["ignore","warning","error"] 78 | - `css.lint.argumentsInColorFunction`: Invalid number of parameters. default: `"error"` 79 | Valid options: ["ignore","warning","error"] 80 | - `css.lint.unknownProperties`: Unknown property. default: `"warning"` 81 | Valid options: ["ignore","warning","error"] 82 | - `css.lint.validProperties`: A list of properties that are not validated against the `unknownProperties` rule. default: `[]` 83 | - `css.lint.ieHack`: IE hacks are only necessary when supporting IE7 and older. default: `"ignore"` 84 | Valid options: ["ignore","warning","error"] 85 | - `css.lint.unknownVendorSpecificProperties`: Unknown vendor specific property. default: `"ignore"` 86 | Valid options: ["ignore","warning","error"] 87 | - `css.lint.propertyIgnoredDueToDisplay`: default: `"warning"` 88 | Valid options: ["ignore","warning","error"] 89 | - `css.lint.important`: default: `"ignore"` 90 | Valid options: ["ignore","warning","error"] 91 | - `css.lint.float`: default: `"ignore"` 92 | Valid options: ["ignore","warning","error"] 93 | - `css.lint.idSelector`: Selectors should not contain IDs because these rules are too tightly coupled with the HTML. default: `"ignore"` 94 | Valid options: ["ignore","warning","error"] 95 | - `css.lint.unknownAtRules`: Unknown at-rule. default: `"warning"` 96 | Valid options: ["ignore","warning","error"] 97 | - `css.trace.server`: Traces the communication between VS Code and the CSS language server. default: `"off"` 98 | Valid options: ["off","messages","verbose"] 99 | - `css.format.enable`: Enable/disable default CSS formatter. default: `true` 100 | - `css.format.newlineBetweenSelectors`: default: `true` 101 | - `css.format.newlineBetweenRules`: default: `true` 102 | - `css.format.spaceAroundSelectorSeparator`: default: `false` 103 | - `css.format.braceStyle`: default: `"collapse"` 104 | Valid options: ["collapse","expand"] 105 | - `css.format.preserveNewLines`: default: `true` 106 | - `css.format.maxPreserveNewLines`: default: `null` 107 | - `scss.completion.triggerPropertyValueCompletion`: By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. default: `true` 108 | - `scss.completion.completePropertyWithSemicolon`: Insert semicolon at end of line when completing CSS properties. default: `true` 109 | - `scss.validate`: Enables or disables all validations. default: `true` 110 | - `scss.hover.documentation`: Show tag and attribute documentation in SCSS hovers. default: `true` 111 | - `scss.hover.references`: Show references to MDN in SCSS hovers. default: `true` 112 | - `scss.lint.compatibleVendorPrefixes`: When using a vendor-specific prefix make sure to also include all other vendor-specific properties. default: `"ignore"` 113 | Valid options: ["ignore","warning","error"] 114 | - `scss.lint.vendorPrefix`: When using a vendor-specific prefix, also include the standard property. default: `"warning"` 115 | Valid options: ["ignore","warning","error"] 116 | - `scss.lint.duplicateProperties`: Do not use duplicate style definitions. default: `"ignore"` 117 | Valid options: ["ignore","warning","error"] 118 | - `scss.lint.emptyRules`: Do not use empty rulesets. default: `"warning"` 119 | Valid options: ["ignore","warning","error"] 120 | - `scss.lint.importStatement`: Import statements do not load in parallel. default: `"ignore"` 121 | Valid options: ["ignore","warning","error"] 122 | - `scss.lint.boxModel`: default: `"ignore"` 123 | Valid options: ["ignore","warning","error"] 124 | - `scss.lint.universalSelector`: default: `"ignore"` 125 | Valid options: ["ignore","warning","error"] 126 | - `scss.lint.zeroUnits`: No unit for zero needed. default: `"ignore"` 127 | Valid options: ["ignore","warning","error"] 128 | - `scss.lint.fontFaceProperties`: default: `"warning"` 129 | Valid options: ["ignore","warning","error"] 130 | - `scss.lint.hexColorLength`: Hex colors must consist of three or six hex numbers. default: `"error"` 131 | Valid options: ["ignore","warning","error"] 132 | - `scss.lint.argumentsInColorFunction`: Invalid number of parameters. default: `"error"` 133 | Valid options: ["ignore","warning","error"] 134 | - `scss.lint.unknownProperties`: Unknown property. default: `"warning"` 135 | Valid options: ["ignore","warning","error"] 136 | - `scss.lint.validProperties`: A list of properties that are not validated against the `unknownProperties` rule. default: `[]` 137 | - `scss.lint.ieHack`: IE hacks are only necessary when supporting IE7 and older. default: `"ignore"` 138 | Valid options: ["ignore","warning","error"] 139 | - `scss.lint.unknownVendorSpecificProperties`: Unknown vendor specific property. default: `"ignore"` 140 | Valid options: ["ignore","warning","error"] 141 | - `scss.lint.propertyIgnoredDueToDisplay`: default: `"warning"` 142 | Valid options: ["ignore","warning","error"] 143 | - `scss.lint.important`: default: `"ignore"` 144 | Valid options: ["ignore","warning","error"] 145 | - `scss.lint.float`: default: `"ignore"` 146 | Valid options: ["ignore","warning","error"] 147 | - `scss.lint.idSelector`: Selectors should not contain IDs because these rules are too tightly coupled with the HTML. default: `"ignore"` 148 | Valid options: ["ignore","warning","error"] 149 | - `scss.lint.unknownAtRules`: Unknown at-rule. default: `"warning"` 150 | Valid options: ["ignore","warning","error"] 151 | - `scss.format.enable`: Enable/disable default SCSS formatter. default: `true` 152 | - `scss.format.newlineBetweenSelectors`: default: `true` 153 | - `scss.format.newlineBetweenRules`: default: `true` 154 | - `scss.format.spaceAroundSelectorSeparator`: default: `false` 155 | - `scss.format.braceStyle`: default: `"collapse"` 156 | Valid options: ["collapse","expand"] 157 | - `scss.format.preserveNewLines`: default: `true` 158 | - `scss.format.maxPreserveNewLines`: default: `null` 159 | - `less.completion.triggerPropertyValueCompletion`: By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. default: `true` 160 | - `less.completion.completePropertyWithSemicolon`: Insert semicolon at end of line when completing CSS properties. default: `true` 161 | - `less.validate`: Enables or disables all validations. default: `true` 162 | - `less.hover.documentation`: Show tag and attribute documentation in LESS hovers. default: `true` 163 | - `less.hover.references`: Show references to MDN in LESS hovers. default: `true` 164 | - `less.lint.compatibleVendorPrefixes`: When using a vendor-specific prefix make sure to also include all other vendor-specific properties. default: `"ignore"` 165 | Valid options: ["ignore","warning","error"] 166 | - `less.lint.vendorPrefix`: When using a vendor-specific prefix, also include the standard property. default: `"warning"` 167 | Valid options: ["ignore","warning","error"] 168 | - `less.lint.duplicateProperties`: Do not use duplicate style definitions. default: `"ignore"` 169 | Valid options: ["ignore","warning","error"] 170 | - `less.lint.emptyRules`: Do not use empty rulesets. default: `"warning"` 171 | Valid options: ["ignore","warning","error"] 172 | - `less.lint.importStatement`: Import statements do not load in parallel. default: `"ignore"` 173 | Valid options: ["ignore","warning","error"] 174 | - `less.lint.boxModel`: default: `"ignore"` 175 | Valid options: ["ignore","warning","error"] 176 | - `less.lint.universalSelector`: default: `"ignore"` 177 | Valid options: ["ignore","warning","error"] 178 | - `less.lint.zeroUnits`: No unit for zero needed. default: `"ignore"` 179 | Valid options: ["ignore","warning","error"] 180 | - `less.lint.fontFaceProperties`: default: `"warning"` 181 | Valid options: ["ignore","warning","error"] 182 | - `less.lint.hexColorLength`: Hex colors must consist of three or six hex numbers. default: `"error"` 183 | Valid options: ["ignore","warning","error"] 184 | - `less.lint.argumentsInColorFunction`: Invalid number of parameters. default: `"error"` 185 | Valid options: ["ignore","warning","error"] 186 | - `less.lint.unknownProperties`: Unknown property. default: `"warning"` 187 | Valid options: ["ignore","warning","error"] 188 | - `less.lint.validProperties`: A list of properties that are not validated against the `unknownProperties` rule. default: `[]` 189 | - `less.lint.ieHack`: IE hacks are only necessary when supporting IE7 and older. default: `"ignore"` 190 | Valid options: ["ignore","warning","error"] 191 | - `less.lint.unknownVendorSpecificProperties`: Unknown vendor specific property. default: `"ignore"` 192 | Valid options: ["ignore","warning","error"] 193 | - `less.lint.propertyIgnoredDueToDisplay`: default: `"warning"` 194 | Valid options: ["ignore","warning","error"] 195 | - `less.lint.important`: default: `"ignore"` 196 | Valid options: ["ignore","warning","error"] 197 | - `less.lint.float`: default: `"ignore"` 198 | Valid options: ["ignore","warning","error"] 199 | - `less.lint.idSelector`: Selectors should not contain IDs because these rules are too tightly coupled with the HTML. default: `"ignore"` 200 | Valid options: ["ignore","warning","error"] 201 | - `less.lint.unknownAtRules`: Unknown at-rule. default: `"warning"` 202 | Valid options: ["ignore","warning","error"] 203 | - `less.format.enable`: Enable/disable default LESS formatter. default: `true` 204 | - `less.format.newlineBetweenSelectors`: default: `true` 205 | - `less.format.newlineBetweenRules`: default: `true` 206 | - `less.format.spaceAroundSelectorSeparator`: default: `false` 207 | - `less.format.braceStyle`: default: `"collapse"` 208 | Valid options: ["collapse","expand"] 209 | - `less.format.preserveNewLines`: default: `true` 210 | - `less.format.maxPreserveNewLines`: default: `null` 211 | 212 | ## License 213 | 214 | MIT 215 | -------------------------------------------------------------------------------- /esbuild.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | let entryPlugin = { 4 | name: 'entry', 5 | setup(build) { 6 | build.onResolve({filter: /^(index|server)\.ts$/}, args => { 7 | return { 8 | path: args.path, 9 | namespace: 'entry-ns' 10 | } 11 | }) 12 | build.onLoad({filter: /.*/, namespace: 'entry-ns'}, args => { 13 | let contents = '' 14 | if (args.path == 'index.ts') { 15 | contents = ` 16 | import {activate} from './src/index' 17 | export {activate} 18 | ` 19 | } else if (args.path == 'server.ts') { 20 | contents = `require('./server/node/cssServerMain.ts')` 21 | } else { 22 | throw new Error('Bad path') 23 | } 24 | return { 25 | contents, 26 | resolveDir: __dirname 27 | } 28 | }) 29 | } 30 | } 31 | 32 | async function start() { 33 | await require('esbuild').build({ 34 | entryPoints: ['index.ts', 'server.ts'], 35 | define: {'process.env.NODE_ENV': JSON.stringify("production")}, 36 | bundle: true, 37 | platform: 'node', 38 | target: 'node12.16', 39 | mainFields: ['module', 'main'], 40 | minify: true, 41 | sourcemap: true, 42 | external: ['coc.nvim'], 43 | outdir: path.resolve(__dirname, 'lib'), 44 | plugins: [entryPlugin] 45 | }) 46 | } 47 | 48 | start().catch(e => { 49 | console.error(e) 50 | }) 51 | 52 | -------------------------------------------------------------------------------- /history.md: -------------------------------------------------------------------------------- 1 | ## v2.0.0 2022-11-03 2 | 3 | - Format support. 4 | - Remove configuration `css.enable` 5 | - Remove support for wxss. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coc-css", 3 | "version": "2.1.0", 4 | "description": "Css extension for coc.nvim", 5 | "main": "lib/index.js", 6 | "publisher": "chemzqm", 7 | "engines": { 8 | "coc": "^0.0.82" 9 | }, 10 | "keywords": [ 11 | "coc.nvim", 12 | "css", 13 | "languageserver" 14 | ], 15 | "scripts": { 16 | "build": "node esbuild.js", 17 | "prepare": "node esbuild.js" 18 | }, 19 | "activationEvents": [ 20 | "onLanguage:css", 21 | "onLanguage:less", 22 | "onLanguage:scss" 23 | ], 24 | "contributes": { 25 | "configuration": { 26 | "type": "object", 27 | "title": "Css", 28 | "properties": { 29 | "css.execArgv": { 30 | "type": "array", 31 | "default": [], 32 | "description": "Extra arguments for node which start language server.", 33 | "items": { 34 | "type": "string" 35 | } 36 | }, 37 | "css.customData": { 38 | "type": "array", 39 | "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", 40 | "default": [], 41 | "items": { 42 | "type": "string" 43 | }, 44 | "scope": "resource" 45 | }, 46 | "css.completion.triggerPropertyValueCompletion": { 47 | "type": "boolean", 48 | "scope": "resource", 49 | "default": true, 50 | "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." 51 | }, 52 | "css.completion.completePropertyWithSemicolon": { 53 | "type": "boolean", 54 | "scope": "resource", 55 | "default": true, 56 | "description": "Insert semicolon at end of line when completing CSS properties." 57 | }, 58 | "css.validate": { 59 | "type": "boolean", 60 | "scope": "resource", 61 | "default": true, 62 | "description": "Enables or disables all validations." 63 | }, 64 | "css.hover.documentation": { 65 | "type": "boolean", 66 | "scope": "resource", 67 | "default": true, 68 | "description": "Show tag and attribute documentation in CSS hovers." 69 | }, 70 | "css.hover.references": { 71 | "type": "boolean", 72 | "scope": "resource", 73 | "default": true, 74 | "description": "Show references to MDN in CSS hovers." 75 | }, 76 | "css.lint.compatibleVendorPrefixes": { 77 | "type": "string", 78 | "scope": "resource", 79 | "enum": [ 80 | "ignore", 81 | "warning", 82 | "error" 83 | ], 84 | "default": "ignore", 85 | "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." 86 | }, 87 | "css.lint.vendorPrefix": { 88 | "type": "string", 89 | "scope": "resource", 90 | "enum": [ 91 | "ignore", 92 | "warning", 93 | "error" 94 | ], 95 | "default": "warning", 96 | "description": "When using a vendor-specific prefix, also include the standard property." 97 | }, 98 | "css.lint.duplicateProperties": { 99 | "type": "string", 100 | "scope": "resource", 101 | "enum": [ 102 | "ignore", 103 | "warning", 104 | "error" 105 | ], 106 | "default": "ignore", 107 | "description": "Do not use duplicate style definitions." 108 | }, 109 | "css.lint.emptyRules": { 110 | "type": "string", 111 | "scope": "resource", 112 | "enum": [ 113 | "ignore", 114 | "warning", 115 | "error" 116 | ], 117 | "default": "warning", 118 | "description": "Do not use empty rulesets." 119 | }, 120 | "css.lint.importStatement": { 121 | "type": "string", 122 | "scope": "resource", 123 | "enum": [ 124 | "ignore", 125 | "warning", 126 | "error" 127 | ], 128 | "default": "ignore", 129 | "description": "Import statements do not load in parallel." 130 | }, 131 | "css.lint.boxModel": { 132 | "type": "string", 133 | "scope": "resource", 134 | "enum": [ 135 | "ignore", 136 | "warning", 137 | "error" 138 | ], 139 | "default": "ignore", 140 | "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." 141 | }, 142 | "css.lint.universalSelector": { 143 | "type": "string", 144 | "scope": "resource", 145 | "enum": [ 146 | "ignore", 147 | "warning", 148 | "error" 149 | ], 150 | "default": "ignore", 151 | "markdownDescription": "The universal selector (`*`) is known to be slow." 152 | }, 153 | "css.lint.zeroUnits": { 154 | "type": "string", 155 | "scope": "resource", 156 | "enum": [ 157 | "ignore", 158 | "warning", 159 | "error" 160 | ], 161 | "default": "ignore", 162 | "description": "No unit for zero needed." 163 | }, 164 | "css.lint.fontFaceProperties": { 165 | "type": "string", 166 | "scope": "resource", 167 | "enum": [ 168 | "ignore", 169 | "warning", 170 | "error" 171 | ], 172 | "default": "warning", 173 | "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." 174 | }, 175 | "css.lint.hexColorLength": { 176 | "type": "string", 177 | "scope": "resource", 178 | "enum": [ 179 | "ignore", 180 | "warning", 181 | "error" 182 | ], 183 | "default": "error", 184 | "description": "Hex colors must consist of three or six hex numbers." 185 | }, 186 | "css.lint.argumentsInColorFunction": { 187 | "type": "string", 188 | "scope": "resource", 189 | "enum": [ 190 | "ignore", 191 | "warning", 192 | "error" 193 | ], 194 | "default": "error", 195 | "description": "Invalid number of parameters." 196 | }, 197 | "css.lint.unknownProperties": { 198 | "type": "string", 199 | "scope": "resource", 200 | "enum": [ 201 | "ignore", 202 | "warning", 203 | "error" 204 | ], 205 | "default": "warning", 206 | "description": "Unknown property." 207 | }, 208 | "css.lint.validProperties": { 209 | "type": "array", 210 | "uniqueItems": true, 211 | "items": { 212 | "type": "string" 213 | }, 214 | "scope": "resource", 215 | "default": [], 216 | "description": "A list of properties that are not validated against the `unknownProperties` rule." 217 | }, 218 | "css.lint.ieHack": { 219 | "type": "string", 220 | "scope": "resource", 221 | "enum": [ 222 | "ignore", 223 | "warning", 224 | "error" 225 | ], 226 | "default": "ignore", 227 | "description": "IE hacks are only necessary when supporting IE7 and older." 228 | }, 229 | "css.lint.unknownVendorSpecificProperties": { 230 | "type": "string", 231 | "scope": "resource", 232 | "enum": [ 233 | "ignore", 234 | "warning", 235 | "error" 236 | ], 237 | "default": "ignore", 238 | "description": "Unknown vendor specific property." 239 | }, 240 | "css.lint.propertyIgnoredDueToDisplay": { 241 | "type": "string", 242 | "scope": "resource", 243 | "enum": [ 244 | "ignore", 245 | "warning", 246 | "error" 247 | ], 248 | "default": "warning", 249 | "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." 250 | }, 251 | "css.lint.important": { 252 | "type": "string", 253 | "scope": "resource", 254 | "enum": [ 255 | "ignore", 256 | "warning", 257 | "error" 258 | ], 259 | "default": "ignore", 260 | "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." 261 | }, 262 | "css.lint.float": { 263 | "type": "string", 264 | "scope": "resource", 265 | "enum": [ 266 | "ignore", 267 | "warning", 268 | "error" 269 | ], 270 | "default": "ignore", 271 | "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." 272 | }, 273 | "css.lint.idSelector": { 274 | "type": "string", 275 | "scope": "resource", 276 | "enum": [ 277 | "ignore", 278 | "warning", 279 | "error" 280 | ], 281 | "default": "ignore", 282 | "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." 283 | }, 284 | "css.lint.unknownAtRules": { 285 | "type": "string", 286 | "scope": "resource", 287 | "enum": [ 288 | "ignore", 289 | "warning", 290 | "error" 291 | ], 292 | "default": "warning", 293 | "description": "Unknown at-rule." 294 | }, 295 | "css.trace.server": { 296 | "type": "string", 297 | "scope": "window", 298 | "enum": [ 299 | "off", 300 | "messages", 301 | "verbose" 302 | ], 303 | "default": "off", 304 | "description": "Traces the communication between VS Code and the CSS language server." 305 | }, 306 | "css.format.enable": { 307 | "type": "boolean", 308 | "scope": "window", 309 | "default": true, 310 | "description": "Enable/disable default CSS formatter." 311 | }, 312 | "css.format.newlineBetweenSelectors": { 313 | "type": "boolean", 314 | "scope": "resource", 315 | "default": true, 316 | "markdownDescription": "Separate selectors with a new line." 317 | }, 318 | "css.format.newlineBetweenRules": { 319 | "type": "boolean", 320 | "scope": "resource", 321 | "default": true, 322 | "markdownDescription": "Separate rulesets by a blank line." 323 | }, 324 | "css.format.spaceAroundSelectorSeparator": { 325 | "type": "boolean", 326 | "scope": "resource", 327 | "default": false, 328 | "markdownDescription": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`)." 329 | }, 330 | "css.format.braceStyle": { 331 | "type": "string", 332 | "scope": "resource", 333 | "default": "collapse", 334 | "enum": [ 335 | "collapse", 336 | "expand" 337 | ], 338 | "markdownDescription": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." 339 | }, 340 | "css.format.preserveNewLines": { 341 | "type": "boolean", 342 | "scope": "resource", 343 | "default": true, 344 | "markdownDescription": "Whether existing line breaks before elements should be preserved." 345 | }, 346 | "css.format.maxPreserveNewLines": { 347 | "type": [ 348 | "number", 349 | "null" 350 | ], 351 | "scope": "resource", 352 | "default": null, 353 | "markdownDescription": "Maximum number of line breaks to be preserved in one chunk, when `#css.format.preserveNewLines#` is enabled." 354 | }, 355 | "scss.completion.triggerPropertyValueCompletion": { 356 | "type": "boolean", 357 | "scope": "resource", 358 | "default": true, 359 | "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." 360 | }, 361 | "scss.completion.completePropertyWithSemicolon": { 362 | "type": "boolean", 363 | "scope": "resource", 364 | "default": true, 365 | "description": "Insert semicolon at end of line when completing CSS properties." 366 | }, 367 | "scss.validate": { 368 | "type": "boolean", 369 | "scope": "resource", 370 | "default": true, 371 | "description": "Enables or disables all validations." 372 | }, 373 | "scss.hover.documentation": { 374 | "type": "boolean", 375 | "scope": "resource", 376 | "default": true, 377 | "description": "Show tag and attribute documentation in SCSS hovers." 378 | }, 379 | "scss.hover.references": { 380 | "type": "boolean", 381 | "scope": "resource", 382 | "default": true, 383 | "description": "Show references to MDN in SCSS hovers." 384 | }, 385 | "scss.lint.compatibleVendorPrefixes": { 386 | "type": "string", 387 | "scope": "resource", 388 | "enum": [ 389 | "ignore", 390 | "warning", 391 | "error" 392 | ], 393 | "default": "ignore", 394 | "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." 395 | }, 396 | "scss.lint.vendorPrefix": { 397 | "type": "string", 398 | "scope": "resource", 399 | "enum": [ 400 | "ignore", 401 | "warning", 402 | "error" 403 | ], 404 | "default": "warning", 405 | "description": "When using a vendor-specific prefix, also include the standard property." 406 | }, 407 | "scss.lint.duplicateProperties": { 408 | "type": "string", 409 | "scope": "resource", 410 | "enum": [ 411 | "ignore", 412 | "warning", 413 | "error" 414 | ], 415 | "default": "ignore", 416 | "description": "Do not use duplicate style definitions." 417 | }, 418 | "scss.lint.emptyRules": { 419 | "type": "string", 420 | "scope": "resource", 421 | "enum": [ 422 | "ignore", 423 | "warning", 424 | "error" 425 | ], 426 | "default": "warning", 427 | "description": "Do not use empty rulesets." 428 | }, 429 | "scss.lint.importStatement": { 430 | "type": "string", 431 | "scope": "resource", 432 | "enum": [ 433 | "ignore", 434 | "warning", 435 | "error" 436 | ], 437 | "default": "ignore", 438 | "description": "Import statements do not load in parallel." 439 | }, 440 | "scss.lint.boxModel": { 441 | "type": "string", 442 | "scope": "resource", 443 | "enum": [ 444 | "ignore", 445 | "warning", 446 | "error" 447 | ], 448 | "default": "ignore", 449 | "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." 450 | }, 451 | "scss.lint.universalSelector": { 452 | "type": "string", 453 | "scope": "resource", 454 | "enum": [ 455 | "ignore", 456 | "warning", 457 | "error" 458 | ], 459 | "default": "ignore", 460 | "markdownDescription": "The universal selector (`*`) is known to be slow." 461 | }, 462 | "scss.lint.zeroUnits": { 463 | "type": "string", 464 | "scope": "resource", 465 | "enum": [ 466 | "ignore", 467 | "warning", 468 | "error" 469 | ], 470 | "default": "ignore", 471 | "description": "No unit for zero needed." 472 | }, 473 | "scss.lint.fontFaceProperties": { 474 | "type": "string", 475 | "scope": "resource", 476 | "enum": [ 477 | "ignore", 478 | "warning", 479 | "error" 480 | ], 481 | "default": "warning", 482 | "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." 483 | }, 484 | "scss.lint.hexColorLength": { 485 | "type": "string", 486 | "scope": "resource", 487 | "enum": [ 488 | "ignore", 489 | "warning", 490 | "error" 491 | ], 492 | "default": "error", 493 | "description": "Hex colors must consist of three or six hex numbers." 494 | }, 495 | "scss.lint.argumentsInColorFunction": { 496 | "type": "string", 497 | "scope": "resource", 498 | "enum": [ 499 | "ignore", 500 | "warning", 501 | "error" 502 | ], 503 | "default": "error", 504 | "description": "Invalid number of parameters." 505 | }, 506 | "scss.lint.unknownProperties": { 507 | "type": "string", 508 | "scope": "resource", 509 | "enum": [ 510 | "ignore", 511 | "warning", 512 | "error" 513 | ], 514 | "default": "warning", 515 | "description": "Unknown property." 516 | }, 517 | "scss.lint.validProperties": { 518 | "type": "array", 519 | "uniqueItems": true, 520 | "items": { 521 | "type": "string" 522 | }, 523 | "scope": "resource", 524 | "default": [], 525 | "description": "A list of properties that are not validated against the `unknownProperties` rule." 526 | }, 527 | "scss.lint.ieHack": { 528 | "type": "string", 529 | "scope": "resource", 530 | "enum": [ 531 | "ignore", 532 | "warning", 533 | "error" 534 | ], 535 | "default": "ignore", 536 | "description": "IE hacks are only necessary when supporting IE7 and older." 537 | }, 538 | "scss.lint.unknownVendorSpecificProperties": { 539 | "type": "string", 540 | "scope": "resource", 541 | "enum": [ 542 | "ignore", 543 | "warning", 544 | "error" 545 | ], 546 | "default": "ignore", 547 | "description": "Unknown vendor specific property." 548 | }, 549 | "scss.lint.propertyIgnoredDueToDisplay": { 550 | "type": "string", 551 | "scope": "resource", 552 | "enum": [ 553 | "ignore", 554 | "warning", 555 | "error" 556 | ], 557 | "default": "warning", 558 | "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." 559 | }, 560 | "scss.lint.important": { 561 | "type": "string", 562 | "scope": "resource", 563 | "enum": [ 564 | "ignore", 565 | "warning", 566 | "error" 567 | ], 568 | "default": "ignore", 569 | "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." 570 | }, 571 | "scss.lint.float": { 572 | "type": "string", 573 | "scope": "resource", 574 | "enum": [ 575 | "ignore", 576 | "warning", 577 | "error" 578 | ], 579 | "default": "ignore", 580 | "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." 581 | }, 582 | "scss.lint.idSelector": { 583 | "type": "string", 584 | "scope": "resource", 585 | "enum": [ 586 | "ignore", 587 | "warning", 588 | "error" 589 | ], 590 | "default": "ignore", 591 | "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." 592 | }, 593 | "scss.lint.unknownAtRules": { 594 | "type": "string", 595 | "scope": "resource", 596 | "enum": [ 597 | "ignore", 598 | "warning", 599 | "error" 600 | ], 601 | "default": "warning", 602 | "description": "Unknown at-rule." 603 | }, 604 | "scss.format.enable": { 605 | "type": "boolean", 606 | "scope": "window", 607 | "default": true, 608 | "description": "Enable/disable default SCSS formatter." 609 | }, 610 | "scss.format.newlineBetweenSelectors": { 611 | "type": "boolean", 612 | "scope": "resource", 613 | "default": true, 614 | "markdownDescription": "Separate selectors with a new line." 615 | }, 616 | "scss.format.newlineBetweenRules": { 617 | "type": "boolean", 618 | "scope": "resource", 619 | "default": true, 620 | "markdownDescription": "Separate rulesets by a blank line." 621 | }, 622 | "scss.format.spaceAroundSelectorSeparator": { 623 | "type": "boolean", 624 | "scope": "resource", 625 | "default": false, 626 | "markdownDescription": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`)." 627 | }, 628 | "scss.format.braceStyle": { 629 | "type": "string", 630 | "scope": "resource", 631 | "default": "collapse", 632 | "enum": [ 633 | "collapse", 634 | "expand" 635 | ], 636 | "markdownDescription": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." 637 | }, 638 | "scss.format.preserveNewLines": { 639 | "type": "boolean", 640 | "scope": "resource", 641 | "default": true, 642 | "markdownDescription": "Whether existing line breaks before elements should be preserved." 643 | }, 644 | "scss.format.maxPreserveNewLines": { 645 | "type": [ 646 | "number", 647 | "null" 648 | ], 649 | "scope": "resource", 650 | "default": null, 651 | "markdownDescription": "Maximum number of line breaks to be preserved in one chunk, when `#scss.format.preserveNewLines#` is enabled." 652 | }, 653 | "less.completion.triggerPropertyValueCompletion": { 654 | "type": "boolean", 655 | "scope": "resource", 656 | "default": true, 657 | "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." 658 | }, 659 | "less.completion.completePropertyWithSemicolon": { 660 | "type": "boolean", 661 | "scope": "resource", 662 | "default": true, 663 | "description": "Insert semicolon at end of line when completing CSS properties." 664 | }, 665 | "less.validate": { 666 | "type": "boolean", 667 | "scope": "resource", 668 | "default": true, 669 | "description": "Enables or disables all validations." 670 | }, 671 | "less.hover.documentation": { 672 | "type": "boolean", 673 | "scope": "resource", 674 | "default": true, 675 | "description": "Show tag and attribute documentation in LESS hovers." 676 | }, 677 | "less.hover.references": { 678 | "type": "boolean", 679 | "scope": "resource", 680 | "default": true, 681 | "description": "Show references to MDN in LESS hovers." 682 | }, 683 | "less.lint.compatibleVendorPrefixes": { 684 | "type": "string", 685 | "scope": "resource", 686 | "enum": [ 687 | "ignore", 688 | "warning", 689 | "error" 690 | ], 691 | "default": "ignore", 692 | "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." 693 | }, 694 | "less.lint.vendorPrefix": { 695 | "type": "string", 696 | "scope": "resource", 697 | "enum": [ 698 | "ignore", 699 | "warning", 700 | "error" 701 | ], 702 | "default": "warning", 703 | "description": "When using a vendor-specific prefix, also include the standard property." 704 | }, 705 | "less.lint.duplicateProperties": { 706 | "type": "string", 707 | "scope": "resource", 708 | "enum": [ 709 | "ignore", 710 | "warning", 711 | "error" 712 | ], 713 | "default": "ignore", 714 | "description": "Do not use duplicate style definitions." 715 | }, 716 | "less.lint.emptyRules": { 717 | "type": "string", 718 | "scope": "resource", 719 | "enum": [ 720 | "ignore", 721 | "warning", 722 | "error" 723 | ], 724 | "default": "warning", 725 | "description": "Do not use empty rulesets." 726 | }, 727 | "less.lint.importStatement": { 728 | "type": "string", 729 | "scope": "resource", 730 | "enum": [ 731 | "ignore", 732 | "warning", 733 | "error" 734 | ], 735 | "default": "ignore", 736 | "description": "Import statements do not load in parallel." 737 | }, 738 | "less.lint.boxModel": { 739 | "type": "string", 740 | "scope": "resource", 741 | "enum": [ 742 | "ignore", 743 | "warning", 744 | "error" 745 | ], 746 | "default": "ignore", 747 | "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." 748 | }, 749 | "less.lint.universalSelector": { 750 | "type": "string", 751 | "scope": "resource", 752 | "enum": [ 753 | "ignore", 754 | "warning", 755 | "error" 756 | ], 757 | "default": "ignore", 758 | "markdownDescription": "The universal selector (`*`) is known to be slow." 759 | }, 760 | "less.lint.zeroUnits": { 761 | "type": "string", 762 | "scope": "resource", 763 | "enum": [ 764 | "ignore", 765 | "warning", 766 | "error" 767 | ], 768 | "default": "ignore", 769 | "description": "No unit for zero needed." 770 | }, 771 | "less.lint.fontFaceProperties": { 772 | "type": "string", 773 | "scope": "resource", 774 | "enum": [ 775 | "ignore", 776 | "warning", 777 | "error" 778 | ], 779 | "default": "warning", 780 | "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." 781 | }, 782 | "less.lint.hexColorLength": { 783 | "type": "string", 784 | "scope": "resource", 785 | "enum": [ 786 | "ignore", 787 | "warning", 788 | "error" 789 | ], 790 | "default": "error", 791 | "description": "Hex colors must consist of three or six hex numbers." 792 | }, 793 | "less.lint.argumentsInColorFunction": { 794 | "type": "string", 795 | "scope": "resource", 796 | "enum": [ 797 | "ignore", 798 | "warning", 799 | "error" 800 | ], 801 | "default": "error", 802 | "description": "Invalid number of parameters." 803 | }, 804 | "less.lint.unknownProperties": { 805 | "type": "string", 806 | "scope": "resource", 807 | "enum": [ 808 | "ignore", 809 | "warning", 810 | "error" 811 | ], 812 | "default": "warning", 813 | "description": "Unknown property." 814 | }, 815 | "less.lint.validProperties": { 816 | "type": "array", 817 | "uniqueItems": true, 818 | "items": { 819 | "type": "string" 820 | }, 821 | "scope": "resource", 822 | "default": [], 823 | "description": "A list of properties that are not validated against the `unknownProperties` rule." 824 | }, 825 | "less.lint.ieHack": { 826 | "type": "string", 827 | "scope": "resource", 828 | "enum": [ 829 | "ignore", 830 | "warning", 831 | "error" 832 | ], 833 | "default": "ignore", 834 | "description": "IE hacks are only necessary when supporting IE7 and older." 835 | }, 836 | "less.lint.unknownVendorSpecificProperties": { 837 | "type": "string", 838 | "scope": "resource", 839 | "enum": [ 840 | "ignore", 841 | "warning", 842 | "error" 843 | ], 844 | "default": "ignore", 845 | "description": "Unknown vendor specific property." 846 | }, 847 | "less.lint.propertyIgnoredDueToDisplay": { 848 | "type": "string", 849 | "scope": "resource", 850 | "enum": [ 851 | "ignore", 852 | "warning", 853 | "error" 854 | ], 855 | "default": "warning", 856 | "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." 857 | }, 858 | "less.lint.important": { 859 | "type": "string", 860 | "scope": "resource", 861 | "enum": [ 862 | "ignore", 863 | "warning", 864 | "error" 865 | ], 866 | "default": "ignore", 867 | "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." 868 | }, 869 | "less.lint.float": { 870 | "type": "string", 871 | "scope": "resource", 872 | "enum": [ 873 | "ignore", 874 | "warning", 875 | "error" 876 | ], 877 | "default": "ignore", 878 | "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." 879 | }, 880 | "less.lint.idSelector": { 881 | "type": "string", 882 | "scope": "resource", 883 | "enum": [ 884 | "ignore", 885 | "warning", 886 | "error" 887 | ], 888 | "default": "ignore", 889 | "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." 890 | }, 891 | "less.lint.unknownAtRules": { 892 | "type": "string", 893 | "scope": "resource", 894 | "enum": [ 895 | "ignore", 896 | "warning", 897 | "error" 898 | ], 899 | "default": "warning", 900 | "description": "Unknown at-rule." 901 | }, 902 | "less.format.enable": { 903 | "type": "boolean", 904 | "scope": "window", 905 | "default": true, 906 | "description": "Enable/disable default LESS formatter." 907 | }, 908 | "less.format.newlineBetweenSelectors": { 909 | "type": "boolean", 910 | "scope": "resource", 911 | "default": true, 912 | "markdownDescription": "Separate selectors with a new line." 913 | }, 914 | "less.format.newlineBetweenRules": { 915 | "type": "boolean", 916 | "scope": "resource", 917 | "default": true, 918 | "markdownDescription": "Separate rulesets by a blank line." 919 | }, 920 | "less.format.spaceAroundSelectorSeparator": { 921 | "type": "boolean", 922 | "scope": "resource", 923 | "default": false, 924 | "markdownDescription": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`)." 925 | }, 926 | "less.format.braceStyle": { 927 | "type": "string", 928 | "scope": "resource", 929 | "default": "collapse", 930 | "enum": [ 931 | "collapse", 932 | "expand" 933 | ], 934 | "markdownDescription": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." 935 | }, 936 | "less.format.preserveNewLines": { 937 | "type": "boolean", 938 | "scope": "resource", 939 | "default": true, 940 | "markdownDescription": "Whether existing line breaks before elements should be preserved." 941 | }, 942 | "less.format.maxPreserveNewLines": { 943 | "type": [ 944 | "number", 945 | "null" 946 | ], 947 | "scope": "resource", 948 | "default": null, 949 | "markdownDescription": "Maximum number of line breaks to be preserved in one chunk, when `#less.format.preserveNewLines#` is enabled." 950 | } 951 | } 952 | }, 953 | "jsonValidation": [ 954 | { 955 | "fileMatch": "*.css-data.json", 956 | "url": "https://raw.githubusercontent.com/microsoft/vscode-css-languageservice/master/docs/customData.schema.json" 957 | }, 958 | { 959 | "fileMatch": "package.json", 960 | "url": "./schemas/package.schema.json" 961 | } 962 | ] 963 | }, 964 | "author": "chemzqm@gmail.com", 965 | "license": "MIT", 966 | "devDependencies": { 967 | "@types/node": "^14.14", 968 | "coc.nvim": "0.0.83-next.8", 969 | "esbuild": "^0.14.7", 970 | "typescript": "^4", 971 | "vscode-css-languageservice": "^6.2.7", 972 | "vscode-languageserver": "^8.2.0-next.3", 973 | "vscode-languageserver-protocol": "^3.17.2", 974 | "vscode-uri": "^3.0.7" 975 | }, 976 | "dependencies": {} 977 | } 978 | -------------------------------------------------------------------------------- /schemas/package.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "CSS contributions to package.json", 4 | "type": "object", 5 | "properties": { 6 | "contributes": { 7 | "type": "object", 8 | "properties": { 9 | "css.customData": { 10 | "type": "array", 11 | "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", 12 | "items": { 13 | "type": "string", 14 | "description": "Relative path to a CSS custom data file" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/cssServer.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { 7 | Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit, Diagnostic 8 | } from 'vscode-languageserver'; 9 | import { URI } from 'vscode-uri'; 10 | import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice'; 11 | import { getLanguageModelCache } from './languageModelCache'; 12 | import { runSafeAsync } from './utils/runner'; 13 | import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; 14 | import { getDocumentContext } from './utils/documentContext'; 15 | import { fetchDataProviders } from './customData'; 16 | import { RequestService, getRequestService } from './requests'; 17 | 18 | namespace CustomDataChangedNotification { 19 | export const type: NotificationType = new NotificationType('css/customDataChanged'); 20 | } 21 | 22 | export interface Settings { 23 | css: LanguageSettings; 24 | less: LanguageSettings; 25 | scss: LanguageSettings; 26 | } 27 | 28 | export interface RuntimeEnvironment { 29 | readonly file?: RequestService; 30 | readonly http?: RequestService; 31 | readonly timer: { 32 | setImmediate(callback: (...args: any[]) => void, ...args: any[]): Disposable; 33 | setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable; 34 | }; 35 | } 36 | 37 | export function startServer(connection: Connection, runtime: RuntimeEnvironment) { 38 | 39 | // Create a text document manager. 40 | const documents = new TextDocuments(TextDocument); 41 | // Make the text document manager listen on the connection 42 | // for open, change and close text document events 43 | documents.listen(connection); 44 | 45 | const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); 46 | documents.onDidClose(e => { 47 | stylesheets.onDocumentRemoved(e.document); 48 | }); 49 | connection.onShutdown(() => { 50 | stylesheets.dispose(); 51 | }); 52 | 53 | let scopedSettingsSupport = false; 54 | let foldingRangeLimit = Number.MAX_VALUE; 55 | let workspaceFolders: WorkspaceFolder[]; 56 | let formatterMaxNumberOfEdits = Number.MAX_VALUE; 57 | 58 | let dataProvidersReady: Promise = Promise.resolve(); 59 | 60 | let diagnosticsSupport: DiagnosticsSupport | undefined; 61 | 62 | const languageServices: { [id: string]: LanguageService } = {}; 63 | 64 | const notReady = () => Promise.reject('Not Ready'); 65 | let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady }; 66 | 67 | // After the server has started the client sends an initialize request. The server receives 68 | // in the passed params the rootPath of the workspace plus the client capabilities. 69 | connection.onInitialize((params: InitializeParams): InitializeResult => { 70 | 71 | const initializationOptions = params.initializationOptions as any || {}; 72 | 73 | workspaceFolders = (params).workspaceFolders; 74 | if (!Array.isArray(workspaceFolders)) { 75 | workspaceFolders = []; 76 | if (params.rootPath) { 77 | workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString(true) }); 78 | } 79 | } 80 | 81 | requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime); 82 | 83 | function getClientCapability(name: string, def: T) { 84 | const keys = name.split('.'); 85 | let c: any = params.capabilities; 86 | for (let i = 0; c && i < keys.length; i++) { 87 | if (!c.hasOwnProperty(keys[i])) { 88 | return def; 89 | } 90 | c = c[keys[i]]; 91 | } 92 | return c; 93 | } 94 | const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); 95 | scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); 96 | foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); 97 | 98 | formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; 99 | 100 | languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); 101 | languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); 102 | languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); 103 | 104 | const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined); 105 | if (supportsDiagnosticPull === undefined) { 106 | diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); 107 | } else { 108 | diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); 109 | } 110 | 111 | const capabilities: ServerCapabilities = { 112 | textDocumentSync: TextDocumentSyncKind.Incremental, 113 | completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-', ':'] } : undefined, 114 | hoverProvider: true, 115 | documentSymbolProvider: true, 116 | referencesProvider: true, 117 | definitionProvider: true, 118 | documentHighlightProvider: true, 119 | documentLinkProvider: { 120 | resolveProvider: false 121 | }, 122 | codeActionProvider: true, 123 | renameProvider: true, 124 | colorProvider: {}, 125 | foldingRangeProvider: true, 126 | selectionRangeProvider: true, 127 | diagnosticProvider: { 128 | documentSelector: null, 129 | interFileDependencies: false, 130 | workspaceDiagnostics: false 131 | }, 132 | documentRangeFormattingProvider: initializationOptions?.provideFormatter === true, 133 | documentFormattingProvider: initializationOptions?.provideFormatter === true, 134 | }; 135 | return { capabilities }; 136 | }); 137 | 138 | function getLanguageService(document: TextDocument) { 139 | let service = languageServices[document.languageId]; 140 | if (!service) { 141 | connection.console.log('Document type is ' + document.languageId + ', using css instead.'); 142 | service = languageServices['css']; 143 | } 144 | return service; 145 | } 146 | 147 | let documentSettings: { [key: string]: Thenable } = {}; 148 | // remove document settings on close 149 | documents.onDidClose(e => { 150 | delete documentSettings[e.document.uri]; 151 | }); 152 | function getDocumentSettings(textDocument: TextDocument): Thenable { 153 | if (scopedSettingsSupport) { 154 | let promise = documentSettings[textDocument.uri]; 155 | if (!promise) { 156 | const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; 157 | promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0] as LanguageSettings | undefined); 158 | documentSettings[textDocument.uri] = promise; 159 | } 160 | return promise; 161 | } 162 | return Promise.resolve(undefined); 163 | } 164 | 165 | // The settings have changed. Is send on server activation as well. 166 | connection.onDidChangeConfiguration(change => { 167 | updateConfiguration(change.settings as any); 168 | }); 169 | 170 | function updateConfiguration(settings: any) { 171 | for (const languageId in languageServices) { 172 | languageServices[languageId].configure(settings[languageId]); 173 | } 174 | // reset all document settings 175 | documentSettings = {}; 176 | diagnosticsSupport?.requestRefresh(); 177 | } 178 | 179 | async function validateTextDocument(textDocument: TextDocument): Promise { 180 | const settingsPromise = getDocumentSettings(textDocument); 181 | const [settings] = await Promise.all([settingsPromise, dataProvidersReady]); 182 | 183 | const stylesheet = stylesheets.get(textDocument); 184 | return getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); 185 | } 186 | 187 | function updateDataProviders(dataPaths: string[]) { 188 | dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => { 189 | for (const lang in languageServices) { 190 | languageServices[lang].setDataProviders(true, customDataProviders); 191 | } 192 | }); 193 | } 194 | 195 | connection.onCompletion((textDocumentPosition, token) => { 196 | return runSafeAsync(runtime, async () => { 197 | const document = documents.get(textDocumentPosition.textDocument.uri); 198 | if (document) { 199 | const [settings,] = await Promise.all([getDocumentSettings(document), dataProvidersReady]); 200 | const styleSheet = stylesheets.get(document); 201 | const documentContext = getDocumentContext(document.uri, workspaceFolders); 202 | return getLanguageService(document).doComplete2(document, textDocumentPosition.position, styleSheet, documentContext, settings?.completion); 203 | } 204 | return null; 205 | }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); 206 | }); 207 | 208 | connection.onHover((textDocumentPosition, token) => { 209 | return runSafeAsync(runtime, async () => { 210 | const document = documents.get(textDocumentPosition.textDocument.uri); 211 | if (document) { 212 | const [settings,] = await Promise.all([getDocumentSettings(document), dataProvidersReady]); 213 | const styleSheet = stylesheets.get(document); 214 | return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet, settings?.hover); 215 | } 216 | return null; 217 | }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); 218 | }); 219 | 220 | connection.onDocumentSymbol((documentSymbolParams, token) => { 221 | return runSafeAsync(runtime, async () => { 222 | const document = documents.get(documentSymbolParams.textDocument.uri); 223 | if (document) { 224 | await dataProvidersReady; 225 | const stylesheet = stylesheets.get(document); 226 | return getLanguageService(document).findDocumentSymbols2(document, stylesheet); 227 | } 228 | return []; 229 | }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); 230 | }); 231 | 232 | connection.onDefinition((documentDefinitionParams, token) => { 233 | return runSafeAsync(runtime, async () => { 234 | const document = documents.get(documentDefinitionParams.textDocument.uri); 235 | if (document) { 236 | await dataProvidersReady; 237 | const stylesheet = stylesheets.get(document); 238 | return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); 239 | } 240 | return null; 241 | }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); 242 | }); 243 | 244 | connection.onDocumentHighlight((documentHighlightParams, token) => { 245 | return runSafeAsync(runtime, async () => { 246 | const document = documents.get(documentHighlightParams.textDocument.uri); 247 | if (document) { 248 | await dataProvidersReady; 249 | const stylesheet = stylesheets.get(document); 250 | return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); 251 | } 252 | return []; 253 | }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); 254 | }); 255 | 256 | 257 | connection.onDocumentLinks(async (documentLinkParams, token) => { 258 | return runSafeAsync(runtime, async () => { 259 | const document = documents.get(documentLinkParams.textDocument.uri); 260 | if (document) { 261 | await dataProvidersReady; 262 | const documentContext = getDocumentContext(document.uri, workspaceFolders); 263 | const stylesheet = stylesheets.get(document); 264 | return getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext); 265 | } 266 | return []; 267 | }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); 268 | }); 269 | 270 | 271 | connection.onReferences((referenceParams, token) => { 272 | return runSafeAsync(runtime, async () => { 273 | const document = documents.get(referenceParams.textDocument.uri); 274 | if (document) { 275 | await dataProvidersReady; 276 | const stylesheet = stylesheets.get(document); 277 | return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); 278 | } 279 | return []; 280 | }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); 281 | }); 282 | 283 | connection.onCodeAction((codeActionParams, token) => { 284 | return runSafeAsync(runtime, async () => { 285 | const document = documents.get(codeActionParams.textDocument.uri); 286 | if (document) { 287 | await dataProvidersReady; 288 | const stylesheet = stylesheets.get(document); 289 | return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); 290 | } 291 | return []; 292 | }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); 293 | }); 294 | 295 | connection.onDocumentColor((params, token) => { 296 | return runSafeAsync(runtime, async () => { 297 | const document = documents.get(params.textDocument.uri); 298 | if (document) { 299 | await dataProvidersReady; 300 | const stylesheet = stylesheets.get(document); 301 | return getLanguageService(document).findDocumentColors(document, stylesheet); 302 | } 303 | return []; 304 | }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); 305 | }); 306 | 307 | connection.onColorPresentation((params, token) => { 308 | return runSafeAsync(runtime, async () => { 309 | const document = documents.get(params.textDocument.uri); 310 | if (document) { 311 | await dataProvidersReady; 312 | const stylesheet = stylesheets.get(document); 313 | return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range); 314 | } 315 | return []; 316 | }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); 317 | }); 318 | 319 | connection.onRenameRequest((renameParameters, token) => { 320 | return runSafeAsync(runtime, async () => { 321 | const document = documents.get(renameParameters.textDocument.uri); 322 | if (document) { 323 | await dataProvidersReady; 324 | const stylesheet = stylesheets.get(document); 325 | return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet); 326 | } 327 | return null; 328 | }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); 329 | }); 330 | 331 | connection.onFoldingRanges((params, token) => { 332 | return runSafeAsync(runtime, async () => { 333 | const document = documents.get(params.textDocument.uri); 334 | if (document) { 335 | await dataProvidersReady; 336 | return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); 337 | } 338 | return null; 339 | }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); 340 | }); 341 | 342 | connection.onSelectionRanges((params, token) => { 343 | return runSafeAsync(runtime, async () => { 344 | const document = documents.get(params.textDocument.uri); 345 | const positions: Position[] = params.positions; 346 | 347 | if (document) { 348 | await dataProvidersReady; 349 | const stylesheet = stylesheets.get(document); 350 | return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); 351 | } 352 | return []; 353 | }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); 354 | }); 355 | 356 | async function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): Promise { 357 | const document = documents.get(textDocument.uri); 358 | if (document) { 359 | const edits = getLanguageService(document).format(document, range ?? getFullRange(document), options); 360 | if (edits.length > formatterMaxNumberOfEdits) { 361 | const newText = TextDocument.applyEdits(document, edits); 362 | return [TextEdit.replace(getFullRange(document), newText)]; 363 | } 364 | return edits; 365 | } 366 | return []; 367 | } 368 | 369 | connection.onDocumentRangeFormatting((formatParams, token) => { 370 | return runSafeAsync(runtime, () => onFormat(formatParams.textDocument, formatParams.range, formatParams.options), [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); 371 | }); 372 | 373 | connection.onDocumentFormatting((formatParams, token) => { 374 | return runSafeAsync(runtime, () => onFormat(formatParams.textDocument, undefined, formatParams.options), [], `Error while formatting ${formatParams.textDocument.uri}`, token); 375 | }); 376 | 377 | connection.onNotification(CustomDataChangedNotification.type, updateDataProviders); 378 | 379 | // Listen on the connection 380 | connection.listen(); 381 | 382 | } 383 | 384 | function getFullRange(document: TextDocument): Range { 385 | return Range.create(Position.create(0, 0), document.positionAt(document.getText().length)); 386 | } 387 | 388 | 389 | 390 | -------------------------------------------------------------------------------- /server/customData.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { ICSSDataProvider, newCSSDataProvider } from 'vscode-css-languageservice'; 7 | import { RequestService } from './requests'; 8 | 9 | export function fetchDataProviders(dataPaths: string[], requestService: RequestService): Promise { 10 | const providers = dataPaths.map(async p => { 11 | try { 12 | const content = await requestService.getContent(p); 13 | return parseCSSData(content); 14 | } catch (e) { 15 | return newCSSDataProvider({ version: 1 }); 16 | } 17 | }); 18 | 19 | return Promise.all(providers); 20 | } 21 | 22 | function parseCSSData(source: string): ICSSDataProvider { 23 | let rawData: any; 24 | 25 | try { 26 | rawData = JSON.parse(source); 27 | } catch (err) { 28 | return newCSSDataProvider({ version: 1 }); 29 | } 30 | 31 | return newCSSDataProvider({ 32 | version: rawData.version || 1, 33 | properties: rawData.properties || [], 34 | atDirectives: rawData.atDirectives || [], 35 | pseudoClasses: rawData.pseudoClasses || [], 36 | pseudoElements: rawData.pseudoElements || [] 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /server/languageModelCache.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { TextDocument } from 'vscode-css-languageservice'; 7 | 8 | export interface LanguageModelCache { 9 | get(document: TextDocument): T; 10 | onDocumentRemoved(document: TextDocument): void; 11 | dispose(): void; 12 | } 13 | 14 | export function getLanguageModelCache(maxEntries: number, cleanupIntervalTimeInSec: number, parse: (document: TextDocument) => T): LanguageModelCache { 15 | let languageModels: { [uri: string]: { version: number; languageId: string; cTime: number; languageModel: T } } = {}; 16 | let nModels = 0; 17 | 18 | let cleanupInterval: NodeJS.Timer | undefined = undefined; 19 | if (cleanupIntervalTimeInSec > 0) { 20 | cleanupInterval = setInterval(() => { 21 | const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; 22 | const uris = Object.keys(languageModels); 23 | for (const uri of uris) { 24 | const languageModelInfo = languageModels[uri]; 25 | if (languageModelInfo.cTime < cutoffTime) { 26 | delete languageModels[uri]; 27 | nModels--; 28 | } 29 | } 30 | }, cleanupIntervalTimeInSec * 1000); 31 | } 32 | 33 | return { 34 | get(document: TextDocument): T { 35 | const version = document.version; 36 | const languageId = document.languageId; 37 | const languageModelInfo = languageModels[document.uri]; 38 | if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) { 39 | languageModelInfo.cTime = Date.now(); 40 | return languageModelInfo.languageModel; 41 | } 42 | const languageModel = parse(document); 43 | languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() }; 44 | if (!languageModelInfo) { 45 | nModels++; 46 | } 47 | 48 | if (nModels === maxEntries) { 49 | let oldestTime = Number.MAX_VALUE; 50 | let oldestUri = null; 51 | for (const uri in languageModels) { 52 | const languageModelInfo = languageModels[uri]; 53 | if (languageModelInfo.cTime < oldestTime) { 54 | oldestUri = uri; 55 | oldestTime = languageModelInfo.cTime; 56 | } 57 | } 58 | if (oldestUri) { 59 | delete languageModels[oldestUri]; 60 | nModels--; 61 | } 62 | } 63 | return languageModel; 64 | 65 | }, 66 | onDocumentRemoved(document: TextDocument) { 67 | const uri = document.uri; 68 | if (languageModels[uri]) { 69 | delete languageModels[uri]; 70 | nModels--; 71 | } 72 | }, 73 | dispose() { 74 | if (typeof cleanupInterval !== 'undefined') { 75 | clearInterval(cleanupInterval); 76 | cleanupInterval = undefined; 77 | languageModels = {}; 78 | nModels = 0; 79 | } 80 | } 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /server/node/cssServerMain.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { createConnection, Connection, Disposable } from 'vscode-languageserver/node'; 7 | import { formatError } from '../utils/runner'; 8 | import { RuntimeEnvironment, startServer } from '../cssServer'; 9 | import { getNodeFSRequestService } from './nodeFs'; 10 | 11 | // Create a connection for the server. 12 | const connection: Connection = createConnection(); 13 | 14 | console.log = connection.console.log.bind(connection.console); 15 | console.error = connection.console.error.bind(connection.console); 16 | 17 | process.on('unhandledRejection', (e: any) => { 18 | connection.console.error(formatError(`Unhandled exception`, e)); 19 | }); 20 | 21 | const runtime: RuntimeEnvironment = { 22 | timer: { 23 | setImmediate(callback: (...args: any[]) => void, ...args: any[]): Disposable { 24 | const handle = setImmediate(callback, ...args); 25 | return { dispose: () => clearImmediate(handle) }; 26 | }, 27 | setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable { 28 | const handle = setTimeout(callback, ms, ...args); 29 | return { dispose: () => clearTimeout(handle) }; 30 | } 31 | }, 32 | file: getNodeFSRequestService() 33 | }; 34 | 35 | startServer(connection, runtime); 36 | -------------------------------------------------------------------------------- /server/node/nodeFs.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RequestService } from '../requests'; 7 | import { URI as Uri } from 'vscode-uri'; 8 | 9 | import * as fs from 'fs'; 10 | import { FileType } from 'vscode-css-languageservice'; 11 | 12 | export function getNodeFSRequestService(): RequestService { 13 | function ensureFileUri(location: string) { 14 | if (!location.startsWith('file://')) { 15 | throw new Error('fileRequestService can only handle file URLs'); 16 | } 17 | } 18 | return { 19 | getContent(location: string, encoding?: BufferEncoding) { 20 | ensureFileUri(location); 21 | return new Promise((c, e) => { 22 | const uri = Uri.parse(location); 23 | fs.readFile(uri.fsPath, encoding, (err, buf) => { 24 | if (err) { 25 | return e(err); 26 | } 27 | c(buf.toString()); 28 | 29 | }); 30 | }); 31 | }, 32 | stat(location: string) { 33 | ensureFileUri(location); 34 | return new Promise((c, e) => { 35 | const uri = Uri.parse(location); 36 | fs.stat(uri.fsPath, (err, stats) => { 37 | if (err) { 38 | if (err.code === 'ENOENT') { 39 | return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }); 40 | } else { 41 | return e(err); 42 | } 43 | } 44 | 45 | let type = FileType.Unknown; 46 | if (stats.isFile()) { 47 | type = FileType.File; 48 | } else if (stats.isDirectory()) { 49 | type = FileType.Directory; 50 | } else if (stats.isSymbolicLink()) { 51 | type = FileType.SymbolicLink; 52 | } 53 | 54 | c({ 55 | type, 56 | ctime: stats.ctime.getTime(), 57 | mtime: stats.mtime.getTime(), 58 | size: stats.size 59 | }); 60 | }); 61 | }); 62 | }, 63 | readDirectory(location: string) { 64 | ensureFileUri(location); 65 | return new Promise((c, e) => { 66 | const path = Uri.parse(location).fsPath; 67 | 68 | fs.readdir(path, { withFileTypes: true }, (err, children) => { 69 | if (err) { 70 | return e(err); 71 | } 72 | c(children.map(stat => { 73 | if (stat.isSymbolicLink()) { 74 | return [stat.name, FileType.SymbolicLink]; 75 | } else if (stat.isDirectory()) { 76 | return [stat.name, FileType.Directory]; 77 | } else if (stat.isFile()) { 78 | return [stat.name, FileType.File]; 79 | } else { 80 | return [stat.name, FileType.Unknown]; 81 | } 82 | })); 83 | }); 84 | }); 85 | } 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /server/requests.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { RequestType, Connection } from 'vscode-languageserver'; 7 | import { RuntimeEnvironment } from './cssServer'; 8 | 9 | export namespace FsContentRequest { 10 | export const type: RequestType<{ uri: string; encoding?: string }, string, any> = new RequestType('fs/content'); 11 | } 12 | export namespace FsStatRequest { 13 | export const type: RequestType = new RequestType('fs/stat'); 14 | } 15 | 16 | export namespace FsReadDirRequest { 17 | export const type: RequestType = new RequestType('fs/readDir'); 18 | } 19 | 20 | export enum FileType { 21 | /** 22 | * The file type is unknown. 23 | */ 24 | Unknown = 0, 25 | /** 26 | * A regular file. 27 | */ 28 | File = 1, 29 | /** 30 | * A directory. 31 | */ 32 | Directory = 2, 33 | /** 34 | * A symbolic link to a file. 35 | */ 36 | SymbolicLink = 64 37 | } 38 | export interface FileStat { 39 | /** 40 | * The type of the file, e.g. is a regular file, a directory, or symbolic link 41 | * to a file. 42 | */ 43 | type: FileType; 44 | /** 45 | * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. 46 | */ 47 | ctime: number; 48 | /** 49 | * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. 50 | */ 51 | mtime: number; 52 | /** 53 | * The size in bytes. 54 | */ 55 | size: number; 56 | } 57 | 58 | export interface RequestService { 59 | getContent(uri: string, encoding?: string): Promise; 60 | 61 | stat(uri: string): Promise; 62 | readDirectory(uri: string): Promise<[string, FileType][]>; 63 | } 64 | 65 | 66 | export function getRequestService(handledSchemas: string[], connection: Connection, runtime: RuntimeEnvironment): RequestService { 67 | const builtInHandlers: { [protocol: string]: RequestService | undefined } = {}; 68 | for (const protocol of handledSchemas) { 69 | if (protocol === 'file') { 70 | builtInHandlers[protocol] = runtime.file; 71 | } else if (protocol === 'http' || protocol === 'https') { 72 | builtInHandlers[protocol] = runtime.http; 73 | } 74 | } 75 | return { 76 | async stat(uri: string): Promise { 77 | const handler = builtInHandlers[getScheme(uri)]; 78 | if (handler) { 79 | return handler.stat(uri); 80 | } 81 | const res = await connection.sendRequest(FsStatRequest.type, uri.toString()); 82 | return res; 83 | }, 84 | readDirectory(uri: string): Promise<[string, FileType][]> { 85 | const handler = builtInHandlers[getScheme(uri)]; 86 | if (handler) { 87 | return handler.readDirectory(uri); 88 | } 89 | return connection.sendRequest(FsReadDirRequest.type, uri.toString()); 90 | }, 91 | getContent(uri: string, encoding?: string): Promise { 92 | const handler = builtInHandlers[getScheme(uri)]; 93 | if (handler) { 94 | return handler.getContent(uri, encoding); 95 | } 96 | return connection.sendRequest(FsContentRequest.type, { uri: uri.toString(), encoding }); 97 | } 98 | }; 99 | } 100 | 101 | function getScheme(uri: string) { 102 | return uri.substr(0, uri.indexOf(':')); 103 | } 104 | -------------------------------------------------------------------------------- /server/utils/documentContext.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { DocumentContext } from 'vscode-css-languageservice'; 7 | import { endsWith, startsWith } from '../utils/strings'; 8 | import { WorkspaceFolder } from 'vscode-languageserver'; 9 | import { Utils, URI } from 'vscode-uri'; 10 | 11 | export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { 12 | function getRootFolder(): string | undefined { 13 | for (const folder of workspaceFolders) { 14 | let folderURI = folder.uri; 15 | if (!endsWith(folderURI, '/')) { 16 | folderURI = folderURI + '/'; 17 | } 18 | if (startsWith(documentUri, folderURI)) { 19 | return folderURI; 20 | } 21 | } 22 | return undefined; 23 | } 24 | 25 | return { 26 | resolveReference: (ref: string, base = documentUri) => { 27 | if (ref[0] === '/') { // resolve absolute path against the current workspace folder 28 | const folderUri = getRootFolder(); 29 | if (folderUri) { 30 | return folderUri + ref.substring(1); 31 | } 32 | } 33 | const baseUri = URI.parse(base); 34 | const baseUriDir = baseUri.path.endsWith('/') ? baseUri : Utils.dirname(baseUri); 35 | return Utils.resolvePath(baseUriDir, ref).toString(true); 36 | }, 37 | }; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /server/utils/runner.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { ResponseError, CancellationToken, LSPErrorCodes } from 'vscode-languageserver'; 7 | import { RuntimeEnvironment } from '../cssServer'; 8 | 9 | export function formatError(message: string, err: any): string { 10 | if (err instanceof Error) { 11 | const error = err; 12 | return `${message}: ${error.message}\n${error.stack}`; 13 | } else if (typeof err === 'string') { 14 | return `${message}: ${err}`; 15 | } else if (err) { 16 | return `${message}: ${err.toString()}`; 17 | } 18 | return message; 19 | } 20 | 21 | export function runSafeAsync(runtime: RuntimeEnvironment, func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { 22 | return new Promise>((resolve) => { 23 | runtime.timer.setImmediate(() => { 24 | if (token.isCancellationRequested) { 25 | resolve(cancelValue()); 26 | return; 27 | } 28 | return func().then(result => { 29 | if (token.isCancellationRequested) { 30 | resolve(cancelValue()); 31 | return; 32 | } else { 33 | resolve(result); 34 | } 35 | }, e => { 36 | console.error(formatError(errorMessage, e)); 37 | resolve(errorVal); 38 | }); 39 | }); 40 | }); 41 | } 42 | 43 | function cancelValue() { 44 | return new ResponseError(LSPErrorCodes.RequestCancelled, 'Request cancelled'); 45 | } 46 | -------------------------------------------------------------------------------- /server/utils/strings.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | export function startsWith(haystack: string, needle: string): boolean { 7 | if (haystack.length < needle.length) { 8 | return false; 9 | } 10 | 11 | for (let i = 0; i < needle.length; i++) { 12 | if (haystack[i] !== needle[i]) { 13 | return false; 14 | } 15 | } 16 | 17 | return true; 18 | } 19 | 20 | /** 21 | * Determines if haystack ends with needle. 22 | */ 23 | export function endsWith(haystack: string, needle: string): boolean { 24 | const diff = haystack.length - needle.length; 25 | if (diff > 0) { 26 | return haystack.lastIndexOf(needle) === diff; 27 | } else if (diff === 0) { 28 | return haystack === needle; 29 | } else { 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/utils/validation.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; 7 | import { TextDocument } from 'vscode-css-languageservice'; 8 | import { formatError, runSafeAsync } from './runner'; 9 | import { RuntimeEnvironment } from '../cssServer'; 10 | 11 | export type Validator = (textDocument: TextDocument) => Promise; 12 | export type DiagnosticsSupport = { 13 | dispose(): void; 14 | requestRefresh(): void; 15 | }; 16 | 17 | export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { 18 | 19 | const pendingValidationRequests: { [uri: string]: Disposable } = {}; 20 | const validationDelayMs = 500; 21 | 22 | const disposables: Disposable[] = []; 23 | 24 | // The content of a text document has changed. This event is emitted 25 | // when the text document first opened or when its content has changed. 26 | documents.onDidChangeContent(change => { 27 | triggerValidation(change.document); 28 | }, undefined, disposables); 29 | 30 | // a document has closed: clear all diagnostics 31 | documents.onDidClose(event => { 32 | cleanPendingValidation(event.document); 33 | connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); 34 | }, undefined, disposables); 35 | 36 | function cleanPendingValidation(textDocument: TextDocument): void { 37 | const request = pendingValidationRequests[textDocument.uri]; 38 | if (request) { 39 | request.dispose(); 40 | delete pendingValidationRequests[textDocument.uri]; 41 | } 42 | } 43 | 44 | function triggerValidation(textDocument: TextDocument): void { 45 | cleanPendingValidation(textDocument); 46 | const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { 47 | if (request === pendingValidationRequests[textDocument.uri]) { 48 | try { 49 | const diagnostics = await validate(textDocument); 50 | if (request === pendingValidationRequests[textDocument.uri]) { 51 | connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); 52 | } 53 | delete pendingValidationRequests[textDocument.uri]; 54 | } catch (e) { 55 | connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); 56 | } 57 | } 58 | }, validationDelayMs); 59 | } 60 | 61 | return { 62 | requestRefresh: () => { 63 | documents.all().forEach(triggerValidation); 64 | }, 65 | dispose: () => { 66 | disposables.forEach(d => d.dispose()); 67 | disposables.length = 0; 68 | const keys = Object.keys(pendingValidationRequests); 69 | for (const key of keys) { 70 | pendingValidationRequests[key].dispose(); 71 | delete pendingValidationRequests[key]; 72 | } 73 | } 74 | }; 75 | } 76 | 77 | export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { 78 | 79 | function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { 80 | return { 81 | kind: DocumentDiagnosticReportKind.Full, 82 | items: diagnostics 83 | }; 84 | } 85 | 86 | const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { 87 | return runSafeAsync(runtime, async () => { 88 | const document = documents.get(params.textDocument.uri); 89 | if (document) { 90 | return newDocumentDiagnosticReport(await validate(document)); 91 | } 92 | return newDocumentDiagnosticReport([]); 93 | 94 | }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); 95 | }); 96 | 97 | function requestRefresh(): void { 98 | connection.languages.diagnostics.refresh(); 99 | } 100 | 101 | return { 102 | requestRefresh, 103 | dispose: () => { 104 | registration.dispose(); 105 | } 106 | }; 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/customData.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import { Disposable, Emitter, extensions, Uri, workspace } from 'coc.nvim' 6 | import { Utils } from 'vscode-uri' 7 | 8 | export function getCustomDataSource(toDispose: Disposable[]) { 9 | let pathsInWorkspace = getCustomDataPathsInAllWorkspaces() 10 | let pathsInExtensions = getCustomDataPathsFromAllExtensions() 11 | 12 | const onChange = new Emitter() 13 | 14 | toDispose.push(extensions.onDidLoadExtension(_ => { 15 | const newPathsInExtensions = getCustomDataPathsFromAllExtensions() 16 | if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) { 17 | pathsInExtensions = newPathsInExtensions 18 | onChange.fire() 19 | } 20 | })) 21 | 22 | toDispose.push(workspace.onDidChangeConfiguration(e => { 23 | if (e.affectsConfiguration('css.customData')) { 24 | pathsInWorkspace = getCustomDataPathsInAllWorkspaces() 25 | onChange.fire() 26 | } 27 | })) 28 | 29 | return { 30 | get uris() { 31 | return pathsInWorkspace.concat(pathsInExtensions) 32 | }, 33 | get onDidChange() { 34 | return onChange.event 35 | } 36 | } 37 | } 38 | 39 | 40 | function getCustomDataPathsInAllWorkspaces(): string[] { 41 | const workspaceFolders = workspace.workspaceFolders 42 | 43 | const dataPaths: string[] = [] 44 | 45 | if (!workspaceFolders) { 46 | return dataPaths 47 | } 48 | 49 | const collect = (paths: string[] | undefined, rootFolder: Uri) => { 50 | if (Array.isArray(paths)) { 51 | for (const path of paths) { 52 | if (typeof path === 'string') { 53 | dataPaths.push(Utils.resolvePath(rootFolder, path).toString()) 54 | } 55 | } 56 | } 57 | } 58 | 59 | for (let i = 0; i < workspaceFolders.length; i++) { 60 | const folderUri = Uri.parse(workspaceFolders[i].uri) 61 | const allCssConfig = workspace.getConfiguration('css', folderUri) 62 | const customDataInspect = allCssConfig.inspect('customData') 63 | if (customDataInspect) { 64 | collect(customDataInspect.workspaceFolderValue, folderUri) 65 | if (i === 0) { 66 | // if (workspace.workspaceFile) { 67 | // collect(customDataInspect.workspaceValue, workspace.workspaceFile) 68 | // } 69 | collect(customDataInspect.globalValue, folderUri) 70 | } 71 | } 72 | 73 | } 74 | return dataPaths 75 | } 76 | 77 | function getCustomDataPathsFromAllExtensions(): string[] { 78 | const dataPaths: string[] = [] 79 | for (const extension of extensions.all) { 80 | const customData = extension.packageJSON?.contributes?.css?.customData 81 | if (Array.isArray(customData)) { 82 | for (const rp of customData) { 83 | const uri = Uri.file(extension.extensionPath) 84 | dataPaths.push(Utils.joinPath(uri, rp).toString()) 85 | } 86 | } 87 | } 88 | return dataPaths 89 | } 90 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, languages, NotificationType, LanguageClient, LanguageClientOptions, ServerOptions, services, TransportKind, workspace, ServiceStat, Disposable, TextDocument, Range, FormattingOptions, CancellationToken, ProviderResult, TextEdit, commands, window } from 'coc.nvim' 2 | import { getCustomDataSource } from './customData' 3 | import { RequestService, serveFileSystemRequests } from './requests' 4 | import { TextDecoder } from 'util' 5 | import { getNodeFSRequestService } from './nodeFs' 6 | import { DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageserver-protocol' 7 | 8 | namespace CustomDataChangedNotification { 9 | export const type: NotificationType = new NotificationType('css/customDataChanged') 10 | } 11 | 12 | export interface Runtime { 13 | TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } } 14 | fs?: RequestService 15 | } 16 | 17 | interface FormatterRegistration { 18 | readonly languageId: string 19 | readonly settingId: string 20 | provider: Disposable | undefined 21 | } 22 | 23 | interface CSSFormatSettings { 24 | newlineBetweenSelectors?: boolean 25 | newlineBetweenRules?: boolean 26 | spaceAroundSelectorSeparator?: boolean 27 | braceStyle?: 'collapse' | 'expand' 28 | preserveNewLines?: boolean 29 | maxPreserveNewLines?: number | null 30 | } 31 | 32 | const cssFormatSettingKeys: (keyof CSSFormatSettings)[] = ['newlineBetweenSelectors', 'newlineBetweenRules', 'spaceAroundSelectorSeparator', 'braceStyle', 'preserveNewLines', 'maxPreserveNewLines'] 33 | 34 | export async function activate(context: ExtensionContext): Promise { 35 | let { subscriptions } = context 36 | const config = workspace.getConfiguration().get('css', {}) as any 37 | const serverModule = context.asAbsolutePath('./lib/server.js') 38 | const documentSelector = ['css', 'scss', 'less'] 39 | const customDataSource = getCustomDataSource(context.subscriptions) 40 | const formatterRegistrations: FormatterRegistration[] = documentSelector.map(languageId => ({ 41 | languageId, settingId: `${languageId}.format.enable`, provider: undefined 42 | })) 43 | 44 | const runtime: Runtime = { fs: getNodeFSRequestService(), TextDecoder } 45 | const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] } 46 | // If the extension is launch in debug mode the debug server options are use 47 | // Otherwise the run options are used 48 | const serverOptions: ServerOptions = { 49 | run: { 50 | module: serverModule, 51 | transport: TransportKind.ipc, 52 | options: { 53 | cwd: workspace.root, 54 | execArgv: config.execArgv || [] 55 | } 56 | }, 57 | debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } 58 | } 59 | 60 | let clientOptions: LanguageClientOptions = { 61 | documentSelector: documentSelector, 62 | synchronize: { 63 | configurationSection: ['css', 'less', 'scss'] 64 | }, 65 | outputChannelName: 'css', 66 | initializationOptions: { 67 | handledSchemas: ['file'], 68 | provideFormatter: false, // tell the server to not provide formatting capability 69 | customCapabilities: { rangeFormatting: { editLimit: 10000 } } 70 | }, 71 | middleware: { 72 | } 73 | } 74 | 75 | let client = new LanguageClient('css', 'Css language server', serverOptions, clientOptions) 76 | 77 | await client.start() 78 | 79 | if (client.serviceState === ServiceStat.Running) { 80 | client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris) 81 | customDataSource.onDidChange(() => { 82 | client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris) 83 | }) 84 | } 85 | 86 | subscriptions.push( 87 | services.registerLanguageClient(client) 88 | ) 89 | 90 | // manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652. 91 | for (const registration of formatterRegistrations) { 92 | updateFormatterRegistration(registration) 93 | context.subscriptions.push({ dispose: () => registration.provider?.dispose() }) 94 | context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration))) 95 | } 96 | serveFileSystemRequests(client, runtime) 97 | 98 | commands.registerCommand('_css.applyCodeAction', applyCodeAction) 99 | 100 | function applyCodeAction(uri: string, documentVersion: number, edits: TextEdit[]) { 101 | const textEditor = window.activeTextEditor 102 | if (textEditor && textEditor.document.uri.toString() === uri) { 103 | if (textEditor.document.version !== documentVersion) { 104 | window.showInformationMessage(`CSS fix is outdated and can't be applied to the document.`) 105 | } 106 | textEditor.document.applyEdits(edits).catch(err => { 107 | window.showErrorMessage(`Failed to apply CSS fix to the document. ${err.message}`) 108 | }) 109 | } 110 | } 111 | 112 | 113 | function updateFormatterRegistration(registration: FormatterRegistration) { 114 | const formatEnabled = workspace.getConfiguration().get(registration.settingId) 115 | if (!formatEnabled && registration.provider) { 116 | registration.provider.dispose() 117 | registration.provider = undefined 118 | } else if (formatEnabled && !registration.provider) { 119 | registration.provider = languages.registerDocumentRangeFormatProvider([registration.languageId], { 120 | provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { 121 | const filesConfig = workspace.getConfiguration('files', document) 122 | const fileFormattingOptions = { 123 | trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), 124 | trimFinalNewlines: filesConfig.get('trimFinalNewlines'), 125 | insertFinalNewline: filesConfig.get('insertFinalNewline'), 126 | } 127 | 128 | const params: DocumentRangeFormattingParams = { 129 | textDocument: { 130 | uri: document.uri 131 | }, 132 | range, 133 | options: Object.assign({}, fileFormattingOptions, options) as any 134 | } 135 | // add the css formatter options from the settings 136 | const formatterSettings = workspace.getConfiguration(registration.languageId, document).get('format') 137 | if (formatterSettings) { 138 | for (const key of cssFormatSettingKeys) { 139 | const val = formatterSettings[key] 140 | if (val !== undefined && val !== null) { 141 | params.options[key] = val 142 | } 143 | } 144 | } 145 | return client.sendRequest(DocumentRangeFormattingRequest.type as any, params, token).then( 146 | edits => edits as TextEdit[], 147 | (error) => { 148 | client.handleFailedRequest(DocumentRangeFormattingRequest.type as any, undefined, error, []) 149 | return Promise.resolve([]) 150 | } 151 | ) 152 | } 153 | }) 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/nodeFs.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as fs from 'fs' 7 | import { Uri } from 'coc.nvim' 8 | import { RequestService, FileType, FileStat } from './requests' 9 | 10 | export function getNodeFSRequestService(): RequestService { 11 | function ensureFileUri(location: string) { 12 | if (!location.startsWith('file://')) { 13 | throw new Error('fileRequestService can only handle file URLs') 14 | } 15 | } 16 | return { 17 | getContent(location: string, encoding?: BufferEncoding): Promise { 18 | ensureFileUri(location) 19 | return new Promise((c, e) => { 20 | const uri = Uri.parse(location) 21 | fs.readFile(uri.fsPath, encoding, (err, buf) => { 22 | if (err) { 23 | return e(err) 24 | } 25 | c(buf.toString()) 26 | 27 | }) 28 | }) 29 | }, 30 | stat(location: string): Promise { 31 | ensureFileUri(location) 32 | return new Promise((c, e) => { 33 | const uri = Uri.parse(location) 34 | fs.stat(uri.fsPath, (err, stats) => { 35 | if (err) { 36 | if (err.code === 'ENOENT') { 37 | return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }) 38 | } else { 39 | return e(err) 40 | } 41 | } 42 | 43 | let type = FileType.Unknown 44 | if (stats.isFile()) { 45 | type = FileType.File 46 | } else if (stats.isDirectory()) { 47 | type = FileType.Directory 48 | } else if (stats.isSymbolicLink()) { 49 | type = FileType.SymbolicLink 50 | } 51 | 52 | c({ 53 | type, 54 | ctime: stats.ctime.getTime(), 55 | mtime: stats.mtime.getTime(), 56 | size: stats.size 57 | }) 58 | }) 59 | }) 60 | }, 61 | readDirectory(location: string): Promise<[string, FileType][]> { 62 | ensureFileUri(location) 63 | return new Promise((c, e) => { 64 | const path = Uri.parse(location).fsPath 65 | 66 | fs.readdir(path, { withFileTypes: true }, (err, children) => { 67 | if (err) { 68 | return e(err) 69 | } 70 | c(children.map(stat => { 71 | if (stat.isSymbolicLink()) { 72 | return [stat.name, FileType.SymbolicLink] 73 | } else if (stat.isDirectory()) { 74 | return [stat.name, FileType.Directory] 75 | } else if (stat.isFile()) { 76 | return [stat.name, FileType.File] 77 | } else { 78 | return [stat.name, FileType.Unknown] 79 | } 80 | })) 81 | }) 82 | }) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/requests.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri, LanguageClient, RequestType } from 'coc.nvim' 7 | import path from 'path' 8 | import type { Runtime } from './index' 9 | 10 | export interface RequestService { 11 | getContent(uri: string, encoding?: string): Promise 12 | 13 | stat(uri: string): Promise 14 | readDirectory(uri: string): Promise<[string, FileType][]> 15 | } 16 | 17 | export namespace FsContentRequest { 18 | export const type: RequestType<{ uri: string; encoding?: string }, string, any> = new RequestType('fs/content') 19 | } 20 | export namespace FsStatRequest { 21 | export const type: RequestType = new RequestType('fs/stat') 22 | } 23 | 24 | export namespace FsReadDirRequest { 25 | export const type: RequestType = new RequestType('fs/readDir') 26 | } 27 | 28 | export function serveFileSystemRequests(client: LanguageClient, runtime: Runtime) { 29 | client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string }) => { 30 | const uri = Uri.parse(param.uri) 31 | if (uri.scheme === 'file' && runtime.fs) { 32 | return runtime.fs.getContent(param.uri) 33 | } 34 | // return promisify(workspace.fs.readFile(uri).then(buffer => { 35 | // return new runtime.TextDecoder(param.encoding).decode(buffer) 36 | // }) 37 | return undefined 38 | }) 39 | client.onRequest(FsReadDirRequest.type, (uriString: string) => { 40 | const uri = Uri.parse(uriString) 41 | if (uri.scheme === 'file' && runtime.fs) { 42 | return runtime.fs.readDirectory(uriString) 43 | } 44 | return undefined 45 | }) 46 | client.onRequest(FsStatRequest.type, (uriString: string) => { 47 | const uri = Uri.parse(uriString) 48 | if (uri.scheme === 'file' && runtime.fs) { 49 | return runtime.fs.stat(uriString) 50 | } 51 | return undefined 52 | }) 53 | } 54 | 55 | export enum FileType { 56 | /** 57 | * The file type is unknown. 58 | */ 59 | Unknown = 0, 60 | /** 61 | * A regular file. 62 | */ 63 | File = 1, 64 | /** 65 | * A directory. 66 | */ 67 | Directory = 2, 68 | /** 69 | * A symbolic link to a file. 70 | */ 71 | SymbolicLink = 64 72 | } 73 | 74 | export interface FileStat { 75 | /** 76 | * The type of the file, e.g. is a regular file, a directory, or symbolic link 77 | * to a file. 78 | */ 79 | type: FileType 80 | /** 81 | * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. 82 | */ 83 | ctime: number 84 | /** 85 | * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. 86 | */ 87 | mtime: number 88 | /** 89 | * The size in bytes. 90 | */ 91 | size: number 92 | } 93 | 94 | export interface RequestService { 95 | getContent(uri: string, encoding?: string): Promise 96 | 97 | stat(uri: string): Promise 98 | readDirectory(uri: string): Promise<[string, FileType][]> 99 | } 100 | 101 | const Dot = '.'.charCodeAt(0) 102 | 103 | export function resolvePath(uri: Uri, p: string): Uri { 104 | if (path.isAbsolute(p)) { 105 | return uri.with({ path: normalizePath(p.split('/')) }) 106 | } 107 | return joinPath(uri, p) 108 | } 109 | 110 | export function normalizePath(parts: string[]): string { 111 | const newParts: string[] = [] 112 | for (const part of parts) { 113 | if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { 114 | // ignore 115 | } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { 116 | newParts.pop() 117 | } else { 118 | newParts.push(part) 119 | } 120 | } 121 | if (parts.length > 1 && parts[parts.length - 1].length === 0) { 122 | newParts.push('') 123 | } 124 | let res = newParts.join('/') 125 | if (parts[0].length === 0) { 126 | res = '/' + res 127 | } 128 | return res 129 | } 130 | 131 | export function joinPath(uri: Uri, ...paths: string[]): Uri { 132 | const parts = uri.path.split('/') 133 | for (let path of paths) { 134 | parts.push(...path.split('/')) 135 | } 136 | return uri.with({ path: normalizePath(parts) }) 137 | } 138 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "esModuleInterop": true, 5 | "strictNullChecks": false, 6 | "strictFunctionTypes": false, 7 | "allowUnreachableCode": true, 8 | "allowUnusedLabels": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noImplicitAny": false, 11 | "noImplicitReturns": false, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "strictPropertyInitialization": false, 15 | "declaration": false, 16 | "outDir": "lib", 17 | "target": "es2015", 18 | "module": "commonjs", 19 | "moduleResolution": "node", 20 | "lib": ["es2018"], 21 | "plugins": [] 22 | }, 23 | "include": ["src"], 24 | "exclude": [] 25 | } 26 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@^14.14": 6 | version "14.18.33" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.33.tgz#8c29a0036771569662e4635790ffa9e057db379b" 8 | integrity sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg== 9 | 10 | "@vscode/l10n@^0.0.16": 11 | version "0.0.16" 12 | resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" 13 | integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== 14 | 15 | coc.nvim@0.0.83-next.8: 16 | version "0.0.83-next.8" 17 | resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.83-next.8.tgz#98478b703d70de0941731e0b1fdac4c0f9fe5f44" 18 | integrity sha512-ngYyjDnrMfQTffHQeEyvWoLCKEMTx+Z/7GSphyzQ/b3+l0jDHfZJY0lt3e+Vl53og8Ylpa9rF32zjVUmztmxYQ== 19 | 20 | esbuild-android-arm64@0.14.7: 21 | version "0.14.7" 22 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz#8c78cbb617f9f216abfb5a84cca453b51421a1b6" 23 | integrity sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w== 24 | 25 | esbuild-darwin-64@0.14.7: 26 | version "0.14.7" 27 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz#7424bdb64c104556d36b7429af79ab51415ab8f4" 28 | integrity sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ== 29 | 30 | esbuild-darwin-arm64@0.14.7: 31 | version "0.14.7" 32 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.7.tgz#6a243dc0132aeb11c1991f968a6a9e393f43c6bc" 33 | integrity sha512-68e7COhmwIiLXBEyxUxZSSU0akgv8t3e50e2QOtKdBUE0F6KIRISzFntLe2rYlNqSsjGWsIO6CCc9tQxijjSkw== 34 | 35 | esbuild-freebsd-64@0.14.7: 36 | version "0.14.7" 37 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz#e7281e50522e724c4da502504dcd75be0db46c94" 38 | integrity sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ== 39 | 40 | esbuild-freebsd-arm64@0.14.7: 41 | version "0.14.7" 42 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz#31e513098efd181d76a3ba3ea285836d37f018a3" 43 | integrity sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ== 44 | 45 | esbuild-linux-32@0.14.7: 46 | version "0.14.7" 47 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz#82cf96accbf55d3007c3338dc3b3144efa9ae108" 48 | integrity sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A== 49 | 50 | esbuild-linux-64@0.14.7: 51 | version "0.14.7" 52 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz#67bdfe23a6ca918a0bb8e9558a3ee0fdf98c0bc0" 53 | integrity sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg== 54 | 55 | esbuild-linux-arm64@0.14.7: 56 | version "0.14.7" 57 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz#f79c69ff0c176559c418de8e59aa3cf388fff992" 58 | integrity sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg== 59 | 60 | esbuild-linux-arm@0.14.7: 61 | version "0.14.7" 62 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz#3d665b35e1c27dbe1c9deb8bf956d7d1f191a21b" 63 | integrity sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ== 64 | 65 | esbuild-linux-mips64le@0.14.7: 66 | version "0.14.7" 67 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz#226114a0cc6649ba0ffd3428118a8f622872f16d" 68 | integrity sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA== 69 | 70 | esbuild-linux-ppc64le@0.14.7: 71 | version "0.14.7" 72 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz#5c67ae56517f2644d567b2ca5ecb97f9520cfc49" 73 | integrity sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA== 74 | 75 | esbuild-netbsd-64@0.14.7: 76 | version "0.14.7" 77 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz#69dc0469ea089013956d8c6aa71c9e7fc25fc567" 78 | integrity sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw== 79 | 80 | esbuild-openbsd-64@0.14.7: 81 | version "0.14.7" 82 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz#3a9d04ecf820708e2e5b7d26fa7332e3f19f6b6c" 83 | integrity sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw== 84 | 85 | esbuild-sunos-64@0.14.7: 86 | version "0.14.7" 87 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz#7c33a682f0fd9565cae7df165d0e8736b7b62623" 88 | integrity sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg== 89 | 90 | esbuild-windows-32@0.14.7: 91 | version "0.14.7" 92 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz#24ec706a5f25b4499048f56146bcff0ed3839dce" 93 | integrity sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A== 94 | 95 | esbuild-windows-64@0.14.7: 96 | version "0.14.7" 97 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz#dd6d5b5bace93cd7a9174d31fbd727ba21885abf" 98 | integrity sha512-CIGKCFpQOSlYsLMbxt8JjxxvVw9MlF1Rz2ABLVfFyHUF5OeqHD5fPhGrCVNaVrhO8Xrm+yFmtjcZudUGr5/WYQ== 99 | 100 | esbuild-windows-arm64@0.14.7: 101 | version "0.14.7" 102 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz#ecfd9ac289606f26760c4f737caaeeadfff3cfe3" 103 | integrity sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw== 104 | 105 | esbuild@^0.14.7: 106 | version "0.14.7" 107 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.7.tgz#e85cead55b0e1001abf1b2ce4a11c1d4d709d13c" 108 | integrity sha512-+u/msd6iu+HvfysUPkZ9VHm83LImmSNnecYPfFI01pQ7TTcsFR+V0BkybZX7mPtIaI7LCrse6YRj+v3eraJSgw== 109 | optionalDependencies: 110 | esbuild-android-arm64 "0.14.7" 111 | esbuild-darwin-64 "0.14.7" 112 | esbuild-darwin-arm64 "0.14.7" 113 | esbuild-freebsd-64 "0.14.7" 114 | esbuild-freebsd-arm64 "0.14.7" 115 | esbuild-linux-32 "0.14.7" 116 | esbuild-linux-64 "0.14.7" 117 | esbuild-linux-arm "0.14.7" 118 | esbuild-linux-arm64 "0.14.7" 119 | esbuild-linux-mips64le "0.14.7" 120 | esbuild-linux-ppc64le "0.14.7" 121 | esbuild-netbsd-64 "0.14.7" 122 | esbuild-openbsd-64 "0.14.7" 123 | esbuild-sunos-64 "0.14.7" 124 | esbuild-windows-32 "0.14.7" 125 | esbuild-windows-64 "0.14.7" 126 | esbuild-windows-arm64 "0.14.7" 127 | 128 | typescript@^4: 129 | version "4.5.4" 130 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" 131 | integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== 132 | 133 | vscode-css-languageservice@^6.2.7: 134 | version "6.2.7" 135 | resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.7.tgz#d64e347e9a432d2b9c1a12d1ea5bc77996a2e9dc" 136 | integrity sha512-Jd8wpIg5kJ15CfrieoEPvu3gGFc36sbM3qXCtjVq5zrnLEX5NhHxikMDtf8AgQsYklXiDqiZLKoBnzkJtRbTHQ== 137 | dependencies: 138 | "@vscode/l10n" "^0.0.16" 139 | vscode-languageserver-textdocument "^1.0.8" 140 | vscode-languageserver-types "^3.17.3" 141 | vscode-uri "^3.0.7" 142 | 143 | vscode-jsonrpc@8.0.2: 144 | version "8.0.2" 145 | resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz#f239ed2cd6004021b6550af9fd9d3e47eee3cac9" 146 | integrity sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ== 147 | 148 | vscode-jsonrpc@8.2.0-next.2: 149 | version "8.2.0-next.2" 150 | resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" 151 | integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== 152 | 153 | vscode-languageserver-protocol@3.17.4-next.3: 154 | version "3.17.4-next.3" 155 | resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" 156 | integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== 157 | dependencies: 158 | vscode-jsonrpc "8.2.0-next.2" 159 | vscode-languageserver-types "3.17.4-next.2" 160 | 161 | vscode-languageserver-protocol@^3.17.2: 162 | version "3.17.2" 163 | resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz#beaa46aea06ed061576586c5e11368a9afc1d378" 164 | integrity sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg== 165 | dependencies: 166 | vscode-jsonrpc "8.0.2" 167 | vscode-languageserver-types "3.17.2" 168 | 169 | vscode-languageserver-textdocument@^1.0.8: 170 | version "1.0.8" 171 | resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" 172 | integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== 173 | 174 | vscode-languageserver-types@3.17.2: 175 | version "3.17.2" 176 | resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz#b2c2e7de405ad3d73a883e91989b850170ffc4f2" 177 | integrity sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA== 178 | 179 | vscode-languageserver-types@3.17.4-next.2: 180 | version "3.17.4-next.2" 181 | resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" 182 | integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== 183 | 184 | vscode-languageserver-types@^3.17.3: 185 | version "3.17.3" 186 | resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" 187 | integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== 188 | 189 | vscode-languageserver@^8.2.0-next.3: 190 | version "8.2.0-next.3" 191 | resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.3.tgz#72e4998392260173fb0c35d2d556fb4015f56ce3" 192 | integrity sha512-fqHRwcIRoxfKke7iLDSeUmdo3uk7o/uWNn/44xdWa4urdhsvpTZ5c1GsL1EX4TAvdDg0qeXy89NBZ5Gld2DkgQ== 193 | dependencies: 194 | vscode-languageserver-protocol "3.17.4-next.3" 195 | 196 | vscode-uri@^3.0.7: 197 | version "3.0.7" 198 | resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" 199 | integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== 200 | --------------------------------------------------------------------------------