Skip to content

Commit 55268d0

Browse files
committed
Add css based completions
1 parent 5005c31 commit 55268d0

File tree

10 files changed

+129
-3
lines changed

10 files changed

+129
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ package-lock.json
2121
packages/theme-check-docs-updater/data
2222
packages/lang-jsonc/src/parser.*
2323
.vscode-test-web
24+
.shopify

packages/theme-language-server-common/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@vscode/web-custom-data": "^0.4.6",
3333
"vscode-json-languageservice": "^5.3.10",
3434
"vscode-languageserver": "^8.0.2",
35+
"vscode-css-languageservice": "6.3.2",
3536
"vscode-languageserver-textdocument": "^1.0.8",
3637
"vscode-uri": "^3.0.7"
3738
}

packages/theme-language-server-common/src/completions/CompletionsProvider.ts

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { GetSnippetNamesForURI } from './providers/RenderSnippetCompletionProvid
3030
import { RenderSnippetParameterCompletionProvider } from './providers/RenderSnippetParameterCompletionProvider';
3131
import { LiquidDocTagCompletionProvider } from './providers/LiquidDocTagCompletionProvider';
3232
import { LiquidDocParamTypeCompletionProvider } from './providers/LiquidDocParamTypeCompletionProvider';
33+
import { CompletionsForStyleSheetProvider } from './providers/CompletionsForStyleSheetProvider';
3334

3435
export interface CompletionProviderDependencies {
3536
documentManager: DocumentManager;
@@ -72,6 +73,7 @@ export class CompletionsProvider {
7273
);
7374

7475
this.providers = [
76+
new CompletionsForStyleSheetProvider(),
7577
new ContentForCompletionProvider(),
7678
new ContentForBlockTypeCompletionProvider(getThemeBlockNames),
7779
new ContentForParameterCompletionProvider(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { CompletionItem, TextDocument } from 'vscode-languageserver';
2+
import { LiquidCompletionParams } from '../params';
3+
import { Provider } from './common';
4+
import { getCssLanguageService } from '../../plugins/css';
5+
import { Position } from 'vscode-json-languageservice';
6+
7+
export class CompletionsForStyleSheetProvider implements Provider {
8+
constructor(
9+
) {}
10+
11+
async completions(params: LiquidCompletionParams): Promise<CompletionItem[]> {
12+
if (!params.completionContext) return [];
13+
14+
const { node } = params.completionContext;
15+
const { document } = params;
16+
if (node && document.stylesheet && document.stylesheet.cssStart >= node.position.start && document.stylesheet.cssEnd <= node.position.end) {
17+
const cssLanguageService = getCssLanguageService('css');
18+
const offset = document.stylesheet.cssStart;
19+
const position = offsetToPosition(document.stylesheet.source, offset);
20+
const stylesheet = cssLanguageService.parseStylesheet(document.stylesheet.source);
21+
const completions = cssLanguageService.doComplete(document.stylesheet.source, position, stylesheet, {triggerPropertyValueCompletion: true });
22+
return completions.items;
23+
} else {
24+
return [];
25+
}
26+
}
27+
}
28+
29+
function offsetToPosition(source: TextDocument, offset: number): Position {
30+
const line = source.getText().slice(0, offset).split('\n').length - 1;
31+
const character = offset - source.getText().slice(0, offset).lastIndexOf('\n');
32+
return { line, character };
33+
}

packages/theme-language-server-common/src/documents/DocumentManager.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,17 @@ export class DocumentManager {
181181
textDocument,
182182
};
183183
case SourceCodeType.LiquidHtml:
184+
const stylesheet = sourceCode.source.match(/{% stylesheet %}([\s\S]*?){% endstylesheet %}/)?.[1];
184185
return {
185186
...sourceCode,
186187
textDocument,
187-
188+
stylesheet: stylesheet ? {
189+
source: TextDocument.create(uri + '-css', 'css', sourceCode.version ?? 0, stylesheet),
190+
tagStart: stylesheet.indexOf('{% stylesheet %}'),
191+
tagEnd: stylesheet.indexOf('{% stylesheet %}'),
192+
cssStart: stylesheet.indexOf('{% stylesheet %}') + '{% stylesheet %}'.length,
193+
cssEnd: stylesheet.indexOf('{% endstylesheet %}') + '{% endstylesheet %}'.length,
194+
} : undefined,
188195
/** Lazy and only computed once per file version */
189196
getSchema: memo(async () => {
190197
if (!this.getModeForUri || !this.isValidSchema) return undefined;

packages/theme-language-server-common/src/documents/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ type _AugmentedSourceCode<SCT extends SourceCodeType = SourceCodeType> = SourceC
1616
/** JsonSourceCode + textDocument */
1717
export type AugmentedJsonSourceCode = _AugmentedSourceCode<SourceCodeType.JSON>;
1818

19+
export type StyleSheetTag = {
20+
source: TextDocument;
21+
tagStart: number;
22+
tagEnd: number;
23+
cssStart: number;
24+
cssEnd: number;
25+
}
26+
1927
/**
2028
* AugmentedLiquidSourceCode may hold the schema for the section or block.
2129
*
@@ -25,6 +33,7 @@ export type AugmentedJsonSourceCode = _AugmentedSourceCode<SourceCodeType.JSON>;
2533
export type AugmentedLiquidSourceCode = _AugmentedSourceCode<SourceCodeType.LiquidHtml> & {
2634
getSchema: () => Promise<SectionSchema | ThemeBlockSchema | AppBlockSchema | undefined>;
2735
getLiquidDoc: (snippetName: string) => Promise<SnippetDefinition | undefined>;
36+
stylesheet: StyleSheetTag | undefined;
2837
};
2938

3039
/**

packages/theme-language-server-common/src/hover/HoverProvider.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { HtmlAttributeValueHoverProvider } from './providers/HtmlAttributeValueH
2424
import { findCurrentNode } from '@shopify/theme-check-common';
2525
import { GetThemeSettingsSchemaForURI } from '../settings';
2626
import { LiquidDocTagHoverProvider } from './providers/LiquidDocTagHoverProvider';
27-
27+
import { CssHoverProvider } from './providers/CssHoverProvider';
2828
export class HoverProvider {
2929
private providers: BaseHoverProvider[] = [];
3030

@@ -47,6 +47,7 @@ export class HoverProvider {
4747
getMetafieldDefinitions,
4848
);
4949
this.providers = [
50+
new CssHoverProvider(documentManager),
5051
new LiquidTagHoverProvider(themeDocset),
5152
new LiquidFilterHoverProvider(themeDocset),
5253
new LiquidObjectHoverProvider(typeSystem),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Hover, TextDocument, HoverParams } from 'vscode-languageserver';
2+
import { BaseHoverProvider } from '../BaseHoverProvider';
3+
import { getCssLanguageService } from '../../plugins/css';
4+
import { Position } from 'vscode-json-languageservice';
5+
import { LiquidHtmlNode } from '@shopify/liquid-html-parser';
6+
import { DocumentManager } from '../../documents';
7+
8+
export class CssHoverProvider implements BaseHoverProvider {
9+
constructor(private documentManager: DocumentManager) {}
10+
11+
async hover(currentNode: LiquidHtmlNode, ancestors: LiquidHtmlNode[], params: HoverParams): Promise<Hover | null> {
12+
const node = currentNode;
13+
const document = this.documentManager.get(params.textDocument.uri);
14+
15+
if (document && node && 'stylesheet' in document && document.stylesheet && document.stylesheet.cssStart >= node.position.start && document.stylesheet.cssEnd <= node.position.end) {
16+
const cssLanguageService = getCssLanguageService('css');
17+
const offset = document.stylesheet.cssStart;
18+
const position = offsetToPosition(document.stylesheet.source, offset);
19+
const stylesheet = cssLanguageService.parseStylesheet(document.stylesheet.source);
20+
const information = cssLanguageService.doHover(document.stylesheet.source, position, stylesheet);
21+
return information ? {
22+
contents: information.contents,
23+
range: information.range ? {
24+
start: offsetToPosition(document.stylesheet.source, positionToOffset(document.stylesheet.source, information.range.start) + offset),
25+
end: offsetToPosition(document.stylesheet.source, positionToOffset(document.stylesheet.source, information.range.end) + offset),
26+
} : undefined,
27+
} : null;
28+
} else {
29+
return null;
30+
}
31+
}
32+
}
33+
34+
function offsetToPosition(source: TextDocument, offset: number): Position {
35+
const line = source.getText().slice(0, offset).split('\n').length - 1;
36+
const character = offset - source.getText().slice(0, offset).lastIndexOf('\n');
37+
return { line, character };
38+
}
39+
40+
function positionToOffset(source: TextDocument, position: Position): number {
41+
return source.getText().slice(0, position.line).lastIndexOf('\n') + position.character;
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {
2+
getCSSLanguageService,
3+
LanguageService,
4+
} from 'vscode-css-languageservice';
5+
6+
const cssLanguageService = getCSSLanguageService();
7+
8+
const languageServices = {
9+
css: cssLanguageService,
10+
}
11+
export type CSSLanguageServices = Record<'css' | 'less' | 'scss', LanguageService>;
12+
13+
export function getCssLanguageService( kind: 'css'): LanguageService {
14+
return languageServices[kind];
15+
}

yarn.lock

+16-1
Original file line numberDiff line numberDiff line change
@@ -6679,6 +6679,16 @@ vitest@^2.1.1:
66796679
vite-node "2.1.1"
66806680
why-is-node-running "^2.3.0"
66816681

6682+
6683+
version "6.3.2"
6684+
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz#dd54161776f1663fa514a1b5df0d3990bda604bb"
6685+
integrity sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==
6686+
dependencies:
6687+
"@vscode/l10n" "^0.0.18"
6688+
vscode-languageserver-textdocument "^1.0.12"
6689+
vscode-languageserver-types "3.17.5"
6690+
vscode-uri "^3.0.8"
6691+
66826692
vscode-json-languageservice@^5.3.10:
66836693
version "5.3.10"
66846694
resolved "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.3.10.tgz#7d56872cbb7460baf0491cea31807e537244dbae"
@@ -6717,12 +6727,17 @@ vscode-languageserver-textdocument@^1.0.11, vscode-languageserver-textdocument@^
67176727
resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf"
67186728
integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==
67196729

6730+
vscode-languageserver-textdocument@^1.0.12:
6731+
version "1.0.12"
6732+
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631"
6733+
integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==
6734+
67206735
67216736
version "3.17.3"
67226737
resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz"
67236738
integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==
67246739

6725-
vscode-languageserver-types@^3.17.5:
6740+
vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.17.5:
67266741
version "3.17.5"
67276742
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a"
67286743
integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==

0 commit comments

Comments
 (0)