@astrojs/language-server 0.13.4 → 0.16.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 +43 -0
- package/dist/check.js +1 -2
- 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.js +1 -1
- package/dist/plugins/astro/features/CompletionsProvider.js +30 -15
- package/dist/plugins/css/CSSPlugin.js +20 -4
- package/dist/plugins/html/features/astro-attributes.js +43 -27
- package/dist/plugins/interfaces.d.ts +2 -2
- package/dist/plugins/typescript/LanguageServiceManager.js +1 -1
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +8 -4
- package/dist/plugins/typescript/TypeScriptPlugin.js +18 -5
- package/dist/plugins/typescript/astro-sys.js +3 -5
- package/dist/plugins/typescript/astro2tsx.d.ts +1 -1
- package/dist/plugins/typescript/astro2tsx.js +12 -8
- package/dist/plugins/typescript/features/CodeActionsProvider.d.ts +14 -0
- package/dist/plugins/typescript/features/CodeActionsProvider.js +141 -0
- package/dist/plugins/typescript/features/CompletionsProvider.d.ts +24 -6
- package/dist/plugins/typescript/features/CompletionsProvider.js +262 -52
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +45 -60
- 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.d.ts +1 -1
- package/dist/plugins/typescript/language-service.js +44 -24
- 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 +44 -7
- package/dist/utils.d.ts +20 -0
- package/dist/utils.js +72 -3
- package/package.json +3 -3
|
@@ -1,25 +1,29 @@
|
|
|
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;
|
|
12
13
|
private readonly signatureHelpProvider;
|
|
13
14
|
private readonly diagnosticsProvider;
|
|
14
15
|
private readonly documentSymbolsProvider;
|
|
15
16
|
private readonly semanticTokensProvider;
|
|
17
|
+
private readonly foldingRangesProvider;
|
|
16
18
|
constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
|
|
17
19
|
doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
|
|
18
20
|
rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
21
|
+
getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
|
|
19
22
|
getSemanticTokens(textDocument: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
20
23
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
getCodeActions(document: AstroDocument, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
|
|
25
|
+
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
|
|
26
|
+
resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
|
|
23
27
|
getDefinitions(document: AstroDocument, position: Position): Promise<DefinitionLink[]>;
|
|
24
28
|
getDiagnostics(document: AstroDocument, cancellationToken?: CancellationToken): Promise<Diagnostic[]>;
|
|
25
29
|
onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesParam[]): Promise<void>;
|
|
@@ -37,17 +37,21 @@ const LanguageServiceManager_1 = require("./LanguageServiceManager");
|
|
|
37
37
|
const utils_3 = require("./utils");
|
|
38
38
|
const DocumentSymbolsProvider_1 = require("./features/DocumentSymbolsProvider");
|
|
39
39
|
const SemanticTokenProvider_1 = require("./features/SemanticTokenProvider");
|
|
40
|
+
const FoldingRangesProvider_1 = require("./features/FoldingRangesProvider");
|
|
41
|
+
const CodeActionsProvider_1 = require("./features/CodeActionsProvider");
|
|
40
42
|
class TypeScriptPlugin {
|
|
41
43
|
constructor(docManager, configManager, workspaceUris) {
|
|
42
44
|
this.__name = 'typescript';
|
|
43
45
|
this.configManager = configManager;
|
|
44
46
|
this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
|
|
47
|
+
this.codeActionsProvider = new CodeActionsProvider_1.CodeActionsProviderImpl(this.languageServiceManager);
|
|
45
48
|
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager);
|
|
46
49
|
this.hoverProvider = new HoverProvider_1.HoverProviderImpl(this.languageServiceManager);
|
|
47
50
|
this.signatureHelpProvider = new SignatureHelpProvider_1.SignatureHelpProviderImpl(this.languageServiceManager);
|
|
48
51
|
this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
|
|
49
52
|
this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
|
|
50
53
|
this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
|
|
54
|
+
this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
|
|
51
55
|
}
|
|
52
56
|
async doHover(document, position) {
|
|
53
57
|
if (!this.featureEnabled('hover')) {
|
|
@@ -78,6 +82,9 @@ class TypeScriptPlugin {
|
|
|
78
82
|
});
|
|
79
83
|
return edit;
|
|
80
84
|
}
|
|
85
|
+
async getFoldingRanges(document) {
|
|
86
|
+
return this.foldingRangesProvider.getFoldingRanges(document);
|
|
87
|
+
}
|
|
81
88
|
async getSemanticTokens(textDocument, range, cancellationToken) {
|
|
82
89
|
if (!this.featureEnabled('semanticTokens')) {
|
|
83
90
|
return null;
|
|
@@ -91,15 +98,21 @@ class TypeScriptPlugin {
|
|
|
91
98
|
const symbols = await this.documentSymbolsProvider.getDocumentSymbols(document);
|
|
92
99
|
return symbols;
|
|
93
100
|
}
|
|
94
|
-
async
|
|
101
|
+
async getCodeActions(document, range, context, cancellationToken) {
|
|
102
|
+
if (!this.featureEnabled('codeActions')) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
return this.codeActionsProvider.getCodeActions(document, range, context, cancellationToken);
|
|
106
|
+
}
|
|
107
|
+
async getCompletions(document, position, completionContext, cancellationToken) {
|
|
95
108
|
if (!this.featureEnabled('completions')) {
|
|
96
109
|
return null;
|
|
97
110
|
}
|
|
98
|
-
const completions = await this.completionProvider.getCompletions(document, position, completionContext);
|
|
111
|
+
const completions = await this.completionProvider.getCompletions(document, position, completionContext, cancellationToken);
|
|
99
112
|
return completions;
|
|
100
113
|
}
|
|
101
|
-
async resolveCompletion(document, completionItem) {
|
|
102
|
-
return this.completionProvider.resolveCompletion(document, completionItem);
|
|
114
|
+
async resolveCompletion(document, completionItem, cancellationToken) {
|
|
115
|
+
return this.completionProvider.resolveCompletion(document, completionItem, cancellationToken);
|
|
103
116
|
}
|
|
104
117
|
async getDefinitions(document, position) {
|
|
105
118
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
@@ -166,7 +179,7 @@ class TypeScriptPlugin {
|
|
|
166
179
|
}
|
|
167
180
|
getGoToDefinitionRefsForImportSpecifier(tsFilePath, offset, lang) {
|
|
168
181
|
const program = lang.getProgram();
|
|
169
|
-
const sourceFile = program
|
|
182
|
+
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
170
183
|
if (sourceFile) {
|
|
171
184
|
let node = typescript_1.default.getTouchingPropertyName(sourceFile, offset);
|
|
172
185
|
if (node && node.kind === typescript_1.SyntaxKind.Identifier) {
|
|
@@ -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, '///');
|
|
@@ -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) => {
|
|
@@ -76,7 +80,7 @@ function default_1(content) {
|
|
|
76
80
|
htmlRaw +
|
|
77
81
|
os_1.EOL +
|
|
78
82
|
// Add TypeScript definitions
|
|
79
|
-
addProps(frontMatterRaw);
|
|
83
|
+
addProps(frontMatterRaw, className);
|
|
80
84
|
return result;
|
|
81
85
|
}
|
|
82
86
|
exports.default = default_1;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CancellationToken } from 'vscode-languageserver';
|
|
2
|
+
import { CodeAction, CodeActionContext, Range } from 'vscode-languageserver-types';
|
|
3
|
+
import { AstroDocument } from '../../../core/documents';
|
|
4
|
+
import { CodeActionsProvider } from '../../interfaces';
|
|
5
|
+
import { LanguageServiceManager } from '../LanguageServiceManager';
|
|
6
|
+
export declare const sortImportKind: string;
|
|
7
|
+
export declare class CodeActionsProviderImpl implements CodeActionsProvider {
|
|
8
|
+
private languageServiceManager;
|
|
9
|
+
constructor(languageServiceManager: LanguageServiceManager);
|
|
10
|
+
getCodeActions(document: AstroDocument, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
|
|
11
|
+
private getComponentQuickFix;
|
|
12
|
+
private organizeSortImports;
|
|
13
|
+
private fixIndentationOfImports;
|
|
14
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
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) {
|
|
19
|
+
this.languageServiceManager = languageServiceManager;
|
|
20
|
+
}
|
|
21
|
+
async getCodeActions(document, range, context, cancellationToken) {
|
|
22
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
23
|
+
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
24
|
+
const fragment = await tsDoc.createFragment();
|
|
25
|
+
const start = fragment.offsetAt(fragment.getGeneratedPosition(range.start));
|
|
26
|
+
const end = fragment.offsetAt(fragment.getGeneratedPosition(range.end));
|
|
27
|
+
let result = [];
|
|
28
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
if (context.only?.[0] === vscode_languageserver_types_1.CodeActionKind.SourceOrganizeImports) {
|
|
32
|
+
return await this.organizeSortImports(document, false, cancellationToken);
|
|
33
|
+
}
|
|
34
|
+
// The difference between Sort Imports and Organize Imports is that Sort Imports won't do anything destructive.
|
|
35
|
+
// For example, it won't remove unused imports whereas Organize Imports will
|
|
36
|
+
if (context.only?.[0] === exports.sortImportKind) {
|
|
37
|
+
return await this.organizeSortImports(document, true, cancellationToken);
|
|
38
|
+
}
|
|
39
|
+
if (context.only?.[0] === vscode_languageserver_types_1.CodeActionKind.Source) {
|
|
40
|
+
return [
|
|
41
|
+
...(await this.organizeSortImports(document, true, cancellationToken)),
|
|
42
|
+
...(await this.organizeSortImports(document, false, cancellationToken)),
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
if (context.diagnostics.length && (!context.only || context.only.includes(vscode_languageserver_types_1.CodeActionKind.QuickFix))) {
|
|
46
|
+
const errorCodes = context.diagnostics
|
|
47
|
+
.map((diag) => Number(diag.code))
|
|
48
|
+
// We currently cannot support quick fix for unreachable code properly due to the way our TSX output is structured
|
|
49
|
+
.filter((code) => code !== 7027);
|
|
50
|
+
let codeFixes = errorCodes.includes(2304) ? this.getComponentQuickFix(start, end, lang, filePath) : undefined;
|
|
51
|
+
codeFixes = codeFixes ?? lang.getCodeFixesAtPosition(filePath, start, end, errorCodes, {}, {});
|
|
52
|
+
const codeActions = codeFixes.map((fix) => codeFixToCodeAction(fix, context.diagnostics, context.only ? vscode_languageserver_types_1.CodeActionKind.QuickFix : vscode_languageserver_types_1.CodeActionKind.Empty));
|
|
53
|
+
result.push(...codeActions);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
function codeFixToCodeAction(codeFix, diagnostics, kind) {
|
|
57
|
+
const documentChanges = codeFix.changes.map((change) => {
|
|
58
|
+
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.getURL(), null), change.textChanges.map((edit) => {
|
|
59
|
+
let originalRange = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
60
|
+
if (codeFix.fixName === 'import') {
|
|
61
|
+
return (0, CompletionsProvider_1.codeActionChangeToTextEdit)(document, fragment, edit);
|
|
62
|
+
}
|
|
63
|
+
if (codeFix.fixName === 'fixMissingFunctionDeclaration') {
|
|
64
|
+
originalRange = (0, utils_2.checkEndOfFileCodeInsert)(originalRange, document);
|
|
65
|
+
}
|
|
66
|
+
return vscode_languageserver_types_1.TextEdit.replace(originalRange, edit.newText);
|
|
67
|
+
}));
|
|
68
|
+
});
|
|
69
|
+
const codeAction = vscode_languageserver_types_1.CodeAction.create(codeFix.description, {
|
|
70
|
+
documentChanges,
|
|
71
|
+
}, kind);
|
|
72
|
+
codeAction.diagnostics = diagnostics;
|
|
73
|
+
return codeAction;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
getComponentQuickFix(start, end, lang, filePath) {
|
|
77
|
+
const sourceFile = lang.getProgram()?.getSourceFile(filePath);
|
|
78
|
+
if (!sourceFile) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const node = (0, utils_3.findContainingNode)(sourceFile, {
|
|
82
|
+
start,
|
|
83
|
+
length: end - start,
|
|
84
|
+
}, (n) => typescript_1.default.isJsxClosingElement(n) || typescript_1.default.isJsxOpeningLikeElement(n));
|
|
85
|
+
if (!node) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const tagName = node.tagName;
|
|
89
|
+
// Unlike quick fixes, completions will be able to find the component, so let's use those to get it
|
|
90
|
+
const completion = lang.getCompletionsAtPosition(filePath, tagName.getEnd(), CompletionsProvider_1.completionOptions);
|
|
91
|
+
if (!completion) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const name = tagName.getText();
|
|
95
|
+
const suffixedName = name + '__AstroComponent_';
|
|
96
|
+
const toFix = (c) => lang.getCompletionEntryDetails(filePath, end, c.name, {}, c.source, {}, c.data)?.codeActions?.map((a) => ({
|
|
97
|
+
...a,
|
|
98
|
+
description: (0, utils_2.removeAstroComponentSuffix)(a.description),
|
|
99
|
+
fixName: 'import',
|
|
100
|
+
})) ?? [];
|
|
101
|
+
return (0, lodash_1.flatten)(completion.entries.filter((c) => c.name === name || c.name === suffixedName).map(toFix));
|
|
102
|
+
}
|
|
103
|
+
async organizeSortImports(document, skipDestructiveCodeActions = false, cancellationToken) {
|
|
104
|
+
if (document.astroMeta.frontmatter.state !== 'closed') {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
108
|
+
const filePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
109
|
+
const fragment = await tsDoc.createFragment();
|
|
110
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const changes = lang.organizeImports({ fileName: filePath, type: 'file', skipDestructiveCodeActions }, {}, {});
|
|
114
|
+
const documentChanges = changes.map((change) => {
|
|
115
|
+
return vscode_languageserver_types_1.TextDocumentEdit.create(vscode_languageserver_types_1.OptionalVersionedTextDocumentIdentifier.create(document.url, null), change.textChanges.map((edit) => {
|
|
116
|
+
const range = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, edit.span));
|
|
117
|
+
return vscode_languageserver_types_1.TextEdit.replace(range, this.fixIndentationOfImports(edit.newText, range, document));
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
120
|
+
return [
|
|
121
|
+
vscode_languageserver_types_1.CodeAction.create(skipDestructiveCodeActions ? 'Sort Imports' : 'Organize Imports', {
|
|
122
|
+
documentChanges,
|
|
123
|
+
}, skipDestructiveCodeActions ? exports.sortImportKind : vscode_languageserver_types_1.CodeActionKind.SourceOrganizeImports),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
// "Organize Imports" will have edits that delete all imports by return empty edits
|
|
127
|
+
// and one edit which contains all the organized imports. Fix indentation
|
|
128
|
+
// of that one by prepending all lines with the indentation of the first line.
|
|
129
|
+
fixIndentationOfImports(edit, range, document) {
|
|
130
|
+
if (!edit || range.start.character === 0) {
|
|
131
|
+
return edit;
|
|
132
|
+
}
|
|
133
|
+
const existingLine = (0, documents_1.getLineAtPosition)(range.start, document.getText());
|
|
134
|
+
const leadingChars = existingLine.substring(0, range.start.character);
|
|
135
|
+
if (leadingChars.trim() !== '') {
|
|
136
|
+
return edit;
|
|
137
|
+
}
|
|
138
|
+
return (0, utils_1.modifyLines)(edit, (line, idx) => (idx === 0 || !line ? line : leadingChars + line));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.CodeActionsProviderImpl = CodeActionsProviderImpl;
|
|
@@ -1,16 +1,34 @@
|
|
|
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
|
+
export declare const completionOptions: ts.GetCompletionsAtPositionOptions;
|
|
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
15
|
constructor(languageServiceManager: LanguageServiceManager);
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
private readonly validTriggerCharacters;
|
|
17
|
+
private isValidTriggerCharacter;
|
|
18
|
+
private lastCompletion?;
|
|
19
|
+
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
|
|
20
|
+
resolveCompletion(document: AstroDocument, item: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
|
|
14
21
|
private toCompletionItem;
|
|
22
|
+
private isValidCompletion;
|
|
15
23
|
private getCompletionDocument;
|
|
24
|
+
/**
|
|
25
|
+
* If the textEdit is out of the word range of the triggered position
|
|
26
|
+
* vscode would refuse to show the completions
|
|
27
|
+
* split those edits into additionalTextEdit to fix it
|
|
28
|
+
*/
|
|
29
|
+
private fixTextEditRange;
|
|
30
|
+
private canReuseLastCompletion;
|
|
31
|
+
private getExistingImports;
|
|
32
|
+
private isAstroComponentImport;
|
|
16
33
|
}
|
|
34
|
+
export declare function codeActionChangeToTextEdit(document: AstroDocument, fragment: AstroSnapshotFragment, change: ts.TextChange): TextEdit;
|