Skip to content

Commit 8090d87

Browse files
authored
#163967 css alias (#368)
1 parent 9734506 commit 8090d87

File tree

5 files changed

+100
-8
lines changed

5 files changed

+100
-8
lines changed

src/cssLanguageService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function createFacade(parser: Parser, completion: CSSCompletion, hover: CSSHover
8080
validation.configure(settings);
8181
completion.configure(settings?.completion);
8282
hover.configure(settings?.hover);
83+
navigation.configure(settings?.importAliases);
8384
},
8485
setDataProviders: cssDataManager.setDataProviders.bind(cssDataManager),
8586
doValidation: validation.doValidation.bind(validation),

src/cssLanguageTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ export interface LanguageSettings {
4747
lint?: LintSettings;
4848
completion?: CompletionSettings;
4949
hover?: HoverSettings;
50+
importAliases?: AliasSettings;
5051
}
5152

53+
export interface AliasSettings {
54+
[key: string]: string;
55+
}
5256

5357
export interface HoverSettings {
5458
documentation?: boolean;

src/services/cssNavigation.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict';
66

77
import {
8-
Color, ColorInformation, ColorPresentation, DocumentHighlight, DocumentHighlightKind, DocumentLink, Location,
8+
AliasSettings, Color, ColorInformation, ColorPresentation, DocumentHighlight, DocumentHighlightKind, DocumentLink, Location,
99
Position, Range, SymbolInformation, SymbolKind, TextEdit, WorkspaceEdit, TextDocument, DocumentContext, FileSystemProvider, FileType, DocumentSymbol
1010
} from '../cssLanguageTypes';
1111
import * as l10n from '@vscode/l10n';
@@ -24,10 +24,15 @@ const startsWithSchemeRegex = /^\w+:\/\//;
2424
const startsWithData = /^data:/;
2525

2626
export class CSSNavigation {
27+
protected defaultSettings?: AliasSettings;
2728

2829
constructor(protected fileSystemProvider: FileSystemProvider | undefined, private readonly resolveModuleReferences: boolean) {
2930
}
3031

32+
public configure(settings: AliasSettings | undefined) {
33+
this.defaultSettings = settings;
34+
}
35+
3136
public findDefinition(document: TextDocument, position: Position, stylesheet: nodes.Node): Location | null {
3237

3338
const symbols = new Symbols(stylesheet);
@@ -228,7 +233,7 @@ export class CSSNavigation {
228233
if (!selectionRange || !containsRange(range, selectionRange)) {
229234
selectionRange = Range.create(range.start, range.start);
230235
}
231-
236+
232237
const entry: DocumentSymbol = {
233238
name: name || l10n.t('<undefined>'),
234239
kind,
@@ -376,7 +381,7 @@ export class CSSNavigation {
376381
return target;
377382
}
378383

379-
protected async resolveReference(target: string, documentUri: string, documentContext: DocumentContext, isRawLink = false): Promise<string | undefined> {
384+
protected async resolveReference(target: string, documentUri: string, documentContext: DocumentContext, isRawLink = false, settings = this.defaultSettings): Promise<string | undefined> {
380385

381386
// Following [css-loader](https://github.com/webpack-contrib/css-loader#url)
382387
// and [sass-loader's](https://github.com/webpack-contrib/sass-loader#imports)
@@ -403,6 +408,26 @@ export class CSSNavigation {
403408
return moduleReference;
404409
}
405410
}
411+
412+
// Try resolving the reference from the language configuration alias settings
413+
if (ref && !(await this.fileExists(ref))) {
414+
const rootFolderUri = documentContext.resolveReference('/', documentUri);
415+
if (settings && rootFolderUri) {
416+
// Specific file reference
417+
if (target in settings) {
418+
return this.mapReference(joinPath(rootFolderUri, settings[target]), isRawLink);
419+
}
420+
// Reference folder
421+
const firstSlash = target.indexOf('/');
422+
const prefix = `${target.substring(0, firstSlash)}/`;
423+
if (prefix in settings) {
424+
const aliasPath = (settings[prefix]).slice(0, -1);
425+
let newPath = joinPath(rootFolderUri, aliasPath);
426+
return this.mapReference(newPath = joinPath(newPath, target.substring(prefix.length - 1)), isRawLink);
427+
}
428+
}
429+
}
430+
406431
// fall back. it might not exists
407432
return ref;
408433
}
@@ -522,4 +547,4 @@ function getModuleNameFromPath(path: string) {
522547
}
523548
// Otherwise get until first instance of '/'
524549
return path.substring(0, firstSlash);
525-
}
550+
}

src/test/css/navigation.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { colorFrom256RGB, colorFromHSL, colorFromHWB } from '../../languageFacts
1212

1313
import {
1414
TextDocument, DocumentHighlightKind, Range, Position, TextEdit, Color,
15-
ColorInformation, DocumentLink, SymbolKind, SymbolInformation, Location, LanguageService, Stylesheet, getCSSLanguageService, DocumentSymbol,
15+
ColorInformation, DocumentLink, SymbolKind, SymbolInformation, Location, LanguageService, Stylesheet, getCSSLanguageService, DocumentSymbol, LanguageSettings,
1616
} from '../../cssLanguageService';
1717

1818
import { URI } from 'vscode-uri';
@@ -184,6 +184,15 @@ function getCSSLS() {
184184
return getCSSLanguageService({ fileSystemProvider: getFsProvider() });
185185
}
186186

187+
function aliasSettings(): LanguageSettings {
188+
return {
189+
"importAliases": {
190+
"@SingleStylesheet": "/src/assets/styles.css",
191+
"@AssetsDir/": "/src/assets/",
192+
}
193+
};
194+
}
195+
187196
suite('CSS - Navigation', () => {
188197

189198
suite('Scope', () => {
@@ -364,6 +373,16 @@ suite('CSS - Navigation', () => {
364373
]);
365374
});
366375

376+
test('aliased @import links', async function () {
377+
const settings = aliasSettings();
378+
const ls = getCSSLS();
379+
ls.configure(settings);
380+
381+
await assertLinks(ls, '@import "@SingleStylesheet"', [{ range: newRange(8, 27), target: "test://test/src/assets/styles.css"}]);
382+
383+
await assertLinks(ls, '@import "@AssetsDir/styles.css"', [{ range: newRange(8, 31), target: "test://test/src/assets/styles.css"}]);
384+
});
385+
367386
test('links in rulesets', async () => {
368387
const ls = getCSSLS();
369388
await assertLinks(ls, `body { background-image: url(./foo.jpg)`, [

src/test/scss/scssNavigation.test.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,33 @@
66

77
import * as nodes from '../../parser/cssNodes';
88
import { assertSymbolsInScope, assertScopesAndSymbols, assertHighlights, assertColorSymbols, assertLinks, newRange, getTestResource, assertDocumentSymbols } from '../css/navigation.test';
9-
import { getSCSSLanguageService, DocumentLink, TextDocument, SymbolKind } from '../../cssLanguageService';
9+
import { getSCSSLanguageService, DocumentLink, TextDocument, SymbolKind, LanguageSettings } from '../../cssLanguageService';
1010
import * as assert from 'assert';
1111
import * as path from 'path';
12-
import { URI, Utils } from 'vscode-uri';
12+
import { URI } from 'vscode-uri';
1313
import { getFsProvider } from '../testUtil/fsProvider';
1414
import { getDocumentContext } from '../testUtil/documentContext';
1515

1616
function getSCSSLS() {
1717
return getSCSSLanguageService({ fileSystemProvider: getFsProvider() });
1818
}
1919

20-
async function assertDynamicLinks(docUri: string, input: string, expected: DocumentLink[]) {
20+
function aliasSettings(): LanguageSettings {
21+
return {
22+
"importAliases": {
23+
"@SassStylesheet": "/src/assets/styles.scss",
24+
"@NoUnderscoreDir/": "/noUnderscore/",
25+
"@UnderscoreDir/": "/underscore/",
26+
"@BothDir/": "/both/",
27+
}
28+
};
29+
}
30+
31+
async function assertDynamicLinks(docUri: string, input: string, expected: DocumentLink[], settings?: LanguageSettings) {
2132
const ls = getSCSSLS();
33+
if (settings) {
34+
ls.configure(settings);
35+
}
2236
const document = TextDocument.create(docUri, 'scss', 0, input);
2337

2438
const stylesheet = ls.parseStylesheet(document);
@@ -177,6 +191,35 @@ suite('SCSS - Navigation', () => {
177191

178192
});
179193

194+
test('SCSS aliased links', async function () {
195+
const fixtureRoot = path.resolve(__dirname, '../../../../src/test/scss/linkFixture');
196+
const getDocumentUri = (relativePath: string) => {
197+
return URI.file(path.resolve(fixtureRoot, relativePath)).toString(true);
198+
};
199+
200+
const settings = aliasSettings();
201+
const ls = getSCSSLS();
202+
ls.configure(settings);
203+
204+
await assertLinks(ls, '@import "@SassStylesheet"', [{ range: newRange(8, 25), target: "test://test/src/assets/styles.scss"}]);
205+
206+
await assertDynamicLinks(getDocumentUri('./'), `@import '@NoUnderscoreDir/foo'`, [
207+
{ range: newRange(8, 30), target: getDocumentUri('./noUnderscore/foo.scss') }
208+
], settings);
209+
210+
await assertDynamicLinks(getDocumentUri('./'), `@import '@UnderscoreDir/foo'`, [
211+
{ range: newRange(8, 28), target: getDocumentUri('./underscore/_foo.scss') }
212+
], settings);
213+
214+
await assertDynamicLinks(getDocumentUri('./'), `@import '@BothDir/foo'`, [
215+
{ range: newRange(8, 22), target: getDocumentUri('./both/foo.scss') }
216+
], settings);
217+
218+
await assertDynamicLinks(getDocumentUri('./'), `@import '@BothDir/_foo'`, [
219+
{ range: newRange(8, 23), target: getDocumentUri('./both/_foo.scss') }
220+
], settings);
221+
});
222+
180223
test('SCSS module file links', async () => {
181224
const fixtureRoot = path.resolve(__dirname, '../../../../src/test/scss/linkFixture/module');
182225
const getDocumentUri = (relativePath: string) => {

0 commit comments

Comments
 (0)