@astrojs/language-server 0.16.1 → 0.17.0
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/CHANGELOG.md +10 -0
- package/dist/core/documents/AstroDocument.d.ts +1 -0
- package/dist/core/documents/AstroDocument.js +1 -0
- package/dist/core/documents/DocumentMapper.d.ts +2 -0
- package/dist/core/documents/DocumentMapper.js +7 -5
- package/dist/core/documents/utils.d.ts +1 -0
- package/dist/core/documents/utils.js +16 -1
- package/dist/plugins/astro/AstroPlugin.js +1 -1
- package/dist/plugins/astro/features/CompletionsProvider.d.ts +4 -5
- package/dist/plugins/astro/features/CompletionsProvider.js +45 -54
- package/dist/plugins/typescript/features/CodeActionsProvider.js +76 -17
- package/dist/plugins/typescript/features/CompletionsProvider.d.ts +2 -1
- package/dist/plugins/typescript/features/CompletionsProvider.js +90 -41
- package/dist/plugins/typescript/features/DefinitionsProvider.js +22 -1
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +58 -15
- package/dist/plugins/typescript/features/FoldingRangesProvider.js +13 -6
- package/dist/plugins/typescript/features/HoverProvider.js +14 -1
- package/dist/plugins/typescript/features/SignatureHelpProvider.js +9 -1
- package/dist/plugins/typescript/language-service.js +18 -0
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.d.ts +22 -2
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.js +48 -1
- package/dist/plugins/typescript/snapshots/SnapshotManager.js +1 -0
- package/dist/plugins/typescript/utils.d.ts +11 -1
- package/dist/plugins/typescript/utils.js +17 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @astrojs/language-server
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3ad0f65: Add support for TypeScript features inside script tags (completions, diagnostics, hover etc)
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 2e9da14: Add support for loading props completions from .d.ts files, improve performance of props completions
|
|
12
|
+
|
|
3
13
|
## 0.16.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -9,6 +9,7 @@ export declare class AstroDocument extends WritableDocument {
|
|
|
9
9
|
astroMeta: AstroMetadata;
|
|
10
10
|
html: HTMLDocument;
|
|
11
11
|
styleTags: TagInformation[];
|
|
12
|
+
scriptTags: TagInformation[];
|
|
12
13
|
constructor(url: string, content: string);
|
|
13
14
|
private updateDocInfo;
|
|
14
15
|
setText(text: string): void;
|
|
@@ -18,6 +18,7 @@ class AstroDocument extends DocumentBase_1.WritableDocument {
|
|
|
18
18
|
this.astroMeta = (0, parseAstro_1.parseAstro)(this.content);
|
|
19
19
|
this.html = (0, parseHtml_1.parseHtml)(this.content);
|
|
20
20
|
this.styleTags = (0, utils_2.extractStyleTags)(this.content, this.html);
|
|
21
|
+
this.scriptTags = (0, utils_2.extractScriptTags)(this.content, this.html);
|
|
21
22
|
}
|
|
22
23
|
setText(text) {
|
|
23
24
|
this.content = text;
|
|
@@ -46,6 +46,8 @@ export declare class FragmentMapper implements DocumentMapper {
|
|
|
46
46
|
private originalText;
|
|
47
47
|
private tagInfo;
|
|
48
48
|
private url;
|
|
49
|
+
private lineOffsetsOriginal;
|
|
50
|
+
private lineOffsetsGenerated;
|
|
49
51
|
constructor(originalText: string, tagInfo: TagInformation, url: string);
|
|
50
52
|
getOriginalPosition(generatedPosition: Position): Position;
|
|
51
53
|
private offsetInParent;
|
|
@@ -45,20 +45,22 @@ class FragmentMapper {
|
|
|
45
45
|
this.originalText = originalText;
|
|
46
46
|
this.tagInfo = tagInfo;
|
|
47
47
|
this.url = url;
|
|
48
|
+
this.lineOffsetsOriginal = (0, utils_1.getLineOffsets)(this.originalText);
|
|
49
|
+
this.lineOffsetsGenerated = (0, utils_1.getLineOffsets)(this.tagInfo.content);
|
|
48
50
|
}
|
|
49
51
|
getOriginalPosition(generatedPosition) {
|
|
50
|
-
const parentOffset = this.offsetInParent((0, utils_1.offsetAt)(generatedPosition, this.tagInfo.content));
|
|
51
|
-
return (0, utils_1.positionAt)(parentOffset, this.originalText);
|
|
52
|
+
const parentOffset = this.offsetInParent((0, utils_1.offsetAt)(generatedPosition, this.tagInfo.content, this.lineOffsetsGenerated));
|
|
53
|
+
return (0, utils_1.positionAt)(parentOffset, this.originalText, this.lineOffsetsOriginal);
|
|
52
54
|
}
|
|
53
55
|
offsetInParent(offset) {
|
|
54
56
|
return this.tagInfo.start + offset;
|
|
55
57
|
}
|
|
56
58
|
getGeneratedPosition(originalPosition) {
|
|
57
|
-
const fragmentOffset = (0, utils_1.offsetAt)(originalPosition, this.originalText) - this.tagInfo.start;
|
|
58
|
-
return (0, utils_1.positionAt)(fragmentOffset, this.tagInfo.content);
|
|
59
|
+
const fragmentOffset = (0, utils_1.offsetAt)(originalPosition, this.originalText, this.lineOffsetsOriginal) - this.tagInfo.start;
|
|
60
|
+
return (0, utils_1.positionAt)(fragmentOffset, this.tagInfo.content, this.lineOffsetsGenerated);
|
|
59
61
|
}
|
|
60
62
|
isInGenerated(pos) {
|
|
61
|
-
const offset = (0, utils_1.offsetAt)(pos, this.originalText);
|
|
63
|
+
const offset = (0, utils_1.offsetAt)(pos, this.originalText, this.lineOffsetsOriginal);
|
|
62
64
|
return offset >= this.tagInfo.start && offset <= this.tagInfo.end;
|
|
63
65
|
}
|
|
64
66
|
getURL() {
|
|
@@ -15,6 +15,7 @@ export interface TagInformation {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function walk(node: Node): Generator<Node, void, unknown>;
|
|
17
17
|
export declare function extractStyleTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
18
|
+
export declare function extractScriptTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
18
19
|
export declare function getLineAtPosition(position: Position, text: string): string;
|
|
19
20
|
/**
|
|
20
21
|
* Returns the node if offset is inside a HTML start tag
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isInComponentStartTag = exports.isComponentTag = exports.getNodeIfIsInHTMLStartTag = exports.getLineAtPosition = exports.extractStyleTags = exports.walk = void 0;
|
|
3
|
+
exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isInComponentStartTag = exports.isComponentTag = exports.getNodeIfIsInHTMLStartTag = exports.getLineAtPosition = exports.extractScriptTags = exports.extractStyleTags = exports.walk = void 0;
|
|
4
4
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
5
5
|
const utils_1 = require("../../utils");
|
|
6
6
|
const parseHtml_1 = require("./parseHtml");
|
|
@@ -28,6 +28,13 @@ function extractTags(text, tag, html) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
if (tag === 'script' && !matchedNodes.length && rootNodes.length) {
|
|
32
|
+
for (let child of walk(rootNodes[0])) {
|
|
33
|
+
if (child.tag === 'script') {
|
|
34
|
+
matchedNodes.push(child);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
31
38
|
return matchedNodes.map(transformToTagInfo);
|
|
32
39
|
function transformToTagInfo(matchedNode) {
|
|
33
40
|
const start = matchedNode.startTagEnd ?? matchedNode.start;
|
|
@@ -60,6 +67,14 @@ function extractStyleTags(source, html) {
|
|
|
60
67
|
return styles;
|
|
61
68
|
}
|
|
62
69
|
exports.extractStyleTags = extractStyleTags;
|
|
70
|
+
function extractScriptTags(source, html) {
|
|
71
|
+
const scripts = extractTags(source, 'script', html);
|
|
72
|
+
if (!scripts.length) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return scripts;
|
|
76
|
+
}
|
|
77
|
+
exports.extractScriptTags = extractScriptTags;
|
|
63
78
|
function parseAttributes(rawAttrs) {
|
|
64
79
|
const attrs = {};
|
|
65
80
|
if (!rawAttrs) {
|
|
@@ -9,7 +9,7 @@ class AstroPlugin {
|
|
|
9
9
|
this.__name = 'astro';
|
|
10
10
|
this.configManager = configManager;
|
|
11
11
|
this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
|
|
12
|
-
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(
|
|
12
|
+
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager);
|
|
13
13
|
}
|
|
14
14
|
async getCompletions(document, position, completionContext) {
|
|
15
15
|
const completions = this.completionProvider.getCompletions(document, position, completionContext);
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { AppCompletionList, CompletionsProvider } from '../../interfaces';
|
|
2
|
-
import type { AstroDocument
|
|
2
|
+
import type { AstroDocument } from '../../../core/documents';
|
|
3
3
|
import { CompletionContext, Position } from 'vscode-languageserver';
|
|
4
4
|
import { LanguageServiceManager as TypeScriptLanguageServiceManager } from '../../typescript/LanguageServiceManager';
|
|
5
5
|
export declare class CompletionsProviderImpl implements CompletionsProvider {
|
|
6
|
-
private readonly docManager;
|
|
7
6
|
private readonly languageServiceManager;
|
|
7
|
+
private lastCompletion;
|
|
8
8
|
directivesHTMLLang: import("vscode-html-languageservice").LanguageService;
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(languageServiceManager: TypeScriptLanguageServiceManager);
|
|
10
10
|
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList | null>;
|
|
11
11
|
private getComponentScriptCompletion;
|
|
12
|
-
private
|
|
12
|
+
private getPropCompletionsAndFilePath;
|
|
13
13
|
private getImportedSymbol;
|
|
14
14
|
private getPropType;
|
|
15
15
|
private getCompletionItemForProperty;
|
|
16
|
-
private isAstroComponent;
|
|
17
16
|
}
|
|
@@ -13,21 +13,18 @@ const vscode_html_languageservice_1 = require("vscode-html-languageservice");
|
|
|
13
13
|
const astro_attributes_1 = require("../../html/features/astro-attributes");
|
|
14
14
|
const utils_4 = require("../../html/utils");
|
|
15
15
|
class CompletionsProviderImpl {
|
|
16
|
-
constructor(
|
|
16
|
+
constructor(languageServiceManager) {
|
|
17
|
+
this.lastCompletion = null;
|
|
17
18
|
this.directivesHTMLLang = (0, vscode_html_languageservice_1.getLanguageService)({
|
|
18
19
|
customDataProviders: [astro_attributes_1.astroDirectives],
|
|
19
20
|
useDefaultDataProvider: false,
|
|
20
21
|
});
|
|
21
|
-
this.docManager = docManager;
|
|
22
22
|
this.languageServiceManager = languageServiceManager;
|
|
23
23
|
}
|
|
24
24
|
async getCompletions(document, position, completionContext) {
|
|
25
|
-
const doc = this.docManager.get(document.uri);
|
|
26
|
-
if (!doc)
|
|
27
|
-
return null;
|
|
28
25
|
let items = [];
|
|
29
26
|
if (completionContext?.triggerCharacter === '-') {
|
|
30
|
-
const frontmatter = this.getComponentScriptCompletion(
|
|
27
|
+
const frontmatter = this.getComponentScriptCompletion(document, position);
|
|
31
28
|
if (frontmatter)
|
|
32
29
|
items.push(frontmatter);
|
|
33
30
|
}
|
|
@@ -35,11 +32,11 @@ class CompletionsProviderImpl {
|
|
|
35
32
|
const offset = document.offsetAt(position);
|
|
36
33
|
const node = html.findNodeAt(offset);
|
|
37
34
|
if ((0, utils_1.isInComponentStartTag)(html, offset) && !(0, utils_1.isInsideExpression)(document.getText(), node.start, offset)) {
|
|
38
|
-
const props = await this.
|
|
35
|
+
const { completions: props, componentFilePath } = await this.getPropCompletionsAndFilePath(document, position, completionContext);
|
|
39
36
|
if (props.length) {
|
|
40
37
|
items.push(...props);
|
|
41
38
|
}
|
|
42
|
-
const isAstro =
|
|
39
|
+
const isAstro = componentFilePath?.endsWith('.astro');
|
|
43
40
|
if (!isAstro) {
|
|
44
41
|
const directives = (0, utils_4.removeDataAttrCompletion)(this.directivesHTMLLang.doComplete(document, position, html).items);
|
|
45
42
|
items.push(...directives);
|
|
@@ -47,7 +44,7 @@ class CompletionsProviderImpl {
|
|
|
47
44
|
}
|
|
48
45
|
return vscode_languageserver_1.CompletionList.create(items, true);
|
|
49
46
|
}
|
|
50
|
-
getComponentScriptCompletion(document, position
|
|
47
|
+
getComponentScriptCompletion(document, position) {
|
|
51
48
|
const base = {
|
|
52
49
|
kind: vscode_languageserver_1.CompletionItemKind.Snippet,
|
|
53
50
|
label: '---',
|
|
@@ -55,7 +52,7 @@ class CompletionsProviderImpl {
|
|
|
55
52
|
preselect: true,
|
|
56
53
|
detail: 'Component script',
|
|
57
54
|
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
|
|
58
|
-
commitCharacters: [
|
|
55
|
+
commitCharacters: [],
|
|
59
56
|
};
|
|
60
57
|
const prefix = document.getLineUntilOffset(document.offsetAt(position));
|
|
61
58
|
if (document.astroMeta.frontmatter.state === null) {
|
|
@@ -78,25 +75,25 @@ class CompletionsProviderImpl {
|
|
|
78
75
|
}
|
|
79
76
|
return null;
|
|
80
77
|
}
|
|
81
|
-
async
|
|
78
|
+
async getPropCompletionsAndFilePath(document, position, completionContext) {
|
|
82
79
|
const offset = document.offsetAt(position);
|
|
83
80
|
const html = document.html;
|
|
84
81
|
const node = html.findNodeAt(offset);
|
|
85
82
|
if (!(0, utils_2.isPossibleComponent)(node)) {
|
|
86
|
-
return [];
|
|
83
|
+
return { completions: [], componentFilePath: null };
|
|
87
84
|
}
|
|
88
85
|
const inAttribute = node.start + node.tag.length < offset;
|
|
89
86
|
if (!inAttribute) {
|
|
90
|
-
return [];
|
|
87
|
+
return { completions: [], componentFilePath: null };
|
|
91
88
|
}
|
|
92
89
|
if (completionContext?.triggerCharacter === '/' || completionContext?.triggerCharacter === '>') {
|
|
93
|
-
return [];
|
|
90
|
+
return { completions: [], componentFilePath: null };
|
|
94
91
|
}
|
|
95
92
|
// If inside of attribute value, skip.
|
|
96
93
|
if (completionContext &&
|
|
97
94
|
completionContext.triggerKind === vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter &&
|
|
98
95
|
completionContext.triggerCharacter === '"') {
|
|
99
|
-
return [];
|
|
96
|
+
return { completions: [], componentFilePath: null };
|
|
100
97
|
}
|
|
101
98
|
const componentName = node.tag;
|
|
102
99
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
@@ -106,18 +103,34 @@ class CompletionsProviderImpl {
|
|
|
106
103
|
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
107
104
|
const typeChecker = program?.getTypeChecker();
|
|
108
105
|
if (!sourceFile || !typeChecker) {
|
|
109
|
-
return [];
|
|
106
|
+
return { completions: [], componentFilePath: null };
|
|
110
107
|
}
|
|
111
108
|
// Get the import statement
|
|
112
109
|
const imp = this.getImportedSymbol(sourceFile, componentName);
|
|
113
110
|
const importType = imp && typeChecker.getTypeAtLocation(imp);
|
|
114
111
|
if (!importType) {
|
|
115
|
-
return [];
|
|
112
|
+
return { completions: [], componentFilePath: null };
|
|
113
|
+
}
|
|
114
|
+
const symbol = importType.getSymbol();
|
|
115
|
+
if (!symbol) {
|
|
116
|
+
return { completions: [], componentFilePath: null };
|
|
117
|
+
}
|
|
118
|
+
const symbolDeclaration = symbol.declarations;
|
|
119
|
+
if (!symbolDeclaration) {
|
|
120
|
+
return { completions: [], componentFilePath: null };
|
|
121
|
+
}
|
|
122
|
+
const filePath = symbolDeclaration[0].getSourceFile().fileName;
|
|
123
|
+
const componentSnapshot = await this.languageServiceManager.getSnapshot(filePath);
|
|
124
|
+
if (this.lastCompletion) {
|
|
125
|
+
if (this.lastCompletion.tag === componentName &&
|
|
126
|
+
this.lastCompletion.documentVersion == componentSnapshot.version) {
|
|
127
|
+
return { completions: this.lastCompletion.completions, componentFilePath: filePath };
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
130
|
// Get the component's props type
|
|
118
|
-
const componentType = this.getPropType(
|
|
131
|
+
const componentType = this.getPropType(symbolDeclaration, typeChecker);
|
|
119
132
|
if (!componentType) {
|
|
120
|
-
return [];
|
|
133
|
+
return { completions: [], componentFilePath: null };
|
|
121
134
|
}
|
|
122
135
|
let completionItems = [];
|
|
123
136
|
// Add completions for this component's props type properties
|
|
@@ -131,7 +144,12 @@ class CompletionsProviderImpl {
|
|
|
131
144
|
completionItems = completionItems.map((item) => {
|
|
132
145
|
return { ...item, sortText: '_' };
|
|
133
146
|
});
|
|
134
|
-
|
|
147
|
+
this.lastCompletion = {
|
|
148
|
+
tag: componentName,
|
|
149
|
+
documentVersion: componentSnapshot.version,
|
|
150
|
+
completions: completionItems,
|
|
151
|
+
};
|
|
152
|
+
return { completions: completionItems, componentFilePath: filePath };
|
|
135
153
|
}
|
|
136
154
|
getImportedSymbol(sourceFile, identifier) {
|
|
137
155
|
for (let list of sourceFile.getChildren()) {
|
|
@@ -159,24 +177,20 @@ class CompletionsProviderImpl {
|
|
|
159
177
|
}
|
|
160
178
|
return null;
|
|
161
179
|
}
|
|
162
|
-
getPropType(
|
|
163
|
-
const
|
|
164
|
-
if (!sym) {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
for (const decl of sym?.getDeclarations() || []) {
|
|
180
|
+
getPropType(declarations, typeChecker) {
|
|
181
|
+
for (const decl of declarations) {
|
|
168
182
|
const fileName = (0, utils_3.toVirtualFilePath)(decl.getSourceFile().fileName);
|
|
169
|
-
if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx')) {
|
|
170
|
-
if (!typescript_1.default.isFunctionDeclaration(decl)) {
|
|
171
|
-
console.error(`We only support
|
|
183
|
+
if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx') || fileName.endsWith('.d.ts')) {
|
|
184
|
+
if (!typescript_1.default.isFunctionDeclaration(decl) && !typescript_1.default.isFunctionTypeNode(decl)) {
|
|
185
|
+
console.error(`We only support functions declarations at the moment`);
|
|
172
186
|
continue;
|
|
173
187
|
}
|
|
174
188
|
const fn = decl;
|
|
175
189
|
if (!fn.parameters.length)
|
|
176
190
|
continue;
|
|
177
191
|
const param1 = fn.parameters[0];
|
|
178
|
-
const
|
|
179
|
-
return
|
|
192
|
+
const propType = typeChecker.getTypeAtLocation(param1);
|
|
193
|
+
return propType;
|
|
180
194
|
}
|
|
181
195
|
}
|
|
182
196
|
return null;
|
|
@@ -216,28 +230,5 @@ class CompletionsProviderImpl {
|
|
|
216
230
|
}
|
|
217
231
|
return item;
|
|
218
232
|
}
|
|
219
|
-
async isAstroComponent(document, node) {
|
|
220
|
-
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
221
|
-
// Get the source file
|
|
222
|
-
const tsFilePath = (0, utils_3.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
223
|
-
const program = lang.getProgram();
|
|
224
|
-
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
225
|
-
const typeChecker = program?.getTypeChecker();
|
|
226
|
-
if (!sourceFile || !typeChecker) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const componentName = node.tag;
|
|
230
|
-
const imp = this.getImportedSymbol(sourceFile, componentName);
|
|
231
|
-
const importType = imp && typeChecker.getTypeAtLocation(imp);
|
|
232
|
-
if (!importType) {
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
const symbolDeclaration = importType.getSymbol()?.declarations;
|
|
236
|
-
if (symbolDeclaration) {
|
|
237
|
-
const fileName = symbolDeclaration[0].getSourceFile().fileName;
|
|
238
|
-
return fileName.endsWith('.astro');
|
|
239
|
-
}
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
233
|
}
|
|
243
234
|
exports.CompletionsProviderImpl = CompletionsProviderImpl;
|
|
@@ -23,8 +23,6 @@ class CodeActionsProviderImpl {
|
|
|
23
23
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
24
24
|
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
25
25
|
const fragment = await tsDoc.createFragment();
|
|
26
|
-
const start = fragment.offsetAt(fragment.getGeneratedPosition(range.start));
|
|
27
|
-
const end = fragment.offsetAt(fragment.getGeneratedPosition(range.end));
|
|
28
26
|
const tsPreferences = await this.configManager.getTSPreferences(document);
|
|
29
27
|
const formatOptions = await this.configManager.getTSFormatConfig(document);
|
|
30
28
|
let result = [];
|
|
@@ -50,24 +48,57 @@ class CodeActionsProviderImpl {
|
|
|
50
48
|
.map((diag) => Number(diag.code))
|
|
51
49
|
// We currently cannot support quick fix for unreachable code properly due to the way our TSX output is structured
|
|
52
50
|
.filter((code) => code !== 7027);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
const html = document.html;
|
|
52
|
+
const node = html.findNodeAt(document.offsetAt(range.start));
|
|
53
|
+
let codeFixes;
|
|
54
|
+
let isInsideScript = false;
|
|
55
|
+
if (node.tag === 'script') {
|
|
56
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, node);
|
|
57
|
+
const start = scriptTagSnapshot.offsetAt(scriptTagSnapshot.getGeneratedPosition(range.start));
|
|
58
|
+
const end = scriptTagSnapshot.offsetAt(scriptTagSnapshot.getGeneratedPosition(range.end));
|
|
59
|
+
codeFixes = lang.getCodeFixesAtPosition(scriptFilePath, start, end, errorCodes, formatOptions, tsPreferences);
|
|
60
|
+
codeFixes = codeFixes.map((fix) => ({
|
|
61
|
+
...fix,
|
|
62
|
+
changes: mapScriptTagFixToOriginal(fix.changes, scriptTagSnapshot),
|
|
63
|
+
}));
|
|
64
|
+
isInsideScript = true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const start = fragment.offsetAt(fragment.getGeneratedPosition(range.start));
|
|
68
|
+
const end = fragment.offsetAt(fragment.getGeneratedPosition(range.end));
|
|
69
|
+
codeFixes = errorCodes.includes(2304)
|
|
70
|
+
? this.getComponentQuickFix(start, end, lang, filePath, formatOptions, tsPreferences)
|
|
71
|
+
: undefined;
|
|
72
|
+
codeFixes =
|
|
73
|
+
codeFixes ?? lang.getCodeFixesAtPosition(filePath, start, end, errorCodes, formatOptions, tsPreferences);
|
|
74
|
+
}
|
|
75
|
+
const codeActions = codeFixes.map((fix) => codeFixToCodeAction(fix, context.diagnostics, context.only ? vscode_languageserver_types_1.CodeActionKind.QuickFix : vscode_languageserver_types_1.CodeActionKind.Empty, isInsideScript));
|
|
59
76
|
result.push(...codeActions);
|
|
60
77
|
}
|
|
61
78
|
return result;
|
|
62
|
-
function codeFixToCodeAction(codeFix, diagnostics, kind) {
|
|
79
|
+
function codeFixToCodeAction(codeFix, diagnostics, kind, isInsideScript) {
|
|
63
80
|
const documentChanges = codeFix.changes.map((change) => {
|
|
64
81
|
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.getURL(), null), change.textChanges.map((edit) => {
|
|
65
82
|
let originalRange = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
// Inside scripts, we don't need to restrain the insertion of code inside a specific zone as it will be
|
|
84
|
+
// restricted to the area of the script tag by default
|
|
85
|
+
if (!isInsideScript) {
|
|
86
|
+
if (codeFix.fixName === 'import') {
|
|
87
|
+
return (0, CompletionsProvider_1.codeActionChangeToTextEdit)(document, fragment, false, edit);
|
|
88
|
+
}
|
|
89
|
+
if (codeFix.fixName === 'fixMissingFunctionDeclaration') {
|
|
90
|
+
originalRange = (0, utils_2.checkEndOfFileCodeInsert)(originalRange, document);
|
|
91
|
+
}
|
|
68
92
|
}
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
else {
|
|
94
|
+
// Make sure new imports are not added on the file line of the script tag
|
|
95
|
+
if (codeFix.fixName === 'import') {
|
|
96
|
+
const existingLine = (0, documents_1.getLineAtPosition)(document.positionAt(edit.span.start), document.getText());
|
|
97
|
+
const isNewImport = !existingLine.trim().startsWith('import');
|
|
98
|
+
if (!(edit.newText.startsWith('\n') || edit.newText.startsWith('\r\n')) && isNewImport) {
|
|
99
|
+
edit.newText = typescript_1.default.sys.newLine + edit.newText;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
71
102
|
}
|
|
72
103
|
return vscode_languageserver_types_1.TextEdit.replace(originalRange, edit.newText);
|
|
73
104
|
}));
|
|
@@ -78,6 +109,15 @@ class CodeActionsProviderImpl {
|
|
|
78
109
|
codeAction.diagnostics = diagnostics;
|
|
79
110
|
return codeAction;
|
|
80
111
|
}
|
|
112
|
+
function mapScriptTagFixToOriginal(changes, scriptTagSnapshot) {
|
|
113
|
+
return changes.map((change) => {
|
|
114
|
+
change.textChanges.map((edit) => {
|
|
115
|
+
edit.span.start = fragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(edit.span.start)));
|
|
116
|
+
return edit;
|
|
117
|
+
});
|
|
118
|
+
return change;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
81
121
|
}
|
|
82
122
|
getComponentQuickFix(start, end, lang, filePath, formatOptions, tsPreferences) {
|
|
83
123
|
const sourceFile = lang.getProgram()?.getSourceFile(filePath);
|
|
@@ -107,16 +147,35 @@ class CodeActionsProviderImpl {
|
|
|
107
147
|
return (0, lodash_1.flatten)(completion.entries.filter((c) => c.name === name || c.name === suffixedName).map(toFix));
|
|
108
148
|
}
|
|
109
149
|
async organizeSortImports(document, skipDestructiveCodeActions = false, cancellationToken) {
|
|
110
|
-
if (document.astroMeta.frontmatter.state !== 'closed') {
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
113
150
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
114
151
|
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
115
152
|
const fragment = await tsDoc.createFragment();
|
|
116
153
|
if (cancellationToken?.isCancellationRequested) {
|
|
117
154
|
return [];
|
|
118
155
|
}
|
|
119
|
-
|
|
156
|
+
let changes = [];
|
|
157
|
+
if (document.astroMeta.frontmatter.state === 'closed') {
|
|
158
|
+
changes.push(...lang.organizeImports({ fileName: filePath, type: 'file', skipDestructiveCodeActions }, {}, {}));
|
|
159
|
+
}
|
|
160
|
+
document.scriptTags.forEach((scriptTag) => {
|
|
161
|
+
const { filePath: scriptFilePath, snapshot: scriptTagSnapshot } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
|
|
162
|
+
const edits = lang.organizeImports({ fileName: scriptFilePath, type: 'file', skipDestructiveCodeActions }, {}, {});
|
|
163
|
+
edits.forEach((edit) => {
|
|
164
|
+
edit.fileName = tsDoc.filePath;
|
|
165
|
+
edit.textChanges = edit.textChanges
|
|
166
|
+
.map((change) => {
|
|
167
|
+
change.span.start = fragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(change.span.start)));
|
|
168
|
+
return change;
|
|
169
|
+
})
|
|
170
|
+
// Since our last line is a (virtual) export, organize imports will try to rewrite it, so let's only take
|
|
171
|
+
// changes that actually happens inside the script tag
|
|
172
|
+
.filter((change) => {
|
|
173
|
+
return scriptTagSnapshot.isInGenerated(document.positionAt(change.span.start));
|
|
174
|
+
});
|
|
175
|
+
return edit;
|
|
176
|
+
});
|
|
177
|
+
changes.push(...edits);
|
|
178
|
+
});
|
|
120
179
|
const documentChanges = changes.map((change) => {
|
|
121
180
|
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.url, null), change.textChanges.map((edit) => {
|
|
122
181
|
const range = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
@@ -8,6 +8,7 @@ import { ConfigManager } from '../../../core/config';
|
|
|
8
8
|
export interface CompletionItemData extends TextDocumentIdentifier {
|
|
9
9
|
filePath: string;
|
|
10
10
|
offset: number;
|
|
11
|
+
scriptTagIndex: number | undefined;
|
|
11
12
|
originalItem: ts.CompletionEntry;
|
|
12
13
|
}
|
|
13
14
|
export declare class CompletionsProviderImpl implements CompletionsProvider<CompletionItemData> {
|
|
@@ -32,4 +33,4 @@ export declare class CompletionsProviderImpl implements CompletionsProvider<Comp
|
|
|
32
33
|
private getExistingImports;
|
|
33
34
|
private isAstroComponentImport;
|
|
34
35
|
}
|
|
35
|
-
export declare function codeActionChangeToTextEdit(document: AstroDocument, fragment: AstroSnapshotFragment, change: ts.TextChange): TextEdit;
|
|
36
|
+
export declare function codeActionChangeToTextEdit(document: AstroDocument, fragment: AstroSnapshotFragment, isInsideScriptTag: boolean, change: ts.TextChange): TextEdit;
|
|
@@ -65,37 +65,64 @@ class CompletionsProviderImpl {
|
|
|
65
65
|
const html = document.html;
|
|
66
66
|
const offset = document.offsetAt(position);
|
|
67
67
|
const node = html.findNodeAt(offset);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
68
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
69
|
+
let filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
70
|
+
let completions;
|
|
72
71
|
const isCompletionInsideFrontmatter = (0, utils_1.isInsideFrontmatter)(document.getText(), offset);
|
|
73
72
|
const isCompletionInsideExpression = (0, utils_1.isInsideExpression)(document.getText(), node.start, offset);
|
|
74
|
-
// PERF: Getting TS completions is fairly slow and I am currently not sure how to speed it up
|
|
75
|
-
// As such, we'll try to avoid getting them when unneeded, such as when we're doing HTML stuff
|
|
76
|
-
// When at the root of the document TypeScript offer all kinds of completions, because it doesn't know yet that
|
|
77
|
-
// it's JSX and not JS. As such, people who are using Emmet to write their template suffer from a very degraded experience
|
|
78
|
-
// from what they're used to in HTML files (which is instant completions). So let's disable ourselves when we're at the root
|
|
79
|
-
if (!isCompletionInsideFrontmatter && !node.parent && !isCompletionInsideExpression) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
// If the user just typed `<` with nothing else, let's disable ourselves until we're more sure if the user wants TS completions
|
|
83
|
-
if (!isCompletionInsideFrontmatter && node.parent && node.tag === undefined && !isCompletionInsideExpression) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
// If the current node is not a component (aka, it doesn't start with a caps), let's disable ourselves as the user
|
|
87
|
-
// is most likely looking for HTML completions
|
|
88
|
-
if (!isCompletionInsideFrontmatter && !(0, utils_1.isComponentTag)(node) && !isCompletionInsideExpression) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
73
|
const tsPreferences = await this.configManager.getTSPreferences(document);
|
|
92
74
|
const formatOptions = await this.configManager.getTSFormatConfig(document);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
75
|
+
let scriptTagIndex = undefined;
|
|
76
|
+
if (node.tag === 'script') {
|
|
77
|
+
const { filePath: scriptFilePath, offset: scriptOffset, index: scriptIndex, } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
78
|
+
filePath = scriptFilePath;
|
|
79
|
+
scriptTagIndex = scriptIndex;
|
|
80
|
+
completions = lang.getCompletionsAtPosition(scriptFilePath, scriptOffset, {
|
|
81
|
+
...tsPreferences,
|
|
82
|
+
// File extensions are required inside script tags, however TypeScript can't return completions with the `ts`
|
|
83
|
+
// extension, so what we'll do instead is force `minimal` (aka, no extension) and manually add the extensions
|
|
84
|
+
importModuleSpecifierEnding: 'minimal',
|
|
85
|
+
triggerCharacter: validTriggerCharacter,
|
|
86
|
+
}, formatOptions);
|
|
87
|
+
if (completions) {
|
|
88
|
+
// Manually adds file extensions to js and ts files
|
|
89
|
+
completions.entries = completions?.entries.map((comp) => {
|
|
90
|
+
if (comp.kind === typescript_1.ScriptElementKind.scriptElement &&
|
|
91
|
+
(comp.kindModifiers === '.js' || comp.kindModifiers === '.ts')) {
|
|
92
|
+
return {
|
|
93
|
+
...comp,
|
|
94
|
+
name: comp.name + comp.kindModifiers,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return comp;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// PERF: Getting TS completions is fairly slow and I am currently not sure how to speed it up
|
|
105
|
+
// As such, we'll try to avoid getting them when unneeded, such as when we're doing HTML stuff
|
|
106
|
+
// When at the root of the document TypeScript offer all kinds of completions, because it doesn't know yet that
|
|
107
|
+
// it's JSX and not JS. As such, people who are using Emmet to write their template suffer from a very degraded experience
|
|
108
|
+
// from what they're used to in HTML files (which is instant completions). So let's disable ourselves when we're at the root
|
|
109
|
+
if (!isCompletionInsideFrontmatter && !node.parent && !isCompletionInsideExpression) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
// If the user just typed `<` with nothing else, let's disable ourselves until we're more sure if the user wants TS completions
|
|
113
|
+
if (!isCompletionInsideFrontmatter && node.parent && node.tag === undefined && !isCompletionInsideExpression) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
// If the current node is not a component (aka, it doesn't start with a caps), let's disable ourselves as the user
|
|
117
|
+
// is most likely looking for HTML completions
|
|
118
|
+
if (!isCompletionInsideFrontmatter && !(0, utils_1.isComponentTag)(node) && !isCompletionInsideExpression) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
completions = lang.getCompletionsAtPosition(filePath, offset, {
|
|
122
|
+
...tsPreferences,
|
|
123
|
+
triggerCharacter: validTriggerCharacter,
|
|
124
|
+
}, formatOptions);
|
|
125
|
+
}
|
|
99
126
|
if (completions === undefined || completions.entries.length === 0) {
|
|
100
127
|
return null;
|
|
101
128
|
}
|
|
@@ -107,7 +134,7 @@ class CompletionsProviderImpl {
|
|
|
107
134
|
const existingImports = this.getExistingImports(document);
|
|
108
135
|
const completionItems = completions.entries
|
|
109
136
|
.filter(this.isValidCompletion)
|
|
110
|
-
.map((entry) => this.toCompletionItem(fragment, entry, filePath, offset, isCompletionInsideFrontmatter, existingImports))
|
|
137
|
+
.map((entry) => this.toCompletionItem(fragment, entry, filePath, offset, isCompletionInsideFrontmatter, scriptTagIndex, existingImports))
|
|
111
138
|
.filter(utils_3.isNotNullOrUndefined)
|
|
112
139
|
.map((comp) => this.fixTextEditRange(wordRangeStartPosition, comp));
|
|
113
140
|
const completionList = vscode_languageserver_2.CompletionList.create(completionItems, true);
|
|
@@ -136,18 +163,29 @@ class CompletionsProviderImpl {
|
|
|
136
163
|
item.documentation = itemDocumentation;
|
|
137
164
|
}
|
|
138
165
|
const actions = detail?.codeActions;
|
|
166
|
+
const isInsideScriptTag = data.scriptTagIndex !== undefined;
|
|
167
|
+
let scriptTagSnapshot;
|
|
168
|
+
if (isInsideScriptTag) {
|
|
169
|
+
const { snapshot } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, document.scriptTags[data.scriptTagIndex].container);
|
|
170
|
+
scriptTagSnapshot = snapshot;
|
|
171
|
+
}
|
|
139
172
|
if (actions) {
|
|
140
173
|
const edit = [];
|
|
141
174
|
for (const action of actions) {
|
|
142
175
|
for (const change of action.changes) {
|
|
143
|
-
|
|
176
|
+
if (isInsideScriptTag) {
|
|
177
|
+
change.textChanges.forEach((textChange) => {
|
|
178
|
+
textChange.span.start = fragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(textChange.span.start)));
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
edit.push(...change.textChanges.map((textChange) => codeActionChangeToTextEdit(document, fragment, isInsideScriptTag, textChange)));
|
|
144
182
|
}
|
|
145
183
|
}
|
|
146
184
|
item.additionalTextEdits = (item.additionalTextEdits ?? []).concat(edit);
|
|
147
185
|
}
|
|
148
186
|
return item;
|
|
149
187
|
}
|
|
150
|
-
toCompletionItem(fragment, comp, filePath, offset, insideFrontmatter, existingImports) {
|
|
188
|
+
toCompletionItem(fragment, comp, filePath, offset, insideFrontmatter, scriptTagIndex, existingImports) {
|
|
151
189
|
let item = vscode_languageserver_protocol_1.CompletionItem.create(comp.name);
|
|
152
190
|
const isAstroComponent = this.isAstroComponentImport(comp.name);
|
|
153
191
|
const isImport = comp.insertText?.includes('import');
|
|
@@ -194,6 +232,7 @@ class CompletionsProviderImpl {
|
|
|
194
232
|
data: {
|
|
195
233
|
uri: fragment.getURL(),
|
|
196
234
|
filePath,
|
|
235
|
+
scriptTagIndex,
|
|
197
236
|
offset,
|
|
198
237
|
originalItem: comp,
|
|
199
238
|
},
|
|
@@ -273,23 +312,33 @@ class CompletionsProviderImpl {
|
|
|
273
312
|
}
|
|
274
313
|
}
|
|
275
314
|
exports.CompletionsProviderImpl = CompletionsProviderImpl;
|
|
276
|
-
function codeActionChangeToTextEdit(document, fragment, change) {
|
|
315
|
+
function codeActionChangeToTextEdit(document, fragment, isInsideScriptTag, change) {
|
|
277
316
|
change.newText = (0, utils_2.removeAstroComponentSuffix)(change.newText);
|
|
278
|
-
// If we don't have a frontmatter already, create one with the import
|
|
279
|
-
const frontmatterState = document.astroMeta.frontmatter.state;
|
|
280
|
-
if (frontmatterState === null) {
|
|
281
|
-
return vscode_languageserver_1.TextEdit.replace(vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 0)), `---${typescript_1.default.sys.newLine}${change.newText}---${typescript_1.default.sys.newLine}${typescript_1.default.sys.newLine}`);
|
|
282
|
-
}
|
|
283
317
|
const { span } = change;
|
|
284
318
|
let range;
|
|
285
319
|
const virtualRange = (0, utils_2.convertRange)(fragment, span);
|
|
286
320
|
range = (0, documents_1.mapRangeToOriginal)(fragment, virtualRange);
|
|
287
|
-
if (!
|
|
288
|
-
|
|
321
|
+
if (!isInsideScriptTag) {
|
|
322
|
+
// If we don't have a frontmatter already, create one with the import
|
|
323
|
+
const frontmatterState = document.astroMeta.frontmatter.state;
|
|
324
|
+
if (frontmatterState === null) {
|
|
325
|
+
return vscode_languageserver_1.TextEdit.replace(vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 0)), `---${typescript_1.default.sys.newLine}${change.newText}---${typescript_1.default.sys.newLine}${typescript_1.default.sys.newLine}`);
|
|
326
|
+
}
|
|
327
|
+
if (!(0, utils_1.isInsideFrontmatter)(document.getText(), document.offsetAt(range.start))) {
|
|
328
|
+
range = (0, utils_2.ensureFrontmatterInsert)(range, document);
|
|
329
|
+
}
|
|
330
|
+
// First import in a file will wrongly have a newline before it due to how the frontmatter is replaced by a comment
|
|
331
|
+
if (range.start.line === 1 && (change.newText.startsWith('\n') || change.newText.startsWith('\r\n'))) {
|
|
332
|
+
change.newText = change.newText.trimStart();
|
|
333
|
+
}
|
|
289
334
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
335
|
+
else {
|
|
336
|
+
const existingLine = (0, utils_1.getLineAtPosition)(document.positionAt(span.start), document.getText());
|
|
337
|
+
const isNewImport = !existingLine.trim().startsWith('import');
|
|
338
|
+
// Avoid putting new imports on the same line as the script tag opening
|
|
339
|
+
if (!(change.newText.startsWith('\n') || change.newText.startsWith('\r\n')) && isNewImport) {
|
|
340
|
+
change.newText = typescript_1.default.sys.newLine + change.newText;
|
|
341
|
+
}
|
|
293
342
|
}
|
|
294
343
|
return vscode_languageserver_1.TextEdit.replace(range, change.newText);
|
|
295
344
|
}
|
|
@@ -15,7 +15,28 @@ class DefinitionsProviderImpl {
|
|
|
15
15
|
const tsFilePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
16
16
|
const fragmentPosition = mainFragment.getGeneratedPosition(position);
|
|
17
17
|
const fragmentOffset = mainFragment.offsetAt(fragmentPosition);
|
|
18
|
-
|
|
18
|
+
let defs;
|
|
19
|
+
const html = document.html;
|
|
20
|
+
const offset = document.offsetAt(position);
|
|
21
|
+
const node = html.findNodeAt(offset);
|
|
22
|
+
if (node.tag === 'script') {
|
|
23
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath, offset: scriptOffset, } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
24
|
+
defs = lang.getDefinitionAndBoundSpan(scriptFilePath, scriptOffset);
|
|
25
|
+
if (defs) {
|
|
26
|
+
defs.definitions = defs.definitions?.map((def) => {
|
|
27
|
+
const isInSameFile = def.fileName === scriptFilePath;
|
|
28
|
+
def.fileName = isInSameFile ? tsFilePath : def.fileName;
|
|
29
|
+
if (isInSameFile) {
|
|
30
|
+
def.textSpan.start = mainFragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(def.textSpan.start)));
|
|
31
|
+
}
|
|
32
|
+
return def;
|
|
33
|
+
});
|
|
34
|
+
defs.textSpan.start = mainFragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(defs.textSpan.start)));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
defs = lang.getDefinitionAndBoundSpan(tsFilePath, fragmentOffset);
|
|
39
|
+
}
|
|
19
40
|
if (!defs || !defs.definitions) {
|
|
20
41
|
return [];
|
|
21
42
|
}
|
|
@@ -20,24 +20,52 @@ class DiagnosticsProviderImpl {
|
|
|
20
20
|
}
|
|
21
21
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
22
22
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
23
|
+
const fragment = await tsDoc.createFragment();
|
|
24
|
+
let scriptDiagnostics = [];
|
|
25
|
+
document.scriptTags.forEach((scriptTag) => {
|
|
26
|
+
const { filePath: scriptFilePath, snapshot: scriptTagSnapshot } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
|
|
27
|
+
const scriptDiagnostic = [
|
|
28
|
+
...lang.getSyntacticDiagnostics(scriptFilePath),
|
|
29
|
+
...lang.getSuggestionDiagnostics(scriptFilePath),
|
|
30
|
+
...lang.getSemanticDiagnostics(scriptFilePath),
|
|
31
|
+
]
|
|
32
|
+
// We need to duplicate the diagnostic creation here because we can't map TS's diagnostics range to the original
|
|
33
|
+
// file due to some internal cache inside TS that would cause it to being mapped twice in some cases
|
|
34
|
+
.map((diagnostic) => ({
|
|
35
|
+
range: (0, utils_1.convertRange)(scriptTagSnapshot, diagnostic),
|
|
36
|
+
severity: (0, utils_1.mapSeverity)(diagnostic.category),
|
|
37
|
+
source: 'ts',
|
|
38
|
+
message: typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
|
39
|
+
code: diagnostic.code,
|
|
40
|
+
tags: getDiagnosticTag(diagnostic),
|
|
41
|
+
}))
|
|
42
|
+
.map(mapRange(scriptTagSnapshot, document));
|
|
43
|
+
scriptDiagnostics.push(...scriptDiagnostic);
|
|
44
|
+
});
|
|
23
45
|
const { script: scriptBoundaries } = this.getTagBoundaries(lang, filePath);
|
|
24
46
|
const syntaxDiagnostics = lang.getSyntacticDiagnostics(filePath);
|
|
25
47
|
const suggestionDiagnostics = lang.getSuggestionDiagnostics(filePath);
|
|
26
|
-
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath)
|
|
27
|
-
|
|
48
|
+
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath);
|
|
49
|
+
const diagnostics = [
|
|
50
|
+
...syntaxDiagnostics,
|
|
51
|
+
...suggestionDiagnostics,
|
|
52
|
+
...semanticDiagnostics,
|
|
53
|
+
].filter((diag) => {
|
|
54
|
+
return isNoWithinBoundary(scriptBoundaries, diag);
|
|
28
55
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
return [
|
|
57
|
+
...diagnostics
|
|
58
|
+
.map((diagnostic) => ({
|
|
59
|
+
range: (0, utils_1.convertRange)(tsDoc, diagnostic),
|
|
60
|
+
severity: (0, utils_1.mapSeverity)(diagnostic.category),
|
|
61
|
+
source: 'ts',
|
|
62
|
+
message: typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
|
63
|
+
code: diagnostic.code,
|
|
64
|
+
tags: getDiagnosticTag(diagnostic),
|
|
65
|
+
}))
|
|
66
|
+
.map(mapRange(fragment, document)),
|
|
67
|
+
...scriptDiagnostics,
|
|
68
|
+
]
|
|
41
69
|
.filter((diag) => {
|
|
42
70
|
return (hasNoNegativeLines(diag) &&
|
|
43
71
|
isNoJSXImplicitRuntimeWarning(diag) &&
|
|
@@ -46,6 +74,7 @@ class DiagnosticsProviderImpl {
|
|
|
46
74
|
isNoSpreadExpected(diag) &&
|
|
47
75
|
isNoCantResolveJSONModule(diag) &&
|
|
48
76
|
isNoCantReturnOutsideFunction(diag) &&
|
|
77
|
+
isNoIsolatedModuleError(diag) &&
|
|
49
78
|
isNoJsxCannotHaveMultipleAttrsError(diag));
|
|
50
79
|
})
|
|
51
80
|
.map(enhanceIfNecessary);
|
|
@@ -146,12 +175,26 @@ function isNoCantReturnOutsideFunction(diagnostic) {
|
|
|
146
175
|
function isNoCantResolveJSONModule(diagnostic) {
|
|
147
176
|
return diagnostic.code !== 2732;
|
|
148
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* When the content of the file is invalid and can't be parsed properly for TSX generation, TS will show an error about
|
|
180
|
+
* how the current module can't be compiled under --isolatedModule, this is confusing to users so let's ignore this
|
|
181
|
+
*/
|
|
182
|
+
function isNoIsolatedModuleError(diagnostic) {
|
|
183
|
+
return diagnostic.code !== 1208;
|
|
184
|
+
}
|
|
149
185
|
/**
|
|
150
186
|
* Some diagnostics have JSX-specific nomenclature or unclear description. Enhance them for more clarity.
|
|
151
187
|
*/
|
|
152
188
|
function enhanceIfNecessary(diagnostic) {
|
|
189
|
+
// JSX element has no closing tag. JSX -> HTML
|
|
190
|
+
if (diagnostic.code === 17008) {
|
|
191
|
+
return {
|
|
192
|
+
...diagnostic,
|
|
193
|
+
message: diagnostic.message.replace('JSX', 'HTML'),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
153
197
|
if (diagnostic.code === 2322) {
|
|
154
|
-
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
155
198
|
if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
|
|
156
199
|
return {
|
|
157
200
|
...diagnostic,
|
|
@@ -15,15 +15,22 @@ class FoldingRangesProviderImpl {
|
|
|
15
15
|
const html = document.html;
|
|
16
16
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
17
17
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
18
|
-
const outliningSpans = lang.getOutliningSpans(filePath)
|
|
19
|
-
const foldingRanges = [];
|
|
20
|
-
for (const span of outliningSpans) {
|
|
18
|
+
const outliningSpans = lang.getOutliningSpans(filePath).filter((span) => {
|
|
21
19
|
const node = html.findNodeAt(span.textSpan.start);
|
|
22
20
|
// Due to how our TSX output transform those tags into function calls or template literals
|
|
23
21
|
// TypeScript thinks of those as outlining spans, which is fine but we don't want folding ranges for those
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
return node.tag !== 'script' && node.tag !== 'style' && node.tag !== 'Markdown';
|
|
23
|
+
});
|
|
24
|
+
const scriptOutliningSpans = [];
|
|
25
|
+
document.scriptTags.forEach((scriptTag) => {
|
|
26
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
|
|
27
|
+
scriptOutliningSpans.push(...lang.getOutliningSpans(scriptFilePath).map((span) => {
|
|
28
|
+
span.textSpan.start = document.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(span.textSpan.start)));
|
|
29
|
+
return span;
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
const foldingRanges = [];
|
|
33
|
+
for (const span of [...outliningSpans, ...scriptOutliningSpans]) {
|
|
27
34
|
const start = document.positionAt(span.textSpan.start);
|
|
28
35
|
const end = adjustFoldingEnd(start, document.positionAt(span.textSpan.start + span.textSpan.length), document);
|
|
29
36
|
// When using this method for generating folding ranges, TypeScript tend to return some
|
|
@@ -18,7 +18,20 @@ class HoverProviderImpl {
|
|
|
18
18
|
const fragment = await tsDoc.createFragment();
|
|
19
19
|
const offset = fragment.offsetAt(fragment.getGeneratedPosition(position));
|
|
20
20
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
21
|
-
|
|
21
|
+
const html = document.html;
|
|
22
|
+
const documentOffset = document.offsetAt(position);
|
|
23
|
+
const node = html.findNodeAt(documentOffset);
|
|
24
|
+
let info;
|
|
25
|
+
if (node.tag === 'script') {
|
|
26
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath, offset: scriptOffset, } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
27
|
+
info = lang.getQuickInfoAtPosition(scriptFilePath, scriptOffset);
|
|
28
|
+
if (info) {
|
|
29
|
+
info.textSpan.start = fragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(info.textSpan.start)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
info = lang.getQuickInfoAtPosition(filePath, offset);
|
|
34
|
+
}
|
|
22
35
|
if (!info) {
|
|
23
36
|
return null;
|
|
24
37
|
}
|
|
@@ -20,8 +20,16 @@ class SignatureHelpProviderImpl {
|
|
|
20
20
|
}
|
|
21
21
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
22
22
|
const offset = fragment.offsetAt(fragment.getGeneratedPosition(position));
|
|
23
|
+
const node = document.html.findNodeAt(offset);
|
|
24
|
+
let info;
|
|
23
25
|
const triggerReason = this.toTsTriggerReason(context);
|
|
24
|
-
|
|
26
|
+
if (node.tag === 'script') {
|
|
27
|
+
const { filePath: scriptFilePath, offset: scriptOffset } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
28
|
+
info = lang.getSignatureHelpItems(scriptFilePath, scriptOffset, triggerReason ? { triggerReason } : undefined);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
info = lang.getSignatureHelpItems(filePath, offset, triggerReason ? { triggerReason } : undefined);
|
|
32
|
+
}
|
|
25
33
|
if (!info) {
|
|
26
34
|
return null;
|
|
27
35
|
}
|
|
@@ -33,6 +33,7 @@ const utils_1 = require("../../utils");
|
|
|
33
33
|
const module_loader_1 = require("./module-loader");
|
|
34
34
|
const SnapshotManager_1 = require("./snapshots/SnapshotManager");
|
|
35
35
|
const utils_2 = require("./utils");
|
|
36
|
+
const DocumentSnapshot_1 = require("./snapshots/DocumentSnapshot");
|
|
36
37
|
const DocumentSnapshotUtils = __importStar(require("./snapshots/utils"));
|
|
37
38
|
const services = new Map();
|
|
38
39
|
async function getLanguageService(path, workspaceUris, docContext) {
|
|
@@ -140,6 +141,12 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
140
141
|
}
|
|
141
142
|
const newSnapshot = DocumentSnapshotUtils.createFromDocument(document);
|
|
142
143
|
snapshotManager.set(filePath, newSnapshot);
|
|
144
|
+
document.scriptTags.forEach((scriptTag, index) => {
|
|
145
|
+
const scriptFilePath = filePath + `.__script${index}.js`;
|
|
146
|
+
const scriptSnapshot = new DocumentSnapshot_1.ScriptTagDocumentSnapshot(scriptTag, document, scriptFilePath);
|
|
147
|
+
snapshotManager.set(scriptFilePath, scriptSnapshot);
|
|
148
|
+
newSnapshot.scriptTagSnapshots?.push(scriptSnapshot);
|
|
149
|
+
});
|
|
143
150
|
if (prevSnapshot && prevSnapshot.scriptKind !== newSnapshot.scriptKind) {
|
|
144
151
|
// Restart language service as it doesn't handle script kind changes.
|
|
145
152
|
languageService.dispose();
|
|
@@ -166,6 +173,16 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
166
173
|
astroModuleLoader.deleteUnresolvedResolutionsFromCache(fileName);
|
|
167
174
|
doc = DocumentSnapshotUtils.createFromFilePath(fileName, docContext.createDocument);
|
|
168
175
|
snapshotManager.set(fileName, doc);
|
|
176
|
+
// If we needed to create an Astro snapshot, also create its script tags snapshots
|
|
177
|
+
if ((0, utils_2.isAstroFilePath)(fileName)) {
|
|
178
|
+
const document = doc.parent;
|
|
179
|
+
document.scriptTags.forEach((scriptTag, index) => {
|
|
180
|
+
const scriptFilePath = fileName + `.__script${index}.js`;
|
|
181
|
+
const scriptSnapshot = new DocumentSnapshot_1.ScriptTagDocumentSnapshot(scriptTag, document, scriptFilePath);
|
|
182
|
+
snapshotManager.set(scriptFilePath, scriptSnapshot);
|
|
183
|
+
doc.scriptTagSnapshots?.push(scriptSnapshot);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
169
186
|
return doc;
|
|
170
187
|
}
|
|
171
188
|
function updateProjectFiles() {
|
|
@@ -214,6 +231,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
214
231
|
jsxFactory: 'astroHTML',
|
|
215
232
|
module: typescript_1.default.ModuleKind.ESNext,
|
|
216
233
|
target: typescript_1.default.ScriptTarget.ESNext,
|
|
234
|
+
isolatedModules: true,
|
|
217
235
|
moduleResolution: typescript_1.default.ModuleResolutionKind.NodeJs,
|
|
218
236
|
};
|
|
219
237
|
const project = typescript_1.default.parseJsonConfigFileContent(configJson, typescript_1.default.sys, tsconfigRoot, forcedCompilerOptions, tsconfigPath, undefined, [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { Position, TextDocumentContentChangeEvent } from 'vscode-languageserver';
|
|
3
|
-
import { AstroDocument, DocumentMapper, IdentityMapper } from '../../../core/documents';
|
|
3
|
+
import { AstroDocument, DocumentMapper, IdentityMapper, FragmentMapper, TagInformation } from '../../../core/documents';
|
|
4
4
|
import { FrameworkExt } from '../utils';
|
|
5
5
|
export interface DocumentSnapshot extends ts.IScriptSnapshot {
|
|
6
6
|
version: number;
|
|
@@ -36,11 +36,12 @@ export interface SnapshotFragment extends DocumentMapper {
|
|
|
36
36
|
* Snapshots used for Astro files
|
|
37
37
|
*/
|
|
38
38
|
export declare class AstroSnapshot implements DocumentSnapshot {
|
|
39
|
-
|
|
39
|
+
readonly parent: AstroDocument;
|
|
40
40
|
private readonly text;
|
|
41
41
|
readonly scriptKind: ts.ScriptKind;
|
|
42
42
|
private fragment?;
|
|
43
43
|
version: number;
|
|
44
|
+
scriptTagSnapshots: ScriptTagDocumentSnapshot[];
|
|
44
45
|
constructor(parent: AstroDocument, text: string, scriptKind: ts.ScriptKind);
|
|
45
46
|
createFragment(): Promise<AstroSnapshotFragment>;
|
|
46
47
|
destroyFragment(): null;
|
|
@@ -65,6 +66,25 @@ export declare class AstroSnapshotFragment implements SnapshotFragment {
|
|
|
65
66
|
isInGenerated(pos: Position): boolean;
|
|
66
67
|
getURL(): string;
|
|
67
68
|
}
|
|
69
|
+
export declare class ScriptTagDocumentSnapshot extends FragmentMapper implements DocumentSnapshot, SnapshotFragment {
|
|
70
|
+
scriptTag: TagInformation;
|
|
71
|
+
private readonly parent;
|
|
72
|
+
filePath: string;
|
|
73
|
+
readonly version: number;
|
|
74
|
+
private text;
|
|
75
|
+
scriptKind: ts.ScriptKind;
|
|
76
|
+
private lineOffsets?;
|
|
77
|
+
constructor(scriptTag: TagInformation, parent: AstroDocument, filePath: string);
|
|
78
|
+
positionAt(offset: number): Position;
|
|
79
|
+
offsetAt(position: Position): number;
|
|
80
|
+
createFragment(): Promise<SnapshotFragment>;
|
|
81
|
+
destroyFragment(): void;
|
|
82
|
+
getText(start: number, end: number): string;
|
|
83
|
+
getLength(): number;
|
|
84
|
+
getFullText(): string;
|
|
85
|
+
getChangeRange(): undefined;
|
|
86
|
+
private getLineOffsets;
|
|
87
|
+
}
|
|
68
88
|
/**
|
|
69
89
|
* Snapshot used for anything that is not an Astro file
|
|
70
90
|
* It's both used for .js(x)/.ts(x) files and .svelte/.vue files
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TypeScriptDocumentSnapshot = exports.AstroSnapshotFragment = exports.AstroSnapshot = void 0;
|
|
6
|
+
exports.TypeScriptDocumentSnapshot = exports.ScriptTagDocumentSnapshot = exports.AstroSnapshotFragment = exports.AstroSnapshot = void 0;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
4
8
|
const documents_1 = require("../../../core/documents");
|
|
5
9
|
const utils_1 = require("../../../utils");
|
|
6
10
|
const utils_2 = require("../utils");
|
|
@@ -13,6 +17,7 @@ class AstroSnapshot {
|
|
|
13
17
|
this.text = text;
|
|
14
18
|
this.scriptKind = scriptKind;
|
|
15
19
|
this.version = this.parent.version;
|
|
20
|
+
this.scriptTagSnapshots = [];
|
|
16
21
|
}
|
|
17
22
|
async createFragment() {
|
|
18
23
|
if (!this.fragment) {
|
|
@@ -72,6 +77,48 @@ class AstroSnapshotFragment {
|
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
exports.AstroSnapshotFragment = AstroSnapshotFragment;
|
|
80
|
+
class ScriptTagDocumentSnapshot extends documents_1.FragmentMapper {
|
|
81
|
+
constructor(scriptTag, parent, filePath) {
|
|
82
|
+
super(parent.getText(), scriptTag, filePath);
|
|
83
|
+
this.scriptTag = scriptTag;
|
|
84
|
+
this.parent = parent;
|
|
85
|
+
this.filePath = filePath;
|
|
86
|
+
this.version = this.parent.version;
|
|
87
|
+
this.text = this.parent.getText().slice(this.scriptTag.start, this.scriptTag.end) + '\nexport {}';
|
|
88
|
+
this.scriptKind = typescript_1.default.ScriptKind.JS;
|
|
89
|
+
}
|
|
90
|
+
positionAt(offset) {
|
|
91
|
+
return (0, documents_1.positionAt)(offset, this.text, this.getLineOffsets());
|
|
92
|
+
}
|
|
93
|
+
offsetAt(position) {
|
|
94
|
+
return (0, documents_1.offsetAt)(position, this.text, this.getLineOffsets());
|
|
95
|
+
}
|
|
96
|
+
async createFragment() {
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
destroyFragment() {
|
|
100
|
+
//
|
|
101
|
+
}
|
|
102
|
+
getText(start, end) {
|
|
103
|
+
return this.text.substring(start, end);
|
|
104
|
+
}
|
|
105
|
+
getLength() {
|
|
106
|
+
return this.text.length;
|
|
107
|
+
}
|
|
108
|
+
getFullText() {
|
|
109
|
+
return this.text;
|
|
110
|
+
}
|
|
111
|
+
getChangeRange() {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
getLineOffsets() {
|
|
115
|
+
if (!this.lineOffsets) {
|
|
116
|
+
this.lineOffsets = (0, documents_1.getLineOffsets)(this.text);
|
|
117
|
+
}
|
|
118
|
+
return this.lineOffsets;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.ScriptTagDocumentSnapshot = ScriptTagDocumentSnapshot;
|
|
75
122
|
/**
|
|
76
123
|
* Snapshot used for anything that is not an Astro file
|
|
77
124
|
* It's both used for .js(x)/.ts(x) files and .svelte/.vue files
|
|
@@ -187,6 +187,7 @@ class SnapshotManager {
|
|
|
187
187
|
const projectFiles = this.getProjectFileNames();
|
|
188
188
|
let allFiles = Array.from(new Set([...projectFiles, ...this.getFileNames()]));
|
|
189
189
|
allFiles = allFiles.map((file) => (0, utils_2.ensureRealFilePath)(file));
|
|
190
|
+
// eslint-disable-next-line no-console
|
|
190
191
|
console.log('SnapshotManager File Statistics:\n' +
|
|
191
192
|
`Project files: ${projectFiles.length}\n` +
|
|
192
193
|
`Astro files: ${allFiles.filter((name) => name.endsWith('.astro')).length}\n` +
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { CompletionItemKind, DiagnosticSeverity, Position, Range, SymbolKind, SemanticTokensLegend } from 'vscode-languageserver';
|
|
3
3
|
import { AstroDocument } from '../../core/documents';
|
|
4
|
-
import { SnapshotFragment } from './snapshots/DocumentSnapshot';
|
|
4
|
+
import { AstroSnapshot, ScriptTagDocumentSnapshot, SnapshotFragment } from './snapshots/DocumentSnapshot';
|
|
5
|
+
import { Node } from 'vscode-html-languageservice';
|
|
5
6
|
export declare const enum TokenType {
|
|
6
7
|
class = 0,
|
|
7
8
|
enum = 1,
|
|
@@ -58,4 +59,13 @@ export declare function toVirtualFilePath(filePath: string): string;
|
|
|
58
59
|
export declare function toRealAstroFilePath(filePath: string): string;
|
|
59
60
|
export declare function ensureRealAstroFilePath(filePath: string): string;
|
|
60
61
|
export declare function ensureRealFilePath(filePath: string): string;
|
|
62
|
+
export declare function getScriptTagSnapshot(snapshot: AstroSnapshot, document: AstroDocument, tagInfo: Node | {
|
|
63
|
+
start: number;
|
|
64
|
+
end: number;
|
|
65
|
+
}, position?: Position): {
|
|
66
|
+
snapshot: ScriptTagDocumentSnapshot;
|
|
67
|
+
filePath: string;
|
|
68
|
+
index: number;
|
|
69
|
+
offset: number;
|
|
70
|
+
};
|
|
61
71
|
export {};
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ensureRealFilePath = exports.ensureRealAstroFilePath = exports.toRealAstroFilePath = exports.toVirtualFilePath = exports.toVirtualAstroFilePath = exports.isVirtualFilePath = exports.isVirtualSvelteFilePath = exports.isVirtualVueFilePath = exports.isVirtualAstroFilePath = exports.isFrameworkFilePath = exports.isAstroFilePath = exports.isVirtualFrameworkFilePath = exports.getFrameworkFromFilePath = exports.removeAstroComponentSuffix = exports.checkEndOfFileCodeInsert = exports.ensureFrontmatterInsert = exports.convertToLocationRange = exports.convertRange = exports.mapSeverity = exports.getScriptKindFromFileName = exports.isSubPath = exports.findTsConfigPath = exports.getExtensionFromScriptKind = exports.getCommitCharactersForScriptElement = exports.scriptElementKindToCompletionItemKind = exports.symbolKindFromString = exports.getSemanticTokenLegend = void 0;
|
|
6
|
+
exports.getScriptTagSnapshot = exports.ensureRealFilePath = exports.ensureRealAstroFilePath = exports.toRealAstroFilePath = exports.toVirtualFilePath = exports.toVirtualAstroFilePath = exports.isVirtualFilePath = exports.isVirtualSvelteFilePath = exports.isVirtualVueFilePath = exports.isVirtualAstroFilePath = exports.isFrameworkFilePath = exports.isAstroFilePath = exports.isVirtualFrameworkFilePath = exports.getFrameworkFromFilePath = exports.removeAstroComponentSuffix = exports.checkEndOfFileCodeInsert = exports.ensureFrontmatterInsert = exports.convertToLocationRange = exports.convertRange = exports.mapSeverity = exports.getScriptKindFromFileName = exports.isSubPath = exports.findTsConfigPath = exports.getExtensionFromScriptKind = exports.getCommitCharactersForScriptElement = exports.scriptElementKindToCompletionItemKind = exports.symbolKindFromString = exports.getSemanticTokenLegend = void 0;
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
const utils_1 = require("../../utils");
|
|
@@ -346,3 +346,19 @@ function ensureRealFilePath(filePath) {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
exports.ensureRealFilePath = ensureRealFilePath;
|
|
349
|
+
function getScriptTagSnapshot(snapshot, document, tagInfo, position) {
|
|
350
|
+
const index = document.scriptTags.findIndex((value) => value.container.start == tagInfo.start);
|
|
351
|
+
const scriptFilePath = snapshot.filePath + `.__script${index}.js`;
|
|
352
|
+
const scriptTagSnapshot = snapshot.scriptTagSnapshots[index];
|
|
353
|
+
let offset = 0;
|
|
354
|
+
if (position) {
|
|
355
|
+
offset = scriptTagSnapshot.offsetAt(scriptTagSnapshot.getGeneratedPosition(position));
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
snapshot: scriptTagSnapshot,
|
|
359
|
+
filePath: scriptFilePath,
|
|
360
|
+
index,
|
|
361
|
+
offset,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
exports.getScriptTagSnapshot = getScriptTagSnapshot;
|