@vue/language-service
Advanced tools
Comparing version 1.7.3 to 1.7.4
@@ -6,4 +6,4 @@ import * as embedded from '@volar/language-core'; | ||
export declare function checkComponentNames(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, sourceFile: embedded.VirtualFile, nativeTags: Set<string>): string[]; | ||
export declare function checkNativeTags(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, fileName: string): Set<string>; | ||
export declare function getElementAttrs(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, fileName: string, tagName: string): string[]; | ||
export declare function checkNativeTags(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, tsLsHost: ts.LanguageServiceHost): Set<string>; | ||
export declare function getElementAttrs(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, tsLsHost: ts.LanguageServiceHost, tagName: string): string[]; | ||
type Tags = Map<string, { | ||
@@ -10,0 +10,0 @@ offsets: number[]; |
@@ -123,8 +123,8 @@ "use strict"; | ||
exports.checkComponentNames = checkComponentNames; | ||
function checkNativeTags(ts, tsLs, fileName) { | ||
const sharedTypesFileName = fileName.substring(0, fileName.lastIndexOf('/')) + '/' + language_core_1.sharedTypes.baseName; | ||
function checkNativeTags(ts, tsLs, tsLsHost) { | ||
const sharedTypesFileName = tsLsHost.getCurrentDirectory() + '/' + language_core_1.sharedTypes.baseName; | ||
const result = new Set(); | ||
let tsSourceFile; | ||
if (tsSourceFile = tsLs.getProgram()?.getSourceFile(sharedTypesFileName)) { | ||
const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === 'IntrinsicElements'); | ||
const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElements'); | ||
const checker = tsLs.getProgram()?.getTypeChecker(); | ||
@@ -142,7 +142,7 @@ if (checker && typeNode) { | ||
exports.checkNativeTags = checkNativeTags; | ||
function getElementAttrs(ts, tsLs, fileName, tagName) { | ||
const sharedTypesFileName = fileName.substring(0, fileName.lastIndexOf('/')) + '/' + language_core_1.sharedTypes.baseName; | ||
function getElementAttrs(ts, tsLs, tsLsHost, tagName) { | ||
const sharedTypesFileName = tsLsHost.getCurrentDirectory() + '/' + language_core_1.sharedTypes.baseName; | ||
let tsSourceFile; | ||
if (tsSourceFile = tsLs.getProgram()?.getSourceFile(sharedTypesFileName)) { | ||
const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === 'IntrinsicElements'); | ||
const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElements'); | ||
const checker = tsLs.getProgram()?.getTypeChecker(); | ||
@@ -149,0 +149,0 @@ if (checker && typeNode) { |
@@ -21,3 +21,3 @@ "use strict"; | ||
const edits = []; | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, rootFile.fileName); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, context.typescript.languageServiceHost); | ||
const components = (0, helpers_1.checkComponentNames)(ts, context.typescript.languageService, rootFile, nativeTags); | ||
@@ -56,3 +56,3 @@ const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile); | ||
const edits = []; | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, rootFile.fileName); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, context.typescript.languageServiceHost); | ||
const components = (0, helpers_1.checkComponentNames)(ts, context.typescript.languageService, rootFile, nativeTags); | ||
@@ -142,3 +142,3 @@ const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile); | ||
} | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, file.fileName); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, context.typescript.languageService, context.typescript.languageServiceHost); | ||
const components = (0, helpers_1.checkComponentNames)(ts, context.typescript.languageService, file, nativeTags); | ||
@@ -145,0 +145,0 @@ const tagNames = (0, helpers_1.getTemplateTagsAndAttrs)(file); |
@@ -5,2 +5,2 @@ export * from '@volar/language-service'; | ||
export * from './types'; | ||
export { injectionKeys } from './plugins/vue'; | ||
export { Provide } from './plugins/vue'; |
@@ -17,3 +17,2 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.injectionKeys = void 0; | ||
__exportStar(require("@volar/language-service"), exports); | ||
@@ -23,4 +22,2 @@ __exportStar(require("./ideFeatures/nameCasing"), exports); | ||
__exportStar(require("./types"), exports); | ||
var vue_1 = require("./plugins/vue"); | ||
Object.defineProperty(exports, "injectionKeys", { enumerable: true, get: function () { return vue_1.injectionKeys; } }); | ||
//# sourceMappingURL=index.js.map |
@@ -1,9 +0,9 @@ | ||
import createJsonPlugin from 'volar-service-json'; | ||
import { Config } from '@volar/language-service'; | ||
import * as vue from '@vue/language-core'; | ||
import type * as ts from 'typescript/lib/tsserverlibrary'; | ||
import { Config } from '@volar/language-service'; | ||
import createJsonService from 'volar-service-json'; | ||
export interface Settings { | ||
json?: Parameters<typeof createJsonPlugin>[0]; | ||
json?: Parameters<typeof createJsonService>[0]; | ||
} | ||
export declare function resolveConfig(config: Config, // volar.config.js | ||
compilerOptions?: ts.CompilerOptions, vueCompilerOptions?: vue.VueCompilerOptions, settings?: Settings, ts?: typeof import('typescript/lib/tsserverlibrary'), codegenStack?: boolean): Config; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resolveConfig = void 0; | ||
const vue = require("@vue/language-core"); | ||
const shared_1 = require("@vue/shared"); | ||
const volar_service_css_1 = require("volar-service-css"); | ||
@@ -9,18 +11,16 @@ const volar_service_emmet_1 = require("volar-service-emmet"); | ||
const volar_service_pug_1 = require("volar-service-pug"); | ||
const volar_service_pug_beautify_1 = require("volar-service-pug-beautify"); | ||
const volar_service_typescript_1 = require("volar-service-typescript"); | ||
const volar_service_typescript_twoslash_queries_1 = require("volar-service-typescript-twoslash-queries"); | ||
const vue = require("@vue/language-core"); | ||
const vscode = require("vscode-languageserver-protocol"); | ||
const nameCasing_1 = require("./ideFeatures/nameCasing"); | ||
const vue_1 = require("./plugins/vue"); | ||
const vue_autoinsert_dotvalue_1 = require("./plugins/vue-autoinsert-dotvalue"); | ||
const vue_autoinsert_parentheses_1 = require("./plugins/vue-autoinsert-parentheses"); | ||
const vue_autoinsert_space_1 = require("./plugins/vue-autoinsert-space"); | ||
const vue_codelens_references_1 = require("./plugins/vue-codelens-references"); | ||
const vue_template_1 = require("./plugins/vue-template"); | ||
const vue_twoslash_queries_1 = require("./plugins/vue-twoslash-queries"); | ||
const vue_template_1 = require("./plugins/vue-template"); | ||
const vue_visualize_hidden_callback_param_1 = require("./plugins/vue-visualize-hidden-callback-param"); | ||
const shared_1 = require("@vue/shared"); | ||
const volar_service_pug_beautify_1 = require("volar-service-pug-beautify"); | ||
const vue_autoinsert_parentheses_1 = require("./plugins/vue-autoinsert-parentheses"); | ||
const vue_autoinsert_space_1 = require("./plugins/vue-autoinsert-space"); | ||
const types_1 = require("./types"); | ||
const nameCasing_1 = require("./ideFeatures/nameCasing"); | ||
function resolveConfig(config, // volar.config.js | ||
@@ -197,6 +197,9 @@ compilerOptions = {}, vueCompilerOptions = vue.resolveVueCompilerOptions({}), settings, ts = require('typescript'), codegenStack = false) { | ||
services.html ??= (0, vue_template_1.default)({ | ||
templateLanguagePlugin: (0, volar_service_html_1.default)(), | ||
getScanner: (document, htmlPlugin) => { | ||
return htmlPlugin.getHtmlLs().createScanner(document.getText()); | ||
baseService: (0, volar_service_html_1.default)(), | ||
getScanner: (htmlService, document) => { | ||
return htmlService.provide['html/languageService']().createScanner(document.getText()); | ||
}, | ||
updateCustomData(htmlService, extraData) { | ||
htmlService.provide['html/updateCustomData'](extraData); | ||
}, | ||
isSupportedDocument: (document) => document.languageId === 'html', | ||
@@ -206,9 +209,12 @@ vueCompilerOptions, | ||
services.pug ??= (0, vue_template_1.default)({ | ||
templateLanguagePlugin: (0, volar_service_pug_1.default)(), | ||
getScanner: (document, pugPlugin) => { | ||
const pugDocument = pugPlugin.getPugDocument(document); | ||
baseService: (0, volar_service_pug_1.default)(), | ||
getScanner: (pugService, document) => { | ||
const pugDocument = pugService.provide['pug/pugDocument'](document); | ||
if (pugDocument) { | ||
return pugPlugin.getPugLs().createScanner(pugDocument); | ||
return pugService.provide['pug/languageService']().createScanner(pugDocument); | ||
} | ||
}, | ||
updateCustomData(pugService, extraData) { | ||
pugService.provide['pug/updateCustomData'](extraData); | ||
}, | ||
isSupportedDocument: (document) => document.languageId === 'jade', | ||
@@ -215,0 +221,0 @@ vueCompilerOptions, |
@@ -1,10 +0,12 @@ | ||
import createHtmlPlugin from 'volar-service-html'; | ||
import { Service } from '@volar/language-service'; | ||
import * as html from 'vscode-html-languageservice'; | ||
import { TextDocument } from 'vscode-languageserver-textdocument'; | ||
import { VueCompilerOptions } from '../types'; | ||
export default function useVueTemplateLanguagePlugin<T extends ReturnType<typeof createHtmlPlugin>>(options: { | ||
getScanner(document: TextDocument, t: ReturnType<T>): html.Scanner | undefined; | ||
templateLanguagePlugin: T; | ||
declare const _default: <S extends Service>(options: { | ||
getScanner(service: ReturnType<S>, document: TextDocument): html.Scanner | undefined; | ||
updateCustomData(service: ReturnType<S>, extraData: html.IHTMLDataProvider[]): void; | ||
baseService: S; | ||
isSupportedDocument: (document: TextDocument) => boolean; | ||
vueCompilerOptions: VueCompilerOptions; | ||
}): T; | ||
}) => Service; | ||
export default _default; |
@@ -13,504 +13,500 @@ "use strict"; | ||
let modelData; | ||
function useVueTemplateLanguagePlugin(options) { | ||
const plugin = (_context, modules) => { | ||
const templatePlugin = options.templateLanguagePlugin(_context); | ||
const triggerCharacters = [ | ||
...templatePlugin.triggerCharacters ?? [], | ||
'@', // vue event shorthand | ||
]; | ||
if (!_context?.typescript || !modules?.typescript) | ||
return { triggerCharacters }; | ||
builtInData ??= (0, data_1.loadTemplateData)(_context.env.locale ?? 'en'); | ||
modelData ??= (0, data_1.loadModelModifiersData)(_context.env.locale ?? 'en'); | ||
// https://vuejs.org/api/built-in-directives.html#v-on | ||
// https://vuejs.org/api/built-in-directives.html#v-bind | ||
const eventModifiers = {}; | ||
const propModifiers = {}; | ||
const vOn = builtInData.globalAttributes?.find(x => x.name === 'v-on'); | ||
const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind'); | ||
if (vOn) { | ||
const markdown = (typeof vOn.description === 'string' ? vOn.description : vOn.description?.value) ?? ''; | ||
const modifiers = markdown | ||
.split('\n- ')[4] | ||
.split('\n').slice(2, -1); | ||
for (let text of modifiers) { | ||
text = text.substring(' - `.'.length); | ||
const [name, disc] = text.split('` - '); | ||
eventModifiers[name] = disc; | ||
} | ||
exports.default = (options) => (_context, modules) => { | ||
const htmlOrPugService = options.baseService(_context, modules); | ||
const triggerCharacters = [ | ||
...htmlOrPugService.triggerCharacters ?? [], | ||
'@', // vue event shorthand | ||
]; | ||
if (!_context?.typescript || !modules?.typescript) | ||
return { triggerCharacters }; | ||
builtInData ??= (0, data_1.loadTemplateData)(_context.env.locale ?? 'en'); | ||
modelData ??= (0, data_1.loadModelModifiersData)(_context.env.locale ?? 'en'); | ||
// https://vuejs.org/api/built-in-directives.html#v-on | ||
// https://vuejs.org/api/built-in-directives.html#v-bind | ||
const eventModifiers = {}; | ||
const propModifiers = {}; | ||
const vOn = builtInData.globalAttributes?.find(x => x.name === 'v-on'); | ||
const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind'); | ||
if (vOn) { | ||
const markdown = (typeof vOn.description === 'string' ? vOn.description : vOn.description?.value) ?? ''; | ||
const modifiers = markdown | ||
.split('\n- ')[4] | ||
.split('\n').slice(2, -1); | ||
for (let text of modifiers) { | ||
text = text.substring(' - `.'.length); | ||
const [name, disc] = text.split('` - '); | ||
eventModifiers[name] = disc; | ||
} | ||
if (vBind) { | ||
const markdown = (typeof vBind.description === 'string' ? vBind.description : vBind.description?.value) ?? ''; | ||
const modifiers = markdown | ||
.split('\n- ')[4] | ||
.split('\n').slice(2, -1); | ||
for (let text of modifiers) { | ||
text = text.substring(' - `.'.length); | ||
const [name, disc] = text.split('` - '); | ||
propModifiers[name] = disc; | ||
} | ||
} | ||
if (vBind) { | ||
const markdown = (typeof vBind.description === 'string' ? vBind.description : vBind.description?.value) ?? ''; | ||
const modifiers = markdown | ||
.split('\n- ')[4] | ||
.split('\n').slice(2, -1); | ||
for (let text of modifiers) { | ||
text = text.substring(' - `.'.length); | ||
const [name, disc] = text.split('` - '); | ||
propModifiers[name] = disc; | ||
} | ||
const ts = modules.typescript; | ||
const _ts = _context.typescript; | ||
return { | ||
...templatePlugin, | ||
triggerCharacters, | ||
async provideCompletionItems(document, position, context, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (virtualFile && virtualFile instanceof vue.VueFile) { | ||
await provideHtmlData(map, virtualFile); | ||
} | ||
} | ||
const ts = modules.typescript; | ||
const _ts = _context.typescript; | ||
return { | ||
...htmlOrPugService, | ||
triggerCharacters, | ||
async provideCompletionItems(document, position, context, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (virtualFile && virtualFile instanceof vue.VueFile) { | ||
await provideHtmlData(map, virtualFile); | ||
} | ||
const htmlComplete = await templatePlugin.provideCompletionItems?.(document, position, context, token); | ||
if (!htmlComplete) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (virtualFile && virtualFile instanceof vue.VueFile) { | ||
afterHtmlCompletion(htmlComplete, map, virtualFile); | ||
} | ||
} | ||
const htmlComplete = await htmlOrPugService.provideCompletionItems?.(document, position, context, token); | ||
if (!htmlComplete) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (virtualFile && virtualFile instanceof vue.VueFile) { | ||
afterHtmlCompletion(htmlComplete, map, virtualFile); | ||
} | ||
return htmlComplete; | ||
}, | ||
async provideInlayHints(document) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const enabled = await _context.env.getConfiguration?.('vue.inlayHints.missingProps') ?? false; | ||
if (!enabled) | ||
return; | ||
const result = []; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
const scanner = options.getScanner(document, templatePlugin); | ||
if (virtualFile && virtualFile instanceof vue.VueFile && scanner) { | ||
// visualize missing required props | ||
const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, virtualFile.fileName); | ||
const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags); | ||
const componentProps = {}; | ||
let token; | ||
let current; | ||
while ((token = scanner.scan()) !== html.TokenType.EOS) { | ||
if (token === html.TokenType.StartTag) { | ||
const tagName = scanner.getTokenText(); | ||
const component = tagName.indexOf('.') >= 0 | ||
? components.find(component => component === tagName.split('.')[0]) | ||
: components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName); | ||
const checkTag = tagName.indexOf('.') >= 0 ? tagName : component; | ||
if (checkTag) { | ||
componentProps[checkTag] ??= (0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, virtualFile, checkTag, nativeTags, true); | ||
current = { | ||
unburnedRequiredProps: [...componentProps[checkTag]], | ||
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(), | ||
insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(), | ||
}; | ||
} | ||
return htmlComplete; | ||
}, | ||
async provideInlayHints(document) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const enabled = await _context.env.getConfiguration?.('vue.inlayHints.missingProps') ?? false; | ||
if (!enabled) | ||
return; | ||
const result = []; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
const scanner = options.getScanner(htmlOrPugService, document); | ||
if (virtualFile && virtualFile instanceof vue.VueFile && scanner) { | ||
// visualize missing required props | ||
const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, _ts.languageServiceHost); | ||
const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags); | ||
const componentProps = {}; | ||
let token; | ||
let current; | ||
while ((token = scanner.scan()) !== html.TokenType.EOS) { | ||
if (token === html.TokenType.StartTag) { | ||
const tagName = scanner.getTokenText(); | ||
const component = tagName.indexOf('.') >= 0 | ||
? components.find(component => component === tagName.split('.')[0]) | ||
: components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName); | ||
const checkTag = tagName.indexOf('.') >= 0 ? tagName : component; | ||
if (checkTag) { | ||
componentProps[checkTag] ??= (0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, virtualFile, checkTag, nativeTags, true); | ||
current = { | ||
unburnedRequiredProps: [...componentProps[checkTag]], | ||
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(), | ||
insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(), | ||
}; | ||
} | ||
} | ||
else if (token === html.TokenType.AttributeName) { | ||
if (current) { | ||
let attrText = scanner.getTokenText(); | ||
if (attrText === 'v-bind') { | ||
current.unburnedRequiredProps = []; | ||
} | ||
} | ||
else if (token === html.TokenType.AttributeName) { | ||
if (current) { | ||
let attrText = scanner.getTokenText(); | ||
if (attrText === 'v-bind') { | ||
current.unburnedRequiredProps = []; | ||
else { | ||
// remove modifiers | ||
if (attrText.indexOf('.') >= 0) { | ||
attrText = attrText.split('.')[0]; | ||
} | ||
else { | ||
// remove modifiers | ||
if (attrText.indexOf('.') >= 0) { | ||
attrText = attrText.split('.')[0]; | ||
} | ||
// normalize | ||
if (attrText.startsWith('v-bind:')) { | ||
attrText = attrText.substring('v-bind:'.length); | ||
} | ||
else if (attrText.startsWith(':')) { | ||
attrText = attrText.substring(':'.length); | ||
} | ||
else if (attrText.startsWith('v-model:')) { | ||
attrText = attrText.substring('v-model:'.length); | ||
} | ||
else if (attrText === 'v-model') { | ||
attrText = options.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName? | ||
} | ||
else if (attrText.startsWith('@')) { | ||
attrText = 'on-' + (0, shared_1.hyphenate)(attrText.substring('@'.length)); | ||
} | ||
current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => { | ||
return attrText !== propName | ||
&& attrText !== (0, shared_1.hyphenate)(propName); | ||
}); | ||
// normalize | ||
if (attrText.startsWith('v-bind:')) { | ||
attrText = attrText.substring('v-bind:'.length); | ||
} | ||
} | ||
} | ||
else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) { | ||
if (current) { | ||
for (const requiredProp of current.unburnedRequiredProps) { | ||
result.push({ | ||
label: `${requiredProp}!`, | ||
paddingLeft: true, | ||
position: document.positionAt(current.labelOffset), | ||
kind: vscode.InlayHintKind.Parameter, | ||
textEdits: [{ | ||
range: { | ||
start: document.positionAt(current.insertOffset), | ||
end: document.positionAt(current.insertOffset), | ||
}, | ||
newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, shared_1.hyphenate)(requiredProp) : requiredProp}=`, | ||
}], | ||
}); | ||
else if (attrText.startsWith(':')) { | ||
attrText = attrText.substring(':'.length); | ||
} | ||
current = undefined; | ||
else if (attrText.startsWith('v-model:')) { | ||
attrText = attrText.substring('v-model:'.length); | ||
} | ||
else if (attrText === 'v-model') { | ||
attrText = options.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName? | ||
} | ||
else if (attrText.startsWith('@')) { | ||
attrText = 'on-' + (0, shared_1.hyphenate)(attrText.substring('@'.length)); | ||
} | ||
current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => { | ||
return attrText !== propName | ||
&& attrText !== (0, shared_1.hyphenate)(propName); | ||
}); | ||
} | ||
} | ||
if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) { | ||
if (current) { | ||
current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength(); | ||
} | ||
else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) { | ||
if (current) { | ||
for (const requiredProp of current.unburnedRequiredProps) { | ||
result.push({ | ||
label: `${requiredProp}!`, | ||
paddingLeft: true, | ||
position: document.positionAt(current.labelOffset), | ||
kind: vscode.InlayHintKind.Parameter, | ||
textEdits: [{ | ||
range: { | ||
start: document.positionAt(current.insertOffset), | ||
end: document.positionAt(current.insertOffset), | ||
}, | ||
newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, shared_1.hyphenate)(requiredProp) : requiredProp}=`, | ||
}], | ||
}); | ||
} | ||
current = undefined; | ||
} | ||
} | ||
if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) { | ||
if (current) { | ||
current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength(); | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
}, | ||
provideHover(document, position, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
if (_context.documents.isVirtualFileUri(document.uri)) | ||
templatePlugin.updateCustomData([]); | ||
return templatePlugin.provideHover?.(document, position, token); | ||
}, | ||
async provideDiagnostics(document, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const originalResult = await templatePlugin.provideDiagnostics?.(document, token); | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (!virtualFile || !(virtualFile instanceof vue.VueFile)) | ||
continue; | ||
const templateErrors = []; | ||
const sfcVueTemplateCompiled = virtualFile.compiledSFCTemplate; | ||
if (sfcVueTemplateCompiled) { | ||
for (const error of sfcVueTemplateCompiled.errors) { | ||
onCompilerError(error, vscode.DiagnosticSeverity.Error); | ||
} | ||
for (const warning of sfcVueTemplateCompiled.warnings) { | ||
onCompilerError(warning, vscode.DiagnosticSeverity.Warning); | ||
} | ||
function onCompilerError(error, severity) { | ||
const templateHtmlRange = { | ||
start: error.loc?.start.offset ?? 0, | ||
end: error.loc?.end.offset ?? 0, | ||
}; | ||
let errorMessage = error.message; | ||
templateErrors.push({ | ||
range: { | ||
start: document.positionAt(templateHtmlRange.start), | ||
end: document.positionAt(templateHtmlRange.end), | ||
}, | ||
severity, | ||
code: error.code, | ||
source: 'vue', | ||
message: errorMessage, | ||
}); | ||
} | ||
} | ||
return result; | ||
}, | ||
provideHover(document, position, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
if (_context.documents.isVirtualFileUri(document.uri)) | ||
options.updateCustomData(htmlOrPugService, []); | ||
return htmlOrPugService.provideHover?.(document, position, token); | ||
}, | ||
async provideDiagnostics(document, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const originalResult = await htmlOrPugService.provideDiagnostics?.(document, token); | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (!virtualFile || !(virtualFile instanceof vue.VueFile)) | ||
continue; | ||
const templateErrors = []; | ||
const sfcVueTemplateCompiled = virtualFile.compiledSFCTemplate; | ||
if (sfcVueTemplateCompiled) { | ||
for (const error of sfcVueTemplateCompiled.errors) { | ||
onCompilerError(error, vscode.DiagnosticSeverity.Error); | ||
} | ||
return [ | ||
...originalResult ?? [], | ||
...templateErrors, | ||
]; | ||
for (const warning of sfcVueTemplateCompiled.warnings) { | ||
onCompilerError(warning, vscode.DiagnosticSeverity.Warning); | ||
} | ||
function onCompilerError(error, severity) { | ||
const templateHtmlRange = { | ||
start: error.loc?.start.offset ?? 0, | ||
end: error.loc?.end.offset ?? 0, | ||
}; | ||
let errorMessage = error.message; | ||
templateErrors.push({ | ||
range: { | ||
start: document.positionAt(templateHtmlRange.start), | ||
end: document.positionAt(templateHtmlRange.end), | ||
}, | ||
severity, | ||
code: error.code, | ||
source: 'vue', | ||
message: errorMessage, | ||
}); | ||
} | ||
} | ||
}, | ||
async provideDocumentSemanticTokens(document, range, legend, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const result = await templatePlugin.provideDocumentSemanticTokens?.(document, range, legend, token) ?? []; | ||
const scanner = options.getScanner(document, templatePlugin); | ||
if (!scanner) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (!virtualFile || !(virtualFile instanceof vue.VueFile)) | ||
continue; | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, virtualFile.fileName); | ||
const templateScriptData = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags); | ||
const components = new Set([ | ||
...templateScriptData, | ||
...templateScriptData.map(shared_1.hyphenate), | ||
]); | ||
const offsetRange = { | ||
start: document.offsetAt(range.start), | ||
end: document.offsetAt(range.end), | ||
}; | ||
let token = scanner.scan(); | ||
while (token !== html.TokenType.EOS) { | ||
const tokenOffset = scanner.getTokenOffset(); | ||
// TODO: fix source map perf and break in while condition | ||
if (tokenOffset > offsetRange.end) | ||
break; | ||
if (tokenOffset >= offsetRange.start && (token === html.TokenType.StartTag || token === html.TokenType.EndTag)) { | ||
const tokenText = scanner.getTokenText(); | ||
if (components.has(tokenText) || tokenText.indexOf('.') >= 0) { | ||
const tokenLength = scanner.getTokenLength(); | ||
const tokenPosition = document.positionAt(tokenOffset); | ||
if (components.has(tokenText)) { | ||
let tokenType = legend.tokenTypes.indexOf('component'); | ||
if (tokenType === -1) { | ||
tokenType = legend.tokenTypes.indexOf('class'); | ||
} | ||
result.push([tokenPosition.line, tokenPosition.character, tokenLength, tokenType, 0]); | ||
return [ | ||
...originalResult ?? [], | ||
...templateErrors, | ||
]; | ||
} | ||
}, | ||
async provideDocumentSemanticTokens(document, range, legend, token) { | ||
if (!options.isSupportedDocument(document)) | ||
return; | ||
const result = await htmlOrPugService.provideDocumentSemanticTokens?.(document, range, legend, token) ?? []; | ||
const scanner = options.getScanner(htmlOrPugService, document); | ||
if (!scanner) | ||
return; | ||
for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { | ||
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; | ||
if (!virtualFile || !(virtualFile instanceof vue.VueFile)) | ||
continue; | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, _ts.languageServiceHost); | ||
const templateScriptData = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags); | ||
const components = new Set([ | ||
...templateScriptData, | ||
...templateScriptData.map(shared_1.hyphenate), | ||
]); | ||
const offsetRange = { | ||
start: document.offsetAt(range.start), | ||
end: document.offsetAt(range.end), | ||
}; | ||
let token = scanner.scan(); | ||
while (token !== html.TokenType.EOS) { | ||
const tokenOffset = scanner.getTokenOffset(); | ||
// TODO: fix source map perf and break in while condition | ||
if (tokenOffset > offsetRange.end) | ||
break; | ||
if (tokenOffset >= offsetRange.start && (token === html.TokenType.StartTag || token === html.TokenType.EndTag)) { | ||
const tokenText = scanner.getTokenText(); | ||
if (components.has(tokenText) || tokenText.indexOf('.') >= 0) { | ||
const tokenLength = scanner.getTokenLength(); | ||
const tokenPosition = document.positionAt(tokenOffset); | ||
if (components.has(tokenText)) { | ||
let tokenType = legend.tokenTypes.indexOf('component'); | ||
if (tokenType === -1) { | ||
tokenType = legend.tokenTypes.indexOf('class'); | ||
} | ||
result.push([tokenPosition.line, tokenPosition.character, tokenLength, tokenType, 0]); | ||
} | ||
} | ||
token = scanner.scan(); | ||
} | ||
token = scanner.scan(); | ||
} | ||
return result; | ||
}, | ||
}; | ||
async function provideHtmlData(map, vueSourceFile) { | ||
const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri); | ||
if (builtInData.tags) { | ||
for (const tag of builtInData.tags) { | ||
if (tag.name === 'slot') | ||
continue; | ||
if (tag.name === 'component') | ||
continue; | ||
if (tag.name === 'template') | ||
continue; | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
tag.name = (0, shared_1.hyphenate)(tag.name); | ||
} | ||
else { | ||
tag.name = (0, shared_1.camelize)((0, shared_1.capitalize)(tag.name)); | ||
} | ||
} | ||
return result; | ||
}, | ||
}; | ||
async function provideHtmlData(map, vueSourceFile) { | ||
const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri); | ||
if (builtInData.tags) { | ||
for (const tag of builtInData.tags) { | ||
if (tag.name === 'slot') | ||
continue; | ||
if (tag.name === 'component') | ||
continue; | ||
if (tag.name === 'template') | ||
continue; | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
tag.name = (0, shared_1.hyphenate)(tag.name); | ||
} | ||
else { | ||
tag.name = (0, shared_1.camelize)((0, shared_1.capitalize)(tag.name)); | ||
} | ||
} | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, vueSourceFile.fileName); | ||
templatePlugin.updateCustomData([ | ||
html.newHTMLDataProvider('vue-template-built-in', builtInData), | ||
{ | ||
getId: () => 'vue-template', | ||
isApplicable: () => true, | ||
provideTags: () => { | ||
const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags) | ||
.filter(name => name !== 'Transition' | ||
&& name !== 'TransitionGroup' | ||
&& name !== 'KeepAlive' | ||
&& name !== 'Suspense' | ||
&& name !== 'Teleport'); | ||
const scriptSetupRanges = vueSourceFile.sfc.scriptSetupAst ? vue.parseScriptSetupRanges(ts, vueSourceFile.sfc.scriptSetupAst, options.vueCompilerOptions) : undefined; | ||
const names = new Set(); | ||
const tags = []; | ||
for (const tag of components) { | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
names.add((0, shared_1.hyphenate)(tag)); | ||
} | ||
else if (casing.tag === types_1.TagNameCasing.Pascal) { | ||
names.add(tag); | ||
} | ||
} | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, _ts.languageServiceHost); | ||
options.updateCustomData(htmlOrPugService, [ | ||
html.newHTMLDataProvider('vue-template-built-in', builtInData), | ||
{ | ||
getId: () => 'vue-template', | ||
isApplicable: () => true, | ||
provideTags: () => { | ||
const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags) | ||
.filter(name => name !== 'Transition' | ||
&& name !== 'TransitionGroup' | ||
&& name !== 'KeepAlive' | ||
&& name !== 'Suspense' | ||
&& name !== 'Teleport'); | ||
const scriptSetupRanges = vueSourceFile.sfc.scriptSetupAst ? vue.parseScriptSetupRanges(ts, vueSourceFile.sfc.scriptSetupAst, options.vueCompilerOptions) : undefined; | ||
const names = new Set(); | ||
const tags = []; | ||
for (const tag of components) { | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
names.add((0, shared_1.hyphenate)(tag)); | ||
} | ||
for (const binding of scriptSetupRanges?.bindings ?? []) { | ||
const name = vueSourceFile.sfc.scriptSetup.content.substring(binding.start, binding.end); | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
names.add((0, shared_1.hyphenate)(name)); | ||
} | ||
else if (casing.tag === types_1.TagNameCasing.Pascal) { | ||
names.add(name); | ||
} | ||
else if (casing.tag === types_1.TagNameCasing.Pascal) { | ||
names.add(tag); | ||
} | ||
for (const name of names) { | ||
tags.push({ | ||
name: name, | ||
attributes: [], | ||
}); | ||
} | ||
for (const binding of scriptSetupRanges?.bindings ?? []) { | ||
const name = vueSourceFile.sfc.scriptSetup.content.substring(binding.start, binding.end); | ||
if (casing.tag === types_1.TagNameCasing.Kebab) { | ||
names.add((0, shared_1.hyphenate)(name)); | ||
} | ||
return tags; | ||
}, | ||
provideAttributes: (tag) => { | ||
const attrs = (0, helpers_1.getElementAttrs)(ts, _ts.languageService, vueSourceFile.fileName, tag); | ||
const props = new Set((0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags)); | ||
const events = (0, helpers_1.checkEventsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags); | ||
const attributes = []; | ||
for (const prop of [...props, ...attrs]) { | ||
const isGlobal = !props.has(prop); | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? prop : (0, shared_1.hyphenate)(prop); | ||
if ((0, shared_1.hyphenate)(name).startsWith('on-')) { | ||
const propNameBase = name.startsWith('on-') | ||
? name.slice('on-'.length) | ||
: (name['on'.length].toLowerCase() + name.slice('onX'.length)); | ||
const propKey = createInternalItemId('componentEvent', [isGlobal ? '*' : tag, propNameBase]); | ||
attributes.push({ | ||
name: 'v-on:' + propNameBase, | ||
description: propKey, | ||
}, { | ||
name: '@' + propNameBase, | ||
description: propKey, | ||
}); | ||
} | ||
{ | ||
const propName = name; | ||
const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, propName]); | ||
attributes.push({ | ||
name: propName, | ||
description: propKey, | ||
}, { | ||
name: ':' + propName, | ||
description: propKey, | ||
}, { | ||
name: 'v-bind:' + propName, | ||
description: propKey, | ||
}); | ||
} | ||
else if (casing.tag === types_1.TagNameCasing.Pascal) { | ||
names.add(name); | ||
} | ||
for (const event of events) { | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? event : (0, shared_1.hyphenate)(event); | ||
const propKey = createInternalItemId('componentEvent', [tag, name]); | ||
} | ||
for (const name of names) { | ||
tags.push({ | ||
name: name, | ||
attributes: [], | ||
}); | ||
} | ||
return tags; | ||
}, | ||
provideAttributes: (tag) => { | ||
const attrs = (0, helpers_1.getElementAttrs)(ts, _ts.languageService, _ts.languageServiceHost, tag); | ||
const props = new Set((0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags)); | ||
const events = (0, helpers_1.checkEventsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags); | ||
const attributes = []; | ||
for (const prop of [...props, ...attrs]) { | ||
const isGlobal = !props.has(prop); | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? prop : (0, shared_1.hyphenate)(prop); | ||
if ((0, shared_1.hyphenate)(name).startsWith('on-')) { | ||
const propNameBase = name.startsWith('on-') | ||
? name.slice('on-'.length) | ||
: (name['on'.length].toLowerCase() + name.slice('onX'.length)); | ||
const propKey = createInternalItemId('componentEvent', [isGlobal ? '*' : tag, propNameBase]); | ||
attributes.push({ | ||
name: 'v-on:' + name, | ||
name: 'v-on:' + propNameBase, | ||
description: propKey, | ||
}, { | ||
name: '@' + propNameBase, | ||
description: propKey, | ||
}); | ||
} | ||
{ | ||
const propName = name; | ||
const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, propName]); | ||
attributes.push({ | ||
name: '@' + name, | ||
name: propName, | ||
description: propKey, | ||
}, { | ||
name: ':' + propName, | ||
description: propKey, | ||
}, { | ||
name: 'v-bind:' + propName, | ||
description: propKey, | ||
}); | ||
} | ||
const models = []; | ||
for (const prop of [...props, ...attrs]) { | ||
if (prop.startsWith('onUpdate:')) { | ||
const isGlobal = !props.has(prop); | ||
models.push([isGlobal, prop.substring('onUpdate:'.length)]); | ||
} | ||
} | ||
for (const event of events) { | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? event : (0, shared_1.hyphenate)(event); | ||
const propKey = createInternalItemId('componentEvent', [tag, name]); | ||
attributes.push({ | ||
name: 'v-on:' + name, | ||
description: propKey, | ||
}); | ||
attributes.push({ | ||
name: '@' + name, | ||
description: propKey, | ||
}); | ||
} | ||
const models = []; | ||
for (const prop of [...props, ...attrs]) { | ||
if (prop.startsWith('onUpdate:')) { | ||
const isGlobal = !props.has(prop); | ||
models.push([isGlobal, prop.substring('onUpdate:'.length)]); | ||
} | ||
for (const event of events) { | ||
if (event.startsWith('update:')) { | ||
models.push([false, event.substring('update:'.length)]); | ||
} | ||
} | ||
for (const event of events) { | ||
if (event.startsWith('update:')) { | ||
models.push([false, event.substring('update:'.length)]); | ||
} | ||
for (const [isGlobal, model] of models) { | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? model : (0, shared_1.hyphenate)(model); | ||
const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, name]); | ||
} | ||
for (const [isGlobal, model] of models) { | ||
const name = casing.attr === types_1.AttrNameCasing.Camel ? model : (0, shared_1.hyphenate)(model); | ||
const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, name]); | ||
attributes.push({ | ||
name: 'v-model:' + name, | ||
description: propKey, | ||
}); | ||
if (model === 'modelValue') { | ||
attributes.push({ | ||
name: 'v-model:' + name, | ||
name: 'v-model', | ||
description: propKey, | ||
}); | ||
if (model === 'modelValue') { | ||
attributes.push({ | ||
name: 'v-model', | ||
description: propKey, | ||
}); | ||
} | ||
} | ||
return attributes; | ||
}, | ||
provideValues: () => [], | ||
} | ||
return attributes; | ||
}, | ||
]); | ||
} | ||
function afterHtmlCompletion(completionList, map, vueSourceFile) { | ||
const replacement = getReplacement(completionList, map.sourceFileDocument); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, vueSourceFile.fileName); | ||
const componentNames = new Set((0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags).map(shared_1.hyphenate)); | ||
if (replacement) { | ||
const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@'); | ||
const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':'); | ||
const isModel = replacement.text.startsWith('v-model:') || replacement.text.split('.')[0] === 'v-model'; | ||
const hasModifier = replacement.text.includes('.'); | ||
const validModifiers = isEvent ? eventModifiers | ||
: isProp ? propModifiers | ||
: undefined; | ||
const modifiers = replacement.text.split('.').slice(1); | ||
const textWithoutModifier = replacement.text.split('.')[0]; | ||
if (validModifiers && hasModifier) { | ||
for (const modifier in validModifiers) { | ||
if (modifiers.includes(modifier)) | ||
continue; | ||
const modifierDes = validModifiers[modifier]; | ||
const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier; | ||
const newItem = { | ||
label: modifier, | ||
filterText: insertText, | ||
documentation: { | ||
kind: 'markdown', | ||
value: modifierDes, | ||
}, | ||
textEdit: { | ||
range: replacement.textEdit.range, | ||
newText: insertText, | ||
}, | ||
kind: vscode.CompletionItemKind.EnumMember, | ||
}; | ||
completionList.items.push(newItem); | ||
} | ||
provideValues: () => [], | ||
}, | ||
]); | ||
} | ||
function afterHtmlCompletion(completionList, map, vueSourceFile) { | ||
const replacement = getReplacement(completionList, map.sourceFileDocument); | ||
const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, _ts.languageServiceHost); | ||
const componentNames = new Set((0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags).map(shared_1.hyphenate)); | ||
if (replacement) { | ||
const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@'); | ||
const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':'); | ||
const isModel = replacement.text.startsWith('v-model:') || replacement.text.split('.')[0] === 'v-model'; | ||
const hasModifier = replacement.text.includes('.'); | ||
const validModifiers = isEvent ? eventModifiers | ||
: isProp ? propModifiers | ||
: undefined; | ||
const modifiers = replacement.text.split('.').slice(1); | ||
const textWithoutModifier = replacement.text.split('.')[0]; | ||
if (validModifiers && hasModifier) { | ||
for (const modifier in validModifiers) { | ||
if (modifiers.includes(modifier)) | ||
continue; | ||
const modifierDes = validModifiers[modifier]; | ||
const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier; | ||
const newItem = { | ||
label: modifier, | ||
filterText: insertText, | ||
documentation: { | ||
kind: 'markdown', | ||
value: modifierDes, | ||
}, | ||
textEdit: { | ||
range: replacement.textEdit.range, | ||
newText: insertText, | ||
}, | ||
kind: vscode.CompletionItemKind.EnumMember, | ||
}; | ||
completionList.items.push(newItem); | ||
} | ||
else if (hasModifier && isModel) { | ||
for (const modifier of modelData.globalAttributes ?? []) { | ||
if (modifiers.includes(modifier.name)) | ||
continue; | ||
const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name; | ||
const newItem = { | ||
label: modifier.name, | ||
filterText: insertText, | ||
documentation: { | ||
kind: 'markdown', | ||
value: (typeof modifier.description === 'object' ? modifier.description.value : modifier.description) | ||
+ '\n\n' + modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | '), | ||
}, | ||
textEdit: { | ||
range: replacement.textEdit.range, | ||
newText: insertText, | ||
}, | ||
kind: vscode.CompletionItemKind.EnumMember, | ||
}; | ||
completionList.items.push(newItem); | ||
} | ||
} | ||
else if (hasModifier && isModel) { | ||
for (const modifier of modelData.globalAttributes ?? []) { | ||
if (modifiers.includes(modifier.name)) | ||
continue; | ||
const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name; | ||
const newItem = { | ||
label: modifier.name, | ||
filterText: insertText, | ||
documentation: { | ||
kind: 'markdown', | ||
value: (typeof modifier.description === 'object' ? modifier.description.value : modifier.description) | ||
+ '\n\n' + modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | '), | ||
}, | ||
textEdit: { | ||
range: replacement.textEdit.range, | ||
newText: insertText, | ||
}, | ||
kind: vscode.CompletionItemKind.EnumMember, | ||
}; | ||
completionList.items.push(newItem); | ||
} | ||
} | ||
for (const item of completionList.items) { | ||
const itemIdKey = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value; | ||
const itemId = itemIdKey ? readInternalItemId(itemIdKey) : undefined; | ||
if (itemId) { | ||
item.documentation = undefined; | ||
} | ||
if (itemIdKey && itemId) { | ||
if (itemId.type === 'componentProp' || itemId.type === 'componentEvent') { | ||
const [componentName] = itemId.args; | ||
} | ||
for (const item of completionList.items) { | ||
const itemIdKey = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value; | ||
const itemId = itemIdKey ? readInternalItemId(itemIdKey) : undefined; | ||
if (itemId) { | ||
item.documentation = undefined; | ||
} | ||
if (itemIdKey && itemId) { | ||
if (itemId.type === 'componentProp' || itemId.type === 'componentEvent') { | ||
const [componentName] = itemId.args; | ||
if (componentName !== '*') { | ||
item.sortText = '\u0000' + (item.sortText ?? item.label); | ||
} | ||
if (itemId.type === 'componentProp') { | ||
if (componentName !== '*') { | ||
item.sortText = '\u0000' + (item.sortText ?? item.label); | ||
item.kind = vscode.CompletionItemKind.Field; | ||
} | ||
if (itemId.type === 'componentProp') { | ||
if (componentName !== '*') { | ||
item.kind = vscode.CompletionItemKind.Field; | ||
} | ||
} | ||
else { | ||
item.kind = componentName !== '*' ? vscode.CompletionItemKind.Function : vscode.CompletionItemKind.Event; | ||
} | ||
} | ||
else if (item.label === 'v-if' | ||
|| item.label === 'v-else-if' | ||
|| item.label === 'v-else' | ||
|| item.label === 'v-for') { | ||
item.kind = vscode.CompletionItemKind.Method; | ||
item.sortText = '\u0003' + (item.sortText ?? item.label); | ||
} | ||
else if (item.label.startsWith('v-')) { | ||
item.kind = vscode.CompletionItemKind.Function; | ||
item.sortText = '\u0002' + (item.sortText ?? item.label); | ||
} | ||
else { | ||
item.sortText = '\u0001' + (item.sortText ?? item.label); | ||
item.kind = componentName !== '*' ? vscode.CompletionItemKind.Function : vscode.CompletionItemKind.Event; | ||
} | ||
} | ||
else if (item.kind === vscode.CompletionItemKind.Property && componentNames.has((0, shared_1.hyphenate)(item.label))) { | ||
item.kind = vscode.CompletionItemKind.Variable; | ||
item.sortText = '\u0000' + (item.sortText ?? item.label); | ||
else if (item.label === 'v-if' | ||
|| item.label === 'v-else-if' | ||
|| item.label === 'v-else' | ||
|| item.label === 'v-for') { | ||
item.kind = vscode.CompletionItemKind.Method; | ||
item.sortText = '\u0003' + (item.sortText ?? item.label); | ||
} | ||
else if (item.label.startsWith('v-')) { | ||
item.kind = vscode.CompletionItemKind.Function; | ||
item.sortText = '\u0002' + (item.sortText ?? item.label); | ||
} | ||
else { | ||
item.sortText = '\u0001' + (item.sortText ?? item.label); | ||
} | ||
} | ||
templatePlugin.updateCustomData([]); | ||
else if (item.kind === vscode.CompletionItemKind.Property && componentNames.has((0, shared_1.hyphenate)(item.label))) { | ||
item.kind = vscode.CompletionItemKind.Variable; | ||
item.sortText = '\u0000' + (item.sortText ?? item.label); | ||
} | ||
} | ||
}; | ||
return plugin; | ||
} | ||
exports.default = useVueTemplateLanguagePlugin; | ||
options.updateCustomData(htmlOrPugService, []); | ||
} | ||
}; | ||
function createInternalItemId(type, args) { | ||
@@ -517,0 +513,0 @@ return '__VLS_::' + type + '::' + args.join(','); |
@@ -1,8 +0,8 @@ | ||
import { InjectionKey, Service } from '@volar/language-service'; | ||
import type { Service } from '@volar/language-service'; | ||
import { TextDocument } from 'vscode-languageserver-textdocument'; | ||
import * as vue from '@vue/language-core'; | ||
export declare const injectionKeys: { | ||
vueFile: InjectionKey<[TextDocument], vue.VueFile>; | ||
}; | ||
declare const _default: () => Service; | ||
export interface Provide { | ||
'vue/vueFile': (document: TextDocument) => vue.VueFile | undefined; | ||
} | ||
declare const _default: () => Service<Provide>; | ||
export default _default; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.injectionKeys = void 0; | ||
const language_service_1 = require("@volar/language-service"); | ||
const html = require("vscode-html-languageservice"); | ||
@@ -11,19 +9,18 @@ const vscode = require("vscode-languageserver-protocol"); | ||
let sfcDataProvider; | ||
exports.injectionKeys = { | ||
vueFile: 'vue/vueFile', | ||
}; | ||
exports.default = () => (context) => { | ||
const htmlPlugin = (0, volar_service_html_1.default)({ validLang: 'vue', disableCustomData: true })(context); | ||
exports.default = () => (context, modules) => { | ||
const htmlPlugin = (0, volar_service_html_1.default)({ validLang: 'vue', disableCustomData: true })(context, modules); | ||
if (!context?.typescript) | ||
return htmlPlugin; | ||
sfcDataProvider ??= html.newHTMLDataProvider('vue', (0, data_1.loadLanguageBlocks)(context.env.locale ?? 'en')); | ||
htmlPlugin.getHtmlLs().setDataProviders(false, [sfcDataProvider]); | ||
htmlPlugin.provide['html/languageService']().setDataProviders(false, [sfcDataProvider]); | ||
const _ts = context.typescript; | ||
return { | ||
provide: (0, language_service_1.defineProvide)(exports.injectionKeys.vueFile, document => { | ||
return worker(document, (vueFile) => { | ||
return vueFile; | ||
}); | ||
}), | ||
...htmlPlugin, | ||
provide: { | ||
'vue/vueFile': document => { | ||
return worker(document, (vueFile) => { | ||
return vueFile; | ||
}); | ||
}, | ||
}, | ||
provideSemanticDiagnostics(document) { | ||
@@ -48,3 +45,3 @@ return worker(document, (vueSourceFile) => { | ||
}, | ||
findDocumentLinks: undefined, | ||
provideDocumentLinks: undefined, | ||
provideDocumentSymbols(document) { | ||
@@ -51,0 +48,0 @@ return worker(document, (vueSourceFile) => { |
{ | ||
"name": "@vue/language-service", | ||
"version": "1.7.3", | ||
"version": "1.7.4", | ||
"main": "out/index.js", | ||
@@ -20,17 +20,17 @@ "license": "MIT", | ||
"dependencies": { | ||
"@volar/language-core": "1.6.3", | ||
"@volar/language-service": "1.6.3", | ||
"@volar/source-map": "1.6.3", | ||
"@volar/language-core": "1.6.5", | ||
"@volar/language-service": "1.6.5", | ||
"@volar/source-map": "1.6.5", | ||
"@vue/compiler-dom": "^3.3.0", | ||
"@vue/language-core": "1.7.3", | ||
"@vue/language-core": "1.7.4", | ||
"@vue/reactivity": "^3.3.0", | ||
"@vue/shared": "^3.3.0", | ||
"volar-service-css": "0.0.2", | ||
"volar-service-emmet": "0.0.2", | ||
"volar-service-html": "0.0.2", | ||
"volar-service-json": "0.0.2", | ||
"volar-service-pug": "0.0.2", | ||
"volar-service-pug-beautify": "0.0.2", | ||
"volar-service-typescript": "0.0.2", | ||
"volar-service-typescript-twoslash-queries": "0.0.2", | ||
"volar-service-css": "0.0.3", | ||
"volar-service-emmet": "0.0.3", | ||
"volar-service-html": "0.0.3", | ||
"volar-service-json": "0.0.3", | ||
"volar-service-pug": "0.0.3", | ||
"volar-service-pug-beautify": "0.0.3", | ||
"volar-service-typescript": "0.0.3", | ||
"volar-service-typescript-twoslash-queries": "0.0.3", | ||
"vscode-html-languageservice": "^5.0.4", | ||
@@ -43,5 +43,5 @@ "vscode-json-languageservice": "^5.2.0", | ||
"devDependencies": { | ||
"@volar/kit": "1.6.3" | ||
"@volar/kit": "1.6.5" | ||
}, | ||
"gitHead": "0afe0eb39ed144513faddbfa4fddc667e8864cdf" | ||
"gitHead": "30fed93ac21d4d4fbf2351b85ebe65acb1216496" | ||
} |
694320
12588
+ Added@volar/language-core@1.6.5(transitive)
+ Added@volar/language-service@1.6.5(transitive)
+ Added@volar/source-map@1.6.5(transitive)
+ Added@vue/language-core@1.7.4(transitive)
+ Addedvolar-service-css@0.0.3(transitive)
+ Addedvolar-service-emmet@0.0.3(transitive)
+ Addedvolar-service-html@0.0.3(transitive)
+ Addedvolar-service-json@0.0.3(transitive)
+ Addedvolar-service-pug@0.0.3(transitive)
+ Addedvolar-service-pug-beautify@0.0.3(transitive)
+ Addedvolar-service-typescript@0.0.3(transitive)
+ Addedvolar-service-typescript-twoslash-queries@0.0.3(transitive)
- Removed@volar/language-core@1.6.3(transitive)
- Removed@volar/language-service@1.6.3(transitive)
- Removed@volar/source-map@1.6.3(transitive)
- Removed@vue/language-core@1.7.3(transitive)
- Removedmuggle-string@0.2.2(transitive)
- Removedvolar-service-css@0.0.2(transitive)
- Removedvolar-service-emmet@0.0.2(transitive)
- Removedvolar-service-html@0.0.2(transitive)
- Removedvolar-service-json@0.0.2(transitive)
- Removedvolar-service-pug@0.0.2(transitive)
- Removedvolar-service-pug-beautify@0.0.2(transitive)
- Removedvolar-service-typescript@0.0.2(transitive)
- Removedvolar-service-typescript-twoslash-queries@0.0.2(transitive)
Updated@volar/language-core@1.6.5
Updated@volar/source-map@1.6.5
Updated@vue/language-core@1.7.4
Updatedvolar-service-css@0.0.3
Updatedvolar-service-emmet@0.0.3
Updatedvolar-service-html@0.0.3
Updatedvolar-service-json@0.0.3
Updatedvolar-service-pug@0.0.3