diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 2963ffe11..756faa539 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -40,11 +40,13 @@ }, "devDependencies": { "@types/estree": "^0.0.42", + "@types/globrex": "^0.1.4", "@types/lodash": "^4.14.116", "@types/mocha": "^9.1.0", "@types/node": "^18.0.0", "@types/sinon": "^7.5.2", "cross-env": "^7.0.2", + "globrex": "^0.1.2", "mocha": "^9.2.0", "sinon": "^11.0.0", "ts-node": "^10.0.0" diff --git a/packages/language-server/src/plugins/css/CSSPlugin.ts b/packages/language-server/src/plugins/css/CSSPlugin.ts index 1147bef81..d5a38d427 100644 --- a/packages/language-server/src/plugins/css/CSSPlugin.ts +++ b/packages/language-server/src/plugins/css/CSSPlugin.ts @@ -50,6 +50,7 @@ import { StyleAttributeDocument } from './StyleAttributeDocument'; import { getDocumentContext } from '../documentContext'; import { FoldingRange, FoldingRangeKind } from 'vscode-languageserver-types'; import { indentBasedFoldingRangeForTag } from '../../lib/foldingRange/indentFolding'; +import { isNotNullOrUndefined, urlToPath } from '../../utils'; export class CSSPlugin implements @@ -68,7 +69,7 @@ export class CSSPlugin private cssLanguageServices: CSSLanguageServices; private workspaceFolders: WorkspaceFolder[]; private triggerCharacters = ['.', ':', '-', '/']; - private globalVars = new GlobalVars(); + private globalVars: GlobalVars; constructor( docManager: DocumentManager, @@ -80,6 +81,10 @@ export class CSSPlugin this.workspaceFolders = workspaceFolders; this.configManager = configManager; this.updateConfigs(); + const workspacePaths = workspaceFolders + .map((folder) => urlToPath(folder.uri)) + .filter(isNotNullOrUndefined); + this.globalVars = new GlobalVars(workspacePaths); this.globalVars.watchFiles(this.configManager.get('css.globals')); this.configManager.onChange((config) => { diff --git a/packages/language-server/src/plugins/css/global-vars.ts b/packages/language-server/src/plugins/css/global-vars.ts index 2495b13d2..06c8a571a 100644 --- a/packages/language-server/src/plugins/css/global-vars.ts +++ b/packages/language-server/src/plugins/css/global-vars.ts @@ -1,6 +1,8 @@ -import { watch, FSWatcher } from 'chokidar'; +import { FSWatcher, watch } from 'chokidar'; import { readFile } from 'fs'; -import { isNotNullOrUndefined, flatten } from '../../utils'; +import globrex from 'globrex'; +import { join } from 'path'; +import { flatten, isNotNullOrUndefined, normalizePath } from '../../utils'; const varRegex = /^\s*(--\w+.*?):\s*?([^;]*)/; @@ -12,19 +14,46 @@ export interface GlobalVar { export class GlobalVars { private fsWatcher?: FSWatcher; + private watchedFiles: string | undefined; private globalVars = new Map(); + private readonly workspaceRoot: string[]; + + constructor(workspaceRoot: string[]) { + this.workspaceRoot = workspaceRoot; + } watchFiles(filesToWatch: string): void { - if (!filesToWatch) { + if (!filesToWatch || this.watchedFiles === filesToWatch) { return; } + this.watchedFiles = filesToWatch; if (this.fsWatcher) { this.fsWatcher.close(); this.globalVars.clear(); } - this.fsWatcher = watch(filesToWatch.split(',')) + const paths = new Set(); + const includePatterns = new Set(); + + for (const root of this.workspaceRoot) { + for (const filePath of filesToWatch.split(',')) { + if (!filePath.includes('*')) { + paths.add(filePath); + continue; + } + + const normalizedPath = normalizePath(join(root, filePath)); + includePatterns.add(normalizedPath); + const pathSegments = normalizedPath.split('**'); + let directory = pathSegments[0] || '.'; + paths.add(directory); + } + } + + this.fsWatcher = watch(Array.from(paths), { + ignored: this.createIgnoreMatcher(includePatterns) + }) .addListener('add', (file) => this.updateForFile(file)) .addListener('change', (file) => { this.updateForFile(file); @@ -32,6 +61,20 @@ export class GlobalVars { .addListener('unlink', (file) => this.globalVars.delete(file)); } + private createIgnoreMatcher(includePatterns: Set) { + if (includePatterns.size === 0) { + return undefined; + } + + const regexList = Array.from(includePatterns).map( + (pattern) => globrex(pattern, { globstar: true }).regex + ); + + return (path: string) => { + return !regexList.some((regex) => regex.test(path)); + }; + } + private updateForFile(filename: string) { // Inside a small timeout because it seems chikidar is "too fast" // and reading the file will then return empty content diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7d19793f..e234ca0f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,6 +82,9 @@ importers: '@types/estree': specifier: ^0.0.42 version: 0.0.42 + '@types/globrex': + specifier: ^0.1.4 + version: 0.1.4 '@types/lodash': specifier: ^4.14.116 version: 4.14.194 @@ -97,6 +100,9 @@ importers: cross-env: specifier: ^7.0.2 version: 7.0.3 + globrex: + specifier: ^0.1.2 + version: 0.1.2 mocha: specifier: ^9.2.0 version: 9.2.2 @@ -473,6 +479,9 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/globrex@0.1.4': + resolution: {integrity: sha512-qm82zaOxfn8Us/GGjNrQQ1XfCBUDV86DxQgIQq/p1zGHlt0xnbUiabNjN9rZUhMNvvIE2gg8iTW+GMfw0TnnLg==} + '@types/lodash@4.14.194': resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==} @@ -1553,6 +1562,8 @@ snapshots: '@types/minimatch': 5.1.2 '@types/node': 18.19.46 + '@types/globrex@0.1.4': {} + '@types/lodash@4.14.194': {} '@types/minimatch@5.1.2': {}