From 4bc952a30e66e2a66e83634bce0bcb123365f267 Mon Sep 17 00:00:00 2001 From: William Killerud Date: Sun, 8 Dec 2024 14:06:23 +0100 Subject: [PATCH] refactor: prepare ls for customData --- .../language-services/src/language-feature.ts | 67 +++-------- .../src/language-services-types.ts | 20 ++++ .../src/language-services.ts | 112 +++++++++++++----- 3 files changed, 124 insertions(+), 75 deletions(-) diff --git a/packages/language-services/src/language-feature.ts b/packages/language-services/src/language-feature.ts index e912b06b..124a985c 100644 --- a/packages/language-services/src/language-feature.ts +++ b/packages/language-services/src/language-feature.ts @@ -5,14 +5,11 @@ import { Scanner, SassScanner, } from "@somesass/vscode-css-languageservice"; -import merge from "lodash.merge"; -import { defaultConfiguration } from "./configuration"; import { LanguageModelCache } from "./language-model-cache"; import { LanguageServiceOptions, TextDocument, LanguageService, - LanguageServerConfiguration, NodeType, Range, SassDocumentSymbol, @@ -22,14 +19,13 @@ import { VariableDeclaration, URI, Utils, - ClientCapabilities, - RecursivePartial, LanguageConfiguration, + LanguageServerConfiguration, + ClientCapabilities, } from "./language-services-types"; import { asDollarlessVariable } from "./utils/sass"; export type LanguageFeatureInternal = { - cache: LanguageModelCache; cssLs: VSCodeLanguageService; sassLs: VSCodeLanguageService; scssLs: VSCodeLanguageService; @@ -52,13 +48,19 @@ type FindOptions = { export abstract class LanguageFeature { protected ls; protected options; - protected clientCapabilities: ClientCapabilities; - protected configuration: LanguageServerConfiguration = defaultConfiguration; private _internal: LanguageFeatureInternal; protected get cache(): LanguageModelCache { - return this._internal.cache; + return this.ls.cache; + } + + protected get configuration(): LanguageServerConfiguration { + return this.ls.configuration; + } + + protected get clientCapabilities(): ClientCapabilities { + return this.ls.clientCapabilities; } constructor( @@ -68,59 +70,25 @@ export abstract class LanguageFeature { ) { this.ls = ls; this.options = options; - this.clientCapabilities = options.clientCapabilities; this._internal = _internal; } languageConfiguration(document: TextDocument): LanguageConfiguration { switch (document.languageId) { case "css": { - return this.configuration.css; + return this.ls.configuration.css; } case "sass": { - return this.configuration.sass; + return this.ls.configuration.sass; } case "scss": { - return this.configuration.scss; + return this.ls.configuration.scss; } } throw new Error(`Unsupported language ${document.languageId}`); } - configure( - configuration: RecursivePartial, - ): void { - this.configuration = merge(defaultConfiguration, configuration); - - this._internal.sassLs.configure({ - validate: this.configuration.sass.diagnostics.enabled, - lint: this.configuration.sass.diagnostics.lint, - completion: this.configuration.sass.completion, - hover: this.configuration.sass.hover, - importAliases: this.configuration.workspace.importAliases, - loadPaths: this.configuration.workspace.loadPaths, - }); - - this._internal.scssLs.configure({ - validate: this.configuration.scss.diagnostics.enabled, - lint: this.configuration.scss.diagnostics.lint, - completion: this.configuration.scss.completion, - hover: this.configuration.scss.hover, - importAliases: this.configuration.workspace.importAliases, - loadPaths: this.configuration.workspace.loadPaths, - }); - - this._internal.cssLs.configure({ - validate: this.configuration.css.diagnostics.enabled, - lint: this.configuration.css.diagnostics.lint, - completion: this.configuration.css.completion, - hover: this.configuration.css.hover, - importAliases: this.configuration.workspace.importAliases, - loadPaths: this.configuration.workspace.loadPaths, - }); - } - protected getUpstreamLanguageServer( document: TextDocument, ): VSCodeLanguageService { @@ -140,9 +108,12 @@ export abstract class LanguageFeature { * @returns The resolved path */ resolveReference: (ref: string, base: string) => { - if (ref.startsWith("/") && this.configuration.workspace.workspaceRoot) { + if ( + ref.startsWith("/") && + this.ls.configuration.workspace.workspaceRoot + ) { return Utils.joinPath( - this.configuration.workspace.workspaceRoot, + this.ls.configuration.workspace.workspaceRoot, ref, ).toString(true); } diff --git a/packages/language-services/src/language-services-types.ts b/packages/language-services/src/language-services-types.ts index f451d2c9..5cb90363 100644 --- a/packages/language-services/src/language-services-types.ts +++ b/packages/language-services/src/language-services-types.ts @@ -25,6 +25,7 @@ import { FoldingRange, FoldingRangeKind, SelectionRange, + ICSSDataProvider, } from "@somesass/vscode-css-languageservice"; import type { ParseResult } from "sassdoc-parser"; import { TextDocument } from "vscode-languageserver-textdocument"; @@ -63,6 +64,7 @@ import { } from "vscode-languageserver-types"; import { URI, Utils } from "vscode-uri"; import { FoldingRangeContext } from "./features/folding-ranges"; +import { LanguageModelCache } from "./language-model-cache"; /** * The root of the abstract syntax tree. @@ -83,6 +85,11 @@ export type RecursivePartial = { }; export interface LanguageService { + readonly cache: LanguageModelCache; + readonly clientCapabilities: ClientCapabilities; + readonly configuration: LanguageServerConfiguration; + readonly fs: FileSystemProvider; + /** * Clears all cached documents, forcing everything to be reparsed the next time a feature is used. */ @@ -179,8 +186,21 @@ export interface LanguageService { ): Promise< null | { defaultBehavior: boolean } | { range: Range; placeholder: string } >; + /** + * Load custom data sets, for example custom at-rules, for use in completions and diagnostics. + * + * @see https://github.com/microsoft/vscode-css-languageservice/blob/main/docs/customData.md + */ + setDataProviders( + customDataProviders: ICSSDataProvider[], + options?: SetDataProvidersOptions, + ): void; } +export type SetDataProvidersOptions = { + useDefaultProviders: boolean; +}; + export type Rename = | { range: Range; placeholder: string } | { defaultBehavior: boolean }; diff --git a/packages/language-services/src/language-services.ts b/packages/language-services/src/language-services.ts index 6166d802..6a85d474 100644 --- a/packages/language-services/src/language-services.ts +++ b/packages/language-services/src/language-services.ts @@ -1,7 +1,10 @@ import { getCSSLanguageService, getSassLanguageService, + ICSSDataProvider, + LanguageService as UpstreamLanguageService, } from "@somesass/vscode-css-languageservice"; +import merge from "lodash.merge"; import { defaultConfiguration } from "./configuration"; import { CodeActions } from "./features/code-actions"; import { DoComplete } from "./features/do-complete"; @@ -17,7 +20,7 @@ import { FindReferences } from "./features/find-references"; import { FindSymbols } from "./features/find-symbols"; import { FoldingRangeContext, FoldingRanges } from "./features/folding-ranges"; import { SelectionRanges } from "./features/selection-ranges"; -import { LanguageModelCache as LanguageServerCache } from "./language-model-cache"; +import { LanguageModelCache } from "./language-model-cache"; import { CodeActionContext, LanguageService, @@ -34,6 +37,9 @@ import { Range, ReferenceContext, URI, + SetDataProvidersOptions, + RecursivePartial, + ClientCapabilities, } from "./language-services-types"; import { mapFsProviders } from "./utils/fs-provider"; @@ -55,7 +61,6 @@ export function getLanguageService( } class LanguageServiceImpl implements LanguageService { - #cache: LanguageServerCache; #codeActions: CodeActions; #doComplete: DoComplete; #doDiagnostics: DoDiagnostics; @@ -71,27 +76,55 @@ class LanguageServiceImpl implements LanguageService { #foldingRanges: FoldingRanges; #selectionRanges: SelectionRanges; + #configuration = defaultConfiguration; + get configuration(): LanguageServerConfiguration { + return this.#configuration; + } + + #cache: LanguageModelCache; + get cache(): LanguageModelCache { + return this.#cache; + } + + #clientCapabilities: ClientCapabilities; + get clientCapabilities(): ClientCapabilities { + return this.#clientCapabilities; + } + + #fs: FileSystemProvider; + get fs(): FileSystemProvider { + return this.#fs; + } + + #cssLs: UpstreamLanguageService; + #sassLs: UpstreamLanguageService; + #scssLs: UpstreamLanguageService; + constructor(options: LanguageServiceOptions) { + this.#clientCapabilities = options.clientCapabilities; + this.#fs = options.fileSystemProvider; + const vscodeLsOptions = { - clientCapabilities: options.clientCapabilities, + clientCapabilities: this.clientCapabilities, fileSystemProvider: mapFsProviders(options.fileSystemProvider), }; - const sassLs = getSassLanguageService(vscodeLsOptions); - const cache = new LanguageServerCache({ - sassLs, + this.#cssLs = getCSSLanguageService(vscodeLsOptions); + this.#sassLs = getSassLanguageService(vscodeLsOptions); + // The server code is the same as sassLs, but separate on syntax in case the user has different settings + this.#scssLs = getSassLanguageService(vscodeLsOptions); + + this.#cache = new LanguageModelCache({ + sassLs: this.#sassLs, ...options.languageModelCache, }); const internal = { - cache, - sassLs, - cssLs: getCSSLanguageService(vscodeLsOptions), - // The server code is the same as sassLs, but separate on syntax in case the user has different settings - scssLs: getSassLanguageService(vscodeLsOptions), + cssLs: this.#cssLs, + sassLs: this.#sassLs, + scssLs: this.#scssLs, }; - this.#cache = cache; this.#codeActions = new CodeActions(this, options, internal); this.#doComplete = new DoComplete(this, options, internal); this.#doDiagnostics = new DoDiagnostics(this, options, internal); @@ -112,21 +145,46 @@ class LanguageServiceImpl implements LanguageService { this.#selectionRanges = new SelectionRanges(this, options, internal); } - configure(configuration: LanguageServerConfiguration): void { - this.#codeActions.configure(configuration); - this.#doComplete.configure(configuration); - this.#doDiagnostics.configure(configuration); - this.#doHover.configure(configuration); - this.#doRename.configure(configuration); - this.#doSignatureHelp.configure(configuration); - this.#findColors.configure(configuration); - this.#findDefinition.configure(configuration); - this.#findDocumentHighlights.configure(configuration); - this.#findDocumentLinks.configure(configuration); - this.#findReferences.configure(configuration); - this.#findSymbols.configure(configuration); - this.#foldingRanges.configure(configuration); - this.#selectionRanges.configure(configuration); + configure( + configuration: RecursivePartial, + ): void { + this.#configuration = merge(defaultConfiguration, configuration); + + this.#sassLs.configure({ + validate: this.configuration.sass.diagnostics.enabled, + lint: this.configuration.sass.diagnostics.lint, + completion: this.configuration.sass.completion, + hover: this.configuration.sass.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); + + this.#scssLs.configure({ + validate: this.configuration.scss.diagnostics.enabled, + lint: this.configuration.scss.diagnostics.lint, + completion: this.configuration.scss.completion, + hover: this.configuration.scss.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); + + this.#cssLs.configure({ + validate: this.configuration.css.diagnostics.enabled, + lint: this.configuration.css.diagnostics.lint, + completion: this.configuration.css.completion, + hover: this.configuration.css.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); + } + + setDataProviders( + providers: ICSSDataProvider[], + options: SetDataProvidersOptions = { useDefaultProviders: true }, + ): void { + this.#cssLs.setDataProviders(options.useDefaultProviders, providers); + this.#sassLs.setDataProviders(options.useDefaultProviders, providers); + this.#scssLs.setDataProviders(options.useDefaultProviders, providers); } parseStylesheet(document: TextDocument) {