summaryrefslogtreecommitdiff
path: root/tools/language-server/src/plugins/css
diff options
context:
space:
mode:
Diffstat (limited to 'tools/language-server/src/plugins/css')
-rw-r--r--tools/language-server/src/plugins/css/CSSDocument.ts95
-rw-r--r--tools/language-server/src/plugins/css/CSSPlugin.ts119
-rw-r--r--tools/language-server/src/plugins/css/StyleAttributeDocument.ts72
-rw-r--r--tools/language-server/src/plugins/css/features/getIdClassCompletion.ts67
-rw-r--r--tools/language-server/src/plugins/css/service.ts48
5 files changed, 0 insertions, 401 deletions
diff --git a/tools/language-server/src/plugins/css/CSSDocument.ts b/tools/language-server/src/plugins/css/CSSDocument.ts
deleted file mode 100644
index 9f1839678..000000000
--- a/tools/language-server/src/plugins/css/CSSDocument.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { Stylesheet, TextDocument } from 'vscode-css-languageservice';
-import { Position } from 'vscode-languageserver';
-import { getLanguageService } from './service';
-import { Document, DocumentMapper, ReadableDocument, TagInformation } from '../../core/documents/index';
-
-export interface CSSDocumentBase extends DocumentMapper, TextDocument {
- languageId: string;
- stylesheet: Stylesheet;
-}
-
-export class CSSDocument extends ReadableDocument implements DocumentMapper {
- private styleInfo: Pick<TagInformation, 'attributes' | 'start' | 'end'>;
- readonly version = this.parent.version;
-
- public stylesheet: Stylesheet;
- public languageId: string;
-
- constructor(private parent: Document) {
- super();
-
- if (this.parent.styleInfo) {
- this.styleInfo = this.parent.styleInfo;
- } else {
- this.styleInfo = {
- attributes: {},
- start: -1,
- end: -1,
- };
- }
-
- this.languageId = this.language;
- this.stylesheet = getLanguageService(this.language).parseStylesheet(this);
- }
-
- /**
- * Get the fragment position relative to the parent
- * @param pos Position in fragment
- */
- getOriginalPosition(pos: Position): Position {
- const parentOffset = this.styleInfo.start + this.offsetAt(pos);
- return this.parent.positionAt(parentOffset);
- }
-
- /**
- * Get the position relative to the start of the fragment
- * @param pos Position in parent
- */
- getGeneratedPosition(pos: Position): Position {
- const fragmentOffset = this.parent.offsetAt(pos) - this.styleInfo.start;
- return this.positionAt(fragmentOffset);
- }
-
- /**
- * Returns true if the given parent position is inside of this fragment
- * @param pos Position in parent
- */
- isInGenerated(pos: Position): boolean {
- const offset = this.parent.offsetAt(pos);
- return offset >= this.styleInfo.start && offset <= this.styleInfo.end;
- }
-
- /**
- * Get the fragment text from the parent
- */
- getText(): string {
- return this.parent.getText().slice(this.styleInfo.start, this.styleInfo.end);
- }
-
- /**
- * Returns the length of the fragment as calculated from the start and end positon
- */
- getTextLength(): number {
- return this.styleInfo.end - this.styleInfo.start;
- }
-
- /**
- * Return the parent file path
- */
- getFilePath(): string | null {
- return this.parent.getFilePath();
- }
-
- getURL() {
- return this.parent.getURL();
- }
-
- getAttributes() {
- return this.styleInfo.attributes;
- }
-
- private get language() {
- const attrs = this.getAttributes();
- return attrs.lang || attrs.type || 'css';
- }
-}
diff --git a/tools/language-server/src/plugins/css/CSSPlugin.ts b/tools/language-server/src/plugins/css/CSSPlugin.ts
deleted file mode 100644
index 3083edc56..000000000
--- a/tools/language-server/src/plugins/css/CSSPlugin.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import type { CompletionsProvider } from '../interfaces';
-import type { Document, DocumentManager } from '../../core/documents';
-import type { ConfigManager } from '../../core/config';
-import { getEmmetCompletionParticipants, doComplete as doEmmetComplete } from 'vscode-emmet-helper';
-import { CompletionContext, CompletionList, CompletionTriggerKind, Position } from 'vscode-languageserver';
-import { isInsideFrontmatter } from '../../core/documents/utils';
-import { CSSDocument, CSSDocumentBase } from './CSSDocument';
-import { getLanguage, getLanguageService } from './service';
-import { StyleAttributeDocument } from './StyleAttributeDocument';
-import { mapCompletionItemToOriginal } from '../../core/documents';
-import { AttributeContext, getAttributeContextAtPosition } from '../../core/documents/parseHtml';
-import { getIdClassCompletion } from './features/getIdClassCompletion';
-
-export class CSSPlugin implements CompletionsProvider {
- private docManager: DocumentManager;
- private configManager: ConfigManager;
- private documents = new WeakMap<Document, CSSDocument>();
- private triggerCharacters = new Set(['.', ':', '-', '/']);
- public pluginName = 'CSS';
-
- constructor(docManager: DocumentManager, configManager: ConfigManager) {
- this.docManager = docManager;
- this.configManager = configManager;
-
- this.docManager.on('documentChange', (document) => {
- this.documents.set(document, new CSSDocument(document));
- });
- }
-
- getCompletions(document: Document, position: Position, completionContext?: CompletionContext): CompletionList | null {
- const triggerCharacter = completionContext?.triggerCharacter;
- const triggerKind = completionContext?.triggerKind;
- const isCustomTriggerCharacter = triggerKind === CompletionTriggerKind.TriggerCharacter;
-
- if (isCustomTriggerCharacter && triggerCharacter && !this.triggerCharacters.has(triggerCharacter)) {
- return null;
- }
-
- if (this.isInsideFrontmatter(document, position)) {
- return null;
- }
-
- const cssDocument = this.getCSSDoc(document);
-
- if (cssDocument.isInGenerated(position)) {
- return this.getCompletionsInternal(document, position, cssDocument);
- }
-
- const attributeContext = getAttributeContextAtPosition(document, position);
- if (!attributeContext) {
- return null;
- }
-
- if (this.inStyleAttributeWithoutInterpolation(attributeContext, document.getText())) {
- const [start, end] = attributeContext.valueRange;
- return this.getCompletionsInternal(document, position, new StyleAttributeDocument(document, start, end));
- } else {
- return getIdClassCompletion(cssDocument, attributeContext);
- }
- }
-
- private getCompletionsInternal(document: Document, position: Position, cssDocument: CSSDocumentBase) {
- if (isSASS(cssDocument)) {
- // the css language service does not support sass, still we can use
- // the emmet helper directly to at least get emmet completions
- return doEmmetComplete(document, position, 'sass', this.configManager.getEmmetConfig());
- }
-
- const type = extractLanguage(cssDocument);
-
- const lang = getLanguageService(type);
- const emmetResults: CompletionList = {
- isIncomplete: true,
- items: [],
- };
- if (false /* this.configManager.getConfig().css.completions.emmet */) {
- lang.setCompletionParticipants([
- getEmmetCompletionParticipants(cssDocument, cssDocument.getGeneratedPosition(position), getLanguage(type), this.configManager.getEmmetConfig(), emmetResults),
- ]);
- }
- const results = lang.doComplete(cssDocument, cssDocument.getGeneratedPosition(position), cssDocument.stylesheet);
- return CompletionList.create(
- [...(results ? results.items : []), ...emmetResults.items].map((completionItem) => mapCompletionItemToOriginal(cssDocument, completionItem)),
- // Emmet completions change on every keystroke, so they are never complete
- emmetResults.items.length > 0
- );
- }
-
- private inStyleAttributeWithoutInterpolation(attrContext: AttributeContext, text: string): attrContext is Required<AttributeContext> {
- return attrContext.name === 'style' && !!attrContext.valueRange && !text.substring(attrContext.valueRange[0], attrContext.valueRange[1]).includes('{');
- }
-
- private getCSSDoc(document: Document) {
- let cssDoc = this.documents.get(document);
- if (!cssDoc || cssDoc.version < document.version) {
- cssDoc = new CSSDocument(document);
- this.documents.set(document, cssDoc);
- }
- return cssDoc;
- }
-
- private isInsideFrontmatter(document: Document, position: Position) {
- return isInsideFrontmatter(document.getText(), document.offsetAt(position));
- }
-}
-
-function isSASS(document: CSSDocumentBase) {
- switch (extractLanguage(document)) {
- case 'sass':
- return true;
- default:
- return false;
- }
-}
-
-function extractLanguage(document: CSSDocumentBase): string {
- const lang = document.languageId;
- return lang.replace(/^text\//, '');
-}
diff --git a/tools/language-server/src/plugins/css/StyleAttributeDocument.ts b/tools/language-server/src/plugins/css/StyleAttributeDocument.ts
deleted file mode 100644
index e00398037..000000000
--- a/tools/language-server/src/plugins/css/StyleAttributeDocument.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Stylesheet } from 'vscode-css-languageservice';
-import { Position } from 'vscode-languageserver';
-import { getLanguageService } from './service';
-import { Document, DocumentMapper, ReadableDocument } from '../../core/documents';
-
-const PREFIX = '__ {';
-const SUFFIX = '}';
-
-export class StyleAttributeDocument extends ReadableDocument implements DocumentMapper {
- readonly version = this.parent.version;
-
- public stylesheet: Stylesheet;
- public languageId = 'css';
-
- constructor(private readonly parent: Document, private readonly attrStart: number, private readonly attrEnd: number) {
- super();
-
- this.stylesheet = getLanguageService(this.languageId).parseStylesheet(this);
- }
-
- /**
- * Get the fragment position relative to the parent
- * @param pos Position in fragment
- */
- getOriginalPosition(pos: Position): Position {
- const parentOffset = this.attrStart + this.offsetAt(pos) - PREFIX.length;
- return this.parent.positionAt(parentOffset);
- }
-
- /**
- * Get the position relative to the start of the fragment
- * @param pos Position in parent
- */
- getGeneratedPosition(pos: Position): Position {
- const fragmentOffset = this.parent.offsetAt(pos) - this.attrStart + PREFIX.length;
- return this.positionAt(fragmentOffset);
- }
-
- /**
- * Returns true if the given parent position is inside of this fragment
- * @param pos Position in parent
- */
- isInGenerated(pos: Position): boolean {
- const offset = this.parent.offsetAt(pos);
- return offset >= this.attrStart && offset <= this.attrEnd;
- }
-
- /**
- * Get the fragment text from the parent
- */
- getText(): string {
- return PREFIX + this.parent.getText().slice(this.attrStart, this.attrEnd) + SUFFIX;
- }
-
- /**
- * Returns the length of the fragment as calculated from the start and end position
- */
- getTextLength(): number {
- return PREFIX.length + this.attrEnd - this.attrStart + SUFFIX.length;
- }
-
- /**
- * Return the parent file path
- */
- getFilePath(): string | null {
- return this.parent.getFilePath();
- }
-
- getURL() {
- return this.parent.getURL();
- }
-}
diff --git a/tools/language-server/src/plugins/css/features/getIdClassCompletion.ts b/tools/language-server/src/plugins/css/features/getIdClassCompletion.ts
deleted file mode 100644
index 45acb5ad6..000000000
--- a/tools/language-server/src/plugins/css/features/getIdClassCompletion.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { CompletionItem, CompletionItemKind, CompletionList } from 'vscode-languageserver';
-import { AttributeContext } from '../../../core/documents/parseHtml';
-import { CSSDocument } from '../CSSDocument';
-
-export function getIdClassCompletion(cssDoc: CSSDocument, attributeContext: AttributeContext): CompletionList | null {
- const collectingType = getCollectingType(attributeContext);
-
- if (!collectingType) {
- return null;
- }
- const items = collectSelectors(cssDoc.stylesheet as CSSNode, collectingType);
-
- console.log('getIdClassCompletion items', items.length);
- return CompletionList.create(items);
-}
-
-function getCollectingType(attributeContext: AttributeContext): number | undefined {
- if (attributeContext.inValue) {
- if (attributeContext.name === 'class') {
- return NodeType.ClassSelector;
- }
- if (attributeContext.name === 'id') {
- return NodeType.IdentifierSelector;
- }
- } else if (attributeContext.name.startsWith('class:')) {
- return NodeType.ClassSelector;
- }
-}
-
-/**
- * incomplete see
- * https://github.com/microsoft/vscode-css-languageservice/blob/master/src/parser/cssNodes.ts#L14
- * The enum is not exported. we have to update this whenever it changes
- */
-export enum NodeType {
- ClassSelector = 14,
- IdentifierSelector = 15,
-}
-
-export type CSSNode = {
- type: number;
- children: CSSNode[] | undefined;
- getText(): string;
-};
-
-export function collectSelectors(stylesheet: CSSNode, type: number) {
- const result: CSSNode[] = [];
- walk(stylesheet, (node) => {
- if (node.type === type) {
- result.push(node);
- }
- });
-
- return result.map(
- (node): CompletionItem => ({
- label: node.getText().substring(1),
- kind: CompletionItemKind.Keyword,
- })
- );
-}
-
-function walk(node: CSSNode, callback: (node: CSSNode) => void) {
- callback(node);
- if (node.children) {
- node.children.forEach((node) => walk(node, callback));
- }
-}
diff --git a/tools/language-server/src/plugins/css/service.ts b/tools/language-server/src/plugins/css/service.ts
deleted file mode 100644
index 78b11296e..000000000
--- a/tools/language-server/src/plugins/css/service.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageService, ICSSDataProvider } from 'vscode-css-languageservice';
-
-const customDataProvider: ICSSDataProvider = {
- providePseudoClasses() {
- return [];
- },
- provideProperties() {
- return [];
- },
- provideAtDirectives() {
- return [];
- },
- providePseudoElements() {
- return [];
- },
-};
-
-const [css, scss, less] = [getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService].map((getService) =>
- getService({
- customDataProviders: [customDataProvider],
- })
-);
-
-const langs = {
- css,
- scss,
- less,
-};
-
-export function getLanguage(kind?: string) {
- switch (kind) {
- case 'scss':
- case 'text/scss':
- return 'scss' as const;
- case 'less':
- case 'text/less':
- return 'less' as const;
- case 'css':
- case 'text/css':
- default:
- return 'css' as const;
- }
-}
-
-export function getLanguageService(kind?: string): LanguageService {
- const lang = getLanguage(kind);
- return langs[lang];
-}