@astrojs/language-server 0.13.2 → 0.14.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 +25 -0
- package/dist/core/documents/DocumentMapper.d.ts +2 -1
- package/dist/core/documents/DocumentMapper.js +8 -1
- package/dist/core/documents/utils.d.ts +1 -0
- package/dist/core/documents/utils.js +2 -0
- package/dist/plugins/PluginHost.d.ts +2 -1
- package/dist/plugins/PluginHost.js +4 -0
- package/dist/plugins/astro/AstroPlugin.d.ts +1 -1
- package/dist/plugins/astro/AstroPlugin.js +1 -1
- package/dist/plugins/astro/features/CompletionsProvider.d.ts +5 -6
- package/dist/plugins/astro/features/CompletionsProvider.js +50 -59
- package/dist/plugins/css/CSSPlugin.d.ts +2 -1
- package/dist/plugins/css/CSSPlugin.js +27 -28
- package/dist/plugins/css/features/astro-selectors.js +1 -2
- package/dist/plugins/html/HTMLPlugin.d.ts +1 -0
- package/dist/plugins/html/HTMLPlugin.js +8 -3
- package/dist/plugins/html/features/astro-attributes.d.ts +1 -0
- package/dist/plugins/html/features/astro-attributes.js +67 -3
- package/dist/plugins/html/utils.d.ts +6 -0
- package/dist/plugins/html/utils.js +11 -0
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +3 -1
- package/dist/plugins/typescript/TypeScriptPlugin.js +8 -0
- package/dist/plugins/typescript/astro2tsx.js +5 -1
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +44 -58
- package/dist/plugins/typescript/features/DocumentSymbolsProvider.js +6 -3
- package/dist/plugins/typescript/features/SemanticTokenProvider.d.ts +15 -0
- package/dist/plugins/typescript/features/SemanticTokenProvider.js +81 -0
- package/dist/plugins/typescript/language-service.d.ts +1 -1
- package/dist/plugins/typescript/language-service.js +44 -23
- package/dist/plugins/typescript/snapshots/SnapshotManager.js +2 -1
- package/dist/plugins/typescript/utils.d.ts +24 -1
- package/dist/plugins/typescript/utils.js +32 -1
- package/dist/server.js +27 -2
- package/dist/utils.d.ts +8 -5
- package/dist/utils.js +34 -16
- package/package.json +3 -3
- package/types/astro-jsx.d.ts +112 -113
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeDataAttrCompletion = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The VS Code HTML language service provides a completion for data attributes that is independent from
|
|
6
|
+
* data providers, which mean that you can't disable it, so this function removes them from completions
|
|
7
|
+
*/
|
|
8
|
+
function removeDataAttrCompletion(items) {
|
|
9
|
+
return items.filter((item) => !item.label.startsWith('data-'));
|
|
10
|
+
}
|
|
11
|
+
exports.removeDataAttrCompletion = removeDataAttrCompletion;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationToken, CompletionContext, DefinitionLink, Diagnostic, Hover, Position, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
|
|
1
|
+
import { CancellationToken, CompletionContext, DefinitionLink, Diagnostic, Hover, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
|
|
2
2
|
import { ConfigManager } from '../../core/config';
|
|
3
3
|
import { AstroDocument, DocumentManager } from '../../core/documents';
|
|
4
4
|
import { AppCompletionItem, AppCompletionList, OnWatchFileChangesParam, Plugin } from '../interfaces';
|
|
@@ -12,9 +12,11 @@ export declare class TypeScriptPlugin implements Plugin {
|
|
|
12
12
|
private readonly signatureHelpProvider;
|
|
13
13
|
private readonly diagnosticsProvider;
|
|
14
14
|
private readonly documentSymbolsProvider;
|
|
15
|
+
private readonly semanticTokensProvider;
|
|
15
16
|
constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
|
|
16
17
|
doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
|
|
17
18
|
rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
19
|
+
getSemanticTokens(textDocument: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
18
20
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
19
21
|
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null>;
|
|
20
22
|
resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionEntryWithIdentifer>): Promise<AppCompletionItem<CompletionEntryWithIdentifer>>;
|
|
@@ -36,6 +36,7 @@ const utils_2 = require("./features/utils");
|
|
|
36
36
|
const LanguageServiceManager_1 = require("./LanguageServiceManager");
|
|
37
37
|
const utils_3 = require("./utils");
|
|
38
38
|
const DocumentSymbolsProvider_1 = require("./features/DocumentSymbolsProvider");
|
|
39
|
+
const SemanticTokenProvider_1 = require("./features/SemanticTokenProvider");
|
|
39
40
|
class TypeScriptPlugin {
|
|
40
41
|
constructor(docManager, configManager, workspaceUris) {
|
|
41
42
|
this.__name = 'typescript';
|
|
@@ -46,6 +47,7 @@ class TypeScriptPlugin {
|
|
|
46
47
|
this.signatureHelpProvider = new SignatureHelpProvider_1.SignatureHelpProviderImpl(this.languageServiceManager);
|
|
47
48
|
this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
|
|
48
49
|
this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
|
|
50
|
+
this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
|
|
49
51
|
}
|
|
50
52
|
async doHover(document, position) {
|
|
51
53
|
if (!this.featureEnabled('hover')) {
|
|
@@ -76,6 +78,12 @@ class TypeScriptPlugin {
|
|
|
76
78
|
});
|
|
77
79
|
return edit;
|
|
78
80
|
}
|
|
81
|
+
async getSemanticTokens(textDocument, range, cancellationToken) {
|
|
82
|
+
if (!this.featureEnabled('semanticTokens')) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return this.semanticTokensProvider.getSemanticTokens(textDocument, range, cancellationToken);
|
|
86
|
+
}
|
|
79
87
|
async getDocumentSymbols(document) {
|
|
80
88
|
if (!this.featureEnabled('documentSymbols')) {
|
|
81
89
|
return [];
|
|
@@ -38,9 +38,13 @@ function default_1(content) {
|
|
|
38
38
|
.replace(/<\s*!--([^-->]*)(.*?)-->/gs, (whole) => {
|
|
39
39
|
return `{/*${whole}*/}`;
|
|
40
40
|
})
|
|
41
|
-
// Turn styles into internal strings
|
|
41
|
+
// Turn styles tags into internal strings
|
|
42
42
|
.replace(/<\s*style([^>]*)>(.*?)<\s*\/\s*style>/gs, (_whole, attrs, children) => {
|
|
43
43
|
return `<style${attrs}>{\`${escapeTemplateLiteralContent(children)}\`}</style>`;
|
|
44
|
+
})
|
|
45
|
+
// Turn Markdown tags into internal strings
|
|
46
|
+
.replace(/<\s*Markdown([^>]*)>(.*?)<\s*\/\s*Markdown>/gs, (_whole, attrs, children) => {
|
|
47
|
+
return `<Markdown${attrs}>{\`${escapeTemplateLiteralContent(children)}\`}</Markdown>`;
|
|
44
48
|
})
|
|
45
49
|
// Turn scripts into function calls
|
|
46
50
|
.replace(/<\s*script([^\/>]*)>(.*?)<\s*\/\s*script>/gs, (_whole, attrs, children, offset) => {
|
|
@@ -13,7 +13,7 @@ class DiagnosticsProviderImpl {
|
|
|
13
13
|
this.languageServiceManager = languageServiceManager;
|
|
14
14
|
}
|
|
15
15
|
async getDiagnostics(document, _cancellationToken) {
|
|
16
|
-
var _a, _b
|
|
16
|
+
var _a, _b;
|
|
17
17
|
// Don't return diagnostics for files inside node_modules. These are considered read-only
|
|
18
18
|
// and they would pollute the output for astro check
|
|
19
19
|
if (((_a = document.getFilePath()) === null || _a === void 0 ? void 0 : _a.includes('/node_modules/')) || ((_b = document.getFilePath()) === null || _b === void 0 ? void 0 : _b.includes('\\node_modules\\'))) {
|
|
@@ -21,16 +21,14 @@ class DiagnosticsProviderImpl {
|
|
|
21
21
|
}
|
|
22
22
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
23
23
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
24
|
-
const { script: scriptBoundaries
|
|
24
|
+
const { script: scriptBoundaries } = this.getTagBoundaries(lang, filePath);
|
|
25
25
|
const syntaxDiagnostics = lang.getSyntacticDiagnostics(filePath);
|
|
26
26
|
const suggestionDiagnostics = lang.getSuggestionDiagnostics(filePath);
|
|
27
27
|
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath).filter((d) => {
|
|
28
|
-
return
|
|
28
|
+
return isNoWithinBoundary(scriptBoundaries, d);
|
|
29
29
|
});
|
|
30
30
|
const diagnostics = [...syntaxDiagnostics, ...suggestionDiagnostics, ...semanticDiagnostics];
|
|
31
31
|
const fragment = await tsDoc.createFragment();
|
|
32
|
-
const sourceFile = (_c = lang.getProgram()) === null || _c === void 0 ? void 0 : _c.getSourceFile(filePath);
|
|
33
|
-
const isNoFalsePositiveInst = isNoFalsePositive();
|
|
34
32
|
return diagnostics
|
|
35
33
|
.map((diagnostic) => ({
|
|
36
34
|
range: (0, utils_1.convertRange)(tsDoc, diagnostic),
|
|
@@ -43,14 +41,13 @@ class DiagnosticsProviderImpl {
|
|
|
43
41
|
.map(mapRange(fragment, document))
|
|
44
42
|
.filter((diag) => {
|
|
45
43
|
return (hasNoNegativeLines(diag) &&
|
|
46
|
-
isNoFalsePositiveInst(diag) &&
|
|
47
44
|
isNoJSXImplicitRuntimeWarning(diag) &&
|
|
48
45
|
isNoJSXMustHaveOneParent(diag) &&
|
|
49
|
-
isNoCantUseJSX(diag) &&
|
|
50
46
|
isNoCantEndWithTS(diag) &&
|
|
51
47
|
isNoSpreadExpected(diag) &&
|
|
52
48
|
isNoCantResolveJSONModule(diag) &&
|
|
53
|
-
|
|
49
|
+
isNoCantReturnOutsideFunction(diag) &&
|
|
50
|
+
isNoJsxCannotHaveMultipleAttrsError(diag));
|
|
54
51
|
})
|
|
55
52
|
.map(enhanceIfNecessary);
|
|
56
53
|
}
|
|
@@ -64,7 +61,7 @@ class DiagnosticsProviderImpl {
|
|
|
64
61
|
if (!sourceFile) {
|
|
65
62
|
return boundaries;
|
|
66
63
|
}
|
|
67
|
-
function
|
|
64
|
+
function findTags(parent) {
|
|
68
65
|
typescript_1.default.forEachChild(parent, (node) => {
|
|
69
66
|
if (typescript_1.default.isJsxElement(node)) {
|
|
70
67
|
let tagName = node.openingElement.tagName.getText();
|
|
@@ -80,10 +77,10 @@ class DiagnosticsProviderImpl {
|
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
|
-
|
|
80
|
+
findTags(node);
|
|
84
81
|
});
|
|
85
82
|
}
|
|
86
|
-
|
|
83
|
+
findTags(sourceFile);
|
|
87
84
|
return boundaries;
|
|
88
85
|
}
|
|
89
86
|
}
|
|
@@ -116,33 +113,55 @@ function mapRange(fragment, _document) {
|
|
|
116
113
|
function hasNoNegativeLines(diagnostic) {
|
|
117
114
|
return diagnostic.range.start.line >= 0 && diagnostic.range.end.line >= 0;
|
|
118
115
|
}
|
|
119
|
-
function isNoFalsePositive() {
|
|
120
|
-
return (diagnostic) => {
|
|
121
|
-
return isNoJsxCannotHaveMultipleAttrsError(diagnostic);
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
116
|
/**
|
|
125
|
-
*
|
|
126
|
-
* but that's allowed for svelte
|
|
117
|
+
* Astro allows multiple attributes to have the same name
|
|
127
118
|
*/
|
|
128
119
|
function isNoJsxCannotHaveMultipleAttrsError(diagnostic) {
|
|
129
120
|
return diagnostic.code !== 17001;
|
|
130
121
|
}
|
|
131
|
-
|
|
132
|
-
return diagnostic.code !== 7016 && diagnostic.code !== 2792;
|
|
133
|
-
}
|
|
122
|
+
/** Astro allows component with multiple root elements */
|
|
134
123
|
function isNoJSXMustHaveOneParent(diagnostic) {
|
|
135
124
|
return diagnostic.code !== 2657;
|
|
136
125
|
}
|
|
137
|
-
|
|
138
|
-
return diagnostic.code !== 17004 && diagnostic.code !== 6142;
|
|
139
|
-
}
|
|
126
|
+
/** Astro allows `.ts` ending for imports, unlike TypeScript */
|
|
140
127
|
function isNoCantEndWithTS(diagnostic) {
|
|
141
128
|
return diagnostic.code !== 2691;
|
|
142
129
|
}
|
|
143
130
|
function isNoSpreadExpected(diagnostic) {
|
|
144
131
|
return diagnostic.code !== 1005;
|
|
145
132
|
}
|
|
133
|
+
function isNoJSXImplicitRuntimeWarning(diagnostic) {
|
|
134
|
+
return diagnostic.code !== 7016 && diagnostic.code !== 2792;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Ignore "Can't return outside of function body"
|
|
138
|
+
* This is technically a valid diagnostic, but due to how we format our TSX, the frontmatter is at top-level so we have
|
|
139
|
+
* to ignore this. It wasn't a problem before because users didn't need to return things but they can now with SSR
|
|
140
|
+
*/
|
|
141
|
+
function isNoCantReturnOutsideFunction(diagnostic) {
|
|
142
|
+
return diagnostic.code !== 1108;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Astro allows users to import JSON modules
|
|
146
|
+
*/
|
|
147
|
+
function isNoCantResolveJSONModule(diagnostic) {
|
|
148
|
+
return diagnostic.code !== 2732;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Some diagnostics have JSX-specific nomenclature or unclear description. Enhance them for more clarity.
|
|
152
|
+
*/
|
|
153
|
+
function enhanceIfNecessary(diagnostic) {
|
|
154
|
+
if (diagnostic.code === 2322) {
|
|
155
|
+
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
156
|
+
if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
|
|
157
|
+
return {
|
|
158
|
+
...diagnostic,
|
|
159
|
+
message: 'Client directives are only available on framework components',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return diagnostic;
|
|
164
|
+
}
|
|
146
165
|
function isWithinBoundaries(boundaries, start) {
|
|
147
166
|
for (let [bstart, bend] of boundaries) {
|
|
148
167
|
if (start > bstart && start < bend) {
|
|
@@ -163,39 +182,6 @@ function diagnosticIsWithinBoundaries(sourceFile, boundaries, diagnostic) {
|
|
|
163
182
|
let pos = typescript_1.default.getPositionOfLineAndCharacter(sourceFile, startRange.line, startRange.character);
|
|
164
183
|
return isWithinBoundaries(boundaries, pos);
|
|
165
184
|
}
|
|
166
|
-
function
|
|
185
|
+
function isNoWithinBoundary(boundaries, diagnostic) {
|
|
167
186
|
return !diagnosticIsWithinBoundaries(undefined, boundaries, diagnostic);
|
|
168
187
|
}
|
|
169
|
-
/**
|
|
170
|
-
* This allows us to have JSON module imports.
|
|
171
|
-
*/
|
|
172
|
-
function isNoCantResolveJSONModule(diagnostic) {
|
|
173
|
-
return diagnostic.code !== 2732;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* This is for using > within a markdown component like:
|
|
177
|
-
* <Markdown>
|
|
178
|
-
* > Blockquote here.
|
|
179
|
-
* </Markdown>
|
|
180
|
-
*/
|
|
181
|
-
function isNoMarkdownBlockQuoteWithinMarkdown(sourceFile, boundaries, diagnostic) {
|
|
182
|
-
if (diagnostic.code !== 1382) {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
return !diagnosticIsWithinBoundaries(sourceFile, boundaries, diagnostic);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Some diagnostics have JSX-specific nomenclature. Enhance them for more clarity.
|
|
189
|
-
*/
|
|
190
|
-
function enhanceIfNecessary(diagnostic) {
|
|
191
|
-
if (diagnostic.code === 2322) {
|
|
192
|
-
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
193
|
-
if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
|
|
194
|
-
return {
|
|
195
|
-
...diagnostic,
|
|
196
|
-
message: 'Client directives are only available on framework components',
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return diagnostic;
|
|
201
|
-
}
|
|
@@ -20,17 +20,20 @@ class DocumentSymbolsProviderImpl {
|
|
|
20
20
|
}
|
|
21
21
|
const symbols = [];
|
|
22
22
|
this.collectSymbols(navTree, fragment, undefined, (symbol) => symbols.push(symbol));
|
|
23
|
+
const originalContainerName = symbols[0].name;
|
|
23
24
|
const result = [];
|
|
24
25
|
// Add a "Frontmatter" namespace for the frontmatter if we have a closed one
|
|
25
26
|
if (document.astroMeta.frontmatter.state === 'closed') {
|
|
26
|
-
result.push(vscode_languageserver_types_1.SymbolInformation.create('Frontmatter', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt(document.astroMeta.frontmatter.startOffset), document.positionAt(document.astroMeta.frontmatter.endOffset))));
|
|
27
|
+
result.push(vscode_languageserver_types_1.SymbolInformation.create('Frontmatter', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt(document.astroMeta.frontmatter.startOffset), document.positionAt(document.astroMeta.frontmatter.endOffset)), document.getURL()));
|
|
27
28
|
}
|
|
28
29
|
// Add a "Template" namespace for everything under the frontmatter
|
|
29
|
-
result.push(vscode_languageserver_types_1.SymbolInformation.create('Template', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt((_a = document.astroMeta.frontmatter.endOffset) !== null && _a !== void 0 ? _a : 0), document.positionAt(document.getTextLength()))));
|
|
30
|
+
result.push(vscode_languageserver_types_1.SymbolInformation.create('Template', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt((_a = document.astroMeta.frontmatter.endOffset) !== null && _a !== void 0 ? _a : 0), document.positionAt(document.getTextLength())), document.getURL()));
|
|
30
31
|
for (let symbol of symbols.splice(1)) {
|
|
31
32
|
symbol = (0, documents_1.mapSymbolInformationToOriginal)(fragment, symbol);
|
|
32
33
|
if (document.offsetAt(symbol.location.range.end) >= ((_b = document.astroMeta.content.firstNonWhitespaceOffset) !== null && _b !== void 0 ? _b : 0)) {
|
|
33
|
-
symbol.containerName
|
|
34
|
+
if (symbol.containerName === originalContainerName) {
|
|
35
|
+
symbol.containerName = 'Template';
|
|
36
|
+
}
|
|
34
37
|
// For some reason, it seems like TypeScript thinks that the "class" attribute is a real class, weird
|
|
35
38
|
if (symbol.kind === vscode_languageserver_types_1.SymbolKind.Class && symbol.name === '<class>') {
|
|
36
39
|
const node = document.html.findNodeAt(document.offsetAt(symbol.location.range.start));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CancellationToken, Range, SemanticTokens } from 'vscode-languageserver';
|
|
2
|
+
import { AstroDocument } from '../../../core/documents';
|
|
3
|
+
import { SemanticTokensProvider } from '../../interfaces';
|
|
4
|
+
import { LanguageServiceManager } from '../LanguageServiceManager';
|
|
5
|
+
export declare class SemanticTokensProviderImpl implements SemanticTokensProvider {
|
|
6
|
+
private languageServiceManager;
|
|
7
|
+
constructor(languageServiceManager: LanguageServiceManager);
|
|
8
|
+
getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
9
|
+
private mapToOrigin;
|
|
10
|
+
/**
|
|
11
|
+
* TSClassification = (TokenType + 1) << TokenEncodingConsts.typeOffset + TokenModifier
|
|
12
|
+
*/
|
|
13
|
+
private getTokenTypeFromClassification;
|
|
14
|
+
private getTokenModifierFromClassification;
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SemanticTokensProviderImpl = void 0;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
9
|
+
const documents_1 = require("../../../core/documents");
|
|
10
|
+
const utils_1 = require("../utils");
|
|
11
|
+
class SemanticTokensProviderImpl {
|
|
12
|
+
constructor(languageServiceManager) {
|
|
13
|
+
this.languageServiceManager = languageServiceManager;
|
|
14
|
+
}
|
|
15
|
+
async getSemanticTokens(document, range, cancellationToken) {
|
|
16
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
17
|
+
const fragment = (await tsDoc.createFragment());
|
|
18
|
+
if (cancellationToken === null || cancellationToken === void 0 ? void 0 : cancellationToken.isCancellationRequested) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
22
|
+
const start = range ? fragment.offsetAt(fragment.getGeneratedPosition(range.start)) : 0;
|
|
23
|
+
const { spans } = lang.getEncodedSemanticClassifications(filePath, {
|
|
24
|
+
start,
|
|
25
|
+
length: range
|
|
26
|
+
? fragment.offsetAt(fragment.getGeneratedPosition(range.end)) - start
|
|
27
|
+
: // We don't want tokens for things added by astro2tsx
|
|
28
|
+
fragment.text.lastIndexOf('export default function (_props:') || fragment.text.length,
|
|
29
|
+
}, typescript_1.default.SemanticClassificationFormat.TwentyTwenty);
|
|
30
|
+
const tokens = [];
|
|
31
|
+
let i = 0;
|
|
32
|
+
while (i < spans.length) {
|
|
33
|
+
const offset = spans[i++];
|
|
34
|
+
const generatedLength = spans[i++];
|
|
35
|
+
const classification = spans[i++];
|
|
36
|
+
const originalPosition = this.mapToOrigin(document, fragment, offset, generatedLength);
|
|
37
|
+
if (!originalPosition) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const [line, character, length] = originalPosition;
|
|
41
|
+
const classificationType = this.getTokenTypeFromClassification(classification);
|
|
42
|
+
if (classificationType < 0) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const modifier = this.getTokenModifierFromClassification(classification);
|
|
46
|
+
tokens.push([line, character, length, classificationType, modifier]);
|
|
47
|
+
}
|
|
48
|
+
const sorted = tokens.sort((a, b) => {
|
|
49
|
+
const [lineA, charA] = a;
|
|
50
|
+
const [lineB, charB] = b;
|
|
51
|
+
return lineA - lineB || charA - charB;
|
|
52
|
+
});
|
|
53
|
+
const builder = new vscode_languageserver_1.SemanticTokensBuilder();
|
|
54
|
+
sorted.forEach((tokenData) => builder.push(...tokenData));
|
|
55
|
+
const build = builder.build();
|
|
56
|
+
return build;
|
|
57
|
+
}
|
|
58
|
+
mapToOrigin(document, fragment, generatedOffset, generatedLength) {
|
|
59
|
+
const range = {
|
|
60
|
+
start: fragment.positionAt(generatedOffset),
|
|
61
|
+
end: fragment.positionAt(generatedOffset + generatedLength),
|
|
62
|
+
};
|
|
63
|
+
const { start: startPosition, end: endPosition } = (0, documents_1.mapRangeToOriginal)(fragment, range);
|
|
64
|
+
if (startPosition.line < 0 || endPosition.line < 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const startOffset = document.offsetAt(startPosition);
|
|
68
|
+
const endOffset = document.offsetAt(endPosition);
|
|
69
|
+
return [startPosition.line, startPosition.character, endOffset - startOffset, startOffset];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* TSClassification = (TokenType + 1) << TokenEncodingConsts.typeOffset + TokenModifier
|
|
73
|
+
*/
|
|
74
|
+
getTokenTypeFromClassification(tsClassification) {
|
|
75
|
+
return (tsClassification >> 8 /* typeOffset */) - 1;
|
|
76
|
+
}
|
|
77
|
+
getTokenModifierFromClassification(tsClassification) {
|
|
78
|
+
return tsClassification & 255 /* modifierMask */;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.SemanticTokensProviderImpl = SemanticTokensProviderImpl;
|
|
@@ -35,4 +35,4 @@ export declare function forAllLanguageServices(cb: (service: LanguageServiceCont
|
|
|
35
35
|
* @param tsconfigPath has to be absolute
|
|
36
36
|
* @param docContext
|
|
37
37
|
*/
|
|
38
|
-
export declare function getLanguageServiceForTsconfig(tsconfigPath: string, docContext: LanguageServiceDocumentContext): Promise<LanguageServiceContainer>;
|
|
38
|
+
export declare function getLanguageServiceForTsconfig(tsconfigPath: string, docContext: LanguageServiceDocumentContext, workspaceUris: string[]): Promise<LanguageServiceContainer>;
|
|
@@ -37,7 +37,7 @@ const DocumentSnapshotUtils = __importStar(require("./snapshots/utils"));
|
|
|
37
37
|
const services = new Map();
|
|
38
38
|
async function getLanguageService(path, workspaceUris, docContext) {
|
|
39
39
|
const tsconfigPath = (0, utils_2.findTsConfigPath)(path, workspaceUris);
|
|
40
|
-
return getLanguageServiceForTsconfig(tsconfigPath, docContext);
|
|
40
|
+
return getLanguageServiceForTsconfig(tsconfigPath, docContext, workspaceUris);
|
|
41
41
|
}
|
|
42
42
|
exports.getLanguageService = getLanguageService;
|
|
43
43
|
async function forAllLanguageServices(cb) {
|
|
@@ -50,34 +50,44 @@ exports.forAllLanguageServices = forAllLanguageServices;
|
|
|
50
50
|
* @param tsconfigPath has to be absolute
|
|
51
51
|
* @param docContext
|
|
52
52
|
*/
|
|
53
|
-
async function getLanguageServiceForTsconfig(tsconfigPath, docContext) {
|
|
53
|
+
async function getLanguageServiceForTsconfig(tsconfigPath, docContext, workspaceUris) {
|
|
54
54
|
let service;
|
|
55
55
|
if (services.has(tsconfigPath)) {
|
|
56
56
|
service = await services.get(tsconfigPath);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
const newService = createLanguageService(tsconfigPath, docContext);
|
|
59
|
+
const newService = createLanguageService(tsconfigPath, docContext, workspaceUris);
|
|
60
60
|
services.set(tsconfigPath, newService);
|
|
61
61
|
service = await newService;
|
|
62
62
|
}
|
|
63
63
|
return service;
|
|
64
64
|
}
|
|
65
65
|
exports.getLanguageServiceForTsconfig = getLanguageServiceForTsconfig;
|
|
66
|
-
async function createLanguageService(tsconfigPath, docContext) {
|
|
67
|
-
const
|
|
66
|
+
async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
67
|
+
const tsconfigRoot = tsconfigPath ? (0, path_1.dirname)(tsconfigPath) : process.cwd();
|
|
68
|
+
const workspacePaths = workspaceUris.map((uri) => (0, utils_1.urlToPath)(uri));
|
|
69
|
+
const workspacePath = workspacePaths.find((uri) => tsconfigRoot.startsWith(uri)) || workspacePaths[0];
|
|
70
|
+
const astroVersion = (0, utils_1.getUserAstroVersion)(workspacePath);
|
|
68
71
|
// `raw` here represent the tsconfig merged with any extended config
|
|
69
72
|
const { compilerOptions, fileNames: files, raw: fullConfig } = getParsedTSConfig();
|
|
70
73
|
let projectVersion = 0;
|
|
71
|
-
const snapshotManager = new SnapshotManager_1.SnapshotManager(docContext.globalSnapshotManager, files, fullConfig,
|
|
74
|
+
const snapshotManager = new SnapshotManager_1.SnapshotManager(docContext.globalSnapshotManager, files, fullConfig, tsconfigRoot || process.cwd());
|
|
72
75
|
const astroModuleLoader = (0, module_loader_1.createAstroModuleLoader)(getScriptSnapshot, compilerOptions);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const scriptFileNames = [];
|
|
77
|
+
// Before Astro 1.0, JSX definitions were inside of the language-server instead of inside Astro
|
|
78
|
+
// TODO: Remove this and astro-jsx.d.ts in types when we consider having support for Astro < 1.0 unnecessary
|
|
79
|
+
if (astroVersion.major === 0 || astroVersion.full === '1.0.0-beta.0') {
|
|
80
|
+
let languageServerDirectory;
|
|
81
|
+
try {
|
|
82
|
+
languageServerDirectory = (0, path_1.dirname)(require.resolve('@astrojs/language-server'));
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
languageServerDirectory = __dirname;
|
|
86
|
+
}
|
|
87
|
+
const astroTSXFile = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/astro-jsx.d.ts'));
|
|
88
|
+
scriptFileNames.push(astroTSXFile);
|
|
89
|
+
console.warn("Version lower than 1.0 detected, using internal types instead of Astro's");
|
|
79
90
|
}
|
|
80
|
-
const astroTSXFile = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/astro-jsx.d.ts'));
|
|
81
91
|
const host = {
|
|
82
92
|
getNewLine: () => typescript_1.default.sys.newLine,
|
|
83
93
|
useCaseSensitiveFileNames: () => typescript_1.default.sys.useCaseSensitiveFileNames,
|
|
@@ -87,10 +97,10 @@ async function createLanguageService(tsconfigPath, docContext) {
|
|
|
87
97
|
fileExists: astroModuleLoader.fileExists,
|
|
88
98
|
readDirectory: astroModuleLoader.readDirectory,
|
|
89
99
|
getCompilationSettings: () => compilerOptions,
|
|
90
|
-
getCurrentDirectory: () =>
|
|
100
|
+
getCurrentDirectory: () => tsconfigRoot,
|
|
91
101
|
getDefaultLibFileName: typescript_1.default.getDefaultLibFilePath,
|
|
92
102
|
getProjectVersion: () => projectVersion.toString(),
|
|
93
|
-
getScriptFileNames: () => Array.from(new Set([...snapshotManager.getProjectFileNames(), ...snapshotManager.getFileNames(),
|
|
103
|
+
getScriptFileNames: () => Array.from(new Set([...snapshotManager.getProjectFileNames(), ...snapshotManager.getFileNames(), ...scriptFileNames])),
|
|
94
104
|
getScriptSnapshot,
|
|
95
105
|
getScriptVersion: (fileName) => getScriptSnapshot(fileName).version.toString(),
|
|
96
106
|
};
|
|
@@ -176,17 +186,24 @@ async function createLanguageService(tsconfigPath, docContext) {
|
|
|
176
186
|
snapshotManager.updateNonAstroFile(fileName, changes);
|
|
177
187
|
}
|
|
178
188
|
function getParsedTSConfig() {
|
|
179
|
-
var _a, _b, _c;
|
|
189
|
+
var _a, _b, _c, _d;
|
|
180
190
|
let configJson = (tsconfigPath && typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile).config) || {};
|
|
181
191
|
// If our user has types in their config but it doesn't include the types needed for Astro, add them to the config
|
|
182
|
-
if ((
|
|
183
|
-
configJson.compilerOptions.types.
|
|
192
|
+
if ((_a = configJson.compilerOptions) === null || _a === void 0 ? void 0 : _a.types) {
|
|
193
|
+
if (!((_b = configJson.compilerOptions) === null || _b === void 0 ? void 0 : _b.types.includes('astro/env'))) {
|
|
194
|
+
configJson.compilerOptions.types.push('astro/env');
|
|
195
|
+
}
|
|
196
|
+
if (astroVersion.major >= 1 &&
|
|
197
|
+
astroVersion.full !== '1.0.0-beta.0' &&
|
|
198
|
+
!((_c = configJson.compilerOptions) === null || _c === void 0 ? void 0 : _c.types.includes('astro/astro-jsx'))) {
|
|
199
|
+
configJson.compilerOptions.types.push('astro/astro-jsx');
|
|
200
|
+
}
|
|
184
201
|
}
|
|
185
|
-
configJson.compilerOptions = Object.assign(getDefaultCompilerOptions(), configJson.compilerOptions);
|
|
202
|
+
configJson.compilerOptions = Object.assign(getDefaultCompilerOptions(astroVersion), configJson.compilerOptions);
|
|
186
203
|
// Delete include so that .astro files don't get mistakenly excluded by the user
|
|
187
204
|
delete configJson.include;
|
|
188
205
|
// If the user supplied exclude, let's use theirs otherwise, use ours
|
|
189
|
-
(
|
|
206
|
+
(_d = configJson.exclude) !== null && _d !== void 0 ? _d : (configJson.exclude = getDefaultExclude());
|
|
190
207
|
// Everything here will always, unconditionally, be in the resulting config
|
|
191
208
|
const forcedCompilerOptions = {
|
|
192
209
|
noEmit: true,
|
|
@@ -200,7 +217,7 @@ async function createLanguageService(tsconfigPath, docContext) {
|
|
|
200
217
|
target: typescript_1.default.ScriptTarget.ESNext,
|
|
201
218
|
moduleResolution: typescript_1.default.ModuleResolutionKind.NodeJs,
|
|
202
219
|
};
|
|
203
|
-
const project = typescript_1.default.parseJsonConfigFileContent(configJson, typescript_1.default.sys,
|
|
220
|
+
const project = typescript_1.default.parseJsonConfigFileContent(configJson, typescript_1.default.sys, tsconfigRoot, forcedCompilerOptions, tsconfigPath, undefined, [
|
|
204
221
|
{ extension: '.vue', isMixedContent: true, scriptKind: typescript_1.default.ScriptKind.Deferred },
|
|
205
222
|
{ extension: '.svelte', isMixedContent: true, scriptKind: typescript_1.default.ScriptKind.Deferred },
|
|
206
223
|
{ extension: '.astro', isMixedContent: true, scriptKind: typescript_1.default.ScriptKind.Deferred },
|
|
@@ -218,11 +235,15 @@ async function createLanguageService(tsconfigPath, docContext) {
|
|
|
218
235
|
/**
|
|
219
236
|
* Default configuration used as a base and when the user doesn't have any
|
|
220
237
|
*/
|
|
221
|
-
function getDefaultCompilerOptions() {
|
|
238
|
+
function getDefaultCompilerOptions(astroVersion) {
|
|
239
|
+
const types = ['astro/env'];
|
|
240
|
+
if (astroVersion.major >= 1 && astroVersion.full !== '1.0.0-beta.0') {
|
|
241
|
+
types.push('astro/astro-jsx');
|
|
242
|
+
}
|
|
222
243
|
return {
|
|
223
244
|
maxNodeModuleJsDepth: 2,
|
|
224
245
|
allowSyntheticDefaultImports: true,
|
|
225
|
-
types:
|
|
246
|
+
types: types,
|
|
226
247
|
};
|
|
227
248
|
}
|
|
228
249
|
function getDefaultExclude() {
|
|
@@ -185,7 +185,8 @@ class SnapshotManager {
|
|
|
185
185
|
if (date.getTime() - this.lastLogged.getTime() > 60000) {
|
|
186
186
|
this.lastLogged = date;
|
|
187
187
|
const projectFiles = this.getProjectFileNames();
|
|
188
|
-
|
|
188
|
+
let allFiles = Array.from(new Set([...projectFiles, ...this.getFileNames()]));
|
|
189
|
+
allFiles = allFiles.map((file) => (0, utils_2.ensureRealFilePath)(file));
|
|
189
190
|
console.log('SnapshotManager File Statistics:\n' +
|
|
190
191
|
`Project files: ${projectFiles.length}\n` +
|
|
191
192
|
`Astro files: ${allFiles.filter((name) => name.endsWith('.astro')).length}\n` +
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import { CompletionItemKind, DiagnosticSeverity, Position, Range, SymbolKind } from 'vscode-languageserver';
|
|
2
|
+
import { CompletionItemKind, DiagnosticSeverity, Position, Range, SymbolKind, SemanticTokensLegend } from 'vscode-languageserver';
|
|
3
3
|
import { SnapshotFragment } from './snapshots/DocumentSnapshot';
|
|
4
|
+
export declare const enum TokenType {
|
|
5
|
+
class = 0,
|
|
6
|
+
enum = 1,
|
|
7
|
+
interface = 2,
|
|
8
|
+
namespace = 3,
|
|
9
|
+
typeParameter = 4,
|
|
10
|
+
type = 5,
|
|
11
|
+
parameter = 6,
|
|
12
|
+
variable = 7,
|
|
13
|
+
enumMember = 8,
|
|
14
|
+
property = 9,
|
|
15
|
+
function = 10,
|
|
16
|
+
method = 11
|
|
17
|
+
}
|
|
18
|
+
export declare const enum TokenModifier {
|
|
19
|
+
declaration = 0,
|
|
20
|
+
static = 1,
|
|
21
|
+
async = 2,
|
|
22
|
+
readonly = 3,
|
|
23
|
+
defaultLibrary = 4,
|
|
24
|
+
local = 5
|
|
25
|
+
}
|
|
26
|
+
export declare function getSemanticTokenLegend(): SemanticTokensLegend;
|
|
4
27
|
export declare function symbolKindFromString(kind: string): SymbolKind;
|
|
5
28
|
export declare function scriptElementKindToCompletionItemKind(kind: ts.ScriptElementKind): CompletionItemKind;
|
|
6
29
|
export declare function getCommitCharactersForScriptElement(kind: ts.ScriptElementKind): string[] | undefined;
|
|
@@ -3,12 +3,43 @@ 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.convertToLocationRange = exports.convertRange = exports.mapSeverity = exports.getScriptKindFromFileName = exports.isSubPath = exports.findTsConfigPath = exports.getExtensionFromScriptKind = exports.getCommitCharactersForScriptElement = exports.scriptElementKindToCompletionItemKind = exports.symbolKindFromString = void 0;
|
|
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.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");
|
|
10
10
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
11
11
|
const documents_1 = require("../../core/documents");
|
|
12
|
+
function getSemanticTokenLegend() {
|
|
13
|
+
const tokenModifiers = [];
|
|
14
|
+
[
|
|
15
|
+
[0 /* declaration */, vscode_languageserver_1.SemanticTokenModifiers.declaration],
|
|
16
|
+
[1 /* static */, vscode_languageserver_1.SemanticTokenModifiers.static],
|
|
17
|
+
[2 /* async */, vscode_languageserver_1.SemanticTokenModifiers.async],
|
|
18
|
+
[3 /* readonly */, vscode_languageserver_1.SemanticTokenModifiers.readonly],
|
|
19
|
+
[4 /* defaultLibrary */, vscode_languageserver_1.SemanticTokenModifiers.defaultLibrary],
|
|
20
|
+
[5 /* local */, 'local'],
|
|
21
|
+
].forEach(([tsModifier, legend]) => (tokenModifiers[tsModifier] = legend));
|
|
22
|
+
const tokenTypes = [];
|
|
23
|
+
[
|
|
24
|
+
[0 /* class */, vscode_languageserver_1.SemanticTokenTypes.class],
|
|
25
|
+
[1 /* enum */, vscode_languageserver_1.SemanticTokenTypes.enum],
|
|
26
|
+
[2 /* interface */, vscode_languageserver_1.SemanticTokenTypes.interface],
|
|
27
|
+
[3 /* namespace */, vscode_languageserver_1.SemanticTokenTypes.namespace],
|
|
28
|
+
[4 /* typeParameter */, vscode_languageserver_1.SemanticTokenTypes.typeParameter],
|
|
29
|
+
[5 /* type */, vscode_languageserver_1.SemanticTokenTypes.type],
|
|
30
|
+
[6 /* parameter */, vscode_languageserver_1.SemanticTokenTypes.parameter],
|
|
31
|
+
[7 /* variable */, vscode_languageserver_1.SemanticTokenTypes.variable],
|
|
32
|
+
[8 /* enumMember */, vscode_languageserver_1.SemanticTokenTypes.enumMember],
|
|
33
|
+
[9 /* property */, vscode_languageserver_1.SemanticTokenTypes.property],
|
|
34
|
+
[10 /* function */, vscode_languageserver_1.SemanticTokenTypes.function],
|
|
35
|
+
[11 /* method */, vscode_languageserver_1.SemanticTokenTypes.method],
|
|
36
|
+
].forEach(([tokenType, legend]) => (tokenTypes[tokenType] = legend));
|
|
37
|
+
return {
|
|
38
|
+
tokenModifiers,
|
|
39
|
+
tokenTypes,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
exports.getSemanticTokenLegend = getSemanticTokenLegend;
|
|
12
43
|
function symbolKindFromString(kind) {
|
|
13
44
|
switch (kind) {
|
|
14
45
|
case 'module':
|