From 839ac0e03b7fb191ba6fa5a53af4977f00a2679f Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 6 Jun 2022 03:02:29 +0800 Subject: [PATCH 01/14] refactor: add `compileFileToVue` plugin api --- .../vue-code-gen/src/generators/template.ts | 10 +- .../src/plugins/vue-template.ts | 4 +- .../vue-language-service/src/vueDocuments.ts | 6 +- packages/vue-tsc/src/apis.ts | 2 +- packages/vue-typescript/package.json | 2 +- .../vue-typescript/src/plugins/file-md.ts | 21 +++ .../vue-typescript/src/plugins/file-vue.ts | 18 +++ .../plugins/{html.ts => vue-template-html.ts} | 4 +- .../plugins/{pug.ts => vue-template-pug.ts} | 10 +- packages/vue-typescript/src/vueFile.ts | 131 +++++++++++------- 10 files changed, 137 insertions(+), 71 deletions(-) create mode 100644 packages/vue-typescript/src/plugins/file-md.ts create mode 100644 packages/vue-typescript/src/plugins/file-vue.ts rename packages/vue-typescript/src/plugins/{html.ts => vue-template-html.ts} (63%) rename packages/vue-typescript/src/plugins/{pug.ts => vue-template-pug.ts} (57%) diff --git a/packages/vue-code-gen/src/generators/template.ts b/packages/vue-code-gen/src/generators/template.ts index 343e1d1e47..0140af9c83 100644 --- a/packages/vue-code-gen/src/generators/template.ts +++ b/packages/vue-code-gen/src/generators/template.ts @@ -65,7 +65,7 @@ export function generate( allowTypeNarrowingInEventExpressions: boolean, hasScriptSetup: boolean, cssScopedClasses: string[] = [], - htmlToTemplate: (htmlStart: number, htmlEnd: number) => { start: number, end: number; } | undefined, + htmlToTemplate: (htmlRange: { start: number, end: number; }) => { start: number, end: number; } | undefined, searchTexts: { getEmitCompletion(tag: string): string, getPropsCompletion(tag: string): string, @@ -228,7 +228,7 @@ export function generate( tagResolves[tagName] = { component: var_rawComponent, emit: var_emit, - offsets: tagOffsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty), + offsets: tagOffsets.map(offset => htmlToTemplate({ start: offset, end: offset })?.start).filter(notEmpty), }; } @@ -1182,7 +1182,7 @@ export function generate( end: prop.arg.loc.start.offset + end, }; - const newStart = htmlToTemplate(sourceRange.start, sourceRange.end)?.start; + const newStart = htmlToTemplate({ start: sourceRange.start, end: sourceRange.end })?.start; if (newStart === undefined) continue; const offset = newStart - sourceRange.start; sourceRange.start += offset; @@ -1847,7 +1847,7 @@ export function generate( function addMapping(gen: typeof tsCodeGen, mapping: SourceMaps.Mapping) { const newMapping = { ...mapping }; - const templateStart = htmlToTemplate(mapping.sourceRange.start, mapping.sourceRange.end)?.start; + const templateStart = htmlToTemplate(mapping.sourceRange)?.start; if (templateStart === undefined) return; // not found const offset = templateStart - mapping.sourceRange.start; newMapping.sourceRange = { @@ -1858,7 +1858,7 @@ export function generate( if (mapping.additional) { newMapping.additional = []; for (const other of mapping.additional) { - let otherTemplateStart = htmlToTemplate(other.sourceRange.start, other.sourceRange.end)?.start; + let otherTemplateStart = htmlToTemplate(other.sourceRange)?.start; if (otherTemplateStart === undefined) continue; const otherOffset = otherTemplateStart - other.sourceRange.start; newMapping.additional.push({ diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 6ab85e09bd..0b299d8cce 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -160,11 +160,11 @@ export default function useVueTemplateLanguagePlugin TextDocument.create(shared.fsPathToUri(vueFile.fileName), 'vue', documentVersion++, vueFile.refs.content.value)); + const document = computed(() => TextDocument.create(shared.fsPathToUri(vueFile.fileName), vueFile.fileName.endsWith('.md') ? 'markdown' : 'vue', documentVersion++, vueFile.refs.content.value)); const sourceMaps = computed(() => { return vueFile.refs.allEmbeddeds.value.map(embedded => sourceMapsMap.get(embedded)); }); @@ -259,13 +259,13 @@ export function parseVueDocument(vueFile: VueFile) { const offsets = tags.get(node.tag)!; const startTagHtmlOffset = node.loc.start.offset + node.loc.source.indexOf(node.tag); - const startTagTemplateOffset = htmlComputed.htmlToTemplate(startTagHtmlOffset, startTagHtmlOffset); + const startTagTemplateOffset = htmlComputed.mapping({ start: startTagHtmlOffset, end: startTagHtmlOffset }); if (startTagTemplateOffset !== undefined) { offsets.push(startTagTemplateOffset.start); } const endTagHtmlOffset = node.loc.start.offset + node.loc.source.lastIndexOf(node.tag); - const endTagTemplateOffset = htmlComputed.htmlToTemplate(endTagHtmlOffset, endTagHtmlOffset); + const endTagTemplateOffset = htmlComputed.mapping({ start: endTagHtmlOffset, end: endTagHtmlOffset }); if (endTagTemplateOffset !== undefined) { offsets.push(endTagTemplateOffset.end); } diff --git a/packages/vue-tsc/src/apis.ts b/packages/vue-tsc/src/apis.ts index 0838673a4e..9036bf7b93 100644 --- a/packages/vue-tsc/src/apis.ts +++ b/packages/vue-tsc/src/apis.ts @@ -160,7 +160,7 @@ export function register( } } else { - file = ts.createSourceFile(fileName, docText, fileName.endsWith('.vue') ? ts.ScriptTarget.JSON : ts.ScriptTarget.Latest); + file = ts.createSourceFile(fileName, docText, fileName.endsWith('.vue') || fileName.endsWith('.md') ? ts.ScriptTarget.JSON : ts.ScriptTarget.Latest); } } const newDiagnostic: T = { diff --git a/packages/vue-typescript/package.json b/packages/vue-typescript/package.json index cefbba828f..331b0fc944 100644 --- a/packages/vue-typescript/package.json +++ b/packages/vue-typescript/package.json @@ -24,6 +24,6 @@ "@vue/reactivity": "^3.2.36" }, "browser": { - "./out/plugins/pug.js": "./out/plugins/empty.js" + "./out/plugins/vue-template-pug.js": "./out/plugins/empty.js" } } diff --git a/packages/vue-typescript/src/plugins/file-md.ts b/packages/vue-typescript/src/plugins/file-md.ts new file mode 100644 index 0000000000..1ca6e8ecde --- /dev/null +++ b/packages/vue-typescript/src/plugins/file-md.ts @@ -0,0 +1,21 @@ +import { VueLanguagePlugin } from '../vueFile'; + +export default function (): VueLanguagePlugin { + + return { + + compileFileToVue(fileName, content) { + + if (fileName.endsWith('.md')) { + + // return { + // vue: content, + // mapping(start, end) { + // return { start, end }; + // }, + // } + return undefined; + } + } + }; +} diff --git a/packages/vue-typescript/src/plugins/file-vue.ts b/packages/vue-typescript/src/plugins/file-vue.ts new file mode 100644 index 0000000000..49798cbf16 --- /dev/null +++ b/packages/vue-typescript/src/plugins/file-vue.ts @@ -0,0 +1,18 @@ +import { VueLanguagePlugin } from '../vueFile'; + +export default function (): VueLanguagePlugin { + + return { + + compileFileToVue(fileName, content) { + + if (fileName.endsWith('.vue')) { + + return { + vue: content, + mapping: vueRange => vueRange, + } + } + } + }; +} diff --git a/packages/vue-typescript/src/plugins/html.ts b/packages/vue-typescript/src/plugins/vue-template-html.ts similarity index 63% rename from packages/vue-typescript/src/plugins/html.ts rename to packages/vue-typescript/src/plugins/vue-template-html.ts index e028171ec4..e755eae8ca 100644 --- a/packages/vue-typescript/src/plugins/html.ts +++ b/packages/vue-typescript/src/plugins/vue-template-html.ts @@ -4,13 +4,13 @@ export default function (): VueLanguagePlugin { return { - compileTemplate(template, lang) { + compileTemplateToHtml(lang, template) { if (lang === 'html') { return { html: template, - mapping: (htmlStart, htmlEnd) => ({ start: htmlStart, end: htmlEnd }), + mapping: htmlRange => htmlRange, }; } } diff --git a/packages/vue-typescript/src/plugins/pug.ts b/packages/vue-typescript/src/plugins/vue-template-pug.ts similarity index 57% rename from packages/vue-typescript/src/plugins/pug.ts rename to packages/vue-typescript/src/plugins/vue-template-pug.ts index 8d581def24..f25d6ac4aa 100644 --- a/packages/vue-typescript/src/plugins/pug.ts +++ b/packages/vue-typescript/src/plugins/vue-template-pug.ts @@ -4,7 +4,7 @@ export default function (): VueLanguagePlugin { return { - compileTemplate(template, lang) { + compileTemplateToHtml(lang, template) { if (lang === 'pug') { @@ -19,15 +19,15 @@ export default function (): VueLanguagePlugin { if (pugDoc) { return { html: pugDoc.htmlCode, - mapping: (htmlStart, htmlEnd) => { - const pugRange = pugDoc.sourceMap.getSourceRange(htmlStart, htmlEnd, data => !data?.isEmptyTagCompletion)?.[0]; + mapping: htmlRange => { + const pugRange = pugDoc.sourceMap.getSourceRange(htmlRange.start, htmlRange.end, data => !data?.isEmptyTagCompletion)?.[0]; if (pugRange) { return pugRange; } else { - const pugStart = pugDoc.sourceMap.getSourceRange(htmlStart, htmlStart, data => !data?.isEmptyTagCompletion)?.[0]?.start; - const pugEnd = pugDoc.sourceMap.getSourceRange(htmlEnd, htmlEnd, data => !data?.isEmptyTagCompletion)?.[0]?.end; + const pugStart = pugDoc.sourceMap.getSourceRange(htmlRange.start, htmlRange.start, data => !data?.isEmptyTagCompletion)?.[0]?.start; + const pugEnd = pugDoc.sourceMap.getSourceRange(htmlRange.end, htmlRange.end, data => !data?.isEmptyTagCompletion)?.[0]?.end; if (pugStart !== undefined && pugEnd !== undefined) { return { start: pugStart, end: pugEnd }; diff --git a/packages/vue-typescript/src/vueFile.ts b/packages/vue-typescript/src/vueFile.ts index 87484bebe1..6a350005dd 100644 --- a/packages/vue-typescript/src/vueFile.ts +++ b/packages/vue-typescript/src/vueFile.ts @@ -12,8 +12,10 @@ import * as templateGen from '@volar/vue-code-gen/out/generators/template'; import { parseCssClassNames } from './utils/parseCssClassNames'; import { parseCssVars } from './utils/parseCssVars'; -import useHtmlPlugin from './plugins/html'; -import usePugPlugin from './plugins/pug'; +import useVueFilePlugin from './plugins/file-vue'; +import useMdFilePlugin from './plugins/file-md'; +import useHtmlPlugin from './plugins/vue-template-html'; +import usePugPlugin from './plugins/vue-template-pug'; import useVueSfcStyles from './plugins/vue-sfc-styles'; import useVueSfcCustomBlocks from './plugins/vue-sfc-customblocks'; import useVueSfcScriptsFormat from './plugins/vue-sfc-scripts'; @@ -22,15 +24,22 @@ import useVueTsScripts from './plugins/vue-typescript-scripts'; import useVueTsTemplate from './plugins/vue-typescript-template'; import type * as _0 from 'typescript/lib/tsserverlibrary'; // fix TS2742 -import { Mapping } from '@volar/source-map'; +import { Mapping, MappingBase } from '@volar/source-map'; export interface VueLanguagePlugin { - compileTemplate?(tmplate: string, lang: string): { + compileFileToVue?(fileName: string, content: string): { + vue: string, + mapping(vueRange: { start: number, end: number; }): { start: number, end: number; } | undefined, + } | undefined; + + compileTemplateToHtml?(lang: string, tmplate: string): { html: string, - mapping(htmlStart: number, htmlEnd: number): { start: number, end: number; } | undefined, + mapping(htmlRange: { start: number, end: number; }): { start: number, end: number; } | undefined, } | undefined; + // TODO: compileHtmlTemplateToAst + getEmbeddedFilesCount?(sfc: Sfc): number; getEmbeddedFile?(fileName: string, sfc: Sfc, i: number): Embedded | undefined; @@ -102,7 +111,7 @@ export function createVueFile( ) { // refs - const content = ref(''); + const fileContent = ref(''); const version = ref(''); const sfc = reactive({ template: null, @@ -117,32 +126,31 @@ export function createVueFile( componentItems: [], }; - // computeds - const parsedSfc = computed(() => parse(content.value, { sourceMap: false, ignoreEmpty: false })); - // use - const templateHtmlCompiled = computed { start: number, end: number; } | undefined, - }>(() => { + const compiledVue = computed(() => { + for (const plugin of plugins) { + const compiled = plugin.compileFileToVue?.(fileName, fileContent.value); + if (compiled) { + return compiled; + } + } + }); + const vueContent = computed(() => compiledVue.value?.vue ?? ''); + const parsedSfc = computed(() => parse(vueContent.value, { sourceMap: false, ignoreEmpty: false })); + const computedHtmlTemplate = computed>>(() => { if (sfc.template) { for (const plugin of plugins) { - const compiledHtml = plugin.compileTemplate?.(sfc.template.content, sfc.template.lang); + const compiledHtml = plugin.compileTemplateToHtml?.(sfc.template.lang, sfc.template.content); if (compiledHtml) { - return { - lang: sfc.template.lang, - htmlText: compiledHtml.html, - htmlToTemplate: compiledHtml.mapping, - }; + return compiledHtml; }; } } }); const templateAstCompiled = computed(() => { - if (templateHtmlCompiled.value) { + if (computedHtmlTemplate.value) { return compileSFCTemplate( - templateHtmlCompiled.value.htmlText, + computedHtmlTemplate.value.html, compilerOptions.experimentalTemplateCompilerOptions, compilerOptions.experimentalCompatMode ?? 3, ); @@ -152,21 +160,21 @@ export function createVueFile( const cssScopedClasses = useCssScopedClasses(sfc, compilerOptions); const templateCodeGens = computed(() => { - if (!templateHtmlCompiled.value) + if (!computedHtmlTemplate.value) return; if (!templateAstCompiled.value?.ast) return; return templateGen.generate( ts, - templateHtmlCompiled.value.lang, + sfc.template?.lang ?? 'html', templateAstCompiled.value.ast, compilerOptions.experimentalCompatMode ?? 3, compilerOptions.experimentalRuntimeMode, !!compilerOptions.experimentalAllowTypeNarrowingInInlineHandlers, !!sfc.scriptSetup, Object.values(cssScopedClasses.value).map(map => Object.keys(map)).flat(), - templateHtmlCompiled.value.htmlToTemplate, + computedHtmlTemplate.value.mapping, { getEmitCompletion: SearchTexts.EmitCompletion, getPropsCompletion: SearchTexts.PropsCompletion, @@ -215,6 +223,8 @@ export function createVueFile( } : undefined)); const plugins: VueLanguagePlugin[] = [ + useVueFilePlugin(), + useMdFilePlugin(), useHtmlPlugin(), usePugPlugin(), useVueSfcStyles(), @@ -242,7 +252,7 @@ export function createVueFile( ), ]; - // getters + // computeds const pluginEmbeddeds = plugins.map(plugin => { if (plugin.getEmbeddedFilesCount && plugin.getEmbeddedFile) { const embeddedsCount = computed(() => plugin.getEmbeddedFilesCount!(sfc)); @@ -259,14 +269,27 @@ export function createVueFile( const sourceMap = raw.value.sourceMap; const newMappings: typeof sourceMap.mappings = []; for (const mapping of sourceMap.mappings) { - newMappings.push({ - ...mapping, - sourceRange: parseMappingSourceRange(mapping.data, mapping.sourceRange), - additional: mapping.additional ? mapping.additional.map(add => ({ - ...add, - sourceRange: parseMappingSourceRange(mapping.data, add.sourceRange), - })) : undefined, - }); + const sourceRange = parseMappingSourceRange(mapping.data, mapping.sourceRange); + if (sourceRange) { + let additional: MappingBase[] | undefined; + if (mapping.additional) { + additional = []; + for (const add of mapping.additional) { + const addSourceRange = parseMappingSourceRange(mapping.data, add.sourceRange); + if (addSourceRange) { + additional.push({ + ...add, + sourceRange: addSourceRange, + }); + } + } + } + newMappings.push({ + ...mapping, + sourceRange, + additional, + }); + } } const newSourceMap = new EmbeddedFileSourceMap(newMappings); const newEmbedded: Embedded = { @@ -374,8 +397,8 @@ export function createVueFile( return { fileName, - getContent: untrack(() => content.value), - getSfcTemplateLanguageCompiled: untrack(() => templateHtmlCompiled.value), + getContent: untrack(() => fileContent.value), + getSfcTemplateLanguageCompiled: untrack(() => computedHtmlTemplate.value), getSfcVueTemplateCompiled: untrack(() => templateAstCompiled.value), getVersion: untrack(() => version.value), update: untrack(update), @@ -391,58 +414,61 @@ export function createVueFile( isJsxMissing: () => !compilerOptions.experimentalDisableTemplateSupport && !(tsHost?.getCompilationSettings().jsx === ts.JsxEmit.Preserve), refs: { - content, + content: fileContent, allEmbeddeds, teleports, }, }; function parseMappingSourceRange(data: EmbeddedFileMappingData, range: Mapping['sourceRange']) { + + if (!compiledVue.value) throw '!compiledVue.value'; + if (data.vueTag === 'scriptSrc') { if (!sfc.script?.src) throw '!sfc.script?.src'; - const vueStart = content.value.substring(0, sfc.script.startTagEnd).lastIndexOf(sfc.script.src); + const vueStart = vueContent.value.substring(0, sfc.script.startTagEnd).lastIndexOf(sfc.script.src); const vueEnd = vueStart + sfc.script.src.length; - return { + return compiledVue.value.mapping({ start: vueStart - 1, end: vueEnd + 1, - }; + }); } else if (data.vueTag === 'script') { if (!sfc.script) throw '!sfc.script'; - return { + return compiledVue.value.mapping({ start: sfc.script.startTagEnd + range.start, end: sfc.script.startTagEnd + range.end, - }; + }); } else if (data.vueTag === 'scriptSetup') { if (!sfc.scriptSetup) throw '!sfc.scriptSetup'; - return { + return compiledVue.value.mapping({ start: sfc.scriptSetup.startTagEnd + range.start, end: sfc.scriptSetup.startTagEnd + range.end, - }; + }); } else if (data.vueTag === 'template') { if (!sfc.template) throw '!sfc.template'; - return { + return compiledVue.value.mapping({ start: sfc.template.startTagEnd + range.start, end: sfc.template.startTagEnd + range.end, - }; + }); } else if (data.vueTag === 'style') { if (data.vueTagIndex === undefined) throw 'data.vueTagIndex === undefined'; - return { + return compiledVue.value.mapping({ start: sfc.styles[data.vueTagIndex].startTagEnd + range.start, end: sfc.styles[data.vueTagIndex].startTagEnd + range.end, - }; + }); } else if (data.vueTag === 'customBlock') { if (data.vueTagIndex === undefined) throw 'data.vueTagIndex === undefined'; - return { + return compiledVue.value.mapping({ start: sfc.customBlocks[data.vueTagIndex].startTagEnd + range.start, end: sfc.customBlocks[data.vueTagIndex].startTagEnd + range.end, - }; + }); } - return range; + return compiledVue.value.mapping(range); } function update(newContent: string, newVersion: string) { @@ -454,9 +480,10 @@ export function createVueFile( } } - content.value = newContent; + fileContent.value = newContent; version.value = newVersion; + // TODO: wait for https://github.com/vuejs/core/pull/5912 updateTemplate(parsedSfc.value.descriptor.template); updateScript(parsedSfc.value.descriptor.script); updateScriptSetup(parsedSfc.value.descriptor.scriptSetup); From c780661c1551941b2cff64ce19a925eeb0f618f6 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 6 Jun 2022 03:45:42 +0800 Subject: [PATCH 02/14] refactor: access markdown files --- .../vscode-vue-language-features/package.json | 1 + .../src/browserClientMain.ts | 2 +- .../src/common.ts | 6 ++++- .../src/features/attrNameCase.ts | 2 +- .../src/features/tagNameCase.ts | 2 +- .../src/features/tsVersion.ts | 1 + .../src/features/tsconfig.ts | 1 + .../src/nodeClientMain.ts | 2 +- packages/shared/src/common.ts | 2 ++ packages/typescript-vue-plugin/src/index.ts | 5 ++-- .../src/features/languageFeatures.ts | 2 +- packages/vue-language-server/src/project.ts | 2 +- .../src/registers/registerlanguageFeatures.ts | 1 + .../src/documentService.ts | 2 +- .../src/languageService.ts | 2 +- .../src/plugins/vue-template.ts | 5 +++- packages/vue-tsc/bin/vue-tsc.js | 6 ++--- .../vue-typescript/src/typescriptRuntime.ts | 9 +++---- packages/vue-typescript/src/vueFile.ts | 24 ++++++++++++------- 19 files changed, 50 insertions(+), 27 deletions(-) diff --git a/extensions/vscode-vue-language-features/package.json b/extensions/vscode-vue-language-features/package.json index 95a31e45df..8172c86048 100644 --- a/extensions/vscode-vue-language-features/package.json +++ b/extensions/vscode-vue-language-features/package.json @@ -25,6 +25,7 @@ ], "activationEvents": [ "onLanguage:vue", + "onLanguage:markdown", "onLanguage:javascript", "onLanguage:typescript", "onLanguage:javascriptreact", diff --git a/extensions/vscode-vue-language-features/src/browserClientMain.ts b/extensions/vscode-vue-language-features/src/browserClientMain.ts index 898597519e..8094361aa0 100644 --- a/extensions/vscode-vue-language-features/src/browserClientMain.ts +++ b/extensions/vscode-vue-language-features/src/browserClientMain.ts @@ -17,7 +17,7 @@ export function activate(context: vscode.ExtensionContext) { initializationOptions: initOptions, progressOnInitialization: true, synchronize: { - fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.vue,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.json}') + fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.vue,**/*.md,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.json}') } }; const client = new lsp.LanguageClient( diff --git a/extensions/vscode-vue-language-features/src/common.ts b/extensions/vscode-vue-language-features/src/common.ts index 537d9d8f63..aac804f5ed 100644 --- a/extensions/vscode-vue-language-features/src/common.ts +++ b/extensions/vscode-vue-language-features/src/common.ts @@ -45,7 +45,7 @@ export async function activate(context: vscode.ExtensionContext, createLc: Creat } const currentlangId = vscode.window.activeTextEditor.document.languageId; - if (currentlangId === 'vue') { + if (currentlangId === 'vue' || currentlangId === 'markdown') { doActivate(context, createLc); stopCheck.dispose(); } @@ -66,6 +66,7 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang const languageFeaturesDocumentSelector: lsp.DocumentSelector = takeOverMode ? [ { scheme: 'file', language: 'vue' }, + { scheme: 'file', language: 'markdown' }, { scheme: 'file', language: 'javascript' }, { scheme: 'file', language: 'typescript' }, { scheme: 'file', language: 'javascriptreact' }, @@ -73,16 +74,19 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang { scheme: 'file', language: 'json' }, ] : [ { scheme: 'file', language: 'vue' }, + { scheme: 'file', language: 'markdown' }, ]; const documentFeaturesDocumentSelector: lsp.DocumentSelector = takeOverMode ? [ { language: 'vue' }, + { language: 'markdown' }, { language: 'javascript' }, { language: 'typescript' }, { language: 'javascriptreact' }, { language: 'typescriptreact' }, ] : [ { language: 'vue' }, + { language: 'markdown' }, ]; const _useSecondServer = useSecondServer(); const _serverMaxOldSpaceSize = serverMaxOldSpaceSize(); diff --git a/extensions/vscode-vue-language-features/src/features/attrNameCase.ts b/extensions/vscode-vue-language-features/src/features/attrNameCase.ts index 1e5742ff2b..3ff023c602 100644 --- a/extensions/vscode-vue-language-features/src/features/attrNameCase.ts +++ b/extensions/vscode-vue-language-features/src/features/attrNameCase.ts @@ -67,7 +67,7 @@ export async function activate(context: vscode.ExtensionContext, languageClient: }; async function onChangeDocument(newDoc: vscode.TextDocument | undefined) { - if (newDoc?.languageId === 'vue') { + if (newDoc?.languageId === 'vue' || newDoc?.languageId === 'markdown') { let attrCase = attrCases.uriGet(newDoc.uri.toString()); if (!attrCase) { const attrMode = vscode.workspace.getConfiguration('volar').get<'auto-kebab' | 'auto-camel' | 'kebab' | 'camel'>('completion.preferredAttrNameCase'); diff --git a/extensions/vscode-vue-language-features/src/features/tagNameCase.ts b/extensions/vscode-vue-language-features/src/features/tagNameCase.ts index 071cabb649..e5f9f8931c 100644 --- a/extensions/vscode-vue-language-features/src/features/tagNameCase.ts +++ b/extensions/vscode-vue-language-features/src/features/tagNameCase.ts @@ -97,7 +97,7 @@ export async function activate(context: vscode.ExtensionContext, languageClient: }; async function onChangeDocument(newDoc: vscode.TextDocument | undefined) { - if (newDoc?.languageId === 'vue') { + if (newDoc?.languageId === 'vue' || newDoc?.languageId === 'markdown') { let tagCase = tagCases.uriGet(newDoc.uri.toString()); if (!tagCase) { const tagMode = vscode.workspace.getConfiguration('volar').get<'auto' | 'both' | 'kebab' | 'pascal'>('completion.preferredTagNameCase'); diff --git a/extensions/vscode-vue-language-features/src/features/tsVersion.ts b/extensions/vscode-vue-language-features/src/features/tsVersion.ts index 8ab92e878d..a38801b78d 100644 --- a/extensions/vscode-vue-language-features/src/features/tsVersion.ts +++ b/extensions/vscode-vue-language-features/src/features/tsVersion.ts @@ -87,6 +87,7 @@ export async function activate(context: vscode.ExtensionContext, clients: BaseLa function updateStatusBar() { if ( vscode.window.activeTextEditor?.document.languageId !== 'vue' + && vscode.window.activeTextEditor?.document.languageId !== 'markdown' && !( takeOverModeEnabled() && vscode.window.activeTextEditor diff --git a/extensions/vscode-vue-language-features/src/features/tsconfig.ts b/extensions/vscode-vue-language-features/src/features/tsconfig.ts index ecda0632b5..3b1b6cb630 100644 --- a/extensions/vscode-vue-language-features/src/features/tsconfig.ts +++ b/extensions/vscode-vue-language-features/src/features/tsconfig.ts @@ -20,6 +20,7 @@ export async function activate(context: vscode.ExtensionContext, languageClient: async function updateStatusBar() { if ( vscode.window.activeTextEditor?.document.languageId !== 'vue' + && vscode.window.activeTextEditor?.document.languageId !== 'markdown' && !( takeOverModeEnabled() && vscode.window.activeTextEditor diff --git a/extensions/vscode-vue-language-features/src/nodeClientMain.ts b/extensions/vscode-vue-language-features/src/nodeClientMain.ts index 79cea850f6..2d5f8a8818 100644 --- a/extensions/vscode-vue-language-features/src/nodeClientMain.ts +++ b/extensions/vscode-vue-language-features/src/nodeClientMain.ts @@ -35,7 +35,7 @@ export function activate(context: vscode.ExtensionContext) { initializationOptions: initOptions, progressOnInitialization: true, synchronize: { - fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.vue,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.json}') + fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.vue,**/*.md,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.json}') } }; const client = new lsp.LanguageClient( diff --git a/packages/shared/src/common.ts b/packages/shared/src/common.ts index 235f46a7e3..b831644c6b 100644 --- a/packages/shared/src/common.ts +++ b/packages/shared/src/common.ts @@ -13,6 +13,7 @@ export function syntaxToLanguageId(syntax: string) { case 'jsx': return 'javascriptreact'; case 'tsx': return 'typescriptreact'; case 'pug': return 'jade'; + case 'md': return 'markdown'; } return syntax; } @@ -24,6 +25,7 @@ export function languageIdToSyntax(languageId: string) { case 'javascriptreact': return 'jsx'; case 'typescriptreact': return 'tsx'; case 'jade': return 'pug'; + case 'markdown': return 'md'; } return languageId; } diff --git a/packages/typescript-vue-plugin/src/index.ts b/packages/typescript-vue-plugin/src/index.ts index b3311faadd..022c35c07c 100644 --- a/packages/typescript-vue-plugin/src/index.ts +++ b/packages/typescript-vue-plugin/src/index.ts @@ -19,6 +19,7 @@ const init: ts.server.PluginModuleFactory = (modules) => { info.project.getScriptKind = fileName => { switch (path.extname(fileName)) { case '.vue': return ts.ScriptKind.TSX; // can't use External, Unknown + case '.md': return ts.ScriptKind.TSX; // can't use External, Unknown case '.js': return ts.ScriptKind.JS; case '.jsx': return ts.ScriptKind.JSX; case '.ts': return ts.ScriptKind.TS; @@ -158,7 +159,7 @@ function createProxyHost(ts: typeof import('typescript/lib/tsserverlibrary'), in }; async function onAnyDriveFileUpdated(fileName: string) { - if (fileName.endsWith('.vue') && info.project.fileExists(fileName) && !vueFiles.has(fileName)) { + if ((fileName.endsWith('.vue') || fileName.endsWith('.md')) && info.project.fileExists(fileName) && !vueFiles.has(fileName)) { onConfigUpdated(); } } @@ -202,7 +203,7 @@ function createProxyHost(ts: typeof import('typescript/lib/tsserverlibrary'), in const parseConfigHost: ts.ParseConfigHost = { useCaseSensitiveFileNames: info.project.useCaseSensitiveFileNames(), readDirectory: (path, extensions, exclude, include, depth) => { - return info.project.readDirectory(path, ['.vue'], exclude, include, depth); + return info.project.readDirectory(path, ['.vue', '.md'], exclude, include, depth); }, fileExists: fileName => info.project.fileExists(fileName), readFile: fileName => info.project.readFile(fileName), diff --git a/packages/vue-language-server/src/features/languageFeatures.ts b/packages/vue-language-server/src/features/languageFeatures.ts index 7da550b221..b8b59f1db0 100644 --- a/packages/vue-language-server/src/features/languageFeatures.ts +++ b/packages/vue-language-server/src/features/languageFeatures.ts @@ -279,7 +279,7 @@ export function register( }); connection.workspace.onWillRenameFiles(async handler => { - const hasTsFile = handler.files.some(file => file.newUri.endsWith('.vue') || file.newUri.endsWith('.ts') || file.newUri.endsWith('.tsx')); + const hasTsFile = handler.files.some(file => file.newUri.endsWith('.vue') || file.newUri.endsWith('.md') || file.newUri.endsWith('.ts') || file.newUri.endsWith('.tsx')); const config: 'prompt' | 'always' | 'never' | null | undefined = await connection.workspace.getConfiguration(hasTsFile ? 'typescript.updateImportsOnFileMove.enabled' : 'javascript.updateImportsOnFileMove.enabled'); if (config === 'always') { diff --git a/packages/vue-language-server/src/project.ts b/packages/vue-language-server/src/project.ts index dbf8e6206e..ae3702e669 100644 --- a/packages/vue-language-server/src/project.ts +++ b/packages/vue-language-server/src/project.ts @@ -243,7 +243,7 @@ export async function createProject( const parseConfigHost: ts.ParseConfigHost = { useCaseSensitiveFileNames: projectSys.useCaseSensitiveFileNames, readDirectory: (path, extensions, exclude, include, depth) => { - return projectSys.readDirectory(path, [...extensions, '.vue'], exclude, include, depth); + return projectSys.readDirectory(path, [...extensions, '.vue', '.md'], exclude, include, depth); }, fileExists: projectSys.fileExists, readFile: projectSys.readFile, diff --git a/packages/vue-language-server/src/registers/registerlanguageFeatures.ts b/packages/vue-language-server/src/registers/registerlanguageFeatures.ts index ea31ff78fe..2fe9f4d65a 100644 --- a/packages/vue-language-server/src/registers/registerlanguageFeatures.ts +++ b/packages/vue-language-server/src/registers/registerlanguageFeatures.ts @@ -36,6 +36,7 @@ export function register( willRename: { filters: [ { pattern: { glob: '**/*.vue' } }, + { pattern: { glob: '**/*.md' } }, { pattern: { glob: '**/*.js' } }, { pattern: { glob: '**/*.ts' } }, { pattern: { glob: '**/*.jsx' } }, diff --git a/packages/vue-language-service/src/documentService.ts b/packages/vue-language-service/src/documentService.ts index b8b4e92b00..7e463c5152 100644 --- a/packages/vue-language-service/src/documentService.ts +++ b/packages/vue-language-service/src/documentService.ts @@ -116,7 +116,7 @@ export function getDocumentService( function getVueDocument(document: TextDocument) { - if (document.languageId !== 'vue') + if (document.languageId !== 'vue' && document.languageId !== 'markdown') return; let vueDoc = vueDocuments.get(document); diff --git a/packages/vue-language-service/src/languageService.ts b/packages/vue-language-service/src/languageService.ts index de9d6459cd..f69523dccb 100644 --- a/packages/vue-language-service/src/languageService.ts +++ b/packages/vue-language-service/src/languageService.ts @@ -327,7 +327,7 @@ export function createLanguageService( document = TextDocument.create( uri, - uri.endsWith('.vue') ? 'vue' : 'typescript', // TODO + shared.syntaxToLanguageId(upath.extname(uri).slice(1)), newVersion, scriptSnapshot.getText(0, scriptSnapshot.getLength()), ); diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 0b299d8cce..bc4274730c 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -534,7 +534,10 @@ export default function useVueTemplateLanguagePlugin { // add *.vue files to allow extensions tsc = tsc.replace( `ts.supportedTSExtensions = [[".ts", ".tsx", ".d.ts"], [".cts", ".d.cts"], [".mts", ".d.mts"]];`, - `ts.supportedTSExtensions = [[".ts", ".tsx", ".d.ts"], [".cts", ".d.cts"], [".mts", ".d.mts"], [".vue"]];`, + `ts.supportedTSExtensions = [[".ts", ".tsx", ".d.ts"], [".cts", ".d.cts"], [".mts", ".d.mts"], [".vue", ".md"]];`, ); tsc = tsc.replace( `ts.supportedJSExtensions = [[".js", ".jsx"], [".mjs"], [".cjs"]];`, - `ts.supportedJSExtensions = [[".js", ".jsx"], [".mjs"], [".cjs"], [".vue"]];`, + `ts.supportedJSExtensions = [[".js", ".jsx"], [".mjs"], [".cjs"], [".vue", ".md"]];`, ); tsc = tsc.replace( `var allSupportedExtensions = [[".ts", ".tsx", ".d.ts", ".js", ".jsx"], [".cts", ".d.cts", ".cjs"], [".mts", ".d.mts", ".mjs"]];`, - `var allSupportedExtensions = [[".ts", ".tsx", ".d.ts", ".js", ".jsx"], [".cts", ".d.cts", ".cjs"], [".mts", ".d.mts", ".mjs"], [".vue"]];`, + `var allSupportedExtensions = [[".ts", ".tsx", ".d.ts", ".js", ".jsx"], [".cts", ".d.cts", ".cjs"], [".mts", ".d.mts", ".mjs"], [".vue", ".md"]];`, ); // proxy createProgram apis diff --git a/packages/vue-typescript/src/typescriptRuntime.ts b/packages/vue-typescript/src/typescriptRuntime.ts index 5e0f60ad5d..dd3ac10134 100644 --- a/packages/vue-typescript/src/typescriptRuntime.ts +++ b/packages/vue-typescript/src/typescriptRuntime.ts @@ -59,8 +59,8 @@ export function createTypeScriptRuntime(options: { lastProjectVersion = newProjectVersion; const fileNames = options.vueLsHost.getScriptFileNames(); - const vueFileNames = new Set(fileNames.filter(file => file.endsWith('.vue'))); - const tsFileNames = new Set(fileNames.filter(file => !file.endsWith('.vue'))); + const vueFileNames = new Set(fileNames.filter(file => file.endsWith('.vue') || file.endsWith('.md'))); + const tsFileNames = new Set(fileNames.filter(file => !file.endsWith('.vue') && !file.endsWith('.md'))); const fileNamesToRemove: string[] = []; const fileNamesToCreate: string[] = []; const fileNamesToUpdate: string[] = []; @@ -140,7 +140,7 @@ export function createTypeScriptRuntime(options: { // .vue.d.ts (never) const fileNameTrim = fileName.substring(0, fileName.lastIndexOf('.')); - if (fileNameTrim.endsWith('.vue')) { + if (fileNameTrim.endsWith('.vue') || fileNameTrim.endsWith('.md')) { const vueFile = vueFiles.get(fileNameTrim); if (!vueFile) { const fileExists = !!options.vueLsHost.fileExists?.(fileNameTrim); @@ -182,6 +182,7 @@ export function createTypeScriptRuntime(options: { getScriptKind(fileName) { switch (path.extname(fileName)) { case '.vue': return ts.ScriptKind.TSX; // can't use External, Unknown + case '.md': return ts.ScriptKind.TSX; // can't use External, Unknown case '.js': return ts.ScriptKind.JS; case '.jsx': return ts.ScriptKind.JSX; case '.ts': return ts.ScriptKind.TS; @@ -214,7 +215,7 @@ export function createTypeScriptRuntime(options: { if (options.isTsPlugin) { tsFileNames.push(fileName); // .vue + .ts } - else if (!fileName.endsWith('.vue')) { + else if (!fileName.endsWith('.vue') && !fileName.endsWith('.md')) { tsFileNames.push(fileName); // .ts } } diff --git a/packages/vue-typescript/src/vueFile.ts b/packages/vue-typescript/src/vueFile.ts index 6a350005dd..e4c2e91321 100644 --- a/packages/vue-typescript/src/vueFile.ts +++ b/packages/vue-typescript/src/vueFile.ts @@ -127,16 +127,21 @@ export function createVueFile( }; // use - const compiledVue = computed(() => { + const compiledVue = computed>>(() => { for (const plugin of plugins) { const compiled = plugin.compileFileToVue?.(fileName, fileContent.value); if (compiled) { return compiled; } } + // given dummy result to avoid language server throw + return { + vue: '', + mapping: vueRange => vueRange, + }; }); - const vueContent = computed(() => compiledVue.value?.vue ?? ''); - const parsedSfc = computed(() => parse(vueContent.value, { sourceMap: false, ignoreEmpty: false })); + const vueContent = computed(() => compiledVue.value?.vue); + const parsedSfc = computed(() => vueContent.value !== undefined ? parse(vueContent.value, { sourceMap: false, ignoreEmpty: false }) : undefined); const computedHtmlTemplate = computed>>(() => { if (sfc.template) { for (const plugin of plugins) { @@ -423,6 +428,7 @@ export function createVueFile( function parseMappingSourceRange(data: EmbeddedFileMappingData, range: Mapping['sourceRange']) { if (!compiledVue.value) throw '!compiledVue.value'; + if (vueContent.value === undefined) throw 'vueContent.value === undefined'; if (data.vueTag === 'scriptSrc') { if (!sfc.script?.src) throw '!sfc.script?.src'; @@ -484,11 +490,13 @@ export function createVueFile( version.value = newVersion; // TODO: wait for https://github.com/vuejs/core/pull/5912 - updateTemplate(parsedSfc.value.descriptor.template); - updateScript(parsedSfc.value.descriptor.script); - updateScriptSetup(parsedSfc.value.descriptor.scriptSetup); - updateStyles(parsedSfc.value.descriptor.styles); - updateCustomBlocks(parsedSfc.value.descriptor.customBlocks); + if (parsedSfc.value) { + updateTemplate(parsedSfc.value.descriptor.template); + updateScript(parsedSfc.value.descriptor.script); + updateScriptSetup(parsedSfc.value.descriptor.scriptSetup); + updateStyles(parsedSfc.value.descriptor.styles); + updateCustomBlocks(parsedSfc.value.descriptor.customBlocks); + } const newScripts: Record = {}; From 0853cc3ee249bee8098494d0512aa75452772e3a Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 6 Jun 2022 06:53:17 +0800 Subject: [PATCH 03/14] feat: implement `file-md` plugin --- .../src/plugins/vue-convert-htmlpug.ts | 2 +- packages/vue-typescript/package.json | 5 +- .../vue-typescript/src/plugins/file-md.ts | 134 +++++++++++++++++- packages/vue-typescript/src/vueFile.ts | 61 +++++++- pnpm-lock.yaml | 45 +++++- 5 files changed, 233 insertions(+), 14 deletions(-) diff --git a/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts b/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts index 7f4e245ab3..542e53ec1a 100644 --- a/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts +++ b/packages/vue-language-service/src/plugins/vue-convert-htmlpug.ts @@ -95,7 +95,7 @@ export default function (options: { function worker(uri: string, callback: (vueDocument: VueDocument) => T) { const vueDocument = options.getVueDocument(uri); - if (!vueDocument) + if (!vueDocument || vueDocument.file.fileName.endsWith('.md')) return; return callback(vueDocument); diff --git a/packages/vue-typescript/package.json b/packages/vue-typescript/package.json index 331b0fc944..fd28f919af 100644 --- a/packages/vue-typescript/package.json +++ b/packages/vue-typescript/package.json @@ -13,6 +13,7 @@ "directory": "packages/vue-typescript" }, "devDependencies": { + "@types/markdown-it": "^12.2.3", "@volar/pug-language-service": "0.36.1", "typescript": "latest" }, @@ -21,7 +22,9 @@ "@volar/source-map": "0.36.1", "@volar/vue-code-gen": "0.36.1", "@vue/compiler-sfc": "^3.2.36", - "@vue/reactivity": "^3.2.36" + "@vue/reactivity": "^3.2.36", + "markdown-it": "^13.0.1", + "markdown-it-ast": "^0.0.1" }, "browser": { "./out/plugins/vue-template-pug.js": "./out/plugins/empty.js" diff --git a/packages/vue-typescript/src/plugins/file-md.ts b/packages/vue-typescript/src/plugins/file-md.ts index 1ca6e8ecde..3244817280 100644 --- a/packages/vue-typescript/src/plugins/file-md.ts +++ b/packages/vue-typescript/src/plugins/file-md.ts @@ -1,4 +1,9 @@ import { VueLanguagePlugin } from '../vueFile'; +import * as MarkdownIt from 'markdown-it'; +// @ts-expect-error +import * as MarkdownItAst from 'markdown-it-ast'; +import { SourceMapBase, Mode } from '@volar/source-map'; +import { CodeGen } from '@volar/code-gen'; export default function (): VueLanguagePlugin { @@ -8,13 +13,128 @@ export default function (): VueLanguagePlugin { if (fileName.endsWith('.md')) { - // return { - // vue: content, - // mapping(start, end) { - // return { start, end }; - // }, - // } - return undefined; + let validTemplateBlock: [number, number] | undefined; + let validScriptBlock: [number, number] | undefined; + + const scriptLines: [number, number][] = []; + const templateLines: [number, number][] = []; + + const tokens = MarkdownIt().parse(content, {}); + const ast = MarkdownItAst.makeAST(tokens); + + for (const node of ast) { + // ') >= 0 + ) { + validScriptBlock[1] = node.children[0].map[1]; + scriptLines.push(validScriptBlock); + validScriptBlock = undefined; + } + else if (!validScriptBlock) { + walkNode(node); + } + } + + breakTemplateBlock(); + + const codeGen = new CodeGen(); + const lines = content.split('\n'); + const lineOffsets: number[] = []; + let lineOffset = 0; + + for (const line of lines) { + lineOffsets.push(lineOffset); + lineOffset += line.length + 1; + } + + for (const _scriptLines of scriptLines) { + const rangeLines = lines.slice(_scriptLines[0], _scriptLines[1]); + const rangeCode = rangeLines.join('\n'); + const start = lineOffsets[_scriptLines[0]]; + codeGen.addCode( + rangeCode, + { + start: start, + end: start + rangeCode.length, + }, + Mode.Offset, + undefined, + ); + } + + if (templateLines.length) { + codeGen.addText('\n\n'); + } + + const sourceMap = new SourceMapBase(codeGen.getMappings()); + + return { + vue: codeGen.getText(), + mapping: vueRange => sourceMap.getSourceRange(vueRange.start, vueRange.end)?.[0], + sourceMap, // for create virtual embedded vue file + }; + + function walkNode(node: any) { + // ignore ``` block + if (node.type === 'fence') { + breakTemplateBlock(); + return false; + } + let shouldAddRange = true; + if (node.children) { + for (const child of node.children) { + shouldAddRange = shouldAddRange && walkNode(child); + } + } + if (shouldAddRange) { + const map = node.map ?? node.openNode?.map; + if (map) { + addValidTemplateBlockRange(map); + } + } + return true; + } + function breakTemplateBlock() { + if (validTemplateBlock) { + templateLines.push(validTemplateBlock); + validTemplateBlock = undefined; + } + } + function addValidTemplateBlockRange(range: [number, number]) { + if (!validTemplateBlock) { + validTemplateBlock = [range[0], range[1]]; + } + else { + validTemplateBlock[1] = range[1]; + } + } } } }; diff --git a/packages/vue-typescript/src/vueFile.ts b/packages/vue-typescript/src/vueFile.ts index e4c2e91321..7f52a82bff 100644 --- a/packages/vue-typescript/src/vueFile.ts +++ b/packages/vue-typescript/src/vueFile.ts @@ -24,13 +24,14 @@ import useVueTsScripts from './plugins/vue-typescript-scripts'; import useVueTsTemplate from './plugins/vue-typescript-template'; import type * as _0 from 'typescript/lib/tsserverlibrary'; // fix TS2742 -import { Mapping, MappingBase } from '@volar/source-map'; +import { Mapping, MappingBase, SourceMapBase } from '@volar/source-map'; export interface VueLanguagePlugin { compileFileToVue?(fileName: string, content: string): { vue: string, mapping(vueRange: { start: number, end: number; }): { start: number, end: number; } | undefined, + sourceMap?: SourceMapBase, } | undefined; compileTemplateToHtml?(lang: string, tmplate: string): { @@ -311,14 +312,70 @@ export function createVueFile( return embeddeds; } }).filter(notEmpty); + const embeddedVue = computed(() => { + if (!fileName.endsWith('.vue') && compiledVue.value?.sourceMap) { + const newSourceMap = new EmbeddedFileSourceMap(); + for (const mapping of compiledVue.value.sourceMap.mappings) { + newSourceMap.mappings.push({ + ...mapping, + data: { + vueTag: undefined, + capabilities: { + basic: true, + references: true, + definitions: true, + diagnostic: true, + rename: true, + completion: true, + semanticTokens: true, + referencesCodeLens: false, + displayWithLink: false, + }, + }, + }); + } + const embeddedFile: EmbeddedFile = { + fileName: fileName + '.vue', + lang: 'vue', + content: compiledVue.value.vue, + capabilities: { + diagnostics: true, + foldingRanges: false, + formatting: false, + documentSymbol: false, + codeActions: true, + inlayHints: true, + }, + isTsHostFile: false, + }; + const embedded: Embedded = { + file: embeddedFile, + sourceMap: newSourceMap, + }; + return embedded; + } + + }); const allEmbeddeds = computed(() => { const all: Embedded[] = []; + if (embeddedVue.value) { + all.push(embeddedVue.value); + } + for (const getEmbeddeds of pluginEmbeddeds) { for (const embedded of getEmbeddeds.value) { if (embedded.value) { - all.push(embedded.value); + if (embeddedVue.value && !embedded.value.parentFileName) { + all.push({ + ...embedded.value, + parentFileName: embeddedVue.value.file.fileName, + }); + } + else { + all.push(embedded.value); + } } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c393c7c747..72e4eff335 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -271,12 +271,15 @@ importers: packages/vue-typescript: specifiers: + '@types/markdown-it': ^12.2.3 '@volar/code-gen': 0.36.1 '@volar/pug-language-service': 0.36.1 '@volar/source-map': 0.36.1 '@volar/vue-code-gen': 0.36.1 '@vue/compiler-sfc': ^3.2.36 '@vue/reactivity': ^3.2.36 + markdown-it: ^13.0.1 + markdown-it-ast: ^0.0.1 typescript: latest dependencies: '@volar/code-gen': link:../code-gen @@ -284,7 +287,10 @@ importers: '@volar/vue-code-gen': link:../vue-code-gen '@vue/compiler-sfc': 3.2.36 '@vue/reactivity': 3.2.36 + markdown-it: 13.0.1 + markdown-it-ast: 0.0.1 devDependencies: + '@types/markdown-it': 12.2.3 '@volar/pug-language-service': link:../pug-language-service typescript: 4.7.3 @@ -782,6 +788,21 @@ packages: resolution: {integrity: sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==} dev: true + /@types/linkify-it/3.0.2: + resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} + dev: true + + /@types/markdown-it/12.2.3: + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + dependencies: + '@types/linkify-it': 3.0.2 + '@types/mdurl': 1.0.2 + dev: true + + /@types/mdurl/1.0.2: + resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} + dev: true + /@types/minimist/1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true @@ -1039,7 +1060,6 @@ packages: /argparse/2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /array-ify/1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -3117,6 +3137,12 @@ packages: uc.micro: 1.0.6 dev: true + /linkify-it/4.0.1: + resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} + dependencies: + uc.micro: 1.0.6 + dev: false + /load-json-file/4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -3275,6 +3301,10 @@ packages: engines: {node: '>=8'} dev: true + /markdown-it-ast/0.0.1: + resolution: {integrity: sha512-+ON/l08iMq47OfXnqBOZtLOWjusuN1aXErJLBmNRmpJn2b0qJ1i27ngiOgqImdhtFJ20YMdXxziGMAAkTCPBSQ==} + dev: false + /markdown-it/12.3.2: resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} hasBin: true @@ -3286,9 +3316,19 @@ packages: uc.micro: 1.0.6 dev: true + /markdown-it/13.0.1: + resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 3.0.1 + linkify-it: 4.0.1 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: false + /mdurl/1.0.1: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} - dev: true /media-typer/0.3.0: resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} @@ -5000,7 +5040,6 @@ packages: /uc.micro/1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} - dev: true /uglify-js/3.15.5: resolution: {integrity: sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==} From 8294d5311cbf8c46f4efccbe4757465056375751 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 6 Jun 2022 06:55:06 +0800 Subject: [PATCH 04/14] feat: add `colorizedBracketPairs` for markdown --- extensions/vscode-vue-language-features/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/vscode-vue-language-features/package.json b/extensions/vscode-vue-language-features/package.json index 8172c86048..539bd9a84f 100644 --- a/extensions/vscode-vue-language-features/package.json +++ b/extensions/vscode-vue-language-features/package.json @@ -128,6 +128,10 @@ "id": "jade", "configuration": "./languages/sfc-template-language-configuration.json" }, + { + "id": "markdown", + "configuration": "./languages/sfc-template-language-configuration.json" + }, { "id": "plaintext", "configuration": "./languages/sfc-template-language-configuration.json" From fe7e790d432068b6265ffa1e80360a8c36d9681b Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 6 Jun 2022 07:12:00 +0800 Subject: [PATCH 05/14] fix: auto insert not working on markdown --- .../vscode-vue-language-features/src/features/autoInsertion.ts | 1 + packages/vue-typescript/src/plugins/file-md.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-vue-language-features/src/features/autoInsertion.ts b/extensions/vscode-vue-language-features/src/features/autoInsertion.ts index c9368410f5..e288e843f4 100644 --- a/extensions/vscode-vue-language-features/src/features/autoInsertion.ts +++ b/extensions/vscode-vue-language-features/src/features/autoInsertion.ts @@ -6,6 +6,7 @@ export async function activate(context: vscode.ExtensionContext, htmlClient: Bas const supportedLanguages: Record = { vue: true, + markdown: true, javascript: true, typescript: true, javascriptreact: true, diff --git a/packages/vue-typescript/src/plugins/file-md.ts b/packages/vue-typescript/src/plugins/file-md.ts index 3244817280..181bb67464 100644 --- a/packages/vue-typescript/src/plugins/file-md.ts +++ b/packages/vue-typescript/src/plugins/file-md.ts @@ -23,7 +23,7 @@ export default function (): VueLanguagePlugin { const ast = MarkdownItAst.makeAST(tokens); for (const node of ast) { - //