Compare commits
No commits in common. "develop" and "master" have entirely different histories.
|
@ -5,4 +5,3 @@ tests/**
|
||||||
.vscode/**
|
.vscode/**
|
||||||
.gitignore
|
.gitignore
|
||||||
.git
|
.git
|
||||||
build.mjs
|
|
36
build.mjs
36
build.mjs
|
@ -1,36 +0,0 @@
|
||||||
/**
|
|
||||||
* {build}
|
|
||||||
* @author yutent<yutent.io@gmail.com>
|
|
||||||
* @date 2021/08/09 11:59:41
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Es from 'esbuild'
|
|
||||||
import fs from 'iofs'
|
|
||||||
|
|
||||||
let args = process.argv.slice(2)
|
|
||||||
let entryPoints = fs.ls('./src', true).filter(it => fs.isfile(it))
|
|
||||||
|
|
||||||
if (args.includes('--watch')) {
|
|
||||||
let res = await Es.context({
|
|
||||||
entryPoints,
|
|
||||||
outdir: 'dist',
|
|
||||||
target: 'es2022',
|
|
||||||
format: 'cjs',
|
|
||||||
// external: ['vscode', 'util'],
|
|
||||||
platform: 'node'
|
|
||||||
// bundle: true
|
|
||||||
})
|
|
||||||
console.log('监听文件变化中...\n')
|
|
||||||
await res.watch()
|
|
||||||
} else {
|
|
||||||
Es.build({
|
|
||||||
entryPoints,
|
|
||||||
outdir: 'dist',
|
|
||||||
target: 'es2022',
|
|
||||||
format: 'cjs',
|
|
||||||
minify: true,
|
|
||||||
// external: ['vscode', 'util'],
|
|
||||||
platform: 'node'
|
|
||||||
// bundle: true
|
|
||||||
})
|
|
||||||
}
|
|
67
package.json
67
package.json
|
@ -14,25 +14,27 @@
|
||||||
"css",
|
"css",
|
||||||
"template",
|
"template",
|
||||||
"polymer",
|
"polymer",
|
||||||
"wkit"
|
"lit-html"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/yutent/vscode-string-html"
|
"url": "https://github.com/yutent/vscode-string-html"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.58.0"
|
"vscode": "^1.22.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node build.mjs --watch",
|
"start": "npx tsc --watch -p .",
|
||||||
"build": "node build.mjs"
|
"build": "npx tsc -p ."
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
"Programming Languages"
|
"Programming Languages"
|
||||||
],
|
],
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onLanguage:javascript",
|
"onLanguage:javascript",
|
||||||
"onLanguage:javascriptreact"
|
"onLanguage:typescript",
|
||||||
|
"onLanguage:javascriptreact",
|
||||||
|
"onLanguage:typescriptreact"
|
||||||
],
|
],
|
||||||
"main": "./dist/main.js",
|
"main": "./dist/main.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
@ -45,42 +47,79 @@
|
||||||
"grammars": [
|
"grammars": [
|
||||||
{
|
{
|
||||||
"injectTo": [
|
"injectTo": [
|
||||||
"source.js"
|
"source.js",
|
||||||
|
"source.js.jsx",
|
||||||
|
"source.jsx",
|
||||||
|
"source.ts",
|
||||||
|
"source.ts.tsx",
|
||||||
|
"source.tsx"
|
||||||
],
|
],
|
||||||
"scopeName": "es6.inline.html",
|
"scopeName": "es6.inline.html",
|
||||||
"path": "./syntaxes/es6.inline.html.json",
|
"path": "./syntaxes/es6.inline.html.json",
|
||||||
"embeddedLanguages": {
|
"embeddedLanguages": {
|
||||||
"meta.embedded.block.html": "html",
|
"meta.embedded.block.html": "html",
|
||||||
"meta.template.expression.js": "javascript"
|
"meta.template.expression.ts": "typescript"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"injectTo": [
|
"injectTo": [
|
||||||
"source.js"
|
"source.js",
|
||||||
|
"source.js.jsx",
|
||||||
|
"source.jsx",
|
||||||
|
"source.ts",
|
||||||
|
"source.ts.tsx",
|
||||||
|
"source.tsx"
|
||||||
],
|
],
|
||||||
"scopeName": "es6.inline.css",
|
"scopeName": "es6.inline.css",
|
||||||
"path": "./syntaxes/es6.inline.css.json",
|
"path": "./syntaxes/es6.inline.css.json",
|
||||||
"embeddedLanguages": {
|
"embeddedLanguages": {
|
||||||
"meta.embedded.block.css": "css",
|
"meta.embedded.block.css": "css",
|
||||||
"meta.template.expression.js": "javascript"
|
"meta.template.expression.ts": "typescript"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"injectTo": [
|
"injectTo": [
|
||||||
"source.js"
|
"source.js",
|
||||||
|
"source.js.jsx",
|
||||||
|
"source.jsx",
|
||||||
|
"source.ts",
|
||||||
|
"source.ts.tsx",
|
||||||
|
"source.tsx"
|
||||||
],
|
],
|
||||||
"scopeName": "es6.inline.scss",
|
"scopeName": "es6.inline.scss",
|
||||||
"path": "./syntaxes/es6.inline.scss.json",
|
"path": "./syntaxes/es6.inline.scss.json",
|
||||||
"embeddedLanguages": {
|
"embeddedLanguages": {
|
||||||
"meta.embedded.block.css": "scss",
|
"meta.embedded.block.css": "scss",
|
||||||
"meta.template.expression.js": "javascript"
|
"meta.template.expression.ts": "typescript"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"injectTo": [
|
||||||
|
"source.js",
|
||||||
|
"source.js.jsx",
|
||||||
|
"source.jsx",
|
||||||
|
"source.ts",
|
||||||
|
"source.ts.tsx",
|
||||||
|
"source.tsx"
|
||||||
|
],
|
||||||
|
"scopeName": "es6.inline.less",
|
||||||
|
"path": "./syntaxes/es6.inline.less.json",
|
||||||
|
"embeddedLanguages": {
|
||||||
|
"meta.embedded.block.css": "less",
|
||||||
|
"meta.template.expression.ts": "typescript"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"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": "^6.2.6",
|
"vscode-css-languageservice": "^3.0.7",
|
||||||
"@vscode/emmet-helper": "^2.8.8",
|
"vscode-emmet-helper": "^1.2.0",
|
||||||
"vscode-html-languageservice": "^5.0.6"
|
"vscode-html-languageservice": "^2.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/cache.js
31
src/cache.js
|
@ -1,31 +0,0 @@
|
||||||
// Code from https://github.com/microsoft/typescript-styled-plugin/blob/main/src/_language-service.ts
|
|
||||||
|
|
||||||
function arePositionsEqual(left, right) {
|
|
||||||
return left.line === right.line && left.character === right.character
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompletionsCache {
|
|
||||||
_cachedCompletionsFile
|
|
||||||
_cachedCompletionsPosition
|
|
||||||
_cachedCompletionsContent
|
|
||||||
_completions
|
|
||||||
|
|
||||||
getCached(context, position) {
|
|
||||||
if (
|
|
||||||
this._completions &&
|
|
||||||
context.fileName === this._cachedCompletionsFile &&
|
|
||||||
this._cachedCompletionsPosition &&
|
|
||||||
arePositionsEqual(position, this._cachedCompletionsPosition) &&
|
|
||||||
context.text === this._cachedCompletionsContent
|
|
||||||
) {
|
|
||||||
return this._completions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCached(context, position, completions) {
|
|
||||||
this._cachedCompletionsFile = context.fileName
|
|
||||||
this._cachedCompletionsPosition = position
|
|
||||||
this._cachedCompletionsContent = context.text
|
|
||||||
this._completions = completions
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Code from https://github.com/Microsoft/typescript-styled-plugin/blob/master/src/styled-template-language-service.ts
|
||||||
|
|
||||||
|
import { CompletionList, TextDocument, Position } from 'vscode'
|
||||||
|
|
||||||
|
export class CompletionsCache {
|
||||||
|
private _cachedCompletionsFile?: string
|
||||||
|
private _cachedCompletionsPosition?: Position
|
||||||
|
private _cachedCompletionsContent?: string
|
||||||
|
private _completions?: CompletionList
|
||||||
|
|
||||||
|
private equalPositions(left: Position, right: Position): boolean {
|
||||||
|
return left.line === right.line && left.character === right.character
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCached(
|
||||||
|
context: TextDocument,
|
||||||
|
position: Position
|
||||||
|
): CompletionList | undefined {
|
||||||
|
if (
|
||||||
|
this._completions &&
|
||||||
|
context.fileName === this._cachedCompletionsFile &&
|
||||||
|
this._cachedCompletionsPosition &&
|
||||||
|
this.equalPositions(position, this._cachedCompletionsPosition) &&
|
||||||
|
context.getText() === this._cachedCompletionsContent
|
||||||
|
) {
|
||||||
|
return this._completions
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateCached(
|
||||||
|
context: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
completions: any
|
||||||
|
) {
|
||||||
|
this._cachedCompletionsFile = context.fileName
|
||||||
|
this._cachedCompletionsPosition = position
|
||||||
|
this._cachedCompletionsContent = context.getText()
|
||||||
|
this._completions = completions
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,28 @@
|
||||||
import { languages } from 'vscode'
|
import {
|
||||||
import { HTMLCompletionItemProvider } from './providers/html.js'
|
languages as Languages,
|
||||||
|
ExtensionContext,
|
||||||
|
commands as Commands,
|
||||||
|
DocumentSelector
|
||||||
|
} from 'vscode'
|
||||||
|
import { HTMLCompletionItemProvider } from './providers/html'
|
||||||
import {
|
import {
|
||||||
CSSCompletionItemProvider,
|
CSSCompletionItemProvider,
|
||||||
HTMLStyleCompletionItemProvider
|
HTMLStyleCompletionItemProvider
|
||||||
} from './providers/css.js'
|
} from './providers/css'
|
||||||
import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover.js'
|
import { HTMLHoverProvider, CSSHoverProvider } from './providers/hover'
|
||||||
import { CodeFormatterProvider } from './providers/formatting.js'
|
import { CodeFormatterProvider } from './providers/formatting'
|
||||||
const selector = ['javascriptreact', 'javascript']
|
|
||||||
|
|
||||||
export function activate(Context) {
|
const selector: DocumentSelector = [
|
||||||
|
'typescriptreact',
|
||||||
|
'javascriptreact',
|
||||||
|
'typescript',
|
||||||
|
'javascript'
|
||||||
|
]
|
||||||
|
|
||||||
|
export function activate(Context: ExtensionContext) {
|
||||||
new CodeFormatterProvider()
|
new CodeFormatterProvider()
|
||||||
|
|
||||||
languages.registerHoverProvider(selector, new HTMLHoverProvider())
|
Languages.registerCompletionItemProvider(
|
||||||
languages.registerHoverProvider(selector, new CSSHoverProvider())
|
|
||||||
|
|
||||||
// HTMLCompletionItemProvider
|
|
||||||
languages.registerCompletionItemProvider(
|
|
||||||
selector,
|
selector,
|
||||||
new HTMLCompletionItemProvider(),
|
new HTMLCompletionItemProvider(),
|
||||||
'<',
|
'<',
|
||||||
|
@ -37,9 +44,8 @@ export function activate(Context) {
|
||||||
'8',
|
'8',
|
||||||
'9'
|
'9'
|
||||||
)
|
)
|
||||||
|
Languages.registerHoverProvider(selector, new HTMLHoverProvider())
|
||||||
// HTMLStyleCompletionItemProvider
|
Languages.registerCompletionItemProvider(
|
||||||
languages.registerCompletionItemProvider(
|
|
||||||
selector,
|
selector,
|
||||||
new HTMLStyleCompletionItemProvider(),
|
new HTMLStyleCompletionItemProvider(),
|
||||||
'!',
|
'!',
|
||||||
|
@ -60,9 +66,8 @@ export function activate(Context) {
|
||||||
'8',
|
'8',
|
||||||
'9'
|
'9'
|
||||||
)
|
)
|
||||||
|
Languages.registerHoverProvider(selector, new CSSHoverProvider())
|
||||||
// CSSCompletionItemProvider
|
Languages.registerCompletionItemProvider(
|
||||||
languages.registerCompletionItemProvider(
|
|
||||||
selector,
|
selector,
|
||||||
new CSSCompletionItemProvider(),
|
new CSSCompletionItemProvider(),
|
||||||
'!',
|
'!',
|
|
@ -1,6 +1,25 @@
|
||||||
import { getLanguageService } from 'vscode-html-languageservice'
|
import {
|
||||||
import { getSCSSLanguageService } from 'vscode-css-languageservice'
|
CompletionList,
|
||||||
import * as emmet from '@vscode/emmet-helper'
|
CompletionItem,
|
||||||
|
TextDocument,
|
||||||
|
Position,
|
||||||
|
CancellationToken,
|
||||||
|
CompletionItemProvider
|
||||||
|
} from 'vscode'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLanguageService as GetHTMLanguageService,
|
||||||
|
LanguageService as HTMLanguageService,
|
||||||
|
CompletionList as HTMLCompletionList
|
||||||
|
} from 'vscode-html-languageservice'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getSCSSLanguageService as GetSCSSLanguageService,
|
||||||
|
LanguageService as CSSLanguageService,
|
||||||
|
CompletionList as CSSCompletionList
|
||||||
|
} from 'vscode-css-languageservice'
|
||||||
|
|
||||||
|
import * as emmet from 'vscode-emmet-helper'
|
||||||
import {
|
import {
|
||||||
GetEmmetConfiguration,
|
GetEmmetConfiguration,
|
||||||
MatchOffset,
|
MatchOffset,
|
||||||
|
@ -8,54 +27,71 @@ import {
|
||||||
GetLanguageRegions,
|
GetLanguageRegions,
|
||||||
GetRegionAtOffset,
|
GetRegionAtOffset,
|
||||||
TranslateCompletionItems
|
TranslateCompletionItems
|
||||||
} from '../util.js'
|
} from '../util'
|
||||||
import { CompletionsCache } from '../cache.js'
|
|
||||||
|
|
||||||
export class HTMLStyleCompletionItemProvider {
|
import { CompletionsCache } from '../cache'
|
||||||
_cssLanguageService = getSCSSLanguageService()
|
|
||||||
_HTMLanguageService = getLanguageService()
|
|
||||||
_expression = /(html\s*`)([^`]*)(`)/g
|
|
||||||
_cache = new CompletionsCache()
|
|
||||||
|
|
||||||
provideCompletionItems(document, position, _token) {
|
export class HTMLStyleCompletionItemProvider implements CompletionItemProvider {
|
||||||
|
private _cssLanguageService: CSSLanguageService = GetSCSSLanguageService()
|
||||||
|
private _HTMLanguageService: HTMLanguageService = GetHTMLanguageService()
|
||||||
|
private _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
|
||||||
|
private _cache = new CompletionsCache()
|
||||||
|
|
||||||
|
public provideCompletionItems(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
_token: CancellationToken
|
||||||
|
): CompletionList {
|
||||||
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: []
|
||||||
}
|
} as CompletionList
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line:no-magic-numbers
|
// tslint:disable-next-line:no-magic-numbers
|
||||||
const matchContent = match[2]
|
const matchContent: string = 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: HTMLCompletionList = {
|
||||||
isIncomplete: true,
|
isIncomplete: true,
|
||||||
items: []
|
items: []
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cssLanguageService.setCompletionParticipants([
|
this._cssLanguageService.setCompletionParticipants([
|
||||||
emmet.getEmmetCompletionParticipants(
|
emmet.getEmmetCompletionParticipants(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
|
@ -65,64 +101,85 @@ export class HTMLStyleCompletionItemProvider {
|
||||||
emmetResults
|
emmetResults
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
const completions = this._cssLanguageService.doComplete(
|
const completions = this._cssLanguageService.doComplete(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
virtualDocument.positionAt(virtualOffset),
|
virtualDocument.positionAt(virtualOffset),
|
||||||
stylesheet
|
stylesheet
|
||||||
)
|
)
|
||||||
|
|
||||||
if (emmetResults.items.length) {
|
if (emmetResults.items.length) {
|
||||||
completions.items.push(...emmetResults.items)
|
completions.items.push(...emmetResults.items)
|
||||||
completions.isIncomplete = true
|
completions.isIncomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cache.updateCached(document, position, completions)
|
this._cache.updateCached(document, position, completions)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isIncomplete: completions.isIncomplete,
|
isIncomplete: completions.isIncomplete,
|
||||||
items: TranslateCompletionItems(completions.items, currentLine)
|
items: TranslateCompletionItems(completions.items, currentLine)
|
||||||
|
} as CompletionList
|
||||||
}
|
}
|
||||||
}
|
|
||||||
resolveCompletionItem(item, _token) {
|
public resolveCompletionItem?(
|
||||||
|
item: CompletionItem,
|
||||||
|
_token: CancellationToken
|
||||||
|
): CompletionItem | Thenable<CompletionItem> {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CSSCompletionItemProvider {
|
export class CSSCompletionItemProvider implements CompletionItemProvider {
|
||||||
_CSSLanguageService = getSCSSLanguageService()
|
private _CSSLanguageService: CSSLanguageService = GetSCSSLanguageService()
|
||||||
_expression = /(css\s*`)([^`]*)(`)/g
|
private _expression =
|
||||||
_cache = new CompletionsCache()
|
/(\/\*\s*(css|less|scss|sass)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g
|
||||||
|
private _cache = new CompletionsCache()
|
||||||
|
|
||||||
provideCompletionItems(document, position, _token) {
|
public provideCompletionItems(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
_token: CancellationToken
|
||||||
|
): CompletionList {
|
||||||
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: []
|
||||||
}
|
} as CompletionList
|
||||||
|
|
||||||
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: string = 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: CSSCompletionList = {
|
||||||
isIncomplete: true,
|
isIncomplete: true,
|
||||||
items: []
|
items: []
|
||||||
}
|
}
|
||||||
|
|
||||||
this._CSSLanguageService.setCompletionParticipants([
|
this._CSSLanguageService.setCompletionParticipants([
|
||||||
emmet.getEmmetCompletionParticipants(
|
emmet.getEmmetCompletionParticipants(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
|
@ -132,22 +189,30 @@ export class CSSCompletionItemProvider {
|
||||||
emmetResults
|
emmetResults
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
const completions = this._CSSLanguageService.doComplete(
|
const completions = this._CSSLanguageService.doComplete(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
virtualDocument.positionAt(virtualOffset),
|
virtualDocument.positionAt(virtualOffset),
|
||||||
vCss
|
vCss
|
||||||
)
|
)
|
||||||
|
|
||||||
if (emmetResults.items.length) {
|
if (emmetResults.items.length) {
|
||||||
completions.items.push(...emmetResults.items)
|
completions.items.push(...emmetResults.items)
|
||||||
completions.isIncomplete = true
|
completions.isIncomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cache.updateCached(document, position, completions)
|
this._cache.updateCached(document, position, completions)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isIncomplete: completions.isIncomplete,
|
isIncomplete: completions.isIncomplete,
|
||||||
items: TranslateCompletionItems(completions.items, currentLine)
|
items: TranslateCompletionItems(completions.items, currentLine)
|
||||||
|
} as CompletionList
|
||||||
}
|
}
|
||||||
}
|
|
||||||
resolveCompletionItem(item, _token) {
|
public resolveCompletionItem?(
|
||||||
|
item: CompletionItem,
|
||||||
|
_token: CancellationToken
|
||||||
|
): CompletionItem | Thenable<CompletionItem> {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,54 +1,78 @@
|
||||||
import { Range, WorkspaceEdit, workspace, commands } from 'vscode'
|
import {
|
||||||
import { getLanguageService } from 'vscode-html-languageservice'
|
Range,
|
||||||
|
TextDocument,
|
||||||
|
WorkspaceEdit,
|
||||||
|
workspace as Workspace,
|
||||||
|
TextEditor,
|
||||||
|
commands as Commands,
|
||||||
|
Uri,
|
||||||
|
TextEdit,
|
||||||
|
Position
|
||||||
|
} from 'vscode'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLanguageService as GetHTMLanguageService,
|
||||||
|
Position as HTMLPosition
|
||||||
|
} from 'vscode-html-languageservice'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateVirtualDocument,
|
CreateVirtualDocument,
|
||||||
TranslateHTMLTextEdits,
|
TranslateHTMLTextEdits,
|
||||||
Match
|
Match,
|
||||||
} from '../util.js'
|
GetLanguageRegions,
|
||||||
|
IEmbeddedRegion
|
||||||
|
} from '../util'
|
||||||
|
|
||||||
export class CodeFormatterProvider {
|
export class CodeFormatterProvider {
|
||||||
_expression = /(html\s*`)([^`]*)(`)/g
|
private _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
|
||||||
document
|
private document: TextDocument
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
commands.registerTextEditorCommand(
|
Commands.registerTextEditorCommand(
|
||||||
'editor.action.formatInlineHtml',
|
'editor.action.formatInlineHtml',
|
||||||
this.format,
|
this.format,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
format(textEditor) {
|
public format(textEditor: 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(
|
var text = this.document.getText(
|
||||||
new Range(matchStartPosition, matchEndPosition)
|
new Range(matchStartPosition, matchEndPosition)
|
||||||
)
|
)
|
||||||
var vHTML = CreateVirtualDocument('html', text)
|
var vHTML = CreateVirtualDocument('html', text)
|
||||||
|
|
||||||
// TODO - Expose Formatting Options
|
// TODO - Expose Formatting Options
|
||||||
const edits = TranslateHTMLTextEdits(
|
const edits = TranslateHTMLTextEdits(
|
||||||
getLanguageService().format(vHTML, null, {
|
GetHTMLanguageService().format(vHTML, null, {
|
||||||
indentInnerHtml: false,
|
indentInnerHtml: false,
|
||||||
preserveNewLines: true,
|
preserveNewLines: true,
|
||||||
tabSize: textEditor.options.tabSize,
|
tabSize: <number>textEditor.options.tabSize,
|
||||||
insertSpaces: textEditor.options.insertSpaces,
|
insertSpaces: <boolean>textEditor.options.insertSpaces,
|
||||||
endWithNewline: true
|
endWithNewline: true
|
||||||
}),
|
}),
|
||||||
matchStartPosition.line + 1
|
matchStartPosition.line + 1
|
||||||
)
|
)
|
||||||
workspace.applyEdit(this.composeEdits(this.document.uri, edits))
|
|
||||||
|
Workspace.applyEdit(this.composeEdits(this.document.uri, edits))
|
||||||
}
|
}
|
||||||
composeEdits(uri, edits) {
|
|
||||||
|
private composeEdits(uri: Uri, edits: TextEdit[]): WorkspaceEdit {
|
||||||
var ws = new WorkspaceEdit()
|
var ws = new WorkspaceEdit()
|
||||||
ws.set(uri, edits)
|
ws.set(uri, edits)
|
||||||
return ws
|
return ws
|
|
@ -1,21 +1,45 @@
|
||||||
import { getLanguageService } from 'vscode-html-languageservice'
|
import {
|
||||||
import { getCSSLanguageService } from 'vscode-css-languageservice'
|
HoverProvider,
|
||||||
import { CreateVirtualDocument, MatchOffset } from '../util.js'
|
TextDocument,
|
||||||
|
Position,
|
||||||
|
CancellationToken,
|
||||||
|
Hover
|
||||||
|
} from 'vscode'
|
||||||
|
|
||||||
export class HTMLHoverProvider {
|
import {
|
||||||
_htmlLanguageService = getLanguageService()
|
getLanguageService as GetHtmlLanguageService,
|
||||||
_cssLanguageService = getCSSLanguageService()
|
LanguageService as HtmlLanguageService,
|
||||||
_expression = /(html\s*`)([^`]*)(`)/g
|
CompletionList as HtmlCompletionList
|
||||||
|
} from 'vscode-html-languageservice'
|
||||||
|
|
||||||
provideHover(document, position, token) {
|
import {
|
||||||
|
getCSSLanguageService as GetCssLanguageService,
|
||||||
|
LanguageService as CssLanguageService
|
||||||
|
} from 'vscode-css-languageservice'
|
||||||
|
|
||||||
|
import { CreateVirtualDocument, MatchOffset } from '../util'
|
||||||
|
|
||||||
|
export class HTMLHoverProvider implements HoverProvider {
|
||||||
|
private _htmlLanguageService: HtmlLanguageService = GetHtmlLanguageService()
|
||||||
|
private _cssLanguageService: CssLanguageService = GetCssLanguageService()
|
||||||
|
// private _expression = /(html\s*`)([^`]*)(`)/g
|
||||||
|
private _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
|
||||||
|
|
||||||
|
provideHover(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
token: CancellationToken
|
||||||
|
): Hover {
|
||||||
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: string = 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)
|
||||||
|
@ -32,25 +56,33 @@ export class HTMLHoverProvider {
|
||||||
virtualDocument.positionAt(virtualOffset),
|
virtualDocument.positionAt(virtualOffset),
|
||||||
stylesheet
|
stylesheet
|
||||||
)
|
)
|
||||||
return hover
|
|
||||||
|
return hover as Hover
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CSSHoverProvider {
|
export class CSSHoverProvider implements HoverProvider {
|
||||||
_htmlLanguageService = getLanguageService()
|
private _htmlLanguageService: HtmlLanguageService = GetHtmlLanguageService()
|
||||||
_cssLanguageService = getCSSLanguageService()
|
private _cssLanguageService: CssLanguageService = GetCssLanguageService()
|
||||||
_expression = /(css\s*`)([^`]*)(`)/g
|
private _expression = /(\/\*\s*(css|less|scss)\s*\*\/\s*`|css\s*`)([^`]*)(`)/g
|
||||||
|
|
||||||
provideHover(document, position, token) {
|
provideHover(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
token: CancellationToken
|
||||||
|
): Hover {
|
||||||
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: string = 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)
|
||||||
|
@ -60,6 +92,7 @@ export class CSSHoverProvider {
|
||||||
virtualDocument.positionAt(virtualOffset),
|
virtualDocument.positionAt(virtualOffset),
|
||||||
stylesheet
|
stylesheet
|
||||||
)
|
)
|
||||||
return hover
|
|
||||||
|
return hover as Hover
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,50 +1,78 @@
|
||||||
import { getLanguageService } from 'vscode-html-languageservice'
|
import {
|
||||||
import * as emmet from '@vscode/emmet-helper'
|
CompletionList,
|
||||||
|
CompletionItem,
|
||||||
|
TextDocument,
|
||||||
|
Position,
|
||||||
|
CancellationToken,
|
||||||
|
CompletionItemProvider
|
||||||
|
} from 'vscode'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLanguageService as GetHTMLanguageService,
|
||||||
|
LanguageService as HTMLanguageService,
|
||||||
|
CompletionList as HTMLCompletionList
|
||||||
|
} from 'vscode-html-languageservice'
|
||||||
|
|
||||||
|
import * as emmet from 'vscode-emmet-helper'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetEmmetConfiguration,
|
GetEmmetConfiguration,
|
||||||
MatchOffset,
|
MatchOffset,
|
||||||
CreateVirtualDocument,
|
CreateVirtualDocument,
|
||||||
TranslateCompletionItems
|
TranslateCompletionItems
|
||||||
} from '../util.js'
|
} from '../util'
|
||||||
import { CompletionsCache } from '../cache.js'
|
|
||||||
|
|
||||||
export class HTMLCompletionItemProvider {
|
import { CompletionsCache } from '../cache'
|
||||||
_htmlLanguageService = getLanguageService()
|
|
||||||
_expression = /(html\s*`)([^`]*)(`)/g
|
|
||||||
_cache = new CompletionsCache()
|
|
||||||
|
|
||||||
provideCompletionItems(document, position, token) {
|
export class HTMLCompletionItemProvider implements CompletionItemProvider {
|
||||||
|
private _htmlLanguageService: HTMLanguageService = GetHTMLanguageService()
|
||||||
|
private _expression = /(\/\*\s*html\s*\*\/\s*`|html\s*`)([^`]*)(`)/g
|
||||||
|
// private _expression = /(html\s*`)([^`]*)(`)/g
|
||||||
|
private _cache = new CompletionsCache()
|
||||||
|
|
||||||
|
public provideCompletionItems(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
token: CancellationToken
|
||||||
|
): CompletionList {
|
||||||
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: []
|
||||||
}
|
} as CompletionList
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line:no-magic-numbers
|
// tslint:disable-next-line:no-magic-numbers
|
||||||
const matchContent = match[2]
|
const matchContent: string = 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: HTMLCompletionList = {
|
||||||
isIncomplete: true,
|
isIncomplete: true,
|
||||||
items: []
|
items: []
|
||||||
}
|
}
|
||||||
|
|
||||||
this._htmlLanguageService.setCompletionParticipants([
|
this._htmlLanguageService.setCompletionParticipants([
|
||||||
emmet.getEmmetCompletionParticipants(
|
emmet.getEmmetCompletionParticipants(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
|
@ -54,22 +82,30 @@ export class HTMLCompletionItemProvider {
|
||||||
emmetResults
|
emmetResults
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
const completions = this._htmlLanguageService.doComplete(
|
const completions = this._htmlLanguageService.doComplete(
|
||||||
virtualDocument,
|
virtualDocument,
|
||||||
virtualDocument.positionAt(virtualOffset),
|
virtualDocument.positionAt(virtualOffset),
|
||||||
vHtml
|
vHtml
|
||||||
)
|
)
|
||||||
|
|
||||||
if (emmetResults.items.length) {
|
if (emmetResults.items.length) {
|
||||||
completions.items.push(...emmetResults.items)
|
completions.items.push(...emmetResults.items)
|
||||||
completions.isIncomplete = true
|
completions.isIncomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cache.updateCached(document, position, completions)
|
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)
|
||||||
|
} as CompletionList
|
||||||
}
|
}
|
||||||
}
|
|
||||||
resolveCompletionItem(item, token) {
|
public resolveCompletionItem?(
|
||||||
|
item: CompletionItem,
|
||||||
|
token: CancellationToken
|
||||||
|
): CompletionItem | Thenable<CompletionItem> {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,23 @@
|
||||||
import { workspace, TextEdit, Position, Range } from 'vscode'
|
import {
|
||||||
import { TextDocument, TokenType } from 'vscode-html-languageservice'
|
workspace,
|
||||||
|
TextLine,
|
||||||
|
TextEdit,
|
||||||
|
Position,
|
||||||
|
Range,
|
||||||
|
CompletionItem,
|
||||||
|
Command
|
||||||
|
} from 'vscode'
|
||||||
|
|
||||||
export function GetEmmetConfiguration() {
|
import {
|
||||||
|
TextDocument as HTMLTextDocument,
|
||||||
|
LanguageService,
|
||||||
|
TokenType as HTMLTokenType,
|
||||||
|
TextEdit as HTMLTextEdit
|
||||||
|
} from 'vscode-html-languageservice'
|
||||||
|
|
||||||
|
import { EmmetConfiguration } from 'vscode-emmet-helper'
|
||||||
|
|
||||||
|
export function GetEmmetConfiguration(): EmmetConfiguration {
|
||||||
const emmetConfig = workspace.getConfiguration('emmet')
|
const emmetConfig = workspace.getConfiguration('emmet')
|
||||||
return {
|
return {
|
||||||
useNewEmmet: true,
|
useNewEmmet: true,
|
||||||
|
@ -9,19 +25,24 @@ export function GetEmmetConfiguration() {
|
||||||
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,
|
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,
|
||||||
syntaxProfiles: emmetConfig.syntaxProfiles,
|
syntaxProfiles: emmetConfig.syntaxProfiles,
|
||||||
variables: emmetConfig.variables
|
variables: emmetConfig.variables
|
||||||
}
|
} as EmmetConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotNull(input) {
|
export function NotNull<T>(input: any): T {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return {}
|
return {} as T
|
||||||
}
|
}
|
||||||
return input
|
return input as T
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MatchOffset(regex, data, offset) {
|
export function MatchOffset(
|
||||||
|
regex: RegExp,
|
||||||
|
data: string,
|
||||||
|
offset: number
|
||||||
|
): RegExpMatchArray {
|
||||||
regex.exec(null)
|
regex.exec(null)
|
||||||
let match
|
|
||||||
|
let match: RegExpExecArray
|
||||||
while ((match = regex.exec(data)) !== null) {
|
while ((match = regex.exec(data)) !== null) {
|
||||||
if (
|
if (
|
||||||
offset > match.index + match[1].length &&
|
offset > match.index + match[1].length &&
|
||||||
|
@ -32,21 +53,28 @@ export function MatchOffset(regex, data, offset) {
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
export function Match(regex, data) {
|
|
||||||
|
export function Match(regex: RegExp, data: string): RegExpMatchArray {
|
||||||
regex.exec(null)
|
regex.exec(null)
|
||||||
let match
|
|
||||||
|
let match: RegExpExecArray
|
||||||
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: LanguageService,
|
||||||
|
data: string
|
||||||
|
): IEmbeddedRegion[] {
|
||||||
const scanner = service.createScanner(data)
|
const scanner = service.createScanner(data)
|
||||||
const regions = []
|
const regions: IEmbeddedRegion[] = []
|
||||||
let tokenType
|
let tokenType: HTMLTokenType
|
||||||
while ((tokenType = scanner.scan()) !== TokenType.EOS) {
|
|
||||||
|
while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) {
|
||||||
switch (tokenType) {
|
switch (tokenType) {
|
||||||
case TokenType.Styles:
|
case HTMLTokenType.Styles:
|
||||||
regions.push({
|
regions.push({
|
||||||
languageId: 'css',
|
languageId: 'css',
|
||||||
start: scanner.getTokenOffset(),
|
start: scanner.getTokenOffset(),
|
||||||
|
@ -59,9 +87,14 @@ export function GetLanguageRegions(service, data) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return regions
|
return regions
|
||||||
}
|
}
|
||||||
export function GetRegionAtOffset(regions, offset) {
|
|
||||||
|
export function GetRegionAtOffset(
|
||||||
|
regions: IEmbeddedRegion[],
|
||||||
|
offset: number
|
||||||
|
): IEmbeddedRegion {
|
||||||
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) {
|
||||||
|
@ -73,8 +106,12 @@ export function GetRegionAtOffset(regions, offset) {
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
export function TranslateHTMLTextEdits(input, offset) {
|
|
||||||
return input.map(item => {
|
export function TranslateHTMLTextEdits(
|
||||||
|
input: HTMLTextEdit[],
|
||||||
|
offset: number
|
||||||
|
): TextEdit[] {
|
||||||
|
return input.map((item: HTMLTextEdit) => {
|
||||||
const startPosition = new Position(
|
const startPosition = new Position(
|
||||||
item.range.start.line + offset,
|
item.range.start.line + offset,
|
||||||
item.range.start.character
|
item.range.start.character
|
||||||
|
@ -87,37 +124,56 @@ export function TranslateHTMLTextEdits(input, offset) {
|
||||||
return new TextEdit(itemRange, item.newText)
|
return new TextEdit(itemRange, item.newText)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function TranslateCompletionItems(items, line, expand = false) {
|
|
||||||
return items.map(item => {
|
export function TranslateCompletionItems(
|
||||||
const result = item
|
items,
|
||||||
|
line: TextLine,
|
||||||
|
expand: boolean = false
|
||||||
|
): CompletionItem[] {
|
||||||
|
return items.map((item: CompletionItem) => {
|
||||||
|
const result = item as CompletionItem
|
||||||
const range = new Range(
|
const range = new Range(
|
||||||
new Position(line.lineNumber, result.textEdit.range.start.character),
|
new Position(line.lineNumber, result.textEdit.range.start.character),
|
||||||
new Position(line.lineNumber, result.textEdit.range.end.character)
|
new Position(line.lineNumber, result.textEdit.range.end.character)
|
||||||
)
|
)
|
||||||
|
|
||||||
result.textEdit = null
|
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'
|
||||||
|
} as Command
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateVirtualDocument(
|
export function CreateVirtualDocument(
|
||||||
// context: TextDocument | TextDocument,
|
// context: TextDocument | HTMLTextDocument,
|
||||||
languageId,
|
languageId: string,
|
||||||
// position: Position | HtmlPosition,
|
// position: Position | HtmlPosition,
|
||||||
content
|
content: string
|
||||||
) {
|
): HTMLTextDocument {
|
||||||
const doc = TextDocument.create(
|
const doc = HTMLTextDocument.create(
|
||||||
`embedded://document.${languageId}`,
|
`embedded://document.${languageId}`,
|
||||||
languageId,
|
languageId,
|
||||||
1,
|
1,
|
||||||
content
|
content
|
||||||
)
|
)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEmbeddedRegion {
|
||||||
|
languageId: string
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
|
length: number
|
||||||
|
content: string
|
||||||
|
}
|
|
@ -1,28 +1,27 @@
|
||||||
{
|
{
|
||||||
"scopeName": "es6.inline.css",
|
"fileTypes": ["js", "jsx", "ts", "tsx"],
|
||||||
"fileTypes": ["js"],
|
"injectionSelector": "L:source.js -comment -string, L:source.jsx -comment -string, L:source.ts -comment -string, L:source.tsx -comment -string",
|
||||||
"injectionSelector": "L:source.js -comment -string",
|
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"contentName": "meta.embedded.block.css",
|
"contentName": "meta.embedded.block.css",
|
||||||
"begin": "(?i)(\\s*css)(`)",
|
"begin": "(?x)(\\s*?(\\w+\\.)?(?:css|/\\*\\s*css\\s*\\*/)\\s*)(`)",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.begin.js"
|
"name": "string.template.ts, punctuation.definition.string.template.begin.ts"
|
||||||
},
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"name": "entity.name.function.tagged-template.js"
|
"name": "entity.name.function.tagged-template.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"end": "(`)",
|
"end": "(`)",
|
||||||
"endCaptures": {
|
"endCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.end.js"
|
"name": "string.template.ts, punctuation.definition.string.template.end.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "source.css.scss"
|
"include": "source.css.scss"
|
||||||
|
@ -30,7 +29,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"scopeName": "es6.inline.css"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"scopeName": "es6.inline.html",
|
"fileTypes": ["js", "jsx", "ts", "tsx"],
|
||||||
"fileTypes": ["js"],
|
"injectionSelector": "L:source.js -comment -string, L:source.jsx -comment -string, L:source.ts -comment -string, L:source.tsx -comment -string",
|
||||||
"injectionSelector": "L:source.js -comment -string",
|
|
||||||
"injections": {
|
"injections": {
|
||||||
"L:source": {
|
"L:source": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
|
@ -15,24 +14,24 @@
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"contentName": "meta.embedded.block.html",
|
"contentName": "meta.embedded.block.html",
|
||||||
"begin": "(?i)(\\s*html)(`)",
|
"begin": "(?x)(\\s*?(\\w+\\.)?(?:html|/\\*\\s*html\\s*\\*/)\\s*)(`)",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.begin.js"
|
"name": "string.template.ts, punctuation.definition.string.template.begin.ts"
|
||||||
},
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"name": "entity.name.function.tagged-template.js"
|
"name": "entity.name.function.tagged-template.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"end": "(`)",
|
"end": "(`)",
|
||||||
"endCaptures": {
|
"endCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.end.js"
|
"name": "string.template.ts, punctuation.definition.string.template.end.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "text.html.basic"
|
"include": "text.html.basic"
|
||||||
|
@ -40,7 +39,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"scopeName": "es6.inline.html"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"fileTypes": ["js", "jsx", "ts", "tsx"],
|
||||||
|
"injectionSelector": "L:source.js -comment -string, L:source.jsx -comment -string, L:source.ts -comment -string, L:source.tsx -comment -string",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"contentName": "meta.embedded.block.css",
|
||||||
|
"begin": "(?x)(\\s*?(\\w+\\.)?(?:less|/\\*\\s*less\\s*\\*/)\\s*)(`)",
|
||||||
|
"beginCaptures": {
|
||||||
|
"0": {
|
||||||
|
"name": "string.template.ts, punctuation.definition.string.template.begin.ts"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"name": "entity.name.function.tagged-template.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"end": "(`)",
|
||||||
|
"endCaptures": {
|
||||||
|
"0": {
|
||||||
|
"name": "string.template.ts, punctuation.definition.string.template.end.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"include": "source.ts#template-substitution-element"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "source.css.less"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "source.ts#template-substitution-element"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopeName": "es6.inline.less"
|
||||||
|
}
|
|
@ -1,28 +1,27 @@
|
||||||
{
|
{
|
||||||
"scopeName": "es6.inline.scss",
|
"fileTypes": ["js", "jsx", "ts", "tsx"],
|
||||||
"fileTypes": ["js"],
|
"injectionSelector": "L:source.js -comment -string, L:source.jsx -comment -string, L:source.ts -comment -string, L:source.tsx -comment -string",
|
||||||
"injectionSelector": "L:source.js -comment -string",
|
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"contentName": "meta.embedded.block.css",
|
"contentName": "meta.embedded.block.css",
|
||||||
"begin": "(?i)(\\s*scss)(`)",
|
"begin": "(?x)(\\s*?(\\w+\\.)?(?:scss|/\\*\\s*scss\\s*\\*/)\\s*)(`)",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.begin.js"
|
"name": "string.template.ts, punctuation.definition.string.template.begin.ts"
|
||||||
},
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"name": "entity.name.function.tagged-template.js"
|
"name": "entity.name.function.tagged-template.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"end": "(`)",
|
"end": "(`)",
|
||||||
"endCaptures": {
|
"endCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "string.template.js, punctuation.definition.string.template.end.js"
|
"name": "string.template.ts, punctuation.definition.string.template.end.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "source.css.scss"
|
"include": "source.css.scss"
|
||||||
|
@ -30,7 +29,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "source.js#template-substitution-element"
|
"include": "source.ts#template-substitution-element"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"scopeName": "es6.inline.scss"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
html`
|
function html()
|
||||||
|
{
|
||||||
|
html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -6,7 +8,7 @@ html`
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<input type="button" @click=${e => this.click(e)} value="deadmau5 🐭" />
|
<input type="button" @click=${(e) => this.click(e)} value="deadmau5 🐭" />
|
||||||
|
|
||||||
<div></div>
|
<div></div>
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -14,27 +16,62 @@ html`
|
||||||
|
|
||||||
<h3>${['❤️', '💛', '💚', '💙', '💜', '🖤']}</h3>
|
<h3>${['❤️', '💛', '💚', '💙', '💜', '🖤']}</h3>
|
||||||
</body>
|
</body>
|
||||||
`
|
`;
|
||||||
|
|
||||||
bug(html` <div></div> `)
|
/* html */`
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
function bug() {
|
bug(/* html*/`
|
||||||
html`div...`
|
<div></div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
bug(html`
|
||||||
|
<div></div>
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
css`
|
function bug()
|
||||||
.foo {
|
{
|
||||||
display: block;
|
html`div...`;
|
||||||
|
}
|
||||||
|
|
||||||
.bar {
|
function css()
|
||||||
color: #f30;
|
{
|
||||||
}
|
css`
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
css`
|
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/* css */`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
`
|
`;
|
||||||
|
|
||||||
|
/* css */`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
bug(/*css */`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
bug(css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"sourceMap": false,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"lib": ["es2016"]
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "tests"]
|
||||||
|
}
|
Loading…
Reference in New Issue