Skip to content

Commit d9124ea

Browse files
committed
feat: rfc suger support
vuejs/rfcs#222
1 parent f8a268a commit d9124ea

28 files changed

+1247
-392
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar
66

77
## Some interesting features:
88

9+
- [x] RFC #222 support (v0.15.2 added)
910
- [x] Scoped CSS services (v0.15.1 added)
10-
- [x] JS support (v0.15.0 added)
1111
- [x] Format all scripts command (v0.13.5 added)
1212
- [x] Verify all scripts command (v0.13.3 added)
1313
- [x] v-slot Type-Checking (v0.12.1 added)

package.json

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,23 @@
6868
{
6969
"language": "vue",
7070
"scopes": {
71-
"template/component": [
71+
"componentTag": [
7272
"support.class.component.vue"
7373
],
74-
"template/conditional": [
74+
"conditionalDirective": [
7575
"keyword.control.conditional.vue"
7676
],
77-
"template/loop": [
77+
"loopDirective": [
7878
"keyword.control.loop.vue"
79+
],
80+
"refLabel": [
81+
"keyword.other"
82+
],
83+
"refVariable": [
84+
"markup.underline"
85+
],
86+
"ref$": [
87+
"punctuation.definition.tag"
7988
]
8089
}
8190
}
@@ -111,6 +120,19 @@
111120
"user-defined"
112121
],
113122
"description": "Formatter for <template>, <script>, <style> region"
123+
},
124+
"volar.scriptSetup.supportRfc": {
125+
"type": "string",
126+
"default": "#182",
127+
"enum": [
128+
"#182",
129+
"#222"
130+
],
131+
"enumDescriptions": [
132+
"SFC Improvements",
133+
"New script setup and ref sugar"
134+
],
135+
"description": "<script setup> support RFC"
114136
}
115137
}
116138
}

packages/client/src/extension.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ function createLanguageService(context: vscode.ExtensionContext, script: string,
125125
// Notify the server about file changes to '.clientrc files contained in the workspace
126126
fileEvents: vscode.workspace.createFileSystemWatcher('**/.clientrc')
127127
},
128+
initializationOptions: {
129+
scriptSetupRfc: vscode.workspace.getConfiguration().get('volar.scriptSetup.supportRfc'),
130+
},
128131
};
129132

130133

packages/server/src/documentServer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
import { createLanguageServiceHost } from './languageServiceHost';
2020
import { TextDocuments } from 'vscode-languageserver';
2121
import { TextDocument } from 'vscode-languageserver-textdocument';
22-
import { getSemanticTokensLegend } from '@volar/vscode-vue-languageservice';
22+
import { getSemanticTokensLegend, setScriptSetupRfc } from '@volar/vscode-vue-languageservice';
2323
import {
2424
uriToFsPath,
2525
VerifyAllScriptsRequest,
@@ -39,6 +39,7 @@ connection.listen();
3939

4040
function onInitialize(params: InitializeParams) {
4141
if (params.rootPath) {
42+
setScriptSetupRfc(params.initializationOptions.scriptSetupRfc);
4243
initLanguageService(params.rootPath);
4344
}
4445
const result: InitializeResult = {

packages/server/src/languageServiceHost.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as ts from 'typescript';
22
import * as upath from 'upath';
3-
import { LanguageService, createLanguageService, LanguageServiceHost } from '@volar/vscode-vue-languageservice';
3+
import { LanguageService, createLanguageService, LanguageServiceHost, setScriptSetupRfc } from '@volar/vscode-vue-languageservice';
44
import { uriToFsPath, fsPathToUri, sleep, SemanticTokensChangedNotification } from '@volar/shared';
55
import type { TextDocument } from 'vscode-languageserver-textdocument';
66
import type { Connection } from 'vscode-languageserver';
@@ -128,6 +128,7 @@ export function createLanguageServiceHost(
128128
const realTsConfig = ts.sys.realpath!(tsConfig);
129129
const config = ts.readJsonConfigFile(realTsConfig, ts.sys.readFile);
130130
const content = ts.parseJsonSourceFileConfigFileContent(config, parseConfigHost, upath.dirname(realTsConfig), {}, upath.basename(realTsConfig));
131+
content.options.allowJs = true;
131132
return content;
132133
}
133134
async function onDidChangeContent(document: TextDocument) {

packages/server/src/server.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
CodeLensRequest,
2626
} from 'vscode-languageserver';
2727
import { createLanguageServiceHost } from './languageServiceHost';
28-
import { Commands, triggerCharacter, SourceMap, TsSourceMap } from '@volar/vscode-vue-languageservice';
28+
import { Commands, triggerCharacter, SourceMap, TsSourceMap, setScriptSetupRfc } from '@volar/vscode-vue-languageservice';
2929
import { TextDocuments } from 'vscode-languageserver';
3030
import { TextDocument } from 'vscode-languageserver-textdocument';
3131
import {
@@ -58,6 +58,7 @@ connection.listen();
5858

5959
function onInitialize(params: InitializeParams) {
6060
if (params.rootPath) {
61+
setScriptSetupRfc(params.initializationOptions.scriptSetupRfc);
6162
initLanguageService(params.rootPath);
6263
}
6364

@@ -263,7 +264,12 @@ function onInitialized() {
263264
retriggerCharacters: [')'],
264265
});
265266
connection.client.register(ExecuteCommandRequest.type, {
266-
commands: [Commands.HTML_TO_PUG, Commands.PUG_TO_HTML]
267+
commands: [
268+
Commands.HTML_TO_PUG,
269+
Commands.PUG_TO_HTML,
270+
Commands.UNUSE_REF_SUGER,
271+
Commands.USE_REF_SUGER,
272+
]
267273
});
268274
connection.client.register(CompletionRequest.type, {
269275
documentSelector: vueOnly.documentSelector,

packages/shared/src/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
export * from './path';
22
export * from './requests';
33

4+
const validScriptSyntaxs = new Set(['js', 'jsx', 'ts', 'tsx']);
5+
46
export function sleep(ms = 0) {
57
return new Promise(resolve => setTimeout(resolve, ms));
68
}
7-
export function syntaxToLanguageId(extName: string) {
8-
switch (extName) {
9+
export function syntaxToLanguageId(syntax: string) {
10+
switch (syntax) {
911
case 'js': return 'javascript';
1012
case 'ts': return 'typescript';
1113
case 'jsx': return 'javascriptreact';
1214
case 'tsx': return 'typescriptreact';
1315
case 'pug': return 'jade';
1416
}
15-
return extName;
17+
return syntax;
1618
}
1719
export function languageIdToSyntax(languageId: string) {
1820
switch (languageId) {
@@ -24,6 +26,12 @@ export function languageIdToSyntax(languageId: string) {
2426
}
2527
return languageId;
2628
}
29+
export function getValidScriptSyntax(syntax: string) {
30+
if (validScriptSyntaxs.has(syntax)) {
31+
return syntax;
32+
}
33+
return 'js';
34+
}
2735
export function randomStr() {
2836
return [...Array(10)].map(i => (~~(Math.random() * 36)).toString(36)).join('');
2937
}

packages/vscode-typescript-languageservice/src/languageFeatures/diagnostics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function register(languageService: ts.LanguageService) {
3939
message: typeof diag.messageText === 'string' ? diag.messageText : diag.messageText.messageText,
4040
};
4141

42-
if (diagnostic.source === 'ts' && diagnostic.code === 6133) {
42+
if (diagnostic.source === 'ts' && (diagnostic.code === 6133 || diagnostic.code === 7028)) {
4343
if (diagnostic.tags === undefined) diagnostic.tags = [];
4444
diagnostic.tags.push(DiagnosticTag.Unnecessary);
4545
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export enum Commands {
22
HTML_TO_PUG = 'volar.html-to-pug',
33
PUG_TO_HTML = 'volar.pug-to-html',
4+
USE_REF_SUGER = 'volar.use-ref-suger',
5+
UNUSE_REF_SUGER = 'volar.unuse-ref-suger',
46
}

packages/vscode-vue-languageservice/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type LanguageService = ReturnType<typeof createLanguageService>;
3333
export { triggerCharacter } from './languageFeatures/completions';
3434
export * from './utils/sourceMaps';
3535
export * from './commands';
36+
export { setScriptSetupRfc } from './virtuals/scriptSetup';
3637

3738
export function getSemanticTokensLegend() {
3839
return getSemanticTokens.semanticTokenLegend;

packages/vscode-vue-languageservice/src/languageFeatures/codeLens.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,55 @@ import {
55
import { SourceFile } from '../sourceFiles';
66
import { Commands } from '../commands';
77
import type { TextDocument } from 'vscode-languageserver-textdocument';
8+
import { rfc } from '../virtuals/scriptSetup';
89

910
export function register(sourceFiles: Map<string, SourceFile>) {
1011
return (document: TextDocument) => {
1112
const sourceFile = sourceFiles.get(document.uri);
1213
if (!sourceFile) return;
1314

15+
const scriptSetupResult = getScriptSetupResult(sourceFile);
1416
const htmlResult = getHtmlResult(sourceFile);
1517
const pugResult = getPugResult(sourceFile);
1618

1719
return [
20+
...scriptSetupResult,
1821
...htmlResult,
1922
...pugResult,
2023
];
2124

22-
function getHtmlResult(sourceFile: SourceFile) {
25+
function getScriptSetupResult(sourceFile: SourceFile) {
2326
const result: CodeLens[] = [];
27+
const descriptor = sourceFile.getDescriptor();
28+
// const data = sourceFile.getScriptSetupData();
29+
// if (descriptor.scriptSetup && data) {
30+
// result.push({
31+
// range: {
32+
// start: document.positionAt(descriptor.scriptSetup.loc.start),
33+
// end: document.positionAt(descriptor.scriptSetup.loc.end),
34+
// },
35+
// command: {
36+
// title: 'ref suger ' + (data.data.labels.length ? '☑' : '☐'),
37+
// command: data.data.labels.length ? Commands.UNUSE_REF_SUGER : Commands.USE_REF_SUGER,
38+
// arguments: [document.uri],
39+
// },
40+
// });
41+
// }
42+
if (descriptor.scriptSetup) {
43+
result.push({
44+
range: {
45+
start: document.positionAt(descriptor.scriptSetup.loc.start),
46+
end: document.positionAt(descriptor.scriptSetup.loc.end),
47+
},
48+
command: {
49+
title: 'RFC: ' + rfc,
50+
command: '',
51+
},
52+
})
53+
}
54+
return result;
55+
}
56+
function getHtmlResult(sourceFile: SourceFile) {
2457
const sourceMaps = sourceFile.getHtmlSourceMaps();
2558
for (const sourceMap of sourceMaps) {
2659
for (const maped of sourceMap) {
@@ -33,10 +66,9 @@ export function register(sourceFiles: Map<string, SourceFile>) {
3366
);
3467
}
3568
}
36-
return result;
69+
return [];
3770
}
3871
function getPugResult(sourceFile: SourceFile) {
39-
const result: CodeLens[] = [];
4072
const sourceMaps = sourceFile.getPugSourceMaps();
4173
for (const sourceMap of sourceMaps) {
4274
for (const maped of sourceMap) {
@@ -49,22 +81,22 @@ export function register(sourceFiles: Map<string, SourceFile>) {
4981
);
5082
}
5183
}
52-
return result;
84+
return [];
5385
}
5486
function getPugHtmlConvertCodeLens(current: 'html' | 'pug', range: Range) {
5587
const result: CodeLens[] = [];
5688
result.push({
5789
range,
5890
command: {
59-
title: 'html' + (current === 'html' ? ' (current)' : ''),
91+
title: 'html ' + (current === 'html' ? '' : ''),
6092
command: Commands.PUG_TO_HTML,
6193
arguments: [document.uri],
6294
},
6395
});
6496
result.push({
6597
range,
6698
command: {
67-
title: 'pug' + (current === 'pug' ? ' (current)' : ''),
99+
title: 'pug ' + (current === 'pug' ? '' : ''),
68100
command: Commands.HTML_TO_PUG,
69101
arguments: [document.uri],
70102
},

packages/vscode-vue-languageservice/src/languageFeatures/executeCommand.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,49 @@ export function register(sourceFiles: Map<string, SourceFile>) {
1414
if (!sourceFile) return;
1515

1616
const desc = sourceFile.getDescriptor();
17-
if (!desc.template) return;
18-
19-
const lang = desc.template.lang;
2017

18+
if (command === Commands.UNUSE_REF_SUGER) {
19+
if (!desc.scriptSetup) return;
20+
const genData = sourceFile.getScriptSetupData();
21+
if (!genData) return;
22+
let edits: TextEdit[] = [];
23+
edits.push(TextEdit.replace({
24+
start: document.positionAt(desc.scriptSetup.loc.start),
25+
end: document.positionAt(desc.scriptSetup.loc.start),
26+
}, `\nimport { ref } from 'vue'\n`));
27+
for (const label of genData.data.labels) {
28+
edits.push(TextEdit.replace({
29+
start: document.positionAt(desc.scriptSetup.loc.start + label.label.start),
30+
end: document.positionAt(desc.scriptSetup.loc.start + label.label.end + 1),
31+
}, 'const'));
32+
edits.push(TextEdit.replace({
33+
start: document.positionAt(desc.scriptSetup.loc.start + label.right.start),
34+
end: document.positionAt(desc.scriptSetup.loc.start + label.right.start),
35+
}, 'ref('));
36+
edits.push(TextEdit.replace({
37+
start: document.positionAt(desc.scriptSetup.loc.start + label.right.end),
38+
end: document.positionAt(desc.scriptSetup.loc.start + label.right.end),
39+
}, ')'));
40+
for (const _var of label.vars) {
41+
for (const reference of _var.references) {
42+
edits.push(TextEdit.replace({
43+
start: document.positionAt(desc.scriptSetup.loc.start + reference.end),
44+
end: document.positionAt(desc.scriptSetup.loc.start + reference.end),
45+
}, '.value'));
46+
}
47+
for (const reference of _var.rawReferences) {
48+
edits.push(TextEdit.replace({
49+
start: document.positionAt(desc.scriptSetup.loc.start + reference.start),
50+
end: document.positionAt(desc.scriptSetup.loc.start + reference.start + 1),
51+
}, ''));
52+
}
53+
}
54+
}
55+
connection.workspace.applyEdit({ changes: { [document.uri]: edits } });
56+
}
2157
if (command === Commands.HTML_TO_PUG) {
58+
if (!desc.template) return;
59+
const lang = desc.template.lang;
2260
if (lang !== 'html') return;
2361

2462
const pug = htmlToPug(desc.template.content) + '\n';
@@ -46,6 +84,8 @@ export function register(sourceFiles: Map<string, SourceFile>) {
4684
connection.workspace.applyEdit({ changes: { [document.uri]: [textEdit] } });
4785
}
4886
if (command === Commands.PUG_TO_HTML) {
87+
if (!desc.template) return;
88+
const lang = desc.template.lang;
4989
if (lang !== 'pug') return;
5090

5191
let html = pugToHtml(desc.template.content);

packages/vscode-vue-languageservice/src/languageFeatures/rangeFormatting.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export function formattingWorker(sourceFile: SourceFile, document: TextDocument,
166166
function getTsFormattingEdits() {
167167
const result: TextEdit[] = [];
168168
for (const sourceMap of sourceFile.getTsSourceMaps()) {
169+
if (!sourceMap.capabilities.formatting) continue;
169170
const textEdits = tsLanguageService.doFormatting(sourceMap.targetDocument, options);
170171
for (const textEdit of textEdits) {
171172
for (const vueLoc of sourceMap.targetToSources(textEdit.range)) {

0 commit comments

Comments
 (0)