@vue/language-service 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +7 -0
- package/index.js +64 -0
- package/lib/ideFeatures/nameCasing.d.ts +13 -0
- package/lib/ideFeatures/nameCasing.js +211 -0
- package/lib/plugins/css.d.ts +2 -0
- package/lib/plugins/css.js +27 -0
- package/lib/plugins/data.d.ts +4 -0
- package/lib/plugins/data.js +91 -0
- package/lib/plugins/vue-autoinsert-dotvalue.d.ts +10 -0
- package/lib/plugins/vue-autoinsert-dotvalue.js +177 -0
- package/lib/plugins/vue-autoinsert-parentheses.d.ts +2 -0
- package/lib/plugins/vue-autoinsert-parentheses.js +60 -0
- package/lib/plugins/vue-autoinsert-space.d.ts +2 -0
- package/lib/plugins/vue-autoinsert-space.js +34 -0
- package/lib/plugins/vue-codelens-references.d.ts +2 -0
- package/lib/plugins/vue-codelens-references.js +38 -0
- package/lib/plugins/vue-directive-comments.d.ts +2 -0
- package/lib/plugins/vue-directive-comments.js +61 -0
- package/lib/plugins/vue-document-drop.d.ts +2 -0
- package/lib/plugins/vue-document-drop.js +81 -0
- package/lib/plugins/vue-extract-file.d.ts +8 -0
- package/lib/plugins/vue-extract-file.js +258 -0
- package/lib/plugins/vue-sfc.d.ts +7 -0
- package/lib/plugins/vue-sfc.js +163 -0
- package/lib/plugins/vue-template.d.ts +3 -0
- package/lib/plugins/vue-template.js +594 -0
- package/lib/plugins/vue-toggle-v-bind-codeaction.d.ts +2 -0
- package/lib/plugins/vue-toggle-v-bind-codeaction.js +126 -0
- package/lib/plugins/vue-twoslash-queries.d.ts +2 -0
- package/lib/plugins/vue-twoslash-queries.js +50 -0
- package/lib/plugins/vue-visualize-hidden-callback-param.d.ts +2 -0
- package/lib/plugins/vue-visualize-hidden-callback-param.js +45 -0
- package/lib/types.d.ts +10 -0
- package/lib/types.js +31 -0
- package/package.json +6 -6
- package/scripts/update-html-data.js +426 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
const vue_autoinsert_dotvalue_1 = require("./vue-autoinsert-dotvalue");
|
|
5
|
+
function create(ts) {
|
|
6
|
+
return {
|
|
7
|
+
name: 'vue-autoinsert-parentheses',
|
|
8
|
+
create(context) {
|
|
9
|
+
return {
|
|
10
|
+
async provideAutoInsertionEdit(document, position, lastChange) {
|
|
11
|
+
const enabled = await context.env.getConfiguration?.('vue.autoInsert.parentheses') ?? false;
|
|
12
|
+
if (!enabled)
|
|
13
|
+
return;
|
|
14
|
+
if (!(0, vue_autoinsert_dotvalue_1.isCharacterTyping)(document, lastChange))
|
|
15
|
+
return;
|
|
16
|
+
const [virtualCode] = context.documents.getVirtualCodeByUri(document.uri);
|
|
17
|
+
if (virtualCode?.id !== 'template_format')
|
|
18
|
+
return;
|
|
19
|
+
const offset = document.offsetAt(position);
|
|
20
|
+
for (const mappedRange of virtualCode.mappings) {
|
|
21
|
+
const generatedCodeEnd = mappedRange.generatedOffsets[mappedRange.generatedOffsets.length - 1]
|
|
22
|
+
+ mappedRange.lengths[mappedRange.lengths.length - 1];
|
|
23
|
+
if (generatedCodeEnd === offset) {
|
|
24
|
+
const text = document.getText().substring(mappedRange.generatedOffsets[0], generatedCodeEnd);
|
|
25
|
+
const ast = ts.createSourceFile('', text, ts.ScriptTarget.Latest);
|
|
26
|
+
if (ast.statements.length === 1) {
|
|
27
|
+
const statement = ast.statements[0];
|
|
28
|
+
if (ts.isExpressionStatement(statement)
|
|
29
|
+
&& ((ts.isAsExpression(statement.expression)
|
|
30
|
+
&& ts.isTypeReferenceNode(statement.expression.type)
|
|
31
|
+
&& ts.isIdentifier(statement.expression.type.typeName)
|
|
32
|
+
&& statement.expression.type.typeName.text)
|
|
33
|
+
|| (ts.isBinaryExpression(statement.expression)
|
|
34
|
+
&& statement.expression.right.getText(ast)
|
|
35
|
+
&& statement.expression.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword)
|
|
36
|
+
|| (ts.isTypeOfExpression(statement.expression)
|
|
37
|
+
&& statement.expression.expression.getText(ast)))) {
|
|
38
|
+
// https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar
|
|
39
|
+
const escapedText = text
|
|
40
|
+
.replaceAll('\\', '\\\\')
|
|
41
|
+
.replaceAll('$', '\\$')
|
|
42
|
+
.replaceAll('}', '\\}');
|
|
43
|
+
return {
|
|
44
|
+
range: {
|
|
45
|
+
start: document.positionAt(mappedRange.generatedOffsets[0]),
|
|
46
|
+
end: document.positionAt(generatedCodeEnd),
|
|
47
|
+
},
|
|
48
|
+
newText: '(' + escapedText + '$0' + ')',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
exports.create = create;
|
|
60
|
+
//# sourceMappingURL=vue-autoinsert-parentheses.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
function create() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'vue-autoinsert-space',
|
|
7
|
+
create(context) {
|
|
8
|
+
return {
|
|
9
|
+
async provideAutoInsertionEdit(document, _, lastChange) {
|
|
10
|
+
if (document.languageId === 'html' || document.languageId === 'jade') {
|
|
11
|
+
const enabled = await context.env.getConfiguration?.('vue.autoInsert.bracketSpacing') ?? true;
|
|
12
|
+
if (!enabled)
|
|
13
|
+
return;
|
|
14
|
+
if (lastChange.text === '{}'
|
|
15
|
+
&& document.getText({
|
|
16
|
+
start: { line: lastChange.range.start.line, character: lastChange.range.start.character - 1 },
|
|
17
|
+
end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 3 }
|
|
18
|
+
}) === '{{}}') {
|
|
19
|
+
return {
|
|
20
|
+
newText: ` $0 `,
|
|
21
|
+
range: {
|
|
22
|
+
start: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 },
|
|
23
|
+
end: { line: lastChange.range.start.line, character: lastChange.range.start.character + 1 }
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
exports.create = create;
|
|
34
|
+
//# sourceMappingURL=vue-autoinsert-space.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
function create() {
|
|
6
|
+
return {
|
|
7
|
+
name: 'vue-codelens-references',
|
|
8
|
+
create(context) {
|
|
9
|
+
return {
|
|
10
|
+
provideReferencesCodeLensRanges(document) {
|
|
11
|
+
return worker(document.uri, virtualCode => {
|
|
12
|
+
const result = [];
|
|
13
|
+
for (const map of context.documents.getMaps(virtualCode) ?? []) {
|
|
14
|
+
for (const mapping of map.map.mappings) {
|
|
15
|
+
if (!mapping.data.__referencesCodeLens)
|
|
16
|
+
continue;
|
|
17
|
+
result.push({
|
|
18
|
+
start: document.positionAt(mapping.generatedOffsets[0]),
|
|
19
|
+
end: document.positionAt(mapping.generatedOffsets[mapping.generatedOffsets.length - 1]
|
|
20
|
+
+ mapping.lengths[mapping.lengths.length - 1]),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function worker(uri, callback) {
|
|
29
|
+
const [virtualCode, sourceFile] = context.documents.getVirtualCodeByUri(uri);
|
|
30
|
+
if (!(sourceFile?.generated?.code instanceof language_core_1.VueGeneratedCode) || !sourceFile)
|
|
31
|
+
return;
|
|
32
|
+
return callback(virtualCode, sourceFile);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
exports.create = create;
|
|
38
|
+
//# sourceMappingURL=vue-codelens-references.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
const cmds = [
|
|
5
|
+
'vue-ignore',
|
|
6
|
+
'vue-skip',
|
|
7
|
+
'vue-expect-error',
|
|
8
|
+
];
|
|
9
|
+
const directiveCommentReg = /<!--\s*@/;
|
|
10
|
+
function create() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'vue-directive-comments',
|
|
13
|
+
triggerCharacters: ['@'],
|
|
14
|
+
create() {
|
|
15
|
+
return {
|
|
16
|
+
provideCompletionItems(document, position) {
|
|
17
|
+
if (document.languageId !== 'html')
|
|
18
|
+
return;
|
|
19
|
+
const line = document.getText({ start: { line: position.line, character: 0 }, end: position });
|
|
20
|
+
const cmdStart = line.match(directiveCommentReg);
|
|
21
|
+
if (!cmdStart)
|
|
22
|
+
return;
|
|
23
|
+
const startIndex = cmdStart.index + cmdStart[0].length;
|
|
24
|
+
const remainText = line.substring(startIndex);
|
|
25
|
+
const result = [];
|
|
26
|
+
for (const cmd of cmds) {
|
|
27
|
+
let match = true;
|
|
28
|
+
for (let i = 0; i < remainText.length; i++) {
|
|
29
|
+
if (remainText[i] !== cmd[i]) {
|
|
30
|
+
console.log(JSON.stringify(remainText[i]), JSON.stringify(cmd[i]));
|
|
31
|
+
match = false;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (match) {
|
|
36
|
+
result.push({
|
|
37
|
+
label: '@' + cmd,
|
|
38
|
+
textEdit: {
|
|
39
|
+
range: {
|
|
40
|
+
start: {
|
|
41
|
+
line: position.line,
|
|
42
|
+
character: startIndex - 1,
|
|
43
|
+
},
|
|
44
|
+
end: position,
|
|
45
|
+
},
|
|
46
|
+
newText: '@' + cmd,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
isIncomplete: false,
|
|
53
|
+
items: result,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
exports.create = create;
|
|
61
|
+
//# sourceMappingURL=vue-directive-comments.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const shared_1 = require("@vue/shared");
|
|
6
|
+
const path = require("path-browserify");
|
|
7
|
+
const vue_extract_file_1 = require("../plugins/vue-extract-file");
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
function create(ts) {
|
|
10
|
+
return {
|
|
11
|
+
name: 'vue-document-drop',
|
|
12
|
+
create(context) {
|
|
13
|
+
let casing = types_1.TagNameCasing.Pascal; // TODO
|
|
14
|
+
return {
|
|
15
|
+
async provideDocumentDropEdits(document, _position, dataTransfer) {
|
|
16
|
+
if (document.languageId !== 'html')
|
|
17
|
+
return;
|
|
18
|
+
const [virtualCode, sourceFile] = context.documents.getVirtualCodeByUri(document.uri);
|
|
19
|
+
const vueVirtualCode = sourceFile?.generated?.code;
|
|
20
|
+
if (!virtualCode || !(vueVirtualCode instanceof language_core_1.VueGeneratedCode))
|
|
21
|
+
return;
|
|
22
|
+
let importUri;
|
|
23
|
+
for (const [mimeType, item] of dataTransfer) {
|
|
24
|
+
if (mimeType === 'text/uri-list') {
|
|
25
|
+
importUri = item.value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!importUri?.endsWith('.vue'))
|
|
29
|
+
return;
|
|
30
|
+
let baseName = importUri.substring(importUri.lastIndexOf('/') + 1);
|
|
31
|
+
baseName = baseName.substring(0, baseName.lastIndexOf('.'));
|
|
32
|
+
const newName = (0, shared_1.capitalize)((0, shared_1.camelize)(baseName));
|
|
33
|
+
const { sfc } = vueVirtualCode;
|
|
34
|
+
const script = sfc.scriptSetup ?? sfc.script;
|
|
35
|
+
if (!script)
|
|
36
|
+
return;
|
|
37
|
+
const additionalEdit = {};
|
|
38
|
+
const code = [...(0, language_core_1.forEachEmbeddedCode)(vueVirtualCode)].find(code => code.id === (sfc.scriptSetup ? 'scriptSetupFormat' : 'scriptFormat'));
|
|
39
|
+
const lastImportNode = (0, vue_extract_file_1.getLastImportNode)(ts, script.ast);
|
|
40
|
+
let importPath = path.relative(path.dirname(document.uri), importUri)
|
|
41
|
+
|| importUri.substring(importUri.lastIndexOf('/') + 1);
|
|
42
|
+
if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
|
|
43
|
+
importPath = './' + importPath;
|
|
44
|
+
}
|
|
45
|
+
additionalEdit.changes ??= {};
|
|
46
|
+
additionalEdit.changes[context.documents.getVirtualCodeUri(sourceFile.id, code.id)] = [];
|
|
47
|
+
additionalEdit.changes[context.documents.getVirtualCodeUri(sourceFile.id, code.id)].push({
|
|
48
|
+
range: lastImportNode ? {
|
|
49
|
+
start: script.ast.getLineAndCharacterOfPosition(lastImportNode.end),
|
|
50
|
+
end: script.ast.getLineAndCharacterOfPosition(lastImportNode.end),
|
|
51
|
+
} : {
|
|
52
|
+
start: script.ast.getLineAndCharacterOfPosition(0),
|
|
53
|
+
end: script.ast.getLineAndCharacterOfPosition(0),
|
|
54
|
+
},
|
|
55
|
+
newText: `\nimport ${newName} from '${importPath}'`
|
|
56
|
+
+ (lastImportNode ? '' : '\n'),
|
|
57
|
+
});
|
|
58
|
+
if (sfc.script) {
|
|
59
|
+
const edit = (0, vue_extract_file_1.createAddComponentToOptionEdit)(ts, sfc.script.ast, newName);
|
|
60
|
+
if (edit) {
|
|
61
|
+
additionalEdit.changes[context.documents.getVirtualCodeUri(sourceFile.id, code.id)].push({
|
|
62
|
+
range: {
|
|
63
|
+
start: document.positionAt(edit.range.start),
|
|
64
|
+
end: document.positionAt(edit.range.end),
|
|
65
|
+
},
|
|
66
|
+
newText: edit.newText,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
insertText: `<${casing === types_1.TagNameCasing.Kebab ? (0, shared_1.hyphenate)(newName) : newName}$0 />`,
|
|
72
|
+
insertTextFormat: 2,
|
|
73
|
+
additionalEdit,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
exports.create = create;
|
|
81
|
+
//# sourceMappingURL=vue-document-drop.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ServicePlugin } from '@volar/language-service';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
export declare function create(ts: typeof import('typescript')): ServicePlugin;
|
|
4
|
+
export declare function getLastImportNode(ts: typeof import('typescript'), sourceFile: ts.SourceFile): ts.Node | undefined;
|
|
5
|
+
export declare function createAddComponentToOptionEdit(ts: typeof import('typescript'), ast: ts.SourceFile, componentName: string): {
|
|
6
|
+
range: import("@vue/language-core").TextRange;
|
|
7
|
+
newText: string;
|
|
8
|
+
} | undefined;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAddComponentToOptionEdit = exports.getLastImportNode = exports.create = void 0;
|
|
4
|
+
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const client_1 = require("@vue/typescript-plugin/lib/client");
|
|
6
|
+
const unicodeReg = /\\u/g;
|
|
7
|
+
function create(ts) {
|
|
8
|
+
return {
|
|
9
|
+
name: 'vue-extract-file',
|
|
10
|
+
create(context) {
|
|
11
|
+
return {
|
|
12
|
+
async provideCodeActions(document, range, _context) {
|
|
13
|
+
const startOffset = document.offsetAt(range.start);
|
|
14
|
+
const endOffset = document.offsetAt(range.end);
|
|
15
|
+
if (startOffset === endOffset) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const [code, vueCode] = context.documents.getVirtualCodeByUri(document.uri);
|
|
19
|
+
if (!(vueCode?.generated?.code instanceof language_core_1.VueGeneratedCode) || code?.id !== 'template')
|
|
20
|
+
return;
|
|
21
|
+
const { sfc } = vueCode.generated.code;
|
|
22
|
+
const script = sfc.scriptSetup ?? sfc.script;
|
|
23
|
+
if (!sfc.template || !script)
|
|
24
|
+
return;
|
|
25
|
+
const templateCodeRange = selectTemplateCode(startOffset, endOffset, sfc.template);
|
|
26
|
+
if (!templateCodeRange)
|
|
27
|
+
return;
|
|
28
|
+
return [
|
|
29
|
+
{
|
|
30
|
+
title: 'Extract into new dumb component',
|
|
31
|
+
kind: 'refactor.move.newFile.dumb',
|
|
32
|
+
data: {
|
|
33
|
+
uri: document.uri,
|
|
34
|
+
range: [startOffset, endOffset],
|
|
35
|
+
newName: 'NewComponent',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
},
|
|
40
|
+
async resolveCodeAction(codeAction) {
|
|
41
|
+
const { uri, range, newName } = codeAction.data;
|
|
42
|
+
const [startOffset, endOffset] = range;
|
|
43
|
+
const [code, sourceFile] = context.documents.getVirtualCodeByUri(uri);
|
|
44
|
+
if (!(sourceFile?.generated?.code instanceof language_core_1.VueGeneratedCode) || code?.id !== 'template')
|
|
45
|
+
return codeAction;
|
|
46
|
+
const document = context.documents.get(uri, code.languageId, code.snapshot);
|
|
47
|
+
const sfcDocument = context.documents.get(sourceFile.id, sourceFile.languageId, sourceFile.snapshot);
|
|
48
|
+
const { sfc } = sourceFile.generated.code;
|
|
49
|
+
const script = sfc.scriptSetup ?? sfc.script;
|
|
50
|
+
if (!sfc.template || !script)
|
|
51
|
+
return codeAction;
|
|
52
|
+
const templateCodeRange = selectTemplateCode(startOffset, endOffset, sfc.template);
|
|
53
|
+
if (!templateCodeRange)
|
|
54
|
+
return codeAction;
|
|
55
|
+
const toExtract = await (0, client_1.collectExtractProps)(sourceFile.generated.code.fileName, templateCodeRange) ?? [];
|
|
56
|
+
if (!toExtract)
|
|
57
|
+
return codeAction;
|
|
58
|
+
const templateInitialIndent = await context.env.getConfiguration('vue.format.template.initialIndent') ?? true;
|
|
59
|
+
const scriptInitialIndent = await context.env.getConfiguration('vue.format.script.initialIndent') ?? false;
|
|
60
|
+
const newUri = sfcDocument.uri.substring(0, sfcDocument.uri.lastIndexOf('/') + 1) + `${newName}.vue`;
|
|
61
|
+
const lastImportNode = getLastImportNode(ts, script.ast);
|
|
62
|
+
let newFileTags = [];
|
|
63
|
+
newFileTags.push(constructTag('template', [], templateInitialIndent, sfc.template.content.substring(templateCodeRange[0], templateCodeRange[1])));
|
|
64
|
+
if (toExtract.length) {
|
|
65
|
+
newFileTags.push(constructTag('script', ['setup', 'lang="ts"'], scriptInitialIndent, generateNewScriptContents()));
|
|
66
|
+
}
|
|
67
|
+
if (sfc.template.startTagEnd > script.startTagEnd) {
|
|
68
|
+
newFileTags = newFileTags.reverse();
|
|
69
|
+
}
|
|
70
|
+
const templateEdits = [
|
|
71
|
+
{
|
|
72
|
+
range: {
|
|
73
|
+
start: document.positionAt(templateCodeRange[0]),
|
|
74
|
+
end: document.positionAt(templateCodeRange[1]),
|
|
75
|
+
},
|
|
76
|
+
newText: generateReplaceTemplate(),
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
const sfcEdits = [
|
|
80
|
+
{
|
|
81
|
+
range: lastImportNode ? {
|
|
82
|
+
start: sfcDocument.positionAt(script.startTagEnd + lastImportNode.end),
|
|
83
|
+
end: sfcDocument.positionAt(script.startTagEnd + lastImportNode.end),
|
|
84
|
+
} : {
|
|
85
|
+
start: sfcDocument.positionAt(script.startTagEnd),
|
|
86
|
+
end: sfcDocument.positionAt(script.startTagEnd),
|
|
87
|
+
},
|
|
88
|
+
newText: `\nimport ${newName} from './${newName}.vue'`,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
if (sfc.script) {
|
|
92
|
+
const edit = createAddComponentToOptionEdit(ts, sfc.script.ast, newName);
|
|
93
|
+
if (edit) {
|
|
94
|
+
sfcEdits.push({
|
|
95
|
+
range: {
|
|
96
|
+
start: sfcDocument.positionAt(sfc.script.startTagEnd + edit.range.start),
|
|
97
|
+
end: sfcDocument.positionAt(sfc.script.startTagEnd + edit.range.end),
|
|
98
|
+
},
|
|
99
|
+
newText: edit.newText,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...codeAction,
|
|
105
|
+
edit: {
|
|
106
|
+
documentChanges: [
|
|
107
|
+
// editing template virtual document
|
|
108
|
+
{
|
|
109
|
+
textDocument: {
|
|
110
|
+
uri: document.uri,
|
|
111
|
+
version: null,
|
|
112
|
+
},
|
|
113
|
+
edits: templateEdits,
|
|
114
|
+
},
|
|
115
|
+
// editing vue sfc
|
|
116
|
+
{
|
|
117
|
+
textDocument: {
|
|
118
|
+
uri: sourceFile.id,
|
|
119
|
+
version: null,
|
|
120
|
+
},
|
|
121
|
+
edits: sfcEdits,
|
|
122
|
+
},
|
|
123
|
+
// creating new file with content
|
|
124
|
+
{
|
|
125
|
+
uri: newUri,
|
|
126
|
+
kind: 'create',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
textDocument: {
|
|
130
|
+
uri: newUri,
|
|
131
|
+
version: null,
|
|
132
|
+
},
|
|
133
|
+
edits: [
|
|
134
|
+
{
|
|
135
|
+
range: {
|
|
136
|
+
start: { line: 0, character: 0 },
|
|
137
|
+
end: { line: 0, character: 0 },
|
|
138
|
+
},
|
|
139
|
+
newText: newFileTags.join('\n'),
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
function generateNewScriptContents() {
|
|
147
|
+
const lines = [];
|
|
148
|
+
const props = toExtract.filter(p => !p.model);
|
|
149
|
+
const models = toExtract.filter(p => p.model);
|
|
150
|
+
if (props.length) {
|
|
151
|
+
lines.push(`defineProps<{ \n\t${props.map(p => `${p.name}: ${p.type};`).join('\n\t')}\n}>()`);
|
|
152
|
+
}
|
|
153
|
+
for (const model of models) {
|
|
154
|
+
lines.push(`const ${model.name} = defineModel<${model.type}>('${model.name}', { required: true })`);
|
|
155
|
+
}
|
|
156
|
+
return lines.join('\n');
|
|
157
|
+
}
|
|
158
|
+
function generateReplaceTemplate() {
|
|
159
|
+
const props = toExtract.filter(p => !p.model);
|
|
160
|
+
const models = toExtract.filter(p => p.model);
|
|
161
|
+
return [
|
|
162
|
+
`<${newName}`,
|
|
163
|
+
...props.map(p => `:${p.name}="${p.name}"`),
|
|
164
|
+
...models.map(p => `v-model:${p.name}="${p.name}"`),
|
|
165
|
+
`/>`,
|
|
166
|
+
].join(' ');
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
exports.create = create;
|
|
174
|
+
function selectTemplateCode(startOffset, endOffset, templateBlock) {
|
|
175
|
+
const insideNodes = [];
|
|
176
|
+
templateBlock.ast?.children.forEach(function visit(node) {
|
|
177
|
+
if (node.loc.start.offset >= startOffset
|
|
178
|
+
&& node.loc.end.offset <= endOffset) {
|
|
179
|
+
insideNodes.push(node);
|
|
180
|
+
}
|
|
181
|
+
if ('children' in node) {
|
|
182
|
+
node.children.forEach(node => {
|
|
183
|
+
if (typeof node === 'object') {
|
|
184
|
+
visit(node);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else if ('branches' in node) {
|
|
189
|
+
node.branches.forEach(visit);
|
|
190
|
+
}
|
|
191
|
+
else if ('content' in node) {
|
|
192
|
+
if (typeof node.content === 'object') {
|
|
193
|
+
visit(node.content);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
if (insideNodes.length) {
|
|
198
|
+
const first = insideNodes.sort((a, b) => a.loc.start.offset - b.loc.start.offset)[0];
|
|
199
|
+
const last = insideNodes.sort((a, b) => b.loc.end.offset - a.loc.end.offset)[0];
|
|
200
|
+
return [first.loc.start.offset, last.loc.end.offset];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function constructTag(name, attributes, initialIndent, content) {
|
|
204
|
+
if (initialIndent)
|
|
205
|
+
content = content.split('\n').map(line => `\t${line}`).join('\n');
|
|
206
|
+
const attributesString = attributes.length ? ` ${attributes.join(' ')}` : '';
|
|
207
|
+
return `<${name}${attributesString}>\n${content}\n</${name}>\n`;
|
|
208
|
+
}
|
|
209
|
+
function getLastImportNode(ts, sourceFile) {
|
|
210
|
+
let lastImportNode;
|
|
211
|
+
for (const statement of sourceFile.statements) {
|
|
212
|
+
if (ts.isImportDeclaration(statement)) {
|
|
213
|
+
lastImportNode = statement;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return lastImportNode;
|
|
220
|
+
}
|
|
221
|
+
exports.getLastImportNode = getLastImportNode;
|
|
222
|
+
function createAddComponentToOptionEdit(ts, ast, componentName) {
|
|
223
|
+
const exportDefault = language_core_1.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault;
|
|
224
|
+
if (!exportDefault)
|
|
225
|
+
return;
|
|
226
|
+
// https://github.com/microsoft/TypeScript/issues/36174
|
|
227
|
+
const printer = ts.createPrinter();
|
|
228
|
+
if (exportDefault.componentsOption && exportDefault.componentsOptionNode) {
|
|
229
|
+
const newNode = {
|
|
230
|
+
...exportDefault.componentsOptionNode,
|
|
231
|
+
properties: [
|
|
232
|
+
...exportDefault.componentsOptionNode.properties,
|
|
233
|
+
ts.factory.createShorthandPropertyAssignment(componentName),
|
|
234
|
+
],
|
|
235
|
+
};
|
|
236
|
+
const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
|
|
237
|
+
return {
|
|
238
|
+
range: exportDefault.componentsOption,
|
|
239
|
+
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
else if (exportDefault.args && exportDefault.argsNode) {
|
|
243
|
+
const newNode = {
|
|
244
|
+
...exportDefault.argsNode,
|
|
245
|
+
properties: [
|
|
246
|
+
...exportDefault.argsNode.properties,
|
|
247
|
+
ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
|
|
251
|
+
return {
|
|
252
|
+
range: exportDefault.args,
|
|
253
|
+
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
exports.createAddComponentToOptionEdit = createAddComponentToOptionEdit;
|
|
258
|
+
//# sourceMappingURL=vue-extract-file.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ServicePlugin } from '@volar/language-service';
|
|
2
|
+
import * as vue from '@vue/language-core';
|
|
3
|
+
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
4
|
+
export interface Provide {
|
|
5
|
+
'vue/vueFile': (document: TextDocument) => vue.VueGeneratedCode | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare function create(): ServicePlugin;
|