develop
yutent 2023-06-05 18:24:57 +08:00
parent a3dd345793
commit 494b9f68e5
9 changed files with 538 additions and 390 deletions

View File

@ -14,7 +14,7 @@
"css", "css",
"template", "template",
"polymer", "polymer",
"lit-html" "wkit"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
@ -24,8 +24,7 @@
"vscode": "^1.22.0" "vscode": "^1.22.0"
}, },
"scripts": { "scripts": {
"start": "npx tsc --watch -p .", "build": "node build.js"
"build": "npx tsc -p ."
}, },
"categories": [ "categories": [
"Programming Languages" "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": { "dependencies": {
"vscode-css-languageservice": "^3.0.7", "vscode-css-languageservice": "^3.0.7",
"vscode-emmet-helper": "^1.2.0", "vscode-emmet-helper": "^1.2.0",

View File

@ -1,26 +1,28 @@
// Code from https://github.com/Microsoft/typescript-styled-plugin/blob/master/src/styled-template-language-service.ts // Code from https://github.com/Microsoft/typescript-styled-plugin/blob/master/src/styled-template-language-service.ts
export class CompletionsCache { export class CompletionsCache {
_cachedCompletionsFile; _cachedCompletionsFile
_cachedCompletionsPosition; _cachedCompletionsPosition
_cachedCompletionsContent; _cachedCompletionsContent
_completions; _completions
equalPositions(left, right) { equalPositions(left, right) {
return left.line === right.line && left.character === right.character; return left.line === right.line && left.character === right.character
} }
getCached(context, position) { getCached(context, position) {
if (this._completions && if (
this._completions &&
context.fileName === this._cachedCompletionsFile && context.fileName === this._cachedCompletionsFile &&
this._cachedCompletionsPosition && this._cachedCompletionsPosition &&
this.equalPositions(position, this._cachedCompletionsPosition) && this.equalPositions(position, this._cachedCompletionsPosition) &&
context.getText() === this._cachedCompletionsContent) { context.getText() === this._cachedCompletionsContent
return this._completions; ) {
return this._completions
} }
return undefined; return undefined
} }
updateCached(context, position, completions) { updateCached(context, position, completions) {
this._cachedCompletionsFile = context.fileName; this._cachedCompletionsFile = context.fileName
this._cachedCompletionsPosition = position; this._cachedCompletionsPosition = position
this._cachedCompletionsContent = context.getText(); this._cachedCompletionsContent = context.getText()
this._completions = completions; this._completions = completions
} }
} }

View File

@ -1,19 +1,84 @@
import { languages as Languages } from 'vscode'; import { languages as Languages } from 'vscode'
import { HTMLCompletionItemProvider } from './providers/html'; import { HTMLCompletionItemProvider } from './providers/html.js'
import { CSSCompletionItemProvider, HTMLStyleCompletionItemProvider } from './providers/css'; import {
import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover'; CSSCompletionItemProvider,
import { CodeFormatterProvider } from './providers/formatting'; HTMLStyleCompletionItemProvider
} from './providers/css.js'
import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover.js'
import { CodeFormatterProvider } from './providers/formatting.js'
const selector = [ const selector = [
'typescriptreact', 'typescriptreact',
'javascriptreact', 'javascriptreact',
'typescript', 'typescript',
'javascript' 'javascript'
]; ]
export function activate(Context) { export function activate(Context) {
new CodeFormatterProvider(); new CodeFormatterProvider()
Languages.registerCompletionItemProvider(selector, new HTMLCompletionItemProvider(), '<', '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); Languages.registerCompletionItemProvider(
Languages.registerHoverProvider(selector, new HTMLHoverProvider()); selector,
Languages.registerCompletionItemProvider(selector, new HTMLStyleCompletionItemProvider(), '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); new HTMLCompletionItemProvider(),
Languages.registerHoverProvider(selector, new CSSHoverProvider()); '<',
Languages.registerCompletionItemProvider(selector, new CSSCompletionItemProvider(), '!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); '!',
'.',
'}',
':',
'*',
'$',
']',
'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'
)
} }

View File

@ -1,122 +1,150 @@
import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'
import { getSCSSLanguageService as GetSCSSLanguageService } from 'vscode-css-languageservice'; import { getSCSSLanguageService as GetSCSSLanguageService } from 'vscode-css-languageservice'
import * as emmet from 'vscode-emmet-helper'; import * as emmet from 'vscode-emmet-helper'
import { GetEmmetConfiguration, MatchOffset, CreateVirtualDocument, GetLanguageRegions, GetRegionAtOffset, TranslateCompletionItems } from '../util'; import {
import { CompletionsCache } from '../cache'; GetEmmetConfiguration,
MatchOffset,
CreateVirtualDocument,
GetLanguageRegions,
GetRegionAtOffset,
TranslateCompletionItems
} from '../util.js'
import { CompletionsCache } from '../cache.js'
export class HTMLStyleCompletionItemProvider { export class HTMLStyleCompletionItemProvider {
_cssLanguageService = GetSCSSLanguageService(); _cssLanguageService = GetSCSSLanguageService()
_HTMLanguageService = GetHTMLanguageService(); _HTMLanguageService = GetHTMLanguageService()
_expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
_cache = new CompletionsCache(); _cache = new CompletionsCache()
provideCompletionItems(document, position, _token) { provideCompletionItems(document, position, _token) {
const cached = this._cache.getCached(document, position); const cached = this._cache.getCached(document, position)
if (cached) { if (cached) {
return cached; return cached
} }
const currentLine = document.lineAt(position.line); const currentLine = document.lineAt(position.line)
const empty = { const empty = {
isIncomplete: false, isIncomplete: false,
items: [] items: []
};
if (currentLine.isEmptyOrWhitespace) {
return empty;
} }
const currentLineText = currentLine.text.trim(); if (currentLine.isEmptyOrWhitespace) {
const currentOffset = document.offsetAt(position); return empty
const documentText = document.getText(); }
const match = MatchOffset(this._expression, documentText, currentOffset); const currentLineText = currentLine.text.trim()
const currentOffset = document.offsetAt(position)
const documentText = document.getText()
const match = MatchOffset(this._expression, documentText, currentOffset)
if (!match) { if (!match) {
return empty; return empty
} }
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
const matchContent = match[2]; const matchContent = match[2]
const matchStartOffset = match.index + match[1].length; const matchStartOffset = match.index + match[1].length
const matchEndOffset = match.index + match[0].length; const matchEndOffset = match.index + match[0].length
const regions = GetLanguageRegions(this._HTMLanguageService, matchContent); const regions = GetLanguageRegions(this._HTMLanguageService, matchContent)
if (regions.length <= 0) { if (regions.length <= 0) {
return empty; return empty
} }
const region = GetRegionAtOffset(regions, currentOffset - matchStartOffset); const region = GetRegionAtOffset(regions, currentOffset - matchStartOffset)
if (!region) { if (!region) {
return empty; return empty
} }
const virtualOffset = currentOffset - (matchStartOffset + region.start); const virtualOffset = currentOffset - (matchStartOffset + region.start)
const virtualDocument = CreateVirtualDocument('css', region.content); const virtualDocument = CreateVirtualDocument('css', region.content)
const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument)
const emmetResults = { const emmetResults = {
isIncomplete: true, isIncomplete: true,
items: [] 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); 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 { return {
isIncomplete: completions.isIncomplete, isIncomplete: completions.isIncomplete,
items: TranslateCompletionItems(completions.items, currentLine) items: TranslateCompletionItems(completions.items, currentLine)
}; }
} }
resolveCompletionItem(item, _token) { resolveCompletionItem(item, _token) {
return item; return item
} }
} }
export class CSSCompletionItemProvider { export class CSSCompletionItemProvider {
_CSSLanguageService = GetSCSSLanguageService(); _CSSLanguageService = GetSCSSLanguageService()
_expression = /(\/\*\s*(css|less|scss|sass)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*(css|less|scss|sass)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g
_cache = new CompletionsCache(); _cache = new CompletionsCache()
provideCompletionItems(document, position, _token) { provideCompletionItems(document, position, _token) {
const cached = this._cache.getCached(document, position); const cached = this._cache.getCached(document, position)
if (cached) { if (cached) {
return cached; return cached
} }
const currentLine = document.lineAt(position.line); const currentLine = document.lineAt(position.line)
const empty = { const empty = {
isIncomplete: false, isIncomplete: false,
items: [] items: []
}; }
if (currentLine.isEmptyOrWhitespace) { if (currentLine.isEmptyOrWhitespace) {
return empty; return empty
} }
const currentLineText = currentLine.text.trim(); const currentLineText = currentLine.text.trim()
const currentOffset = document.offsetAt(position); const currentOffset = document.offsetAt(position)
const documentText = document.getText(); const documentText = document.getText()
const match = MatchOffset(this._expression, documentText, currentOffset); const match = MatchOffset(this._expression, documentText, currentOffset)
if (!match) { if (!match) {
return empty; return empty
} }
const dialect = match[2]; const dialect = match[2]
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
const matchContent = match[3]; const matchContent = match[3]
const matchStartOffset = match.index + match[1].length; const matchStartOffset = match.index + match[1].length
const matchEndOffset = match.index + match[0].length; const matchEndOffset = match.index + match[0].length
const matchPosition = document.positionAt(matchStartOffset); const matchPosition = document.positionAt(matchStartOffset)
const virtualOffset = currentOffset - matchStartOffset; const virtualOffset = currentOffset - matchStartOffset
const virtualDocument = CreateVirtualDocument(dialect, matchContent); const virtualDocument = CreateVirtualDocument(dialect, matchContent)
const vCss = this._CSSLanguageService.parseStylesheet(virtualDocument); const vCss = this._CSSLanguageService.parseStylesheet(virtualDocument)
const emmetResults = { const emmetResults = {
isIncomplete: true, isIncomplete: true,
items: [] 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); 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 { return {
isIncomplete: completions.isIncomplete, isIncomplete: completions.isIncomplete,
items: TranslateCompletionItems(completions.items, currentLine) items: TranslateCompletionItems(completions.items, currentLine)
}; }
} }
resolveCompletionItem(item, _token) { resolveCompletionItem(item, _token) {
return item; return item
} }
} }

View File

@ -1,40 +1,59 @@
import { Range, WorkspaceEdit, workspace as Workspace, commands as Commands } from 'vscode'; import {
import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; Range,
import { CreateVirtualDocument, TranslateHTMLTextEdits, Match } from '../util'; 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 { export class CodeFormatterProvider {
_expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
document; document
constructor() { constructor() {
Commands.registerTextEditorCommand('editor.action.formatInlineHtml', this.format, this); Commands.registerTextEditorCommand(
'editor.action.formatInlineHtml',
this.format,
this
)
} }
format(textEditor) { format(textEditor) {
this.document = textEditor.document; this.document = textEditor.document
var documentText = this.document.getText(); var documentText = this.document.getText()
var match = Match(this._expression, documentText); var match = Match(this._expression, documentText)
if (!match) { if (!match) {
return []; return []
} }
// TODO - Refactor, This have been used multiple times thourgh out the // TODO - Refactor, This have been used multiple times thourgh out the
// TODO - extension. // TODO - extension.
var matchStartOffset = match.index + match[1].length; var matchStartOffset = match.index + match[1].length
var matchEndOffset = match.index + (match[2].length + match[3].length + 1); var matchEndOffset = match.index + (match[2].length + match[3].length + 1)
var matchStartPosition = this.document.positionAt(matchStartOffset); var matchStartPosition = this.document.positionAt(matchStartOffset)
var matchEndPosition = this.document.positionAt(matchEndOffset); var matchEndPosition = this.document.positionAt(matchEndOffset)
var text = this.document.getText(new Range(matchStartPosition, matchEndPosition)); var text = this.document.getText(
var vHTML = CreateVirtualDocument('html', text); new Range(matchStartPosition, matchEndPosition)
)
var vHTML = CreateVirtualDocument('html', text)
// TODO - Expose Formatting Options // TODO - Expose Formatting Options
const edits = TranslateHTMLTextEdits(GetHTMLanguageService().format(vHTML, null, { const edits = TranslateHTMLTextEdits(
GetHTMLanguageService().format(vHTML, null, {
indentInnerHtml: false, indentInnerHtml: false,
preserveNewLines: true, preserveNewLines: true,
tabSize: textEditor.options.tabSize, tabSize: textEditor.options.tabSize,
insertSpaces: textEditor.options.insertSpaces, insertSpaces: textEditor.options.insertSpaces,
endWithNewline: true endWithNewline: true
}), matchStartPosition.line + 1); }),
Workspace.applyEdit(this.composeEdits(this.document.uri, edits)); matchStartPosition.line + 1
)
Workspace.applyEdit(this.composeEdits(this.document.uri, edits))
} }
composeEdits(uri, edits) { composeEdits(uri, edits) {
var ws = new WorkspaceEdit(); var ws = new WorkspaceEdit()
ws.set(uri, edits); ws.set(uri, edits)
return ws; return ws
} }
} }

View File

@ -1,49 +1,63 @@
import { getLanguageService as GetHtmlLanguageService } from 'vscode-html-languageservice'; import { getLanguageService as GetHtmlLanguageService } from 'vscode-html-languageservice'
import { getCSSLanguageService as GetCssLanguageService } from 'vscode-css-languageservice'; import { getCSSLanguageService as GetCssLanguageService } from 'vscode-css-languageservice'
import { CreateVirtualDocument, MatchOffset } from '../util'; import { CreateVirtualDocument, MatchOffset } from '../util.js'
export class HTMLHoverProvider { export class HTMLHoverProvider {
_htmlLanguageService = GetHtmlLanguageService(); _htmlLanguageService = GetHtmlLanguageService()
_cssLanguageService = GetCssLanguageService(); _cssLanguageService = GetCssLanguageService()
// private _expression = /(html\s*`)([^`]*)(`)/g // private _expression = /(html\s*`)([^`]*)(`)/g
_expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
provideHover(document, position, token) { provideHover(document, position, token) {
const currentOffset = document.offsetAt(position); const currentOffset = document.offsetAt(position)
const documentText = document.getText(); const documentText = document.getText()
const match = MatchOffset(this._expression, documentText, currentOffset); const match = MatchOffset(this._expression, documentText, currentOffset)
if (!match) { if (!match) {
return null; return null
} }
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
const matchContent = match[2]; const matchContent = match[2]
const matchStartOffset = match.index + match[1].length; const matchStartOffset = match.index + match[1].length
const virtualOffset = currentOffset - matchStartOffset; const virtualOffset = currentOffset - matchStartOffset
const virtualDocument = CreateVirtualDocument('html', matchContent); const virtualDocument = CreateVirtualDocument('html', matchContent)
const html = this._htmlLanguageService.parseHTMLDocument(virtualDocument); const html = this._htmlLanguageService.parseHTMLDocument(virtualDocument)
const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument)
const hover = this._htmlLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), html) || const hover =
this._cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet); this._htmlLanguageService.doHover(
return hover; virtualDocument,
virtualDocument.positionAt(virtualOffset),
html
) ||
this._cssLanguageService.doHover(
virtualDocument,
virtualDocument.positionAt(virtualOffset),
stylesheet
)
return hover
} }
} }
export class CSSHoverProvider { export class CSSHoverProvider {
_htmlLanguageService = GetHtmlLanguageService(); _htmlLanguageService = GetHtmlLanguageService()
_cssLanguageService = GetCssLanguageService(); _cssLanguageService = GetCssLanguageService()
_expression = /(\/\*\s*(css|less|scss)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*(css|less|scss)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g
provideHover(document, position, token) { provideHover(document, position, token) {
const currentOffset = document.offsetAt(position); const currentOffset = document.offsetAt(position)
const documentText = document.getText(); const documentText = document.getText()
const match = MatchOffset(this._expression, documentText, currentOffset); const match = MatchOffset(this._expression, documentText, currentOffset)
if (!match) { if (!match) {
return null; return null
} }
const dialect = match[2]; const dialect = match[2]
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
const matchContent = match[3]; const matchContent = match[3]
const matchStartOffset = match.index + match[1].length; const matchStartOffset = match.index + match[1].length
const virtualOffset = currentOffset - matchStartOffset; const virtualOffset = currentOffset - matchStartOffset
const virtualDocument = CreateVirtualDocument(dialect, matchContent); const virtualDocument = CreateVirtualDocument(dialect, matchContent)
const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument); const stylesheet = this._cssLanguageService.parseStylesheet(virtualDocument)
const hover = this._cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet); const hover = this._cssLanguageService.doHover(
return hover; virtualDocument,
virtualDocument.positionAt(virtualOffset),
stylesheet
)
return hover
} }
} }

View File

@ -1,59 +1,75 @@
import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'; import { getLanguageService as GetHTMLanguageService } from 'vscode-html-languageservice'
import * as emmet from 'vscode-emmet-helper'; import * as emmet from 'vscode-emmet-helper'
import { GetEmmetConfiguration, MatchOffset, CreateVirtualDocument, TranslateCompletionItems } from '../util'; import {
import { CompletionsCache } from '../cache'; GetEmmetConfiguration,
MatchOffset,
CreateVirtualDocument,
TranslateCompletionItems
} from '../util.js'
import { CompletionsCache } from '../cache.js'
export class HTMLCompletionItemProvider { export class HTMLCompletionItemProvider {
_htmlLanguageService = GetHTMLanguageService(); _htmlLanguageService = GetHTMLanguageService()
_expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g; _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
// private _expression = /(html\s*`)([^`]*)(`)/g // private _expression = /(html\s*`)([^`]*)(`)/g
_cache = new CompletionsCache(); _cache = new CompletionsCache()
provideCompletionItems(document, position, token) { provideCompletionItems(document, position, token) {
const cached = this._cache.getCached(document, position); const cached = this._cache.getCached(document, position)
if (cached) { if (cached) {
return cached; return cached
} }
const currentLine = document.lineAt(position.line); const currentLine = document.lineAt(position.line)
const empty = { const empty = {
isIncomplete: false, isIncomplete: false,
items: [] items: []
};
if (currentLine.isEmptyOrWhitespace) {
return empty;
} }
const currentLineText = currentLine.text.trim(); if (currentLine.isEmptyOrWhitespace) {
const currentOffset = document.offsetAt(position); return empty
const documentText = document.getText(); }
const match = MatchOffset(this._expression, documentText, currentOffset); const currentLineText = currentLine.text.trim()
const currentOffset = document.offsetAt(position)
const documentText = document.getText()
const match = MatchOffset(this._expression, documentText, currentOffset)
if (!match) { if (!match) {
return empty; return empty
} }
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
const matchContent = match[2]; const matchContent = match[2]
const matchStartOffset = match.index + match[1].length; const matchStartOffset = match.index + match[1].length
const matchEndOffset = match.index + match[0].length; const matchEndOffset = match.index + match[0].length
const matchPosition = document.positionAt(matchStartOffset); const matchPosition = document.positionAt(matchStartOffset)
const virtualOffset = currentOffset - matchStartOffset; const virtualOffset = currentOffset - matchStartOffset
const virtualDocument = CreateVirtualDocument('html', matchContent); const virtualDocument = CreateVirtualDocument('html', matchContent)
const vHtml = this._htmlLanguageService.parseHTMLDocument(virtualDocument); const vHtml = this._htmlLanguageService.parseHTMLDocument(virtualDocument)
const emmetResults = { const emmetResults = {
isIncomplete: true, isIncomplete: true,
items: [] 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); 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 { return {
isIncomplete: completions.isIncomplete, isIncomplete: completions.isIncomplete,
items: TranslateCompletionItems(completions.items, currentLine, true) items: TranslateCompletionItems(completions.items, currentLine, true)
}; }
} }
resolveCompletionItem(item, token) { resolveCompletionItem(item, token) {
return item; return item
} }
} }

View File

@ -1,44 +1,52 @@
import { workspace, TextEdit, Position, Range } from 'vscode'; import { workspace, TextEdit, Position, Range } from 'vscode'
import { TextDocument as HTMLTextDocument, TokenType as HTMLTokenType } from 'vscode-html-languageservice'; import {
TextDocument as HTMLTextDocument,
TokenType as HTMLTokenType
} from 'vscode-html-languageservice'
export function GetEmmetConfiguration() { export function GetEmmetConfiguration() {
const emmetConfig = workspace.getConfiguration('emmet'); const emmetConfig = workspace.getConfiguration('emmet')
return { return {
useNewEmmet: true, useNewEmmet: true,
showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation, showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation,
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions, showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,
syntaxProfiles: emmetConfig.syntaxProfiles, syntaxProfiles: emmetConfig.syntaxProfiles,
variables: emmetConfig.variables variables: emmetConfig.variables
};
} }
}
export function NotNull(input) { export function NotNull(input) {
if (!input) { if (!input) {
return {}; return {}
} }
return input; return input
} }
export function MatchOffset(regex, data, offset) { export function MatchOffset(regex, data, offset) {
regex.exec(null); regex.exec(null)
let match; let match
while ((match = regex.exec(data)) !== null) { while ((match = regex.exec(data)) !== null) {
if (offset > match.index + match[1].length && if (
offset < match.index + match[0].length) { offset > match.index + match[1].length &&
return match; offset < match.index + match[0].length
) {
return match
} }
} }
return null; return null
} }
export function Match(regex, data) { export function Match(regex, data) {
regex.exec(null); regex.exec(null)
let match; let match
while ((match = regex.exec(data)) !== null) { while ((match = regex.exec(data)) !== null) {
return match; return match
} }
return null; return null
} }
export function GetLanguageRegions(service, data) { export function GetLanguageRegions(service, data) {
const scanner = service.createScanner(data); const scanner = service.createScanner(data)
const regions = []; const regions = []
let tokenType; let tokenType
while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) { while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) {
switch (tokenType) { switch (tokenType) {
case HTMLTokenType.Styles: case HTMLTokenType.Styles:
@ -48,57 +56,71 @@ export function GetLanguageRegions(service, data) {
end: scanner.getTokenEnd(), end: scanner.getTokenEnd(),
length: scanner.getTokenLength(), length: scanner.getTokenLength(),
content: scanner.getTokenText() content: scanner.getTokenText()
}); })
break; break
default: default:
break; break
} }
} }
return regions; return regions
} }
export function GetRegionAtOffset(regions, offset) { export function GetRegionAtOffset(regions, offset) {
for (let region of regions) { for (let region of regions) {
if (region.start <= offset) { if (region.start <= offset) {
if (offset <= region.end) { if (offset <= region.end) {
return region; return region
}
} else {
break
} }
} }
else { return null
break;
}
}
return null;
} }
export function TranslateHTMLTextEdits(input, offset) { export function TranslateHTMLTextEdits(input, offset) {
return input.map((item) => { return input.map(item => {
const startPosition = new Position(item.range.start.line + offset, item.range.start.character); const startPosition = new Position(
const endPosition = new Position(item.range.end.line + offset - 1, item.range.end.character); item.range.start.line + offset,
const itemRange = new Range(startPosition, endPosition); item.range.start.character
return new TextEdit(itemRange, item.newText); )
}); 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) { export function TranslateCompletionItems(items, line, expand = false) {
return items.map((item) => { return items.map(item => {
const result = 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)); const range = new Range(
result.textEdit = null; 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 // @ts-ignore - setting range for intellisense to show results properly
result.range = range; result.range = range
if (expand) { if (expand) {
// i use this to both expand html abbreviations and auto complete tags // i use this to both expand html abbreviations and auto complete tags
result.command = { result.command = {
title: 'Emmet Expand Abbreviation', title: 'Emmet Expand Abbreviation',
command: 'editor.emmet.action.expandAbbreviation' command: 'editor.emmet.action.expandAbbreviation'
};
} }
return result; }
}); return result
})
} }
export function CreateVirtualDocument( export function CreateVirtualDocument(
// context: TextDocument | HTMLTextDocument, // context: TextDocument | HTMLTextDocument,
languageId, languageId,
// position: Position | HtmlPosition, // position: Position | HtmlPosition,
content) { content
const doc = HTMLTextDocument.create(`embedded://document.${languageId}`, languageId, 1, content); ) {
return doc; const doc = HTMLTextDocument.create(
`embedded://document.${languageId}`,
languageId,
1,
content
)
return doc
} }

View File

@ -1,11 +0,0 @@
{
"compilerOptions": {
"module": "esnext",
"target": "esnext",
"sourceMap": false,
"outDir": "dist",
"rootDir": "src",
"lib": ["esnext"]
},
"exclude": ["node_modules", "tests"]
}