Rework language boundary detection (#502)
* Fix `classRegex` error * Rework language boundary detectionmaster
parent
a082bb3fd7
commit
86497bb380
File diff suppressed because it is too large
Load Diff
|
@ -29,6 +29,7 @@
|
||||||
"semver": "7.3.2",
|
"semver": "7.3.2",
|
||||||
"sift-string": "0.0.2",
|
"sift-string": "0.0.2",
|
||||||
"stringify-object": "3.3.0",
|
"stringify-object": "3.3.0",
|
||||||
|
"tmp-cache": "1.1.0",
|
||||||
"vscode-emmet-helper-bundled": "0.0.1",
|
"vscode-emmet-helper-bundled": "0.0.1",
|
||||||
"vscode-languageclient": "7.0.0",
|
"vscode-languageclient": "7.0.0",
|
||||||
"vscode-languageserver": "7.0.0",
|
"vscode-languageserver": "7.0.0",
|
||||||
|
|
|
@ -43,7 +43,9 @@ export async function provideInvalidApplyCodeActions(
|
||||||
if (!isCssDoc(state, document)) {
|
if (!isCssDoc(state, document)) {
|
||||||
let languageBoundaries = getLanguageBoundaries(state, document)
|
let languageBoundaries = getLanguageBoundaries(state, document)
|
||||||
if (!languageBoundaries) return []
|
if (!languageBoundaries) return []
|
||||||
cssRange = languageBoundaries.css.find((range) => isWithinRange(diagnostic.range.start, range))
|
cssRange = languageBoundaries
|
||||||
|
.filter((b) => b.type === 'css')
|
||||||
|
.find(({ range }) => isWithinRange(diagnostic.range.start, range))?.range
|
||||||
if (!cssRange) return []
|
if (!cssRange) return []
|
||||||
cssText = document.getText(cssRange)
|
cssText = document.getText(cssRange)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { stringifyScreen, Screen } from './util/screens'
|
||||||
import isObject from './util/isObject'
|
import isObject from './util/isObject'
|
||||||
import * as emmetHelper from 'vscode-emmet-helper-bundled'
|
import * as emmetHelper from 'vscode-emmet-helper-bundled'
|
||||||
import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation'
|
import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation'
|
||||||
import { isJsContext } from './util/js'
|
import { isJsDoc, isJsxContext } from './util/js'
|
||||||
import { naturalExpand } from './util/naturalExpand'
|
import { naturalExpand } from './util/naturalExpand'
|
||||||
import semver from 'semver'
|
import semver from 'semver'
|
||||||
import { docsUrl } from './util/docsUrl'
|
import { docsUrl } from './util/docsUrl'
|
||||||
|
@ -511,7 +511,7 @@ async function provideClassNameCompletions(
|
||||||
return provideAtApplyCompletions(state, document, position)
|
return provideAtApplyCompletions(state, document, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHtmlContext(state, document, position) || isJsContext(state, document, position)) {
|
if (isHtmlContext(state, document, position) || isJsxContext(state, document, position)) {
|
||||||
return provideClassAttributeCompletions(state, document, position, context)
|
return provideClassAttributeCompletions(state, document, position, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,8 +973,8 @@ async function provideEmmetCompletions(
|
||||||
let settings = await state.editor.getConfiguration(document.uri)
|
let settings = await state.editor.getConfiguration(document.uri)
|
||||||
if (settings.tailwindCSS.emmetCompletions !== true) return null
|
if (settings.tailwindCSS.emmetCompletions !== true) return null
|
||||||
|
|
||||||
const isHtml = isHtmlContext(state, document, position)
|
const isHtml = !isJsDoc(state, document) && isHtmlContext(state, document, position)
|
||||||
const isJs = !isHtml && isJsContext(state, document, position)
|
const isJs = isJsDoc(state, document) || isJsxContext(state, document, position)
|
||||||
|
|
||||||
const syntax = isHtml ? 'html' : isJs ? 'jsx' : null
|
const syntax = isHtml ? 'html' : isJs ? 'jsx' : null
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ export function getInvalidConfigPathDiagnostics(
|
||||||
} else {
|
} else {
|
||||||
let boundaries = getLanguageBoundaries(state, document)
|
let boundaries = getLanguageBoundaries(state, document)
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
ranges.push(...boundaries.css)
|
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
||||||
}
|
}
|
||||||
|
|
||||||
ranges.forEach((range) => {
|
ranges.forEach((range) => {
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function getInvalidScreenDiagnostics(
|
||||||
} else {
|
} else {
|
||||||
let boundaries = getLanguageBoundaries(state, document)
|
let boundaries = getLanguageBoundaries(state, document)
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
ranges.push(...boundaries.css)
|
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
||||||
}
|
}
|
||||||
|
|
||||||
ranges.forEach((range) => {
|
ranges.forEach((range) => {
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function getInvalidTailwindDirectiveDiagnostics(
|
||||||
} else {
|
} else {
|
||||||
let boundaries = getLanguageBoundaries(state, document)
|
let boundaries = getLanguageBoundaries(state, document)
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
ranges.push(...boundaries.css)
|
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
||||||
}
|
}
|
||||||
|
|
||||||
let notSemicolonLanguages = ['sass', 'sugarss', 'stylus']
|
let notSemicolonLanguages = ['sass', 'sugarss', 'stylus']
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function getInvalidVariantDiagnostics(
|
||||||
} else {
|
} else {
|
||||||
let boundaries = getLanguageBoundaries(state, document)
|
let boundaries = getLanguageBoundaries(state, document)
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
ranges.push(...boundaries.css)
|
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
||||||
}
|
}
|
||||||
|
|
||||||
let possibleVariants = Object.keys(state.variants)
|
let possibleVariants = Object.keys(state.variants)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import type { TextDocument, Position } from 'vscode-languageserver'
|
import type { TextDocument, Position } from 'vscode-languageserver'
|
||||||
import { isInsideTag, isVueDoc, isSvelteDoc, isHtmlDoc } from './html'
|
import { isVueDoc, isSvelteDoc, isHtmlDoc } from './html'
|
||||||
import { isJsDoc } from './js'
|
import { isJsDoc } from './js'
|
||||||
import { State } from './state'
|
import { State } from './state'
|
||||||
import { cssLanguages } from './languages'
|
import { cssLanguages } from './languages'
|
||||||
|
import { getLanguageBoundaries } from './getLanguageBoundaries'
|
||||||
|
|
||||||
export function isCssDoc(state: State, doc: TextDocument): boolean {
|
export function isCssDoc(state: State, doc: TextDocument): boolean {
|
||||||
const userCssLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
const userCssLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
||||||
|
@ -23,7 +24,9 @@ export function isCssContext(state: State, doc: TextDocument, position: Position
|
||||||
end: position,
|
end: position,
|
||||||
})
|
})
|
||||||
|
|
||||||
return isInsideTag(str, ['style'])
|
let boundaries = getLanguageBoundaries(state, doc, str)
|
||||||
|
|
||||||
|
return boundaries ? boundaries[boundaries.length - 1].type === 'css' : false
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -4,7 +4,7 @@ import lineColumn from 'line-column'
|
||||||
import { isCssContext, isCssDoc } from './css'
|
import { isCssContext, isCssDoc } from './css'
|
||||||
import { isHtmlContext } from './html'
|
import { isHtmlContext } from './html'
|
||||||
import { isWithinRange } from './isWithinRange'
|
import { isWithinRange } from './isWithinRange'
|
||||||
import { isJsContext } from './js'
|
import { isJsxContext } from './js'
|
||||||
import { flatten } from './array'
|
import { flatten } from './array'
|
||||||
import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers'
|
import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers'
|
||||||
import { getLanguageBoundaries } from './getLanguageBoundaries'
|
import { getLanguageBoundaries } from './getLanguageBoundaries'
|
||||||
|
@ -306,9 +306,13 @@ export async function findClassListsInDocument(
|
||||||
|
|
||||||
return flatten([
|
return flatten([
|
||||||
...(await Promise.all(
|
...(await Promise.all(
|
||||||
boundaries.html.map((range) => findClassListsInHtmlRange(state, doc, range))
|
boundaries
|
||||||
|
.filter((b) => b.type === 'html' || b.type === 'jsx')
|
||||||
|
.map(({ range }) => findClassListsInHtmlRange(state, doc, range))
|
||||||
)),
|
)),
|
||||||
...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
|
...boundaries
|
||||||
|
.filter((b) => b.type === 'css')
|
||||||
|
.map(({ range }) => findClassListsInCssRange(doc, range)),
|
||||||
await findCustomClassLists(state, doc),
|
await findCustomClassLists(state, doc),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -324,7 +328,11 @@ export function findHelperFunctionsInDocument(
|
||||||
let boundaries = getLanguageBoundaries(state, doc)
|
let boundaries = getLanguageBoundaries(state, doc)
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
|
|
||||||
return flatten(boundaries.css.map((range) => findHelperFunctionsInRange(doc, range)))
|
return flatten(
|
||||||
|
boundaries
|
||||||
|
.filter((b) => b.type === 'css')
|
||||||
|
.map(({ range }) => findHelperFunctionsInRange(doc, range))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findHelperFunctionsInRange(
|
export function findHelperFunctionsInRange(
|
||||||
|
@ -385,7 +393,7 @@ export async function findClassNameAtPosition(
|
||||||
|
|
||||||
if (isCssContext(state, doc, position)) {
|
if (isCssContext(state, doc, position)) {
|
||||||
classNames = await findClassNamesInRange(state, doc, searchRange, 'css')
|
classNames = await findClassNamesInRange(state, doc, searchRange, 'css')
|
||||||
} else if (isHtmlContext(state, doc, position) || isJsContext(state, doc, position)) {
|
} else if (isHtmlContext(state, doc, position) || isJsxContext(state, doc, position)) {
|
||||||
classNames = await findClassNamesInRange(state, doc, searchRange, 'html')
|
classNames = await findClassNamesInRange(state, doc, searchRange, 'html')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,78 +1,153 @@
|
||||||
import type { TextDocument, Range } from 'vscode-languageserver'
|
import type { TextDocument, Range } from 'vscode-languageserver'
|
||||||
import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
|
import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
|
||||||
import { State } from './state'
|
import { State } from './state'
|
||||||
import { findAll, indexToPosition } from './find'
|
import { indexToPosition } from './find'
|
||||||
import { isJsDoc } from './js'
|
import { isJsDoc } from './js'
|
||||||
|
import moo from 'moo'
|
||||||
|
import Cache from 'tmp-cache'
|
||||||
|
|
||||||
export interface LanguageBoundaries {
|
export type LanguageBoundary = { type: 'html' | 'js' | 'css' | string; range: Range }
|
||||||
html: Range[]
|
|
||||||
css: Range[]
|
let text = { text: { match: /[^]/, lineBreaks: true } }
|
||||||
|
|
||||||
|
let states = {
|
||||||
|
main: {
|
||||||
|
cssBlockStart: { match: '<style', push: 'cssBlock' },
|
||||||
|
jsBlockStart: { match: '<script', push: 'jsBlock' },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
cssBlock: {
|
||||||
|
styleStart: { match: '>', next: 'style' },
|
||||||
|
cssBlockEnd: { match: '/>', pop: 1 },
|
||||||
|
attrStartDouble: { match: '"', push: 'attrDouble' },
|
||||||
|
attrStartSingle: { match: "'", push: 'attrSingle' },
|
||||||
|
interp: { match: '{', push: 'interp' },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
jsBlock: {
|
||||||
|
scriptStart: { match: '>', next: 'script' },
|
||||||
|
jsBlockEnd: { match: '/>', pop: 1 },
|
||||||
|
langAttrStartDouble: { match: 'lang="', push: 'langAttrDouble' },
|
||||||
|
langAttrStartSingle: { match: "lang='", push: 'langAttrSingle' },
|
||||||
|
attrStartDouble: { match: '"', push: 'attrDouble' },
|
||||||
|
attrStartSingle: { match: "'", push: 'attrSingle' },
|
||||||
|
interp: { match: '{', push: 'interp' },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
interp: {
|
||||||
|
interp: { match: '{', push: 'interp' },
|
||||||
|
end: { match: '}', pop: 1 },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
langAttrDouble: {
|
||||||
|
langAttrEnd: { match: '"', pop: 1 },
|
||||||
|
lang: { match: /[^"]+/, lineBreaks: true },
|
||||||
|
},
|
||||||
|
langAttrSingle: {
|
||||||
|
langAttrEnd: { match: "'", pop: 1 },
|
||||||
|
lang: { match: /[^']+/, lineBreaks: true },
|
||||||
|
},
|
||||||
|
attrDouble: {
|
||||||
|
attrEnd: { match: '"', pop: 1 },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
attrSingle: {
|
||||||
|
attrEnd: { match: "'", pop: 1 },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
cssBlockEnd: { match: '</style>', pop: 1 },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
|
script: {
|
||||||
|
jsBlockEnd: { match: '</script>', pop: 1 },
|
||||||
|
...text,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLanguageBoundaries(state: State, doc: TextDocument): LanguageBoundaries | null {
|
let vueStates = {
|
||||||
if (isVueDoc(doc)) {
|
...states,
|
||||||
let text = doc.getText()
|
main: {
|
||||||
let blocks = findAll(
|
htmlBlockStart: { match: '<template', push: 'htmlBlock' },
|
||||||
/(?<open><(?<type>template|style|script)\b[^>]*>).*?(?<close><\/\k<type>>|$)/gis,
|
...states.main,
|
||||||
text
|
},
|
||||||
)
|
htmlBlock: {
|
||||||
let htmlRanges: Range[] = []
|
htmlStart: { match: '>', next: 'html' },
|
||||||
let cssRanges: Range[] = []
|
htmlBlockEnd: { match: '/>', pop: 1 },
|
||||||
for (let i = 0; i < blocks.length; i++) {
|
attrStartDouble: { match: '"', push: 'attrDouble' },
|
||||||
let range = {
|
attrStartSingle: { match: "'", push: 'attrSingle' },
|
||||||
start: indexToPosition(text, blocks[i].index + blocks[i].groups.open.length),
|
interp: { match: '{', push: 'interp' },
|
||||||
end: indexToPosition(
|
...text,
|
||||||
text,
|
},
|
||||||
blocks[i].index + blocks[i][0].length - blocks[i].groups.close.length
|
html: {
|
||||||
),
|
htmlBlockEnd: { match: '</template>', pop: 1 },
|
||||||
}
|
...text,
|
||||||
if (blocks[i].groups.type === 'style') {
|
},
|
||||||
cssRanges.push(range)
|
|
||||||
} else {
|
|
||||||
htmlRanges.push(range)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
let defaultLexer = moo.states(states)
|
||||||
html: htmlRanges,
|
let vueLexer = moo.states(vueStates)
|
||||||
css: cssRanges,
|
|
||||||
}
|
let cache = new Cache<string, LanguageBoundary[] | null>({ max: 25, maxAge: 1000 })
|
||||||
|
|
||||||
|
export function getLanguageBoundaries(
|
||||||
|
state: State,
|
||||||
|
doc: TextDocument,
|
||||||
|
text: string = doc.getText()
|
||||||
|
): LanguageBoundary[] | null {
|
||||||
|
let cacheKey = `${doc.languageId}:${text}`
|
||||||
|
if (cache.has(cacheKey)) {
|
||||||
|
return cache.get(cacheKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) {
|
let defaultType = isVueDoc(doc)
|
||||||
let text = doc.getText()
|
? 'none'
|
||||||
let styleBlocks = findAll(
|
: isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)
|
||||||
/(?<open><style(?:\s[^>]*[^\/]>|\s*>)).*?(?<close><\/style>|$)/gis,
|
? 'html'
|
||||||
text
|
: null
|
||||||
)
|
|
||||||
let htmlRanges: Range[] = []
|
|
||||||
let cssRanges: Range[] = []
|
|
||||||
let currentIndex = 0
|
|
||||||
|
|
||||||
for (let i = 0; i < styleBlocks.length; i++) {
|
|
||||||
htmlRanges.push({
|
|
||||||
start: indexToPosition(text, currentIndex),
|
|
||||||
end: indexToPosition(text, styleBlocks[i].index),
|
|
||||||
})
|
|
||||||
cssRanges.push({
|
|
||||||
start: indexToPosition(text, styleBlocks[i].index + styleBlocks[i].groups.open.length),
|
|
||||||
end: indexToPosition(
|
|
||||||
text,
|
|
||||||
styleBlocks[i].index + styleBlocks[i][0].length - styleBlocks[i].groups.close.length
|
|
||||||
),
|
|
||||||
})
|
|
||||||
currentIndex = styleBlocks[i].index + styleBlocks[i][0].length
|
|
||||||
}
|
|
||||||
htmlRanges.push({
|
|
||||||
start: indexToPosition(text, currentIndex),
|
|
||||||
end: indexToPosition(text, text.length),
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
html: htmlRanges,
|
|
||||||
css: cssRanges,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (defaultType === null) {
|
||||||
|
cache.set(cacheKey, null)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lexer = defaultType === 'none' ? vueLexer : defaultLexer
|
||||||
|
lexer.reset(text)
|
||||||
|
|
||||||
|
let type = defaultType
|
||||||
|
let boundaries: LanguageBoundary[] = [
|
||||||
|
{ type: defaultType, range: { start: { line: 0, character: 0 }, end: undefined } },
|
||||||
|
]
|
||||||
|
let offset = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let token of lexer) {
|
||||||
|
if (token.type.endsWith('BlockStart')) {
|
||||||
|
let position = indexToPosition(text, offset)
|
||||||
|
if (!boundaries[boundaries.length - 1].range.end) {
|
||||||
|
boundaries[boundaries.length - 1].range.end = position
|
||||||
|
}
|
||||||
|
type = token.type.replace(/BlockStart$/, '')
|
||||||
|
boundaries.push({ type, range: { start: position, end: undefined } })
|
||||||
|
} else if (token.type.endsWith('BlockEnd')) {
|
||||||
|
let position = indexToPosition(text, offset)
|
||||||
|
boundaries[boundaries.length - 1].range.end = position
|
||||||
|
boundaries.push({ type: defaultType, range: { start: position, end: undefined } })
|
||||||
|
} else if (token.type === 'lang') {
|
||||||
|
boundaries[boundaries.length - 1].type = token.text
|
||||||
|
}
|
||||||
|
offset += token.text.length
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
cache.set(cacheKey, null)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boundaries[boundaries.length - 1].range.end) {
|
||||||
|
boundaries[boundaries.length - 1].range.end = indexToPosition(text, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.set(cacheKey, boundaries)
|
||||||
|
|
||||||
|
return boundaries
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { TextDocument, Position } from 'vscode-languageserver'
|
import type { TextDocument, Position } from 'vscode-languageserver'
|
||||||
import { State } from './state'
|
import { State } from './state'
|
||||||
import { htmlLanguages } from './languages'
|
import { htmlLanguages } from './languages'
|
||||||
|
import { getLanguageBoundaries } from './getLanguageBoundaries'
|
||||||
|
|
||||||
export function isHtmlDoc(state: State, doc: TextDocument): boolean {
|
export function isHtmlDoc(state: State, doc: TextDocument): boolean {
|
||||||
const userHtmlLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
const userHtmlLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
||||||
|
@ -24,33 +25,7 @@ export function isHtmlContext(state: State, doc: TextDocument, position: Positio
|
||||||
end: position,
|
end: position,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isHtmlDoc(state, doc) && !isInsideTag(str, ['script', 'style'])) {
|
let boundaries = getLanguageBoundaries(state, doc, str)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVueDoc(doc)) {
|
return boundaries ? boundaries[boundaries.length - 1].type === 'html' : false
|
||||||
return isInsideTag(str, ['template'])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSvelteDoc(doc)) {
|
|
||||||
return !isInsideTag(str, ['script', 'style'])
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isInsideTag(str: string, tag: string | string[]): boolean {
|
|
||||||
let open = 0
|
|
||||||
let close = 0
|
|
||||||
let match: RegExpExecArray
|
|
||||||
let tags = Array.isArray(tag) ? tag : [tag]
|
|
||||||
let regex = new RegExp(`<(?<slash>/?)(?:${tags.join('|')})(?:\\s[^>]*[^\/]>|\\s*>)`, 'ig')
|
|
||||||
while ((match = regex.exec(str)) !== null) {
|
|
||||||
if (match.groups.slash) {
|
|
||||||
close += 1
|
|
||||||
} else {
|
|
||||||
open += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return open > 0 && open > close
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { TextDocument, Position } from 'vscode-languageserver'
|
import type { TextDocument, Position } from 'vscode-languageserver'
|
||||||
import { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html'
|
|
||||||
import { State } from './state'
|
import { State } from './state'
|
||||||
import { jsLanguages } from './languages'
|
import { jsLanguages } from './languages'
|
||||||
|
import { getLanguageBoundaries } from './getLanguageBoundaries'
|
||||||
|
|
||||||
export function isJsDoc(state: State, doc: TextDocument): boolean {
|
export function isJsDoc(state: State, doc: TextDocument): boolean {
|
||||||
const userJsLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
const userJsLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
|
||||||
|
@ -11,23 +11,13 @@ export function isJsDoc(state: State, doc: TextDocument): boolean {
|
||||||
return [...jsLanguages, ...userJsLanguages].indexOf(doc.languageId) !== -1
|
return [...jsLanguages, ...userJsLanguages].indexOf(doc.languageId) !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isJsContext(state: State, doc: TextDocument, position: Position): boolean {
|
export function isJsxContext(state: State, doc: TextDocument, position: Position): boolean {
|
||||||
if (isJsDoc(state, doc)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
let str = doc.getText({
|
let str = doc.getText({
|
||||||
start: { line: 0, character: 0 },
|
start: { line: 0, character: 0 },
|
||||||
end: position,
|
end: position,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isHtmlDoc(state, doc) && isInsideTag(str, ['script'])) {
|
let boundaries = getLanguageBoundaries(state, doc, str)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVueDoc(doc) || isSvelteDoc(doc)) {
|
return boundaries ? boundaries[boundaries.length - 1].type === 'jsx' : false
|
||||||
return isInsideTag(str, ['script'])
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue