@astrojs/language-server 0.14.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/check.js +1 -2
- package/dist/core/config/ConfigManager.d.ts +20 -16
- package/dist/core/config/ConfigManager.js +112 -46
- package/dist/core/config/interfaces.d.ts +0 -52
- package/dist/core/documents/DocumentMapper.js +2 -4
- package/dist/core/documents/parseAstro.js +1 -1
- package/dist/core/documents/utils.d.ts +5 -0
- package/dist/core/documents/utils.js +18 -5
- package/dist/plugins/PluginHost.d.ts +3 -2
- package/dist/plugins/PluginHost.js +37 -10
- package/dist/plugins/astro/AstroPlugin.d.ts +1 -6
- package/dist/plugins/astro/AstroPlugin.js +0 -82
- package/dist/plugins/astro/features/CompletionsProvider.js +30 -15
- package/dist/plugins/css/CSSPlugin.d.ts +5 -5
- package/dist/plugins/css/CSSPlugin.js +41 -20
- package/dist/plugins/html/HTMLPlugin.d.ts +4 -4
- package/dist/plugins/html/HTMLPlugin.js +20 -16
- package/dist/plugins/html/features/astro-attributes.js +44 -27
- package/dist/plugins/interfaces.d.ts +2 -2
- package/dist/plugins/typescript/LanguageServiceManager.js +1 -1
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +10 -7
- package/dist/plugins/typescript/TypeScriptPlugin.js +39 -112
- package/dist/plugins/typescript/astro-sys.js +3 -5
- package/dist/plugins/typescript/astro2tsx.d.ts +1 -1
- package/dist/plugins/typescript/astro2tsx.js +7 -7
- package/dist/plugins/typescript/features/CodeActionsProvider.d.ts +16 -0
- package/dist/plugins/typescript/features/CodeActionsProvider.js +147 -0
- package/dist/plugins/typescript/features/CompletionsProvider.d.ts +26 -7
- package/dist/plugins/typescript/features/CompletionsProvider.js +260 -56
- package/dist/plugins/typescript/features/DefinitionsProvider.d.ts +9 -0
- package/dist/plugins/typescript/features/DefinitionsProvider.js +36 -0
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +2 -3
- package/dist/plugins/typescript/features/DocumentSymbolsProvider.js +5 -6
- package/dist/plugins/typescript/features/FoldingRangesProvider.d.ts +9 -0
- package/dist/plugins/typescript/features/FoldingRangesProvider.js +64 -0
- package/dist/plugins/typescript/features/SemanticTokenProvider.js +2 -2
- package/dist/plugins/typescript/features/SignatureHelpProvider.js +2 -2
- package/dist/plugins/typescript/features/utils.d.ts +4 -0
- package/dist/plugins/typescript/features/utils.js +25 -3
- package/dist/plugins/typescript/language-service.js +5 -6
- package/dist/plugins/typescript/module-loader.js +1 -1
- package/dist/plugins/typescript/previewer.js +1 -1
- package/dist/plugins/typescript/snapshots/SnapshotManager.js +1 -1
- package/dist/plugins/typescript/snapshots/utils.js +27 -9
- package/dist/plugins/typescript/utils.d.ts +4 -0
- package/dist/plugins/typescript/utils.js +29 -1
- package/dist/server.js +43 -14
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +39 -3
- package/package.json +2 -2
|
@@ -29,7 +29,7 @@ class LanguageServiceManager {
|
|
|
29
29
|
const handleDocumentChange = (document) => {
|
|
30
30
|
this.getSnapshot(document);
|
|
31
31
|
};
|
|
32
|
-
docManager.on('documentChange', (0, utils_1.debounceSameArg)(handleDocumentChange, (newDoc, prevDoc) => newDoc.uri ===
|
|
32
|
+
docManager.on('documentChange', (0, utils_1.debounceSameArg)(handleDocumentChange, (newDoc, prevDoc) => newDoc.uri === prevDoc?.uri, 1000));
|
|
33
33
|
docManager.on('documentOpen', handleDocumentChange);
|
|
34
34
|
}
|
|
35
35
|
async getSnapshot(pathOrDoc) {
|
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
import { CancellationToken, CompletionContext, DefinitionLink, Diagnostic, Hover, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
|
|
1
|
+
import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Diagnostic, FoldingRange, 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';
|
|
5
|
-
import {
|
|
5
|
+
import { CompletionItemData } from './features/CompletionsProvider';
|
|
6
6
|
export declare class TypeScriptPlugin implements Plugin {
|
|
7
7
|
__name: string;
|
|
8
8
|
private configManager;
|
|
9
9
|
private readonly languageServiceManager;
|
|
10
|
+
private readonly codeActionsProvider;
|
|
10
11
|
private readonly completionProvider;
|
|
11
12
|
private readonly hoverProvider;
|
|
13
|
+
private readonly definitionsProvider;
|
|
12
14
|
private readonly signatureHelpProvider;
|
|
13
15
|
private readonly diagnosticsProvider;
|
|
14
16
|
private readonly documentSymbolsProvider;
|
|
15
17
|
private readonly semanticTokensProvider;
|
|
18
|
+
private readonly foldingRangesProvider;
|
|
16
19
|
constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
|
|
17
20
|
doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
|
|
18
21
|
rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
19
|
-
|
|
22
|
+
getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
|
|
23
|
+
getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
20
24
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
getCodeActions(document: AstroDocument, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
|
|
26
|
+
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
|
|
27
|
+
resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
|
|
23
28
|
getDefinitions(document: AstroDocument, position: Position): Promise<DefinitionLink[]>;
|
|
24
29
|
getDiagnostics(document: AstroDocument, cancellationToken?: CancellationToken): Promise<Diagnostic[]>;
|
|
25
30
|
onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesParam[]): Promise<void>;
|
|
26
31
|
updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): Promise<void>;
|
|
27
32
|
getSignatureHelp(document: AstroDocument, position: Position, context: SignatureHelpContext | undefined, cancellationToken?: CancellationToken): Promise<SignatureHelp | null>;
|
|
28
|
-
private goToDefinitionFoundOnlyAlias;
|
|
29
|
-
private getGoToDefinitionRefsForImportSpecifier;
|
|
30
33
|
private featureEnabled;
|
|
31
34
|
}
|
|
@@ -1,56 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
4
|
};
|
|
25
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
6
|
exports.TypeScriptPlugin = void 0;
|
|
27
|
-
const typescript_1 =
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
28
8
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
29
|
-
const path_1 = require("path");
|
|
30
|
-
const utils_1 = require("../../utils");
|
|
31
9
|
const CompletionsProvider_1 = require("./features/CompletionsProvider");
|
|
32
10
|
const DiagnosticsProvider_1 = require("./features/DiagnosticsProvider");
|
|
33
11
|
const HoverProvider_1 = require("./features/HoverProvider");
|
|
34
12
|
const SignatureHelpProvider_1 = require("./features/SignatureHelpProvider");
|
|
35
|
-
const utils_2 = require("./features/utils");
|
|
36
13
|
const LanguageServiceManager_1 = require("./LanguageServiceManager");
|
|
37
|
-
const
|
|
14
|
+
const utils_1 = require("./utils");
|
|
38
15
|
const DocumentSymbolsProvider_1 = require("./features/DocumentSymbolsProvider");
|
|
39
16
|
const SemanticTokenProvider_1 = require("./features/SemanticTokenProvider");
|
|
17
|
+
const FoldingRangesProvider_1 = require("./features/FoldingRangesProvider");
|
|
18
|
+
const CodeActionsProvider_1 = require("./features/CodeActionsProvider");
|
|
19
|
+
const DefinitionsProvider_1 = require("./features/DefinitionsProvider");
|
|
40
20
|
class TypeScriptPlugin {
|
|
41
21
|
constructor(docManager, configManager, workspaceUris) {
|
|
42
22
|
this.__name = 'typescript';
|
|
43
23
|
this.configManager = configManager;
|
|
44
24
|
this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
|
|
45
|
-
this.
|
|
25
|
+
this.codeActionsProvider = new CodeActionsProvider_1.CodeActionsProviderImpl(this.languageServiceManager, this.configManager);
|
|
26
|
+
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager, this.configManager);
|
|
46
27
|
this.hoverProvider = new HoverProvider_1.HoverProviderImpl(this.languageServiceManager);
|
|
28
|
+
this.definitionsProvider = new DefinitionsProvider_1.DefinitionsProviderImpl(this.languageServiceManager);
|
|
47
29
|
this.signatureHelpProvider = new SignatureHelpProvider_1.SignatureHelpProviderImpl(this.languageServiceManager);
|
|
48
30
|
this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
|
|
49
31
|
this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
|
|
50
32
|
this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
|
|
33
|
+
this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
|
|
51
34
|
}
|
|
52
35
|
async doHover(document, position) {
|
|
53
|
-
if (!this.featureEnabled('hover')) {
|
|
36
|
+
if (!(await this.featureEnabled(document, 'hover'))) {
|
|
54
37
|
return null;
|
|
55
38
|
}
|
|
56
39
|
return this.hoverProvider.doHover(document, position);
|
|
@@ -59,7 +42,7 @@ class TypeScriptPlugin {
|
|
|
59
42
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
60
43
|
const fragment = await tsDoc.createFragment();
|
|
61
44
|
const offset = fragment.offsetAt(fragment.getGeneratedPosition(position));
|
|
62
|
-
let renames = lang.findRenameLocations((0,
|
|
45
|
+
let renames = lang.findRenameLocations((0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath), offset, false, false, true);
|
|
63
46
|
if (!renames) {
|
|
64
47
|
return null;
|
|
65
48
|
}
|
|
@@ -67,71 +50,54 @@ class TypeScriptPlugin {
|
|
|
67
50
|
changes: {},
|
|
68
51
|
};
|
|
69
52
|
renames.forEach((rename) => {
|
|
70
|
-
const filePath = (0,
|
|
53
|
+
const filePath = (0, utils_1.ensureRealFilePath)(rename.fileName);
|
|
71
54
|
if (!(filePath in edit.changes)) {
|
|
72
55
|
edit.changes[filePath] = [];
|
|
73
56
|
}
|
|
74
57
|
edit.changes[filePath].push({
|
|
75
58
|
newText: newName,
|
|
76
|
-
range: (0,
|
|
59
|
+
range: (0, utils_1.convertToLocationRange)(fragment, rename.textSpan),
|
|
77
60
|
});
|
|
78
61
|
});
|
|
79
62
|
return edit;
|
|
80
63
|
}
|
|
81
|
-
async
|
|
82
|
-
|
|
64
|
+
async getFoldingRanges(document) {
|
|
65
|
+
return this.foldingRangesProvider.getFoldingRanges(document);
|
|
66
|
+
}
|
|
67
|
+
async getSemanticTokens(document, range, cancellationToken) {
|
|
68
|
+
if (!(await this.featureEnabled(document, 'semanticTokens'))) {
|
|
83
69
|
return null;
|
|
84
70
|
}
|
|
85
|
-
return this.semanticTokensProvider.getSemanticTokens(
|
|
71
|
+
return this.semanticTokensProvider.getSemanticTokens(document, range, cancellationToken);
|
|
86
72
|
}
|
|
87
73
|
async getDocumentSymbols(document) {
|
|
88
|
-
if (!this.featureEnabled('documentSymbols')) {
|
|
74
|
+
if (!(await this.featureEnabled(document, 'documentSymbols'))) {
|
|
89
75
|
return [];
|
|
90
76
|
}
|
|
91
77
|
const symbols = await this.documentSymbolsProvider.getDocumentSymbols(document);
|
|
92
78
|
return symbols;
|
|
93
79
|
}
|
|
94
|
-
async
|
|
95
|
-
if (!this.featureEnabled('
|
|
80
|
+
async getCodeActions(document, range, context, cancellationToken) {
|
|
81
|
+
if (!(await this.featureEnabled(document, 'codeActions'))) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
return this.codeActionsProvider.getCodeActions(document, range, context, cancellationToken);
|
|
85
|
+
}
|
|
86
|
+
async getCompletions(document, position, completionContext, cancellationToken) {
|
|
87
|
+
if (!(await this.featureEnabled(document, 'completions'))) {
|
|
96
88
|
return null;
|
|
97
89
|
}
|
|
98
|
-
const completions = await this.completionProvider.getCompletions(document, position, completionContext);
|
|
90
|
+
const completions = await this.completionProvider.getCompletions(document, position, completionContext, cancellationToken);
|
|
99
91
|
return completions;
|
|
100
92
|
}
|
|
101
|
-
async resolveCompletion(document, completionItem) {
|
|
102
|
-
return this.completionProvider.resolveCompletion(document, completionItem);
|
|
93
|
+
async resolveCompletion(document, completionItem, cancellationToken) {
|
|
94
|
+
return this.completionProvider.resolveCompletion(document, completionItem, cancellationToken);
|
|
103
95
|
}
|
|
104
96
|
async getDefinitions(document, position) {
|
|
105
|
-
|
|
106
|
-
const mainFragment = await tsDoc.createFragment();
|
|
107
|
-
const filePath = tsDoc.filePath;
|
|
108
|
-
const tsFilePath = (0, utils_3.toVirtualAstroFilePath)(filePath);
|
|
109
|
-
const fragmentPosition = mainFragment.getGeneratedPosition(position);
|
|
110
|
-
const fragmentOffset = mainFragment.offsetAt(fragmentPosition);
|
|
111
|
-
let defs = lang.getDefinitionAndBoundSpan(tsFilePath, fragmentOffset);
|
|
112
|
-
if (!defs || !defs.definitions) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
// Resolve all imports if we can
|
|
116
|
-
if (this.goToDefinitionFoundOnlyAlias(tsFilePath, defs.definitions)) {
|
|
117
|
-
let importDef = this.getGoToDefinitionRefsForImportSpecifier(tsFilePath, fragmentOffset, lang);
|
|
118
|
-
if (importDef) {
|
|
119
|
-
defs = importDef;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const docs = new utils_2.SnapshotFragmentMap(this.languageServiceManager);
|
|
123
|
-
docs.set(tsDoc.filePath, { fragment: mainFragment, snapshot: tsDoc });
|
|
124
|
-
const result = await Promise.all(defs.definitions.map(async (def) => {
|
|
125
|
-
const { fragment, snapshot } = await docs.retrieve(def.fileName);
|
|
126
|
-
const fileName = (0, utils_3.ensureRealFilePath)(def.fileName);
|
|
127
|
-
// Since we converted our files to TSX and we don't have sourcemaps, we don't know where the function is, unfortunate
|
|
128
|
-
const textSpan = (0, utils_3.isVirtualFilePath)(tsFilePath) ? { start: 0, length: 0 } : def.textSpan;
|
|
129
|
-
return vscode_languageserver_1.LocationLink.create((0, utils_1.pathToUrl)(fileName), (0, utils_3.convertToLocationRange)(fragment, textSpan), (0, utils_3.convertToLocationRange)(fragment, textSpan), (0, utils_3.convertToLocationRange)(mainFragment, defs.textSpan));
|
|
130
|
-
}));
|
|
131
|
-
return result.filter(utils_1.isNotNullOrUndefined);
|
|
97
|
+
return this.definitionsProvider.getDefinitions(document, position);
|
|
132
98
|
}
|
|
133
99
|
async getDiagnostics(document, cancellationToken) {
|
|
134
|
-
if (!this.featureEnabled('diagnostics')) {
|
|
100
|
+
if (!(await this.featureEnabled(document, 'diagnostics'))) {
|
|
135
101
|
return [];
|
|
136
102
|
}
|
|
137
103
|
return this.diagnosticsProvider.getDiagnostics(document, cancellationToken);
|
|
@@ -139,7 +105,7 @@ class TypeScriptPlugin {
|
|
|
139
105
|
async onWatchFileChanges(onWatchFileChangesParas) {
|
|
140
106
|
let doneUpdateProjectFiles = false;
|
|
141
107
|
for (const { fileName, changeType } of onWatchFileChangesParas) {
|
|
142
|
-
const scriptKind = (0,
|
|
108
|
+
const scriptKind = (0, utils_1.getScriptKindFromFileName)(fileName);
|
|
143
109
|
if (scriptKind === typescript_1.default.ScriptKind.Unknown) {
|
|
144
110
|
continue;
|
|
145
111
|
}
|
|
@@ -161,48 +127,9 @@ class TypeScriptPlugin {
|
|
|
161
127
|
async getSignatureHelp(document, position, context, cancellationToken) {
|
|
162
128
|
return this.signatureHelpProvider.getSignatureHelp(document, position, context, cancellationToken);
|
|
163
129
|
}
|
|
164
|
-
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
getGoToDefinitionRefsForImportSpecifier(tsFilePath, offset, lang) {
|
|
168
|
-
const program = lang.getProgram();
|
|
169
|
-
const sourceFile = program === null || program === void 0 ? void 0 : program.getSourceFile(tsFilePath);
|
|
170
|
-
if (sourceFile) {
|
|
171
|
-
let node = typescript_1.default.getTouchingPropertyName(sourceFile, offset);
|
|
172
|
-
if (node && node.kind === typescript_1.SyntaxKind.Identifier) {
|
|
173
|
-
if (node.parent.kind === typescript_1.SyntaxKind.ImportClause) {
|
|
174
|
-
let decl = node.parent.parent;
|
|
175
|
-
let spec = typescript_1.default.isStringLiteral(decl.moduleSpecifier) && decl.moduleSpecifier.text;
|
|
176
|
-
if (spec) {
|
|
177
|
-
let fileName = (0, path_1.join)((0, path_1.dirname)(tsFilePath), spec);
|
|
178
|
-
let start = node.pos + 1;
|
|
179
|
-
let def = {
|
|
180
|
-
definitions: [
|
|
181
|
-
{
|
|
182
|
-
kind: 'alias',
|
|
183
|
-
fileName,
|
|
184
|
-
name: '',
|
|
185
|
-
containerKind: '',
|
|
186
|
-
containerName: '',
|
|
187
|
-
textSpan: {
|
|
188
|
-
start: 0,
|
|
189
|
-
length: 0,
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
textSpan: {
|
|
194
|
-
start,
|
|
195
|
-
length: node.end - start,
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
return def;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
featureEnabled(feature) {
|
|
205
|
-
return (this.configManager.enabled('typescript.enabled') && this.configManager.enabled(`typescript.${feature}.enabled`));
|
|
130
|
+
async featureEnabled(document, feature) {
|
|
131
|
+
return ((await this.configManager.isEnabled(document, 'typescript')) &&
|
|
132
|
+
(await this.configManager.isEnabled(document, 'typescript', feature)));
|
|
206
133
|
}
|
|
207
134
|
}
|
|
208
135
|
exports.TypeScriptPlugin = TypeScriptPlugin;
|
|
@@ -14,9 +14,8 @@ function createAstroSys(getSnapshot) {
|
|
|
14
14
|
const AstroSys = {
|
|
15
15
|
...typescript_1.default.sys,
|
|
16
16
|
fileExists(path) {
|
|
17
|
-
var _a;
|
|
18
17
|
path = (0, utils_1.ensureRealFilePath)(path);
|
|
19
|
-
const exists =
|
|
18
|
+
const exists = fileExistsCache.get(path) ?? typescript_1.default.sys.fileExists(path);
|
|
20
19
|
fileExistsCache.set(path, exists);
|
|
21
20
|
return exists;
|
|
22
21
|
},
|
|
@@ -25,14 +24,13 @@ function createAstroSys(getSnapshot) {
|
|
|
25
24
|
return snapshot.getText(0, snapshot.getLength());
|
|
26
25
|
},
|
|
27
26
|
readDirectory(path, extensions, exclude, include, depth) {
|
|
28
|
-
const extensionsWithAstro = (extensions
|
|
27
|
+
const extensionsWithAstro = (extensions ?? []).concat(...['.astro', '.svelte', '.vue']);
|
|
29
28
|
const result = typescript_1.default.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);
|
|
30
29
|
return result;
|
|
31
30
|
},
|
|
32
31
|
deleteFile(path) {
|
|
33
|
-
var _a, _b;
|
|
34
32
|
fileExistsCache.delete((0, utils_1.ensureRealFilePath)(path));
|
|
35
|
-
return
|
|
33
|
+
return typescript_1.default.sys.deleteFile?.(path);
|
|
36
34
|
},
|
|
37
35
|
deleteFromCache(path) {
|
|
38
36
|
fileExistsCache.delete((0, utils_1.ensureRealFilePath)(path));
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const os_1 = require("os");
|
|
4
4
|
const parseAstro_1 = require("../../core/documents/parseAstro");
|
|
5
|
-
function addProps(content) {
|
|
5
|
+
function addProps(content, className) {
|
|
6
6
|
let defaultExportType = 'Record<string, any>';
|
|
7
7
|
if (/(interface|type) Props/.test(content)) {
|
|
8
8
|
defaultExportType = 'Props';
|
|
9
9
|
}
|
|
10
|
-
return os_1.EOL + `export default function (_props: ${defaultExportType}) {
|
|
10
|
+
return os_1.EOL + `export default function ${className}__AstroComponent_(_props: ${defaultExportType}): any {}`;
|
|
11
11
|
}
|
|
12
12
|
function escapeTemplateLiteralContent(content) {
|
|
13
13
|
return content.replace(/`/g, '\\`');
|
|
14
14
|
}
|
|
15
|
-
function default_1(content) {
|
|
16
|
-
var _a, _b;
|
|
15
|
+
function default_1(content, className) {
|
|
17
16
|
let result = {
|
|
18
17
|
code: '',
|
|
19
18
|
};
|
|
@@ -22,10 +21,11 @@ function default_1(content) {
|
|
|
22
21
|
let frontMatterRaw = '';
|
|
23
22
|
if (astroDocument.frontmatter.state === 'closed') {
|
|
24
23
|
frontMatterRaw = content
|
|
25
|
-
.substring(
|
|
24
|
+
.substring(astroDocument.frontmatter.startOffset ?? 0, (astroDocument.frontmatter.endOffset ?? 0) + 3)
|
|
26
25
|
// Handle case where semicolons is not used in the frontmatter section
|
|
26
|
+
// We need to add something before the semi-colon or TypeScript won't be able to do completions
|
|
27
27
|
.replace(/((?!^)(?<!;)\n)(---)/g, (_whole, start, _dashes) => {
|
|
28
|
-
return start + ';'
|
|
28
|
+
return start + '"";';
|
|
29
29
|
})
|
|
30
30
|
// Replace frontmatter marks with comments
|
|
31
31
|
.replace(/---/g, '///');
|
|
@@ -80,7 +80,7 @@ function default_1(content) {
|
|
|
80
80
|
htmlRaw +
|
|
81
81
|
os_1.EOL +
|
|
82
82
|
// Add TypeScript definitions
|
|
83
|
-
addProps(frontMatterRaw);
|
|
83
|
+
addProps(frontMatterRaw, className);
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
86
|
exports.default = default_1;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CancellationToken } from 'vscode-languageserver';
|
|
2
|
+
import { CodeAction, CodeActionContext, Range } from 'vscode-languageserver-types';
|
|
3
|
+
import { ConfigManager } from '../../../core/config';
|
|
4
|
+
import { AstroDocument } from '../../../core/documents';
|
|
5
|
+
import { CodeActionsProvider } from '../../interfaces';
|
|
6
|
+
import { LanguageServiceManager } from '../LanguageServiceManager';
|
|
7
|
+
export declare const sortImportKind: string;
|
|
8
|
+
export declare class CodeActionsProviderImpl implements CodeActionsProvider {
|
|
9
|
+
private languageServiceManager;
|
|
10
|
+
private configManager;
|
|
11
|
+
constructor(languageServiceManager: LanguageServiceManager, configManager: ConfigManager);
|
|
12
|
+
getCodeActions(document: AstroDocument, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
|
|
13
|
+
private getComponentQuickFix;
|
|
14
|
+
private organizeSortImports;
|
|
15
|
+
private fixIndentationOfImports;
|
|
16
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
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.CodeActionsProviderImpl = exports.sortImportKind = void 0;
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
8
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
9
|
+
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
|
|
10
|
+
const documents_1 = require("../../../core/documents");
|
|
11
|
+
const utils_1 = require("../../../utils");
|
|
12
|
+
const utils_2 = require("../utils");
|
|
13
|
+
const CompletionsProvider_1 = require("./CompletionsProvider");
|
|
14
|
+
const utils_3 = require("./utils");
|
|
15
|
+
// These are VS Code specific CodeActionKind so they're not in the language server protocol
|
|
16
|
+
exports.sortImportKind = `${vscode_languageserver_types_1.CodeActionKind.Source}.sortImports`;
|
|
17
|
+
class CodeActionsProviderImpl {
|
|
18
|
+
constructor(languageServiceManager, configManager) {
|
|
19
|
+
this.languageServiceManager = languageServiceManager;
|
|
20
|
+
this.configManager = configManager;
|
|
21
|
+
}
|
|
22
|
+
async getCodeActions(document, range, context, cancellationToken) {
|
|
23
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
24
|
+
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
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
|
+
const tsPreferences = await this.configManager.getTSPreferences(document);
|
|
29
|
+
const formatOptions = await this.configManager.getTSFormatConfig(document);
|
|
30
|
+
let result = [];
|
|
31
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
if (context.only?.[0] === vscode_languageserver_types_1.CodeActionKind.SourceOrganizeImports) {
|
|
35
|
+
return await this.organizeSortImports(document, false, cancellationToken);
|
|
36
|
+
}
|
|
37
|
+
// The difference between Sort Imports and Organize Imports is that Sort Imports won't do anything destructive.
|
|
38
|
+
// For example, it won't remove unused imports whereas Organize Imports will
|
|
39
|
+
if (context.only?.[0] === exports.sortImportKind) {
|
|
40
|
+
return await this.organizeSortImports(document, true, cancellationToken);
|
|
41
|
+
}
|
|
42
|
+
if (context.only?.[0] === vscode_languageserver_types_1.CodeActionKind.Source) {
|
|
43
|
+
return [
|
|
44
|
+
...(await this.organizeSortImports(document, true, cancellationToken)),
|
|
45
|
+
...(await this.organizeSortImports(document, false, cancellationToken)),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
if (context.diagnostics.length && (!context.only || context.only.includes(vscode_languageserver_types_1.CodeActionKind.QuickFix))) {
|
|
49
|
+
const errorCodes = context.diagnostics
|
|
50
|
+
.map((diag) => Number(diag.code))
|
|
51
|
+
// We currently cannot support quick fix for unreachable code properly due to the way our TSX output is structured
|
|
52
|
+
.filter((code) => code !== 7027);
|
|
53
|
+
let codeFixes = errorCodes.includes(2304)
|
|
54
|
+
? this.getComponentQuickFix(start, end, lang, filePath, formatOptions, tsPreferences)
|
|
55
|
+
: undefined;
|
|
56
|
+
codeFixes =
|
|
57
|
+
codeFixes ?? lang.getCodeFixesAtPosition(filePath, start, end, errorCodes, formatOptions, tsPreferences);
|
|
58
|
+
const codeActions = codeFixes.map((fix) => codeFixToCodeAction(fix, context.diagnostics, context.only ? vscode_languageserver_types_1.CodeActionKind.QuickFix : vscode_languageserver_types_1.CodeActionKind.Empty));
|
|
59
|
+
result.push(...codeActions);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
function codeFixToCodeAction(codeFix, diagnostics, kind) {
|
|
63
|
+
const documentChanges = codeFix.changes.map((change) => {
|
|
64
|
+
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.getURL(), null), change.textChanges.map((edit) => {
|
|
65
|
+
let originalRange = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
66
|
+
if (codeFix.fixName === 'import') {
|
|
67
|
+
return (0, CompletionsProvider_1.codeActionChangeToTextEdit)(document, fragment, edit);
|
|
68
|
+
}
|
|
69
|
+
if (codeFix.fixName === 'fixMissingFunctionDeclaration') {
|
|
70
|
+
originalRange = (0, utils_2.checkEndOfFileCodeInsert)(originalRange, document);
|
|
71
|
+
}
|
|
72
|
+
return vscode_languageserver_types_1.TextEdit.replace(originalRange, edit.newText);
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
const codeAction = vscode_languageserver_types_1.CodeAction.create(codeFix.description, {
|
|
76
|
+
documentChanges,
|
|
77
|
+
}, kind);
|
|
78
|
+
codeAction.diagnostics = diagnostics;
|
|
79
|
+
return codeAction;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
getComponentQuickFix(start, end, lang, filePath, formatOptions, tsPreferences) {
|
|
83
|
+
const sourceFile = lang.getProgram()?.getSourceFile(filePath);
|
|
84
|
+
if (!sourceFile) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const node = (0, utils_3.findContainingNode)(sourceFile, {
|
|
88
|
+
start,
|
|
89
|
+
length: end - start,
|
|
90
|
+
}, (n) => typescript_1.default.isJsxClosingElement(n) || typescript_1.default.isJsxOpeningLikeElement(n));
|
|
91
|
+
if (!node) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const tagName = node.tagName;
|
|
95
|
+
// Unlike quick fixes, completions will be able to find the component, so let's use those to get it
|
|
96
|
+
const completion = lang.getCompletionsAtPosition(filePath, tagName.getEnd(), tsPreferences, formatOptions);
|
|
97
|
+
if (!completion) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const name = tagName.getText();
|
|
101
|
+
const suffixedName = name + '__AstroComponent_';
|
|
102
|
+
const toFix = (c) => lang.getCompletionEntryDetails(filePath, end, c.name, {}, c.source, {}, c.data)?.codeActions?.map((a) => ({
|
|
103
|
+
...a,
|
|
104
|
+
description: (0, utils_2.removeAstroComponentSuffix)(a.description),
|
|
105
|
+
fixName: 'import',
|
|
106
|
+
})) ?? [];
|
|
107
|
+
return (0, lodash_1.flatten)(completion.entries.filter((c) => c.name === name || c.name === suffixedName).map(toFix));
|
|
108
|
+
}
|
|
109
|
+
async organizeSortImports(document, skipDestructiveCodeActions = false, cancellationToken) {
|
|
110
|
+
if (document.astroMeta.frontmatter.state !== 'closed') {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
114
|
+
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
115
|
+
const fragment = await tsDoc.createFragment();
|
|
116
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
const changes = lang.organizeImports({ fileName: filePath, type: 'file', skipDestructiveCodeActions }, {}, {});
|
|
120
|
+
const documentChanges = changes.map((change) => {
|
|
121
|
+
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.url, null), change.textChanges.map((edit) => {
|
|
122
|
+
const range = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
123
|
+
return vscode_languageserver_types_1.TextEdit.replace(range, this.fixIndentationOfImports(edit.newText, range, document));
|
|
124
|
+
}));
|
|
125
|
+
});
|
|
126
|
+
return [
|
|
127
|
+
vscode_languageserver_types_1.CodeAction.create(skipDestructiveCodeActions ? 'Sort Imports' : 'Organize Imports', {
|
|
128
|
+
documentChanges,
|
|
129
|
+
}, skipDestructiveCodeActions ? exports.sortImportKind : vscode_languageserver_types_1.CodeActionKind.SourceOrganizeImports),
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
// "Organize Imports" will have edits that delete all imports by return empty edits
|
|
133
|
+
// and one edit which contains all the organized imports. Fix indentation
|
|
134
|
+
// of that one by prepending all lines with the indentation of the first line.
|
|
135
|
+
fixIndentationOfImports(edit, range, document) {
|
|
136
|
+
if (!edit || range.start.character === 0) {
|
|
137
|
+
return edit;
|
|
138
|
+
}
|
|
139
|
+
const existingLine = (0, documents_1.getLineAtPosition)(range.start, document.getText());
|
|
140
|
+
const leadingChars = existingLine.substring(0, range.start.character);
|
|
141
|
+
if (leadingChars.trim() !== '') {
|
|
142
|
+
return edit;
|
|
143
|
+
}
|
|
144
|
+
return (0, utils_1.modifyLines)(edit, (line, idx) => (idx === 0 || !line ? line : leadingChars + line));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.CodeActionsProviderImpl = CodeActionsProviderImpl;
|
|
@@ -1,16 +1,35 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CompletionContext, Position, TextDocumentIdentifier, TextEdit, CancellationToken } from 'vscode-languageserver';
|
|
2
2
|
import type { LanguageServiceManager } from '../LanguageServiceManager';
|
|
3
3
|
import { AstroDocument } from '../../../core/documents';
|
|
4
4
|
import ts from 'typescript';
|
|
5
5
|
import { AppCompletionItem, AppCompletionList, CompletionsProvider } from '../../interfaces';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { AstroSnapshotFragment } from '../snapshots/DocumentSnapshot';
|
|
7
|
+
import { ConfigManager } from '../../../core/config';
|
|
8
|
+
export interface CompletionItemData extends TextDocumentIdentifier {
|
|
9
|
+
filePath: string;
|
|
10
|
+
offset: number;
|
|
11
|
+
originalItem: ts.CompletionEntry;
|
|
8
12
|
}
|
|
9
|
-
export declare class CompletionsProviderImpl implements CompletionsProvider<
|
|
13
|
+
export declare class CompletionsProviderImpl implements CompletionsProvider<CompletionItemData> {
|
|
10
14
|
private languageServiceManager;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
private configManager;
|
|
16
|
+
constructor(languageServiceManager: LanguageServiceManager, configManager: ConfigManager);
|
|
17
|
+
private readonly validTriggerCharacters;
|
|
18
|
+
private isValidTriggerCharacter;
|
|
19
|
+
private lastCompletion?;
|
|
20
|
+
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
|
|
21
|
+
resolveCompletion(document: AstroDocument, item: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
|
|
14
22
|
private toCompletionItem;
|
|
23
|
+
private isValidCompletion;
|
|
15
24
|
private getCompletionDocument;
|
|
25
|
+
/**
|
|
26
|
+
* If the textEdit is out of the word range of the triggered position
|
|
27
|
+
* vscode would refuse to show the completions
|
|
28
|
+
* split those edits into additionalTextEdit to fix it
|
|
29
|
+
*/
|
|
30
|
+
private fixTextEditRange;
|
|
31
|
+
private canReuseLastCompletion;
|
|
32
|
+
private getExistingImports;
|
|
33
|
+
private isAstroComponentImport;
|
|
16
34
|
}
|
|
35
|
+
export declare function codeActionChangeToTextEdit(document: AstroDocument, fragment: AstroSnapshotFragment, change: ts.TextChange): TextEdit;
|