diff options
Diffstat (limited to 'tools/language-server/src/plugins/css')
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]; -} |