@vue/language-service 3.0.0-alpha.0 → 3.0.0-alpha.2

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 CHANGED
@@ -12,4 +12,7 @@ declare module '@volar/language-service' {
12
12
  }
13
13
  }
14
14
  export declare function getFullLanguageServicePlugins(ts: typeof import('typescript')): LanguageServicePlugin<any>[];
15
- export declare function getHybridModeLanguageServicePlugins(ts: typeof import('typescript'), getTsPluginClient: import('@vue/typescript-plugin/lib/requests').Requests | undefined): LanguageServicePlugin<any>[];
15
+ import type * as ts from 'typescript';
16
+ export declare function getHybridModeLanguageServicePlugins(ts: typeof import('typescript'), tsPluginClient: (import('@vue/typescript-plugin/lib/requests').Requests & {
17
+ getDocumentHighlights: (fileName: string, position: number) => Promise<ts.DocumentHighlights[] | null>;
18
+ }) | undefined): LanguageServicePlugin<any>[];
package/index.js CHANGED
@@ -36,6 +36,7 @@ const vue_compiler_dom_errors_1 = require("./lib/plugins/vue-compiler-dom-errors
36
36
  const vue_complete_define_assignment_1 = require("./lib/plugins/vue-complete-define-assignment");
37
37
  const vue_directive_comments_1 = require("./lib/plugins/vue-directive-comments");
38
38
  const vue_document_drop_1 = require("./lib/plugins/vue-document-drop");
39
+ const vue_document_highlights_1 = require("./lib/plugins/vue-document-highlights");
39
40
  const vue_document_links_1 = require("./lib/plugins/vue-document-links");
40
41
  const vue_extract_file_1 = require("./lib/plugins/vue-extract-file");
41
42
  const vue_inlayhints_1 = require("./lib/plugins/vue-inlayhints");
@@ -105,31 +106,31 @@ function getFullLanguageServicePlugins(ts) {
105
106
  };
106
107
  return {
107
108
  async collectExtractProps(...args) {
108
- return await collectExtractProps_1.collectExtractProps.apply(requestContext, args);
109
+ return collectExtractProps_1.collectExtractProps.apply(requestContext, args);
109
110
  },
110
111
  async getPropertiesAtLocation(...args) {
111
- return await getPropertiesAtLocation_1.getPropertiesAtLocation.apply(requestContext, args);
112
+ return getPropertiesAtLocation_1.getPropertiesAtLocation.apply(requestContext, args);
112
113
  },
113
114
  async getImportPathForFile(...args) {
114
- return await getImportPathForFile_1.getImportPathForFile.apply(requestContext, args);
115
+ return getImportPathForFile_1.getImportPathForFile.apply(requestContext, args);
115
116
  },
116
117
  async getComponentEvents(...args) {
117
- return await getComponentEvents_1.getComponentEvents.apply(requestContext, args);
118
+ return getComponentEvents_1.getComponentEvents.apply(requestContext, args);
118
119
  },
119
120
  async getComponentDirectives(...args) {
120
- return await getComponentDirectives_1.getComponentDirectives.apply(requestContext, args);
121
+ return getComponentDirectives_1.getComponentDirectives.apply(requestContext, args);
121
122
  },
122
123
  async getComponentNames(...args) {
123
- return await getComponentNames_1.getComponentNames.apply(requestContext, args);
124
+ return getComponentNames_1.getComponentNames.apply(requestContext, args);
124
125
  },
125
126
  async getComponentProps(...args) {
126
- return await getComponentProps_1.getComponentProps.apply(requestContext, args);
127
+ return getComponentProps_1.getComponentProps.apply(requestContext, args);
127
128
  },
128
129
  async getElementAttrs(...args) {
129
- return await getElementAttrs_1.getElementAttrs.apply(requestContext, args);
130
+ return getElementAttrs_1.getElementAttrs.apply(requestContext, args);
130
131
  },
131
132
  async getElementNames(...args) {
132
- return await getElementNames_1.getElementNames.apply(requestContext, args);
133
+ return getElementNames_1.getElementNames.apply(requestContext, args);
133
134
  },
134
135
  async getQuickInfoAtPosition(fileName, position) {
135
136
  const languageService = context.getLanguageService();
@@ -138,8 +139,7 @@ function getFullLanguageServicePlugins(ts) {
138
139
  if (!sourceScript) {
139
140
  return;
140
141
  }
141
- const document = context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
142
- const hover = await languageService.getHover(uri, document.positionAt(position));
142
+ const hover = await languageService.getHover(uri, position);
143
143
  let text = '';
144
144
  if (typeof hover?.contents === 'string') {
145
145
  text = hover.contents;
@@ -167,12 +167,15 @@ function getFullLanguageServicePlugins(ts) {
167
167
  };
168
168
  }
169
169
  }
170
- function getHybridModeLanguageServicePlugins(ts, getTsPluginClient) {
170
+ function getHybridModeLanguageServicePlugins(ts, tsPluginClient) {
171
171
  const plugins = [
172
172
  (0, syntactic_1.create)(ts),
173
173
  (0, docCommentTemplate_1.create)(ts),
174
- ...getCommonLanguageServicePlugins(ts, () => getTsPluginClient)
174
+ ...getCommonLanguageServicePlugins(ts, () => tsPluginClient)
175
175
  ];
176
+ if (tsPluginClient) {
177
+ plugins.push((0, vue_document_highlights_1.create)(tsPluginClient.getDocumentHighlights));
178
+ }
176
179
  for (const plugin of plugins) {
177
180
  // avoid affecting TS plugin
178
181
  delete plugin.capabilities.semanticTokensProvider;
@@ -191,8 +194,8 @@ function getCommonLanguageServicePlugins(ts, getTsPluginClient) {
191
194
  (0, vue_compiler_dom_errors_1.create)(),
192
195
  (0, vue_sfc_1.create)(),
193
196
  (0, vue_twoslash_queries_1.create)(getTsPluginClient),
194
- (0, vue_document_links_1.create)(),
195
197
  (0, vue_document_drop_1.create)(ts, getTsPluginClient),
198
+ (0, vue_document_links_1.create)(),
196
199
  (0, vue_complete_define_assignment_1.create)(),
197
200
  (0, vue_autoinsert_dotvalue_1.create)(ts, getTsPluginClient),
198
201
  (0, vue_autoinsert_space_1.create)(),
@@ -0,0 +1,14 @@
1
+ import type { LanguageServiceContext } from '@volar/language-service';
2
+ import type * as vscode from 'vscode-languageserver-protocol';
3
+ import type { URI } from 'vscode-uri';
4
+ import { AttrNameCasing, TagNameCasing } from '../types';
5
+ export declare function convertTagName(context: LanguageServiceContext, uri: URI, casing: TagNameCasing, tsPluginClient: typeof import('@vue/typescript-plugin/lib/client') | undefined): Promise<vscode.TextEdit[] | undefined>;
6
+ export declare function convertAttrName(context: LanguageServiceContext, uri: URI, casing: AttrNameCasing, tsPluginClient?: typeof import('@vue/typescript-plugin/lib/client')): Promise<vscode.TextEdit[] | undefined>;
7
+ export declare function getNameCasing(context: LanguageServiceContext, uri: URI): Promise<{
8
+ tag: TagNameCasing;
9
+ attr: AttrNameCasing;
10
+ }>;
11
+ export declare function detect(context: LanguageServiceContext, uri: URI): Promise<{
12
+ tag: TagNameCasing[];
13
+ attr: AttrNameCasing[];
14
+ }>;
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertTagName = convertTagName;
4
+ exports.convertAttrName = convertAttrName;
5
+ exports.getNameCasing = getNameCasing;
6
+ exports.detect = detect;
7
+ const vue = require("@vue/language-core");
8
+ const language_core_1 = require("@vue/language-core");
9
+ const alien_signals_1 = require("alien-signals");
10
+ const types_1 = require("../types");
11
+ async function convertTagName(context, uri, casing, tsPluginClient) {
12
+ const sourceFile = context.language.scripts.get(uri);
13
+ if (!sourceFile) {
14
+ return;
15
+ }
16
+ const root = sourceFile?.generated?.root;
17
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
18
+ return;
19
+ }
20
+ const { template } = root._sfc;
21
+ if (!template) {
22
+ return;
23
+ }
24
+ const document = context.documents.get(sourceFile.id, sourceFile.languageId, sourceFile.snapshot);
25
+ const edits = [];
26
+ const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
27
+ const tags = getTemplateTagsAndAttrs(root);
28
+ for (const [tagName, { offsets }] of tags) {
29
+ const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
30
+ if (componentName) {
31
+ for (const offset of offsets) {
32
+ const start = document.positionAt(template.startTagEnd + offset);
33
+ const end = document.positionAt(template.startTagEnd + offset + tagName.length);
34
+ const range = { start, end };
35
+ if (casing === types_1.TagNameCasing.Kebab && tagName !== (0, language_core_1.hyphenateTag)(componentName)) {
36
+ edits.push({ range, newText: (0, language_core_1.hyphenateTag)(componentName) });
37
+ }
38
+ if (casing === types_1.TagNameCasing.Pascal && tagName !== componentName) {
39
+ edits.push({ range, newText: componentName });
40
+ }
41
+ }
42
+ }
43
+ }
44
+ return edits;
45
+ }
46
+ async function convertAttrName(context, uri, casing, tsPluginClient) {
47
+ const sourceFile = context.language.scripts.get(uri);
48
+ if (!sourceFile) {
49
+ return;
50
+ }
51
+ const root = sourceFile?.generated?.root;
52
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
53
+ return;
54
+ }
55
+ const { template } = root._sfc;
56
+ if (!template) {
57
+ return;
58
+ }
59
+ const document = context.documents.get(uri, sourceFile.languageId, sourceFile.snapshot);
60
+ const edits = [];
61
+ const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
62
+ const tags = getTemplateTagsAndAttrs(root);
63
+ for (const [tagName, { attrs }] of tags) {
64
+ const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
65
+ if (componentName) {
66
+ const props = (await tsPluginClient?.getComponentProps(root.fileName, componentName) ?? []).map(prop => prop.name);
67
+ for (const [attrName, { offsets }] of attrs) {
68
+ const propName = props.find(prop => prop === attrName || (0, language_core_1.hyphenateAttr)(prop) === attrName);
69
+ if (propName) {
70
+ for (const offset of offsets) {
71
+ const start = document.positionAt(template.startTagEnd + offset);
72
+ const end = document.positionAt(template.startTagEnd + offset + attrName.length);
73
+ const range = { start, end };
74
+ if (casing === types_1.AttrNameCasing.Kebab && attrName !== (0, language_core_1.hyphenateAttr)(propName)) {
75
+ edits.push({ range, newText: (0, language_core_1.hyphenateAttr)(propName) });
76
+ }
77
+ if (casing === types_1.AttrNameCasing.Camel && attrName !== propName) {
78
+ edits.push({ range, newText: propName });
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ return edits;
86
+ }
87
+ async function getNameCasing(context, uri) {
88
+ const detected = await detect(context, uri);
89
+ const [attr, tag] = await Promise.all([
90
+ context.env.getConfiguration?.('vue.complete.casing.props', uri.toString()),
91
+ context.env.getConfiguration?.('vue.complete.casing.tags', uri.toString()),
92
+ ]);
93
+ const tagNameCasing = detected.tag.length === 1 && (tag === 'autoPascal' || tag === 'autoKebab') ? detected.tag[0] : (tag === 'autoKebab' || tag === 'kebab') ? types_1.TagNameCasing.Kebab : types_1.TagNameCasing.Pascal;
94
+ const attrNameCasing = detected.attr.length === 1 && (attr === 'autoCamel' || attr === 'autoKebab') ? detected.attr[0] : (attr === 'autoCamel' || attr === 'camel') ? types_1.AttrNameCasing.Camel : types_1.AttrNameCasing.Kebab;
95
+ return {
96
+ tag: tagNameCasing,
97
+ attr: attrNameCasing,
98
+ };
99
+ }
100
+ async function detect(context, uri) {
101
+ const rootFile = context.language.scripts.get(uri)?.generated?.root;
102
+ if (!(rootFile instanceof language_core_1.VueVirtualCode)) {
103
+ return {
104
+ tag: [],
105
+ attr: [],
106
+ };
107
+ }
108
+ return {
109
+ tag: await getTagNameCase(rootFile),
110
+ attr: getAttrNameCase(rootFile),
111
+ };
112
+ function getAttrNameCase(file) {
113
+ const tags = getTemplateTagsAndAttrs(file);
114
+ const result = [];
115
+ for (const [_, { attrs }] of tags) {
116
+ for (const [tagName] of attrs) {
117
+ // attrName
118
+ if (tagName !== (0, language_core_1.hyphenateTag)(tagName)) {
119
+ result.push(types_1.AttrNameCasing.Camel);
120
+ break;
121
+ }
122
+ }
123
+ for (const [tagName] of attrs) {
124
+ // attr-name
125
+ if (tagName.includes('-')) {
126
+ result.push(types_1.AttrNameCasing.Kebab);
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ return result;
132
+ }
133
+ function getTagNameCase(file) {
134
+ const result = new Set();
135
+ if (file._sfc.template?.ast) {
136
+ for (const element of vue.forEachElementNode(file._sfc.template.ast)) {
137
+ if (element.tagType === 1) {
138
+ if (element.tag !== (0, language_core_1.hyphenateTag)(element.tag)) {
139
+ // TagName
140
+ result.add(types_1.TagNameCasing.Pascal);
141
+ }
142
+ else {
143
+ // Tagname -> tagname
144
+ // TagName -> tag-name
145
+ result.add(types_1.TagNameCasing.Kebab);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ return [...result];
151
+ }
152
+ }
153
+ const map = new WeakMap();
154
+ function getTemplateTagsAndAttrs(sourceFile) {
155
+ if (!map.has(sourceFile)) {
156
+ const getter = (0, alien_signals_1.computed)(() => {
157
+ if (!(sourceFile instanceof vue.VueVirtualCode)) {
158
+ return;
159
+ }
160
+ const ast = sourceFile._sfc.template?.ast;
161
+ const tags = new Map();
162
+ if (ast) {
163
+ for (const node of vue.forEachElementNode(ast)) {
164
+ if (!tags.has(node.tag)) {
165
+ tags.set(node.tag, { offsets: [], attrs: new Map() });
166
+ }
167
+ const tag = tags.get(node.tag);
168
+ const startTagHtmlOffset = node.loc.start.offset + node.loc.source.indexOf(node.tag);
169
+ const endTagHtmlOffset = node.loc.start.offset + node.loc.source.lastIndexOf(node.tag);
170
+ tag.offsets.push(startTagHtmlOffset);
171
+ if (!node.isSelfClosing) {
172
+ tag.offsets.push(endTagHtmlOffset);
173
+ }
174
+ for (const prop of node.props) {
175
+ let name;
176
+ let offset;
177
+ if (prop.type === 7
178
+ && prop.arg?.type === 4
179
+ && prop.arg.isStatic) {
180
+ name = prop.arg.content;
181
+ offset = prop.arg.loc.start.offset;
182
+ }
183
+ else if (prop.type === 6) {
184
+ name = prop.name;
185
+ offset = prop.loc.start.offset;
186
+ }
187
+ if (name !== undefined && offset !== undefined) {
188
+ if (!tag.attrs.has(name)) {
189
+ tag.attrs.set(name, { offsets: [] });
190
+ }
191
+ tag.attrs.get(name).offsets.push(offset);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ return tags;
197
+ });
198
+ map.set(sourceFile, getter);
199
+ }
200
+ return map.get(sourceFile).get() ?? new Map();
201
+ }
202
+ //# sourceMappingURL=nameCasing.js.map
@@ -0,0 +1,2 @@
1
+ import type { LanguageServicePlugin } from '@volar/language-service';
2
+ export declare function create(): LanguageServicePlugin;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.create = create;
4
+ function create() {
5
+ return {
6
+ name: 'vue-autoinsert-selfClosing',
7
+ capabilities: {
8
+ autoInsertionProvider: {
9
+ triggerCharacters: ['/'],
10
+ configurationSections: ['vue.autoInsert.selfClosing'],
11
+ },
12
+ },
13
+ create(context) {
14
+ return {
15
+ async provideAutoInsertSnippet(document, selection, change) {
16
+ if (document.languageId !== 'html') {
17
+ return;
18
+ }
19
+ const enabled = await context.env.getConfiguration?.('vue.autoInsert.selfClosing') ?? true;
20
+ if (!enabled) {
21
+ return;
22
+ }
23
+ if (change.text === '{}'
24
+ && document.getText().slice(change.rangeOffset - 1, change.rangeOffset + 3) === '{{}}'
25
+ && document.offsetAt(selection) === change.rangeOffset + 1) {
26
+ return ` $0 `;
27
+ }
28
+ },
29
+ };
30
+ },
31
+ };
32
+ }
33
+ //# sourceMappingURL=vue-autoinsert-self-closing.js.map
@@ -0,0 +1,3 @@
1
+ import type { LanguageServicePlugin } from '@volar/language-service';
2
+ import type * as ts from 'typescript';
3
+ export declare function create(getDocumentHighlights: (fileName: string, position: number) => Promise<ts.DocumentHighlights[] | null>): LanguageServicePlugin;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.create = create;
4
+ const language_core_1 = require("@vue/language-core");
5
+ const vscode_uri_1 = require("vscode-uri");
6
+ function create(getDocumentHighlights) {
7
+ return {
8
+ name: 'vue-document-highlights',
9
+ capabilities: {
10
+ documentHighlightProvider: true,
11
+ },
12
+ create(context) {
13
+ return {
14
+ async provideDocumentHighlights(document, position) {
15
+ const uri = vscode_uri_1.URI.parse(document.uri);
16
+ const decoded = context.decodeEmbeddedDocumentUri(uri);
17
+ const sourceScript = decoded && context.language.scripts.get(decoded[0]);
18
+ const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
19
+ if (!sourceScript?.generated || virtualCode?.id !== 'main') {
20
+ return;
21
+ }
22
+ const root = sourceScript.generated.root;
23
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
24
+ return;
25
+ }
26
+ const result = await getDocumentHighlights(root.fileName, document.offsetAt(position));
27
+ return result
28
+ ?.filter(({ fileName }) => fileName === root.fileName)
29
+ .flatMap(({ highlightSpans }) => highlightSpans)
30
+ .map(({ textSpan, kind }) => ({
31
+ range: {
32
+ start: document.positionAt(textSpan.start),
33
+ end: document.positionAt(textSpan.start + textSpan.length),
34
+ },
35
+ kind: kind === 'reference'
36
+ ? 2
37
+ : kind === 'writtenReference'
38
+ ? 3
39
+ : 1,
40
+ }));
41
+ },
42
+ };
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=vue-document-highlights.js.map
@@ -38,7 +38,7 @@ function create(getTsPluginClient) {
38
38
  for (const [pointerPosition, hoverOffset] of hoverOffsets) {
39
39
  const map = context.language.maps.get(virtualCode, sourceScript);
40
40
  for (const [sourceOffset] of map.toSourceLocation(hoverOffset)) {
41
- const quickInfo = await tsPluginClient?.getQuickInfoAtPosition(root.fileName, sourceOffset);
41
+ const quickInfo = await tsPluginClient?.getQuickInfoAtPosition(root.fileName, document.positionAt(sourceOffset));
42
42
  if (quickInfo) {
43
43
  inlayHints.push({
44
44
  position: { line: pointerPosition.line, character: pointerPosition.character + 2 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue/language-service",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "data",
@@ -21,9 +21,9 @@
21
21
  "@volar/language-service": "~2.4.11",
22
22
  "@volar/typescript": "~2.4.11",
23
23
  "@vue/compiler-dom": "^3.5.0",
24
- "@vue/language-core": "3.0.0-alpha.0",
24
+ "@vue/language-core": "3.0.0-alpha.2",
25
25
  "@vue/shared": "^3.5.0",
26
- "@vue/typescript-plugin": "3.0.0-alpha.0",
26
+ "@vue/typescript-plugin": "3.0.0-alpha.2",
27
27
  "alien-signals": "^1.0.3",
28
28
  "path-browserify": "^1.0.1",
29
29
  "volar-service-css": "0.0.62",
@@ -45,5 +45,5 @@
45
45
  "@volar/kit": "~2.4.11",
46
46
  "vscode-languageserver-protocol": "^3.17.5"
47
47
  },
48
- "gitHead": "4b49cbe09097e482def4603b90f6c3b93bb2e913"
48
+ "gitHead": "79247b7c24b7202ec676723440fdb36c38e6d450"
49
49
  }