Skip to content

Commit 06bb2cb

Browse files
committed
refactor: trim down server.ts
1 parent 2bbf830 commit 06bb2cb

File tree

7 files changed

+126
-84
lines changed

7 files changed

+126
-84
lines changed

server/src/cache.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Cache Manager
3+
*
4+
* {
5+
* src/styles/variables.css: {
6+
* },
7+
* all: {
8+
* --red: #355324,
9+
* --green: #664435
10+
* }
11+
* }
12+
*/
13+
14+
export default class Cache<T> {
15+
private cachedVariables: Record<string, Map<string, T>> = {};
16+
private allVariables: Map<string, T>
17+
18+
public get(key: string, filePath?: string) {
19+
if (filePath) {
20+
return this.cachedVariables[filePath]?.get(key);
21+
}
22+
23+
return this.allVariables?.get(key);
24+
}
25+
26+
public getAll() {
27+
return this.allVariables;
28+
}
29+
30+
public set(filePath: string, key: string, value: T) {
31+
if (!this.cachedVariables[filePath]) {
32+
this.cachedVariables[filePath] = new Map();
33+
}
34+
if (!this.allVariables) {
35+
this.allVariables = new Map();
36+
}
37+
38+
this.allVariables?.set(key, value);
39+
this.cachedVariables[filePath].set(key, value);
40+
}
41+
42+
public clearFileCache(filePath: string) {
43+
this.cachedVariables[filePath]?.forEach((_, key) => {
44+
this.allVariables?.delete(key);
45+
});
46+
this.cachedVariables[filePath]?.clear();
47+
}
48+
49+
public clearAllCache() {
50+
this.allVariables?.clear();
51+
this.cachedVariables = {};
52+
}
53+
}

server/src/server.ts

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
import * as fs from 'fs';
2121
import * as path from 'path';
2222
import fastGlob from 'fast-glob';
23-
import lineColumn from 'line-column';
2423

2524
import * as culori from 'culori';
2625

@@ -36,6 +35,12 @@ import { Symbols } from 'vscode-css-languageservice/lib/umd/parser/cssSymbolScop
3635
import isColor from './utils/isColor';
3736
import { uriToPath } from './utils/protocol';
3837
import { pathToFileURL } from 'url';
38+
import { findAll } from './utils/findAll';
39+
import { indexToPosition } from './utils/indexToPosition';
40+
import { culoriColorToVscodeColor } from './utils/culoriColorToVscodeColor';
41+
import { getCurrentWord } from './utils/getCurrentWord';
42+
import { isInFunctionExpression } from './utils/isInFunctionExpression';
43+
import Cache from './cache';
3944

4045
// Create a connection for the server, using Node's IPC as a transport.
4146
// Also include all preview / proposed LSP features.
@@ -60,8 +65,6 @@ type CSSVariable = {
6065
color?: Color
6166
}
6267

63-
let cachedVariables: Record<string, Map<string, CSSVariable>> = {};
64-
6568
export const getLanguageService = (fileExtension: string) => {
6669
switch (fileExtension) {
6770
case '.less':
@@ -74,51 +77,7 @@ export const getLanguageService = (fileExtension: string) => {
7477
}
7578
};
7679

77-
function getCurrentWord(document: TextDocument, offset: number): string {
78-
let left = offset - 1;
79-
let right = offset + 1;
80-
const text = document.getText();
81-
82-
while (left >= 0 && ' \t\n\r":{[()]},*>+'.indexOf(text.charAt(left)) === -1) {
83-
left--;
84-
}
85-
86-
while (
87-
right <= text.length &&
88-
' \t\n\r":{[()]},*>+'.indexOf(text.charAt(right)) === -1
89-
) {
90-
right++;
91-
}
92-
93-
return text.substring(left, right);
94-
}
95-
96-
function isInFunctionExpression(word: string): boolean {
97-
if (word.length < 1) {
98-
return false;
99-
}
100-
101-
return '(' === word.charAt(0);
102-
}
103-
104-
const toRgb = culori.converter('rgb');
105-
106-
export function culoriColorToVscodeColor(color: culori.Color): Color {
107-
const rgb = toRgb(color);
108-
return { red: rgb.r, green: rgb.g, blue: rgb.b, alpha: rgb.alpha ?? 1 };
109-
}
110-
111-
const clearFileCache = (filePath: string) => {
112-
cachedVariables[filePath]?.forEach((_, key) => {
113-
cachedVariables['all']?.delete(key);
114-
});
115-
cachedVariables[filePath]?.clear();
116-
};
117-
118-
const clearAllCache = () => {
119-
cachedVariables['all']?.clear();
120-
cachedVariables = {};
121-
};
80+
const cacheManager = new Cache<CSSVariable>();
12281

12382
const parseCSSVariablesFromText = ({
12483
content,
@@ -129,7 +88,7 @@ const parseCSSVariablesFromText = ({
12988
}) => {
13089
try {
13190
// reset cache for this file
132-
clearFileCache(filePath);
91+
cacheManager.clearFileCache(filePath);
13392

13493
const fileExtension = path.extname(filePath);
13594
const languageService = getLanguageService(fileExtension);
@@ -145,13 +104,6 @@ const parseCSSVariablesFromText = ({
145104

146105
symbolContext.global.symbols.forEach((symbol: CSSSymbol) => {
147106
if (symbol.name.startsWith('--')) {
148-
if (!cachedVariables[filePath]) {
149-
cachedVariables[filePath] = new Map();
150-
}
151-
if (!cachedVariables['all']) {
152-
cachedVariables['all'] = new Map();
153-
}
154-
155107
const variable: CSSVariable = {
156108
symbol,
157109
definition: {
@@ -171,8 +123,7 @@ const parseCSSVariablesFromText = ({
171123
}
172124

173125
// add to cache
174-
cachedVariables['all']?.set(symbol.name, variable);
175-
cachedVariables[filePath].set(symbol.name, variable);
126+
cacheManager.set(filePath, symbol.name, variable);
176127
}
177128
});
178129
} catch (error) {
@@ -300,7 +251,7 @@ connection.onDidChangeConfiguration(async (change) => {
300251
if (hasConfigurationCapability) {
301252
// Reset all cached document settings
302253
documentSettings.clear();
303-
clearAllCache();
254+
cacheManager.clearAllCache();
304255

305256
const validFolders = await connection.workspace
306257
.getWorkspaceFolders()
@@ -346,8 +297,8 @@ connection.onDidChangeWatchedFiles((_change) => {
346297
const filePath = uriToPath(change.uri);
347298
if (filePath) {
348299
// remove variables from cache
349-
if (change.type === FileChangeType.Deleted && cachedVariables[filePath]) {
350-
clearFileCache(filePath);
300+
if (change.type === FileChangeType.Deleted) {
301+
cacheManager.clearFileCache(filePath);
351302
} else {
352303
const content = fs.readFileSync(filePath, 'utf8');
353304
parseCSSVariablesFromText({
@@ -373,7 +324,7 @@ connection.onCompletion(
373324
const isFunctionCall = isInFunctionExpression(currentWord);
374325

375326
const items: CompletionItem[] = [];
376-
cachedVariables['all']?.forEach((variable) => {
327+
cacheManager.getAll().forEach((variable) => {
377328
const varSymbol = variable.symbol;
378329
const insertText = isFunctionCall
379330
? varSymbol.name
@@ -382,6 +333,7 @@ connection.onCompletion(
382333
label: varSymbol.name,
383334
detail: varSymbol.value,
384335
documentation: varSymbol.value,
336+
commitCharacters: [' ', ';', '{', '}'],
385337
insertText,
386338
kind: isColor(varSymbol.value)
387339
? CompletionItemKind.Color
@@ -406,25 +358,6 @@ connection.onCompletionResolve((item: CompletionItem): CompletionItem => {
406358
return item;
407359
});
408360

409-
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
410-
let match: RegExpMatchArray;
411-
const matches: RegExpMatchArray[] = [];
412-
while ((match = re.exec(str)) !== null) {
413-
matches.push({ ...match });
414-
}
415-
return matches;
416-
}
417-
418-
export function indexToPosition(str: string, index: number): Position {
419-
const data = lineColumn(str + '\n', index);
420-
421-
if (!data) {
422-
return { line: 0, character: 0 };
423-
}
424-
const { line, col } = data;
425-
return { line: line - 1, character: col - 1 };
426-
}
427-
428361
connection.onDocumentColor((params): ColorInformation[] => {
429362
const document = documents.get(params.textDocument.uri);
430363
if (!document) {
@@ -442,7 +375,7 @@ connection.onDocumentColor((params): ColorInformation[] => {
442375
const start = indexToPosition(text, match.index + 4);
443376
const end = indexToPosition(text, match.index + match[0].length);
444377

445-
const cssVariable = cachedVariables['all']?.get(match.groups.varName);
378+
const cssVariable = cacheManager.getAll().get(match.groups.varName);
446379

447380
if (cssVariable?.color) {
448381
const range = {
@@ -482,7 +415,7 @@ connection.onHover((params) => {
482415

483416
const nornalizedWord = currentWord.slice(1);
484417

485-
const cssVariable = cachedVariables['all']?.get(nornalizedWord);
418+
const cssVariable = cacheManager.getAll().get(nornalizedWord);
486419

487420
if (cssVariable) {
488421
return {
@@ -504,6 +437,7 @@ connection.onColorPresentation((params) => {
504437

505438
return [];
506439
});
440+
507441
connection.onDefinition((params) => {
508442
const doc = documents.get(params.textDocument.uri);
509443

@@ -517,7 +451,7 @@ connection.onDefinition((params) => {
517451
if (!currentWord) return null;
518452

519453
const nornalizedWord = currentWord.slice(1);
520-
const cssVariable = cachedVariables['all']?.get(nornalizedWord);
454+
const cssVariable = cacheManager.getAll().get(nornalizedWord);
521455

522456
if (cssVariable) {
523457
return cssVariable.definition;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Color } from 'vscode-languageserver/node';
2+
import * as culori from 'culori';
3+
4+
export function culoriColorToVscodeColor(color: culori.Color): Color {
5+
const toRgb = culori.converter('rgb');
6+
const rgb = toRgb(color);
7+
return { red: rgb.r, green: rgb.g, blue: rgb.b, alpha: rgb.alpha ?? 1 };
8+
}

server/src/utils/findAll.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
2+
let match: RegExpMatchArray;
3+
const matches: RegExpMatchArray[] = [];
4+
while ((match = re.exec(str)) !== null) {
5+
matches.push({ ...match });
6+
}
7+
return matches;
8+
}

server/src/utils/getCurrentWord.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { TextDocument } from 'vscode-languageserver-textdocument';
2+
3+
export function getCurrentWord(document: TextDocument, offset: number): string {
4+
let left = offset - 1;
5+
let right = offset + 1;
6+
const text = document.getText();
7+
8+
while (left >= 0 && ' \t\n\r":{[()]},*>+'.indexOf(text.charAt(left)) === -1) {
9+
left--;
10+
}
11+
12+
while (
13+
right <= text.length &&
14+
' \t\n\r":{[()]},*>+'.indexOf(text.charAt(right)) === -1
15+
) {
16+
right++;
17+
}
18+
19+
return text.substring(left, right);
20+
}

server/src/utils/indexToPosition.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import lineColumn from 'line-column';
2+
import { Position } from 'vscode-languageserver-textdocument';
3+
4+
export function indexToPosition(str: string, index: number): Position {
5+
const data = lineColumn(str + '\n', index);
6+
7+
if (!data) {
8+
return { line: 0, character: 0 };
9+
}
10+
const { line, col } = data;
11+
return { line: line - 1, character: col - 1 };
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function isInFunctionExpression(word: string): boolean {
2+
if (word.length < 1) {
3+
return false;
4+
}
5+
6+
return '(' === word.charAt(0);
7+
}

0 commit comments

Comments
 (0)