diff --git a/package.json b/package.json index bf40ae9..1b079aa 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "css", "template", "polymer", - "lit-html" + "wkit" ], "repository": { "type": "git", @@ -24,8 +24,7 @@ "vscode": "^1.22.0" }, "scripts": { - "start": "npx tsc --watch -p .", - "build": "npx tsc -p ." + "build": "node build.js" }, "categories": [ "Programming Languages" @@ -111,12 +110,6 @@ } ] }, - "devDependencies": { - "@types/node": "^18.14.5", - "@types/vscode": "^1.22.0", - "typescript": "^4.9.5", - "vscode-languageserver-types": "^3.6.0" - }, "dependencies": { "vscode-css-languageservice": "^3.0.7", "vscode-emmet-helper": "^1.2.0", diff --git a/src/cache.js b/src/cache.js index 4be1d71..b16f5a9 100644 --- a/src/cache.js +++ b/src/cache.js @@ -1,26 +1,28 @@ // Code from https://github.com/Microsoft/typescript-styled-plugin/blob/master/src/styled-template-language-service.ts export class CompletionsCache { - _cachedCompletionsFile; - _cachedCompletionsPosition; - _cachedCompletionsContent; - _completions; - equalPositions(left, right) { - return left.line === right.line && left.character === right.character; - } - getCached(context, position) { - if (this._completions && - context.fileName === this._cachedCompletionsFile && - this._cachedCompletionsPosition && - this.equalPositions(position, this._cachedCompletionsPosition) && - context.getText() === this._cachedCompletionsContent) { - return this._completions; - } - return undefined; - } - updateCached(context, position, completions) { - this._cachedCompletionsFile = context.fileName; - this._cachedCompletionsPosition = position; - this._cachedCompletionsContent = context.getText(); - this._completions = completions; + _cachedCompletionsFile + _cachedCompletionsPosition + _cachedCompletionsContent + _completions + equalPositions(left, right) { + return left.line === right.line && left.character === right.character + } + getCached(context, position) { + if ( + this._completions && + context.fileName === this._cachedCompletionsFile && + this._cachedCompletionsPosition && + this.equalPositions(position, this._cachedCompletionsPosition) && + context.getText() === this._cachedCompletionsContent + ) { + return this._completions } + return undefined + } + updateCached(context, position, completions) { + this._cachedCompletionsFile = context.fileName + this._cachedCompletionsPosition = position + this._cachedCompletionsContent = context.getText() + this._completions = completions + } } diff --git a/src/main.js b/src/main.js index 086cc7c..7b5863c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,19 +1,84 @@ -import { languages as Languages } from 'vscode'; -import { HTMLCompletionItemProvider } from './providers/html'; -import { CSSCompletionItemProvider, HTMLStyleCompletionItemProvider } from './providers/css'; -import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover'; -import { CodeFormatterProvider } from './providers/formatting'; +import { languages as Languages } from 'vscode' +import { HTMLCompletionItemProvider } from './providers/html.js' +import { + CSSCompletionItemProvider, + HTMLStyleCompletionItemProvider +} from './providers/css.js' +import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover.js' +import { CodeFormatterProvider } from './providers/formatting.js' const selector = [ - 'typescriptreact', - 'javascriptreact', - 'typescript', - 'javascript' -]; + 'typescriptreact', + 'javascriptreact', + 'typescript', + 'javascript' +] + export function activate(Context) { - new CodeFormatterProvider(); - Languages.registerCompletionItemProvider(selector, new HTMLCompletionItemProvider(), '<', '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); - Languages.registerHoverProvider(selector, new HTMLHoverProvider()); - Languages.registerCompletionItemProvider(selector, new HTMLStyleCompletionItemProvider(), '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); - Languages.registerHoverProvider(selector, new CSSHoverProvider()); - Languages.registerCompletionItemProvider(selector, new CSSCompletionItemProvider(), '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); + new CodeFormatterProvider() + Languages.registerCompletionItemProvider( + selector, + new HTMLCompletionItemProvider(), + '<', + '!', + '.', + '}', + ':', + '*', + '$', + ']', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9' + ) + Languages.registerHoverProvider(selector, new HTMLHoverProvider()) + Languages.registerCompletionItemProvider( + selector, + new HTMLStyleCompletionItemProvider(), + '!', + '.', + '}', + ':', + '*', + '$', + ']', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9' + ) + Languages.registerHoverProvider(selector, new CSSHoverProvider()) + Languages.registerCompletionItemProvider( + selector, + new CSSCompletionItemProvider(), + '!', + '.', + '}', + ':', + '*', + '$', + ']', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9' + ) } diff --git a/src/providers/css.js b/src/providers/css.js index edd74b4..f40585f 100644 --- a/src/providers/css.js +++ b/src/providers/css.js @@ -1,122 +1,150 @@ -import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; -import { getSCSSLanguageService as GetSCSSLanguageService } from 'vscode-css-languageservice'; -import * as emmet from 'vscode-emmet-helper'; -import { GetEmmetConfiguration, MatchOffset, CreateVirtualDocument, GetLanguageRegions, GetRegionAtOffset, TranslateCompletionItems } from '../util'; -import { CompletionsCache } from '../cache'; +import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice' +import { getSCSSLanguageService as GetSCSSLanguageService } from 'vscode-css-languageservice' +import * as emmet from 'vscode-emmet-helper' +import { + GetEmmetConfiguration, + MatchOffset, + CreateVirtualDocument, + GetLanguageRegions, + GetRegionAtOffset, + TranslateCompletionItems +} from '../util.js' +import { CompletionsCache } from '../cache.js' + export class HTMLStyleCompletionItemProvider { - _cssLanguageService = GetSCSSLanguageService(); - _HTMLanguageService = GetHTMLanguageService(); - _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; - _cache = new CompletionsCache(); - provideCompletionItems(document, position, _token) { - const cached = this._cache.getCached(document, position); - if (cached) { - return cached; - } - const currentLine = document.lineAt(position.line); - const empty = { - isIncomplete: false, - items: [] - }; - if (currentLine.isEmptyOrWhitespace) { - return empty; - } - const currentLineText = currentLine.text.trim(); - const currentOffset = document.offsetAt(position); - const documentText = document.getText(); - const match = MatchOffset(this._expression, documentText, currentOffset); - if (!match) { - return empty; - } - // tslint:disable-next-line:no-magic-numbers - const matchContent = match[2]; - const matchStartOffset = match.index + match[1].length; - const matchEndOffset = match.index + match[0].length; - const regions = GetLanguageRegions(this._HTMLanguageService, matchContent); - if (regions.length <= 0) { - return empty; - } - const region = GetRegionAtOffset(regions, currentOffset - matchStartOffset); - if (!region) { - return empty; - } - const virtualOffset = currentOffset - (matchStartOffset + region.start); - const virtualDocument = CreateVirtualDocument('css', region.content); - const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); - const emmetResults = { - isIncomplete: true, - items: [] - }; - this._cssLanguageService.setCompletionParticipants([ - emmet.getEmmetCompletionParticipants(virtualDocument, virtualDocument.positionAt(virtualOffset), 'css', GetEmmetConfiguration(), emmetResults) - ]); - const completions = this._cssLanguageService.doComplete(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet); - if (emmetResults.items.length) { - completions.items.push(...emmetResults.items); - completions.isIncomplete = true; - } - this._cache.updateCached(document, position, completions); - return { - isIncomplete: completions.isIncomplete, - items: TranslateCompletionItems(completions.items, currentLine) - }; + _cssLanguageService = GetSCSSLanguageService() + _HTMLanguageService = GetHTMLanguageService() + _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g + _cache = new CompletionsCache() + provideCompletionItems(document, position, _token) { + const cached = this._cache.getCached(document, position) + if (cached) { + return cached } - resolveCompletionItem(item, _token) { - return item; + const currentLine = document.lineAt(position.line) + const empty = { + isIncomplete: false, + items: [] } + if (currentLine.isEmptyOrWhitespace) { + return empty + } + const currentLineText = currentLine.text.trim() + const currentOffset = document.offsetAt(position) + const documentText = document.getText() + const match = MatchOffset(this._expression, documentText, currentOffset) + if (!match) { + return empty + } + // tslint:disable-next-line:no-magic-numbers + const matchContent = match[2] + const matchStartOffset = match.index + match[1].length + const matchEndOffset = match.index + match[0].length + const regions = GetLanguageRegions(this._HTMLanguageService, matchContent) + if (regions.length <= 0) { + return empty + } + const region = GetRegionAtOffset(regions, currentOffset - matchStartOffset) + if (!region) { + return empty + } + const virtualOffset = currentOffset - (matchStartOffset + region.start) + const virtualDocument = CreateVirtualDocument('css', region.content) + const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument) + const emmetResults = { + isIncomplete: true, + items: [] + } + this._cssLanguageService.setCompletionParticipants([ + emmet.getEmmetCompletionParticipants( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + 'css', + GetEmmetConfiguration(), + emmetResults + ) + ]) + const completions = this._cssLanguageService.doComplete( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + stylesheet + ) + if (emmetResults.items.length) { + completions.items.push(...emmetResults.items) + completions.isIncomplete = true + } + this._cache.updateCached(document, position, completions) + return { + isIncomplete: completions.isIncomplete, + items: TranslateCompletionItems(completions.items, currentLine) + } + } + resolveCompletionItem(item, _token) { + return item + } } export class CSSCompletionItemProvider { - _CSSLanguageService = GetSCSSLanguageService(); - _expression = /(\/\*\s*(css|less|scss|sass)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g; - _cache = new CompletionsCache(); - provideCompletionItems(document, position, _token) { - const cached = this._cache.getCached(document, position); - if (cached) { - return cached; - } - const currentLine = document.lineAt(position.line); - const empty = { - isIncomplete: false, - items: [] - }; - if (currentLine.isEmptyOrWhitespace) { - return empty; - } - const currentLineText = currentLine.text.trim(); - const currentOffset = document.offsetAt(position); - const documentText = document.getText(); - const match = MatchOffset(this._expression, documentText, currentOffset); - if (!match) { - return empty; - } - const dialect = match[2]; - // tslint:disable-next-line:no-magic-numbers - const matchContent = match[3]; - const matchStartOffset = match.index + match[1].length; - const matchEndOffset = match.index + match[0].length; - const matchPosition = document.positionAt(matchStartOffset); - const virtualOffset = currentOffset - matchStartOffset; - const virtualDocument = CreateVirtualDocument(dialect, matchContent); - const vCss = this._CSSLanguageService.parseStylesheet(virtualDocument); - const emmetResults = { - isIncomplete: true, - items: [] - }; - this._CSSLanguageService.setCompletionParticipants([ - emmet.getEmmetCompletionParticipants(virtualDocument, virtualDocument.positionAt(virtualOffset), dialect, GetEmmetConfiguration(), emmetResults) - ]); - const completions = this._CSSLanguageService.doComplete(virtualDocument, virtualDocument.positionAt(virtualOffset), vCss); - if (emmetResults.items.length) { - completions.items.push(...emmetResults.items); - completions.isIncomplete = true; - } - this._cache.updateCached(document, position, completions); - return { - isIncomplete: completions.isIncomplete, - items: TranslateCompletionItems(completions.items, currentLine) - }; + _CSSLanguageService = GetSCSSLanguageService() + _expression = /(\/\*\s*(css|less|scss|sass)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g + _cache = new CompletionsCache() + provideCompletionItems(document, position, _token) { + const cached = this._cache.getCached(document, position) + if (cached) { + return cached } - resolveCompletionItem(item, _token) { - return item; + const currentLine = document.lineAt(position.line) + const empty = { + isIncomplete: false, + items: [] } + if (currentLine.isEmptyOrWhitespace) { + return empty + } + const currentLineText = currentLine.text.trim() + const currentOffset = document.offsetAt(position) + const documentText = document.getText() + const match = MatchOffset(this._expression, documentText, currentOffset) + if (!match) { + return empty + } + const dialect = match[2] + // tslint:disable-next-line:no-magic-numbers + const matchContent = match[3] + const matchStartOffset = match.index + match[1].length + const matchEndOffset = match.index + match[0].length + const matchPosition = document.positionAt(matchStartOffset) + const virtualOffset = currentOffset - matchStartOffset + const virtualDocument = CreateVirtualDocument(dialect, matchContent) + const vCss = this._CSSLanguageService.parseStylesheet(virtualDocument) + const emmetResults = { + isIncomplete: true, + items: [] + } + this._CSSLanguageService.setCompletionParticipants([ + emmet.getEmmetCompletionParticipants( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + dialect, + GetEmmetConfiguration(), + emmetResults + ) + ]) + const completions = this._CSSLanguageService.doComplete( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + vCss + ) + if (emmetResults.items.length) { + completions.items.push(...emmetResults.items) + completions.isIncomplete = true + } + this._cache.updateCached(document, position, completions) + return { + isIncomplete: completions.isIncomplete, + items: TranslateCompletionItems(completions.items, currentLine) + } + } + resolveCompletionItem(item, _token) { + return item + } } diff --git a/src/providers/formatting.js b/src/providers/formatting.js index fed3081..ee8a5ce 100644 --- a/src/providers/formatting.js +++ b/src/providers/formatting.js @@ -1,40 +1,59 @@ -import { Range, WorkspaceEdit, workspace as Workspace, commands as Commands } from 'vscode'; -import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; -import { CreateVirtualDocument, TranslateHTMLTextEdits, Match } from '../util'; +import { + Range, + WorkspaceEdit, + workspace as Workspace, + commands as Commands +} from 'vscode' +import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice' +import { + CreateVirtualDocument, + TranslateHTMLTextEdits, + Match +} from '../util.js' + export class CodeFormatterProvider { - _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; - document; - constructor() { - Commands.registerTextEditorCommand('editor.action.formatInlineHtml', this.format, this); - } - format(textEditor) { - this.document = textEditor.document; - var documentText = this.document.getText(); - var match = Match(this._expression, documentText); - if (!match) { - return []; - } - // TODO - Refactor, This have been used multiple times thourgh out the - // TODO - extension. - var matchStartOffset = match.index + match[1].length; - var matchEndOffset = match.index + (match[2].length + match[3].length + 1); - var matchStartPosition = this.document.positionAt(matchStartOffset); - var matchEndPosition = this.document.positionAt(matchEndOffset); - var text = this.document.getText(new Range(matchStartPosition, matchEndPosition)); - var vHTML = CreateVirtualDocument('html', text); - // TODO - Expose Formatting Options - const edits = TranslateHTMLTextEdits(GetHTMLanguageService().format(vHTML, null, { - indentInnerHtml: false, - preserveNewLines: true, - tabSize: textEditor.options.tabSize, - insertSpaces: textEditor.options.insertSpaces, - endWithNewline: true - }), matchStartPosition.line + 1); - Workspace.applyEdit(this.composeEdits(this.document.uri, edits)); - } - composeEdits(uri, edits) { - var ws = new WorkspaceEdit(); - ws.set(uri, edits); - return ws; + _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g + document + constructor() { + Commands.registerTextEditorCommand( + 'editor.action.formatInlineHtml', + this.format, + this + ) + } + format(textEditor) { + this.document = textEditor.document + var documentText = this.document.getText() + var match = Match(this._expression, documentText) + if (!match) { + return [] } + // TODO - Refactor, This have been used multiple times thourgh out the + // TODO - extension. + var matchStartOffset = match.index + match[1].length + var matchEndOffset = match.index + (match[2].length + match[3].length + 1) + var matchStartPosition = this.document.positionAt(matchStartOffset) + var matchEndPosition = this.document.positionAt(matchEndOffset) + var text = this.document.getText( + new Range(matchStartPosition, matchEndPosition) + ) + var vHTML = CreateVirtualDocument('html', text) + // TODO - Expose Formatting Options + const edits = TranslateHTMLTextEdits( + GetHTMLanguageService().format(vHTML, null, { + indentInnerHtml: false, + preserveNewLines: true, + tabSize: textEditor.options.tabSize, + insertSpaces: textEditor.options.insertSpaces, + endWithNewline: true + }), + matchStartPosition.line + 1 + ) + Workspace.applyEdit(this.composeEdits(this.document.uri, edits)) + } + composeEdits(uri, edits) { + var ws = new WorkspaceEdit() + ws.set(uri, edits) + return ws + } } diff --git a/src/providers/hover.js b/src/providers/hover.js index 1bef9d1..471e774 100644 --- a/src/providers/hover.js +++ b/src/providers/hover.js @@ -1,49 +1,63 @@ -import { getLanguageService as GetHtmlLanguageService } from 'vscode-html-languageservice'; -import { getCSSLanguageService as GetCssLanguageService } from 'vscode-css-languageservice'; -import { CreateVirtualDocument, MatchOffset } from '../util'; +import { getLanguageService as GetHtmlLanguageService } from 'vscode-html-languageservice' +import { getCSSLanguageService as GetCssLanguageService } from 'vscode-css-languageservice' +import { CreateVirtualDocument, MatchOffset } from '../util.js' + export class HTMLHoverProvider { - _htmlLanguageService = GetHtmlLanguageService(); - _cssLanguageService = GetCssLanguageService(); - // private _expression = /(html\s*`)([^`]*)(`)/g - _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; - provideHover(document, position, token) { - const currentOffset = document.offsetAt(position); - const documentText = document.getText(); - const match = MatchOffset(this._expression, documentText, currentOffset); - if (!match) { - return null; - } - // tslint:disable-next-line:no-magic-numbers - const matchContent = match[2]; - const matchStartOffset = match.index + match[1].length; - const virtualOffset = currentOffset - matchStartOffset; - const virtualDocument = CreateVirtualDocument('html', matchContent); - const html = this._htmlLanguageService.parseHTMLDocument(virtualDocument); - const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); - const hover = this._htmlLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), html) || - this._cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet); - return hover; + _htmlLanguageService = GetHtmlLanguageService() + _cssLanguageService = GetCssLanguageService() + // private _expression = /(html\s*`)([^`]*)(`)/g + _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g + provideHover(document, position, token) { + const currentOffset = document.offsetAt(position) + const documentText = document.getText() + const match = MatchOffset(this._expression, documentText, currentOffset) + if (!match) { + return null } + // tslint:disable-next-line:no-magic-numbers + const matchContent = match[2] + const matchStartOffset = match.index + match[1].length + const virtualOffset = currentOffset - matchStartOffset + const virtualDocument = CreateVirtualDocument('html', matchContent) + const html = this._htmlLanguageService.parseHTMLDocument(virtualDocument) + const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument) + const hover = + this._htmlLanguageService.doHover( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + html + ) || + this._cssLanguageService.doHover( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + stylesheet + ) + return hover + } } export class CSSHoverProvider { - _htmlLanguageService = GetHtmlLanguageService(); - _cssLanguageService = GetCssLanguageService(); - _expression = /(\/\*\s*(css|less|scss)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g; - provideHover(document, position, token) { - const currentOffset = document.offsetAt(position); - const documentText = document.getText(); - const match = MatchOffset(this._expression, documentText, currentOffset); - if (!match) { - return null; - } - const dialect = match[2]; - // tslint:disable-next-line:no-magic-numbers - const matchContent = match[3]; - const matchStartOffset = match.index + match[1].length; - const virtualOffset = currentOffset - matchStartOffset; - const virtualDocument = CreateVirtualDocument(dialect, matchContent); - const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); - const hover = this._cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet); - return hover; + _htmlLanguageService = GetHtmlLanguageService() + _cssLanguageService = GetCssLanguageService() + _expression = /(\/\*\s*(css|less|scss)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g + provideHover(document, position, token) { + const currentOffset = document.offsetAt(position) + const documentText = document.getText() + const match = MatchOffset(this._expression, documentText, currentOffset) + if (!match) { + return null } + const dialect = match[2] + // tslint:disable-next-line:no-magic-numbers + const matchContent = match[3] + const matchStartOffset = match.index + match[1].length + const virtualOffset = currentOffset - matchStartOffset + const virtualDocument = CreateVirtualDocument(dialect, matchContent) + const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument) + const hover = this._cssLanguageService.doHover( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + stylesheet + ) + return hover + } } diff --git a/src/providers/html.js b/src/providers/html.js index 40b16fd..c8ce87f 100644 --- a/src/providers/html.js +++ b/src/providers/html.js @@ -1,59 +1,75 @@ -import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; -import * as emmet from 'vscode-emmet-helper'; -import { GetEmmetConfiguration, MatchOffset, CreateVirtualDocument, TranslateCompletionItems } from '../util'; -import { CompletionsCache } from '../cache'; +import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice' +import * as emmet from 'vscode-emmet-helper' +import { + GetEmmetConfiguration, + MatchOffset, + CreateVirtualDocument, + TranslateCompletionItems +} from '../util.js' +import { CompletionsCache } from '../cache.js' + export class HTMLCompletionItemProvider { - _htmlLanguageService = GetHTMLanguageService(); - _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; - // private _expression = /(html\s*`)([^`]*)(`)/g - _cache = new CompletionsCache(); - provideCompletionItems(document, position, token) { - const cached = this._cache.getCached(document, position); - if (cached) { - return cached; - } - const currentLine = document.lineAt(position.line); - const empty = { - isIncomplete: false, - items: [] - }; - if (currentLine.isEmptyOrWhitespace) { - return empty; - } - const currentLineText = currentLine.text.trim(); - const currentOffset = document.offsetAt(position); - const documentText = document.getText(); - const match = MatchOffset(this._expression, documentText, currentOffset); - if (!match) { - return empty; - } - // tslint:disable-next-line:no-magic-numbers - const matchContent = match[2]; - const matchStartOffset = match.index + match[1].length; - const matchEndOffset = match.index + match[0].length; - const matchPosition = document.positionAt(matchStartOffset); - const virtualOffset = currentOffset - matchStartOffset; - const virtualDocument = CreateVirtualDocument('html', matchContent); - const vHtml = this._htmlLanguageService.parseHTMLDocument(virtualDocument); - const emmetResults = { - isIncomplete: true, - items: [] - }; - this._htmlLanguageService.setCompletionParticipants([ - emmet.getEmmetCompletionParticipants(virtualDocument, virtualDocument.positionAt(virtualOffset), 'html', GetEmmetConfiguration(), emmetResults) - ]); - const completions = this._htmlLanguageService.doComplete(virtualDocument, virtualDocument.positionAt(virtualOffset), vHtml); - if (emmetResults.items.length) { - completions.items.push(...emmetResults.items); - completions.isIncomplete = true; - } - this._cache.updateCached(document, position, completions); - return { - isIncomplete: completions.isIncomplete, - items: TranslateCompletionItems(completions.items, currentLine, true) - }; + _htmlLanguageService = GetHTMLanguageService() + _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g + // private _expression = /(html\s*`)([^`]*)(`)/g + _cache = new CompletionsCache() + provideCompletionItems(document, position, token) { + const cached = this._cache.getCached(document, position) + if (cached) { + return cached } - resolveCompletionItem(item, token) { - return item; + const currentLine = document.lineAt(position.line) + const empty = { + isIncomplete: false, + items: [] } + if (currentLine.isEmptyOrWhitespace) { + return empty + } + const currentLineText = currentLine.text.trim() + const currentOffset = document.offsetAt(position) + const documentText = document.getText() + const match = MatchOffset(this._expression, documentText, currentOffset) + if (!match) { + return empty + } + // tslint:disable-next-line:no-magic-numbers + const matchContent = match[2] + const matchStartOffset = match.index + match[1].length + const matchEndOffset = match.index + match[0].length + const matchPosition = document.positionAt(matchStartOffset) + const virtualOffset = currentOffset - matchStartOffset + const virtualDocument = CreateVirtualDocument('html', matchContent) + const vHtml = this._htmlLanguageService.parseHTMLDocument(virtualDocument) + const emmetResults = { + isIncomplete: true, + items: [] + } + this._htmlLanguageService.setCompletionParticipants([ + emmet.getEmmetCompletionParticipants( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + 'html', + GetEmmetConfiguration(), + emmetResults + ) + ]) + const completions = this._htmlLanguageService.doComplete( + virtualDocument, + virtualDocument.positionAt(virtualOffset), + vHtml + ) + if (emmetResults.items.length) { + completions.items.push(...emmetResults.items) + completions.isIncomplete = true + } + this._cache.updateCached(document, position, completions) + return { + isIncomplete: completions.isIncomplete, + items: TranslateCompletionItems(completions.items, currentLine, true) + } + } + resolveCompletionItem(item, token) { + return item + } } diff --git a/src/util.js b/src/util.js index 4a92cb9..38edc75 100644 --- a/src/util.js +++ b/src/util.js @@ -1,104 +1,126 @@ -import { workspace, TextEdit, Position, Range } from 'vscode'; -import { TextDocument as HTMLTextDocument, TokenType as HTMLTokenType } from 'vscode-html-languageservice'; +import { workspace, TextEdit, Position, Range } from 'vscode' +import { + TextDocument as HTMLTextDocument, + TokenType as HTMLTokenType +} from 'vscode-html-languageservice' + export function GetEmmetConfiguration() { - const emmetConfig = workspace.getConfiguration('emmet'); - return { - useNewEmmet: true, - showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation, - showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions, - syntaxProfiles: emmetConfig.syntaxProfiles, - variables: emmetConfig.variables - }; + const emmetConfig = workspace.getConfiguration('emmet') + return { + useNewEmmet: true, + showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation, + showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions, + syntaxProfiles: emmetConfig.syntaxProfiles, + variables: emmetConfig.variables + } } + export function NotNull(input) { - if (!input) { - return {}; - } - return input; + if (!input) { + return {} + } + return input } + export function MatchOffset(regex, data, offset) { - regex.exec(null); - let match; - while ((match = regex.exec(data)) !== null) { - if (offset > match.index + match[1].length && - offset < match.index + match[0].length) { - return match; - } + regex.exec(null) + let match + while ((match = regex.exec(data)) !== null) { + if ( + offset > match.index + match[1].length && + offset < match.index + match[0].length + ) { + return match } - return null; + } + return null } export function Match(regex, data) { - regex.exec(null); - let match; - while ((match = regex.exec(data)) !== null) { - return match; - } - return null; + regex.exec(null) + let match + while ((match = regex.exec(data)) !== null) { + return match + } + return null } export function GetLanguageRegions(service, data) { - const scanner = service.createScanner(data); - const regions = []; - let tokenType; - while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) { - switch (tokenType) { - case HTMLTokenType.Styles: - regions.push({ - languageId: 'css', - start: scanner.getTokenOffset(), - end: scanner.getTokenEnd(), - length: scanner.getTokenLength(), - content: scanner.getTokenText() - }); - break; - default: - break; - } + const scanner = service.createScanner(data) + const regions = [] + let tokenType + while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) { + switch (tokenType) { + case HTMLTokenType.Styles: + regions.push({ + languageId: 'css', + start: scanner.getTokenOffset(), + end: scanner.getTokenEnd(), + length: scanner.getTokenLength(), + content: scanner.getTokenText() + }) + break + default: + break } - return regions; + } + return regions } export function GetRegionAtOffset(regions, offset) { - for (let region of regions) { - if (region.start <= offset) { - if (offset <= region.end) { - return region; - } - } - else { - break; - } + for (let region of regions) { + if (region.start <= offset) { + if (offset <= region.end) { + return region + } + } else { + break } - return null; + } + return null } export function TranslateHTMLTextEdits(input, offset) { - return input.map((item) => { - const startPosition = new Position(item.range.start.line + offset, item.range.start.character); - const endPosition = new Position(item.range.end.line + offset - 1, item.range.end.character); - const itemRange = new Range(startPosition, endPosition); - return new TextEdit(itemRange, item.newText); - }); + return input.map(item => { + const startPosition = new Position( + item.range.start.line + offset, + item.range.start.character + ) + const endPosition = new Position( + item.range.end.line + offset - 1, + item.range.end.character + ) + const itemRange = new Range(startPosition, endPosition) + return new TextEdit(itemRange, item.newText) + }) } export function TranslateCompletionItems(items, line, expand = false) { - return items.map((item) => { - const result = item; - const range = new Range(new Position(line.lineNumber, result.textEdit.range.start.character), new Position(line.lineNumber, result.textEdit.range.end.character)); - result.textEdit = null; - // @ts-ignore - setting range for intellisense to show results properly - result.range = range; - if (expand) { - // i use this to both expand html abbreviations and auto complete tags - result.command = { - title: 'Emmet Expand Abbreviation', - command: 'editor.emmet.action.expandAbbreviation' - }; - } - return result; - }); + return items.map(item => { + const result = item + const range = new Range( + new Position(line.lineNumber, result.textEdit.range.start.character), + new Position(line.lineNumber, result.textEdit.range.end.character) + ) + result.textEdit = null + // @ts-ignore - setting range for intellisense to show results properly + result.range = range + if (expand) { + // i use this to both expand html abbreviations and auto complete tags + result.command = { + title: 'Emmet Expand Abbreviation', + command: 'editor.emmet.action.expandAbbreviation' + } + } + return result + }) } export function CreateVirtualDocument( -// context: TextDocument | HTMLTextDocument, -languageId, -// position: Position | HtmlPosition, -content) { - const doc = HTMLTextDocument.create(`embedded://document.${languageId}`, languageId, 1, content); - return doc; + // context: TextDocument | HTMLTextDocument, + languageId, + // position: Position | HtmlPosition, + content +) { + const doc = HTMLTextDocument.create( + `embedded://document.${languageId}`, + languageId, + 1, + content + ) + return doc } diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 6d6b203..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "module": "esnext", - "target": "esnext", - "sourceMap": false, - "outDir": "dist", - "rootDir": "src", - "lib": ["esnext"] - }, - "exclude": ["node_modules", "tests"] -}