Skip to content

Commit

Permalink
#163967 css alias (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
SStranks authored Nov 27, 2023
1 parent 9734506 commit 8090d87
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/cssLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function createFacade(parser: Parser, completion: CSSCompletion, hover: CSSHover
validation.configure(settings);
completion.configure(settings?.completion);
hover.configure(settings?.hover);
navigation.configure(settings?.importAliases);
},
setDataProviders: cssDataManager.setDataProviders.bind(cssDataManager),
doValidation: validation.doValidation.bind(validation),
Expand Down
4 changes: 4 additions & 0 deletions src/cssLanguageTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ export interface LanguageSettings {
lint?: LintSettings;
completion?: CompletionSettings;
hover?: HoverSettings;
importAliases?: AliasSettings;
}

export interface AliasSettings {
[key: string]: string;
}

export interface HoverSettings {
documentation?: boolean;
Expand Down
33 changes: 29 additions & 4 deletions src/services/cssNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

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

export class CSSNavigation {
protected defaultSettings?: AliasSettings;

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

public configure(settings: AliasSettings | undefined) {
this.defaultSettings = settings;
}

public findDefinition(document: TextDocument, position: Position, stylesheet: nodes.Node): Location | null {

const symbols = new Symbols(stylesheet);
Expand Down Expand Up @@ -228,7 +233,7 @@ export class CSSNavigation {
if (!selectionRange || !containsRange(range, selectionRange)) {
selectionRange = Range.create(range.start, range.start);
}

const entry: DocumentSymbol = {
name: name || l10n.t('<undefined>'),
kind,
Expand Down Expand Up @@ -376,7 +381,7 @@ export class CSSNavigation {
return target;
}

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

// Following [css-loader](https://github.com/webpack-contrib/css-loader#url)
// and [sass-loader's](https://github.com/webpack-contrib/sass-loader#imports)
Expand All @@ -403,6 +408,26 @@ export class CSSNavigation {
return moduleReference;
}
}

// Try resolving the reference from the language configuration alias settings
if (ref && !(await this.fileExists(ref))) {
const rootFolderUri = documentContext.resolveReference('/', documentUri);
if (settings && rootFolderUri) {
// Specific file reference
if (target in settings) {
return this.mapReference(joinPath(rootFolderUri, settings[target]), isRawLink);
}
// Reference folder
const firstSlash = target.indexOf('/');
const prefix = `${target.substring(0, firstSlash)}/`;
if (prefix in settings) {
const aliasPath = (settings[prefix]).slice(0, -1);
let newPath = joinPath(rootFolderUri, aliasPath);
return this.mapReference(newPath = joinPath(newPath, target.substring(prefix.length - 1)), isRawLink);
}
}
}

// fall back. it might not exists
return ref;
}
Expand Down Expand Up @@ -522,4 +547,4 @@ function getModuleNameFromPath(path: string) {
}
// Otherwise get until first instance of '/'
return path.substring(0, firstSlash);
}
}
21 changes: 20 additions & 1 deletion src/test/css/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { colorFrom256RGB, colorFromHSL, colorFromHWB } from '../../languageFacts

import {
TextDocument, DocumentHighlightKind, Range, Position, TextEdit, Color,
ColorInformation, DocumentLink, SymbolKind, SymbolInformation, Location, LanguageService, Stylesheet, getCSSLanguageService, DocumentSymbol,
ColorInformation, DocumentLink, SymbolKind, SymbolInformation, Location, LanguageService, Stylesheet, getCSSLanguageService, DocumentSymbol, LanguageSettings,
} from '../../cssLanguageService';

import { URI } from 'vscode-uri';
Expand Down Expand Up @@ -184,6 +184,15 @@ function getCSSLS() {
return getCSSLanguageService({ fileSystemProvider: getFsProvider() });
}

function aliasSettings(): LanguageSettings {
return {
"importAliases": {
"@SingleStylesheet": "/src/assets/styles.css",
"@AssetsDir/": "/src/assets/",
}
};
}

suite('CSS - Navigation', () => {

suite('Scope', () => {
Expand Down Expand Up @@ -364,6 +373,16 @@ suite('CSS - Navigation', () => {
]);
});

test('aliased @import links', async function () {
const settings = aliasSettings();
const ls = getCSSLS();
ls.configure(settings);

await assertLinks(ls, '@import "@SingleStylesheet"', [{ range: newRange(8, 27), target: "test://test/src/assets/styles.css"}]);

await assertLinks(ls, '@import "@AssetsDir/styles.css"', [{ range: newRange(8, 31), target: "test://test/src/assets/styles.css"}]);
});

test('links in rulesets', async () => {
const ls = getCSSLS();
await assertLinks(ls, `body { background-image: url(./foo.jpg)`, [
Expand Down
49 changes: 46 additions & 3 deletions src/test/scss/scssNavigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,33 @@

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

function getSCSSLS() {
return getSCSSLanguageService({ fileSystemProvider: getFsProvider() });
}

async function assertDynamicLinks(docUri: string, input: string, expected: DocumentLink[]) {
function aliasSettings(): LanguageSettings {
return {
"importAliases": {
"@SassStylesheet": "/src/assets/styles.scss",
"@NoUnderscoreDir/": "/noUnderscore/",
"@UnderscoreDir/": "/underscore/",
"@BothDir/": "/both/",
}
};
}

async function assertDynamicLinks(docUri: string, input: string, expected: DocumentLink[], settings?: LanguageSettings) {
const ls = getSCSSLS();
if (settings) {
ls.configure(settings);
}
const document = TextDocument.create(docUri, 'scss', 0, input);

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

});

test('SCSS aliased links', async function () {
const fixtureRoot = path.resolve(__dirname, '../../../../src/test/scss/linkFixture');
const getDocumentUri = (relativePath: string) => {
return URI.file(path.resolve(fixtureRoot, relativePath)).toString(true);
};

const settings = aliasSettings();
const ls = getSCSSLS();
ls.configure(settings);

await assertLinks(ls, '@import "@SassStylesheet"', [{ range: newRange(8, 25), target: "test://test/src/assets/styles.scss"}]);

await assertDynamicLinks(getDocumentUri('./'), `@import '@NoUnderscoreDir/foo'`, [
{ range: newRange(8, 30), target: getDocumentUri('./noUnderscore/foo.scss') }
], settings);

await assertDynamicLinks(getDocumentUri('./'), `@import '@UnderscoreDir/foo'`, [
{ range: newRange(8, 28), target: getDocumentUri('./underscore/_foo.scss') }
], settings);

await assertDynamicLinks(getDocumentUri('./'), `@import '@BothDir/foo'`, [
{ range: newRange(8, 22), target: getDocumentUri('./both/foo.scss') }
], settings);

await assertDynamicLinks(getDocumentUri('./'), `@import '@BothDir/_foo'`, [
{ range: newRange(8, 23), target: getDocumentUri('./both/_foo.scss') }
], settings);
});

test('SCSS module file links', async () => {
const fixtureRoot = path.resolve(__dirname, '../../../../src/test/scss/linkFixture/module');
const getDocumentUri = (relativePath: string) => {
Expand Down

0 comments on commit 8090d87

Please sign in to comment.