@astrojs/language-server 0.16.1 → 0.18.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 +29 -0
- package/dist/core/config/ConfigManager.d.ts +4 -3
- package/dist/core/config/ConfigManager.js +15 -1
- package/dist/core/config/interfaces.d.ts +5 -0
- package/dist/core/documents/AstroDocument.d.ts +1 -0
- package/dist/core/documents/AstroDocument.js +1 -0
- package/dist/core/documents/DocumentMapper.d.ts +2 -0
- package/dist/core/documents/DocumentMapper.js +7 -5
- package/dist/core/documents/utils.d.ts +1 -0
- package/dist/core/documents/utils.js +16 -1
- package/dist/plugins/PluginHost.d.ts +2 -1
- package/dist/plugins/PluginHost.js +4 -0
- package/dist/plugins/astro/AstroPlugin.js +10 -3
- package/dist/plugins/astro/features/CompletionsProvider.d.ts +4 -5
- package/dist/plugins/astro/features/CompletionsProvider.js +53 -58
- package/dist/plugins/html/HTMLPlugin.d.ts +2 -1
- package/dist/plugins/html/HTMLPlugin.js +18 -0
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +3 -1
- package/dist/plugins/typescript/TypeScriptPlugin.js +5 -0
- package/dist/plugins/typescript/features/CodeActionsProvider.js +76 -17
- package/dist/plugins/typescript/features/CompletionsProvider.d.ts +2 -1
- package/dist/plugins/typescript/features/CompletionsProvider.js +107 -45
- package/dist/plugins/typescript/features/DefinitionsProvider.js +22 -1
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +58 -15
- package/dist/plugins/typescript/features/FoldingRangesProvider.js +13 -6
- package/dist/plugins/typescript/features/FormattingProvider.d.ts +11 -0
- package/dist/plugins/typescript/features/FormattingProvider.js +132 -0
- package/dist/plugins/typescript/features/HoverProvider.js +14 -1
- package/dist/plugins/typescript/features/SignatureHelpProvider.js +9 -1
- package/dist/plugins/typescript/language-service.js +18 -0
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.d.ts +22 -2
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.js +48 -1
- package/dist/plugins/typescript/snapshots/SnapshotManager.js +1 -0
- package/dist/plugins/typescript/snapshots/utils.js +3 -2
- package/dist/plugins/typescript/utils.d.ts +11 -1
- package/dist/plugins/typescript/utils.js +17 -1
- package/dist/server.js +2 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @astrojs/language-server
|
|
2
2
|
|
|
3
|
+
## 0.18.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 666739a: Revert update to latest LSP and inlay hints support
|
|
8
|
+
|
|
9
|
+
## 0.18.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- d3c6fd8: Add support for formatting
|
|
14
|
+
- 09e1163: Updated language server to latest version of LSP, added support for Inlay Hints
|
|
15
|
+
- fcaba8e: Add support for completions and type checking for Vue props
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 4138005: Fix frontmatter folding not working properly when last few lines of frontmatter are empty
|
|
20
|
+
- 76ff46a: Add `?` in the label of completions of optional parameters (including component props)
|
|
21
|
+
|
|
22
|
+
## 0.17.0
|
|
23
|
+
|
|
24
|
+
### Minor Changes
|
|
25
|
+
|
|
26
|
+
- 3ad0f65: Add support for TypeScript features inside script tags (completions, diagnostics, hover etc)
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- 2e9da14: Add support for loading props completions from .d.ts files, improve performance of props completions
|
|
31
|
+
|
|
3
32
|
## 0.16.1
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VSCodeEmmetConfig } from '@vscode/emmet-helper';
|
|
2
|
-
import { LSConfig, LSCSSConfig, LSHTMLConfig, LSTypescriptConfig } from './interfaces';
|
|
3
|
-
import { Connection } from 'vscode-languageserver';
|
|
2
|
+
import { LSConfig, LSCSSConfig, LSFormatConfig, LSHTMLConfig, LSTypescriptConfig } from './interfaces';
|
|
3
|
+
import { Connection, FormattingOptions } from 'vscode-languageserver';
|
|
4
4
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
5
5
|
import { FormatCodeSettings, UserPreferences } from 'typescript';
|
|
6
6
|
export declare const defaultLSConfig: LSConfig;
|
|
@@ -22,7 +22,8 @@ export declare class ConfigManager {
|
|
|
22
22
|
removeDocument(scopeUri: string): void;
|
|
23
23
|
getConfig<T>(section: string, scopeUri: string): Promise<T>;
|
|
24
24
|
getEmmetConfig(document: TextDocument): Promise<VSCodeEmmetConfig>;
|
|
25
|
-
|
|
25
|
+
getAstroFormatConfig(document: TextDocument): Promise<LSFormatConfig>;
|
|
26
|
+
getTSFormatConfig(document: TextDocument, vscodeOptions?: FormattingOptions): Promise<FormatCodeSettings>;
|
|
26
27
|
getTSPreferences(document: TextDocument): Promise<UserPreferences>;
|
|
27
28
|
/**
|
|
28
29
|
* Return true if a plugin and an optional feature is enabled
|
|
@@ -30,6 +30,10 @@ exports.defaultLSConfig = {
|
|
|
30
30
|
tagComplete: { enabled: true },
|
|
31
31
|
documentSymbols: { enabled: true },
|
|
32
32
|
},
|
|
33
|
+
format: {
|
|
34
|
+
indentFrontmatter: true,
|
|
35
|
+
newLineAfterFrontmatter: true,
|
|
36
|
+
},
|
|
33
37
|
};
|
|
34
38
|
/**
|
|
35
39
|
* Manager class to facilitate accessing and updating the user's config
|
|
@@ -69,9 +73,19 @@ class ConfigManager {
|
|
|
69
73
|
const emmetConfig = (await this.getConfig('emmet', document.uri)) ?? {};
|
|
70
74
|
return emmetConfig;
|
|
71
75
|
}
|
|
72
|
-
async
|
|
76
|
+
async getAstroFormatConfig(document) {
|
|
77
|
+
const astroFormatConfig = (await this.getConfig('astro.format', document.uri)) ?? {};
|
|
78
|
+
return {
|
|
79
|
+
indentFrontmatter: astroFormatConfig.indentFrontmatter ?? true,
|
|
80
|
+
newLineAfterFrontmatter: astroFormatConfig.newLineAfterFrontmatter ?? true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async getTSFormatConfig(document, vscodeOptions) {
|
|
73
84
|
const formatConfig = (await this.getConfig('typescript.format', document.uri)) ?? {};
|
|
74
85
|
return {
|
|
86
|
+
tabSize: vscodeOptions?.tabSize,
|
|
87
|
+
indentSize: vscodeOptions?.tabSize,
|
|
88
|
+
convertTabsToSpaces: vscodeOptions?.insertSpaces,
|
|
75
89
|
// We can use \n here since the editor normalizes later on to its line endings.
|
|
76
90
|
newLineCharacter: '\n',
|
|
77
91
|
insertSpaceAfterCommaDelimiter: formatConfig.insertSpaceAfterCommaDelimiter ?? true,
|
|
@@ -6,6 +6,11 @@ export interface LSConfig {
|
|
|
6
6
|
typescript: LSTypescriptConfig;
|
|
7
7
|
html: LSHTMLConfig;
|
|
8
8
|
css: LSCSSConfig;
|
|
9
|
+
format: LSFormatConfig;
|
|
10
|
+
}
|
|
11
|
+
export interface LSFormatConfig {
|
|
12
|
+
indentFrontmatter: boolean;
|
|
13
|
+
newLineAfterFrontmatter: boolean;
|
|
9
14
|
}
|
|
10
15
|
export interface LSTypescriptConfig {
|
|
11
16
|
enabled: boolean;
|
|
@@ -9,6 +9,7 @@ export declare class AstroDocument extends WritableDocument {
|
|
|
9
9
|
astroMeta: AstroMetadata;
|
|
10
10
|
html: HTMLDocument;
|
|
11
11
|
styleTags: TagInformation[];
|
|
12
|
+
scriptTags: TagInformation[];
|
|
12
13
|
constructor(url: string, content: string);
|
|
13
14
|
private updateDocInfo;
|
|
14
15
|
setText(text: string): void;
|
|
@@ -18,6 +18,7 @@ class AstroDocument extends DocumentBase_1.WritableDocument {
|
|
|
18
18
|
this.astroMeta = (0, parseAstro_1.parseAstro)(this.content);
|
|
19
19
|
this.html = (0, parseHtml_1.parseHtml)(this.content);
|
|
20
20
|
this.styleTags = (0, utils_2.extractStyleTags)(this.content, this.html);
|
|
21
|
+
this.scriptTags = (0, utils_2.extractScriptTags)(this.content, this.html);
|
|
21
22
|
}
|
|
22
23
|
setText(text) {
|
|
23
24
|
this.content = text;
|
|
@@ -46,6 +46,8 @@ export declare class FragmentMapper implements DocumentMapper {
|
|
|
46
46
|
private originalText;
|
|
47
47
|
private tagInfo;
|
|
48
48
|
private url;
|
|
49
|
+
private lineOffsetsOriginal;
|
|
50
|
+
private lineOffsetsGenerated;
|
|
49
51
|
constructor(originalText: string, tagInfo: TagInformation, url: string);
|
|
50
52
|
getOriginalPosition(generatedPosition: Position): Position;
|
|
51
53
|
private offsetInParent;
|
|
@@ -45,20 +45,22 @@ class FragmentMapper {
|
|
|
45
45
|
this.originalText = originalText;
|
|
46
46
|
this.tagInfo = tagInfo;
|
|
47
47
|
this.url = url;
|
|
48
|
+
this.lineOffsetsOriginal = (0, utils_1.getLineOffsets)(this.originalText);
|
|
49
|
+
this.lineOffsetsGenerated = (0, utils_1.getLineOffsets)(this.tagInfo.content);
|
|
48
50
|
}
|
|
49
51
|
getOriginalPosition(generatedPosition) {
|
|
50
|
-
const parentOffset = this.offsetInParent((0, utils_1.offsetAt)(generatedPosition, this.tagInfo.content));
|
|
51
|
-
return (0, utils_1.positionAt)(parentOffset, this.originalText);
|
|
52
|
+
const parentOffset = this.offsetInParent((0, utils_1.offsetAt)(generatedPosition, this.tagInfo.content, this.lineOffsetsGenerated));
|
|
53
|
+
return (0, utils_1.positionAt)(parentOffset, this.originalText, this.lineOffsetsOriginal);
|
|
52
54
|
}
|
|
53
55
|
offsetInParent(offset) {
|
|
54
56
|
return this.tagInfo.start + offset;
|
|
55
57
|
}
|
|
56
58
|
getGeneratedPosition(originalPosition) {
|
|
57
|
-
const fragmentOffset = (0, utils_1.offsetAt)(originalPosition, this.originalText) - this.tagInfo.start;
|
|
58
|
-
return (0, utils_1.positionAt)(fragmentOffset, this.tagInfo.content);
|
|
59
|
+
const fragmentOffset = (0, utils_1.offsetAt)(originalPosition, this.originalText, this.lineOffsetsOriginal) - this.tagInfo.start;
|
|
60
|
+
return (0, utils_1.positionAt)(fragmentOffset, this.tagInfo.content, this.lineOffsetsGenerated);
|
|
59
61
|
}
|
|
60
62
|
isInGenerated(pos) {
|
|
61
|
-
const offset = (0, utils_1.offsetAt)(pos, this.originalText);
|
|
63
|
+
const offset = (0, utils_1.offsetAt)(pos, this.originalText, this.lineOffsetsOriginal);
|
|
62
64
|
return offset >= this.tagInfo.start && offset <= this.tagInfo.end;
|
|
63
65
|
}
|
|
64
66
|
getURL() {
|
|
@@ -15,6 +15,7 @@ export interface TagInformation {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function walk(node: Node): Generator<Node, void, unknown>;
|
|
17
17
|
export declare function extractStyleTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
18
|
+
export declare function extractScriptTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
18
19
|
export declare function getLineAtPosition(position: Position, text: string): string;
|
|
19
20
|
/**
|
|
20
21
|
* Returns the node if offset is inside a HTML start tag
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isInComponentStartTag = exports.isComponentTag = exports.getNodeIfIsInHTMLStartTag = exports.getLineAtPosition = exports.extractStyleTags = exports.walk = void 0;
|
|
3
|
+
exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isInComponentStartTag = exports.isComponentTag = exports.getNodeIfIsInHTMLStartTag = exports.getLineAtPosition = exports.extractScriptTags = exports.extractStyleTags = exports.walk = void 0;
|
|
4
4
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
5
5
|
const utils_1 = require("../../utils");
|
|
6
6
|
const parseHtml_1 = require("./parseHtml");
|
|
@@ -28,6 +28,13 @@ function extractTags(text, tag, html) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
if (tag === 'script' && !matchedNodes.length && rootNodes.length) {
|
|
32
|
+
for (let child of walk(rootNodes[0])) {
|
|
33
|
+
if (child.tag === 'script') {
|
|
34
|
+
matchedNodes.push(child);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
31
38
|
return matchedNodes.map(transformToTagInfo);
|
|
32
39
|
function transformToTagInfo(matchedNode) {
|
|
33
40
|
const start = matchedNode.startTagEnd ?? matchedNode.start;
|
|
@@ -60,6 +67,14 @@ function extractStyleTags(source, html) {
|
|
|
60
67
|
return styles;
|
|
61
68
|
}
|
|
62
69
|
exports.extractStyleTags = extractStyleTags;
|
|
70
|
+
function extractScriptTags(source, html) {
|
|
71
|
+
const scripts = extractTags(source, 'script', html);
|
|
72
|
+
if (!scripts.length) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return scripts;
|
|
76
|
+
}
|
|
77
|
+
exports.extractScriptTags = extractScriptTags;
|
|
63
78
|
function parseAttributes(rawAttrs) {
|
|
64
79
|
const attrs = {};
|
|
65
80
|
if (!rawAttrs) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationToken, Color, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FoldingRange, Hover, Position, Range, Location, SignatureHelp, SignatureHelpContext, TextDocumentContentChangeEvent, TextDocumentIdentifier, WorkspaceEdit, SymbolInformation, SemanticTokens, CodeActionContext, CodeAction } from 'vscode-languageserver';
|
|
1
|
+
import { CancellationToken, Color, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FoldingRange, Hover, Position, Range, Location, SignatureHelp, SignatureHelpContext, TextDocumentContentChangeEvent, TextDocumentIdentifier, WorkspaceEdit, SymbolInformation, SemanticTokens, CodeActionContext, CodeAction, FormattingOptions, TextEdit } from 'vscode-languageserver';
|
|
2
2
|
import type { AppCompletionItem, Plugin } from './interfaces';
|
|
3
3
|
import { DocumentManager } from '../core/documents/DocumentManager';
|
|
4
4
|
interface PluginHostConfig {
|
|
@@ -16,6 +16,7 @@ export declare class PluginHost {
|
|
|
16
16
|
resolveCompletion(textDocument: TextDocumentIdentifier, completionItem: AppCompletionItem): Promise<CompletionItem>;
|
|
17
17
|
getDiagnostics(textDocument: TextDocumentIdentifier): Promise<Diagnostic[]>;
|
|
18
18
|
doHover(textDocument: TextDocumentIdentifier, position: Position): Promise<Hover | null>;
|
|
19
|
+
formatDocument(textDocument: TextDocumentIdentifier, options: FormattingOptions): Promise<TextEdit[]>;
|
|
19
20
|
getCodeActions(textDocument: TextDocumentIdentifier, range: Range, context: CodeActionContext, cancellationToken: CancellationToken): Promise<CodeAction[]>;
|
|
20
21
|
doTagComplete(textDocument: TextDocumentIdentifier, position: Position): Promise<string | null>;
|
|
21
22
|
getFoldingRanges(textDocument: TextDocumentIdentifier): Promise<FoldingRange[] | null>;
|
|
@@ -69,6 +69,10 @@ class PluginHost {
|
|
|
69
69
|
const document = this.getDocument(textDocument.uri);
|
|
70
70
|
return this.execute('doHover', [document, position], ExecuteMode.FirstNonNull);
|
|
71
71
|
}
|
|
72
|
+
async formatDocument(textDocument, options) {
|
|
73
|
+
const document = this.getDocument(textDocument.uri);
|
|
74
|
+
return (0, lodash_1.flatten)(await this.execute('formatDocument', [document, options], ExecuteMode.Collect));
|
|
75
|
+
}
|
|
72
76
|
async getCodeActions(textDocument, range, context, cancellationToken) {
|
|
73
77
|
const document = this.getDocument(textDocument.uri);
|
|
74
78
|
return (0, lodash_1.flatten)(await this.execute('getCodeActions', [document, range, context, cancellationToken], ExecuteMode.Collect));
|
|
@@ -9,7 +9,7 @@ class AstroPlugin {
|
|
|
9
9
|
this.__name = 'astro';
|
|
10
10
|
this.configManager = configManager;
|
|
11
11
|
this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
|
|
12
|
-
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(
|
|
12
|
+
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager);
|
|
13
13
|
}
|
|
14
14
|
async getCompletions(document, position, completionContext) {
|
|
15
15
|
const completions = this.completionProvider.getCompletions(document, position, completionContext);
|
|
@@ -21,8 +21,15 @@ class AstroPlugin {
|
|
|
21
21
|
// Currently editing frontmatter, don't fold
|
|
22
22
|
if (frontmatter.state !== 'closed')
|
|
23
23
|
return foldingRanges;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// The way folding ranges work is by folding anything between the starting position and the ending one, as such
|
|
25
|
+
// the start in this case should be after the frontmatter start (after the starting ---) until the last character
|
|
26
|
+
// of the last line of the frontmatter before its ending (before the closing ---)
|
|
27
|
+
// ---
|
|
28
|
+
// ^ -- start
|
|
29
|
+
// console.log("Astro")
|
|
30
|
+
// --- ^ -- end
|
|
31
|
+
const start = document.positionAt(frontmatter.startOffset + 3);
|
|
32
|
+
const end = document.positionAt(frontmatter.endOffset - 1);
|
|
26
33
|
return [
|
|
27
34
|
{
|
|
28
35
|
startLine: start.line,
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { AppCompletionList, CompletionsProvider } from '../../interfaces';
|
|
2
|
-
import type { AstroDocument
|
|
2
|
+
import type { AstroDocument } from '../../../core/documents';
|
|
3
3
|
import { CompletionContext, Position } from 'vscode-languageserver';
|
|
4
4
|
import { LanguageServiceManager as TypeScriptLanguageServiceManager } from '../../typescript/LanguageServiceManager';
|
|
5
5
|
export declare class CompletionsProviderImpl implements CompletionsProvider {
|
|
6
|
-
private readonly docManager;
|
|
7
6
|
private readonly languageServiceManager;
|
|
7
|
+
private lastCompletion;
|
|
8
8
|
directivesHTMLLang: import("vscode-html-languageservice").LanguageService;
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(languageServiceManager: TypeScriptLanguageServiceManager);
|
|
10
10
|
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList | null>;
|
|
11
11
|
private getComponentScriptCompletion;
|
|
12
|
-
private
|
|
12
|
+
private getPropCompletionsAndFilePath;
|
|
13
13
|
private getImportedSymbol;
|
|
14
14
|
private getPropType;
|
|
15
15
|
private getCompletionItemForProperty;
|
|
16
|
-
private isAstroComponent;
|
|
17
16
|
}
|
|
@@ -13,21 +13,18 @@ const vscode_html_languageservice_1 = require("vscode-html-languageservice");
|
|
|
13
13
|
const astro_attributes_1 = require("../../html/features/astro-attributes");
|
|
14
14
|
const utils_4 = require("../../html/utils");
|
|
15
15
|
class CompletionsProviderImpl {
|
|
16
|
-
constructor(
|
|
16
|
+
constructor(languageServiceManager) {
|
|
17
|
+
this.lastCompletion = null;
|
|
17
18
|
this.directivesHTMLLang = (0, vscode_html_languageservice_1.getLanguageService)({
|
|
18
19
|
customDataProviders: [astro_attributes_1.astroDirectives],
|
|
19
20
|
useDefaultDataProvider: false,
|
|
20
21
|
});
|
|
21
|
-
this.docManager = docManager;
|
|
22
22
|
this.languageServiceManager = languageServiceManager;
|
|
23
23
|
}
|
|
24
24
|
async getCompletions(document, position, completionContext) {
|
|
25
|
-
const doc = this.docManager.get(document.uri);
|
|
26
|
-
if (!doc)
|
|
27
|
-
return null;
|
|
28
25
|
let items = [];
|
|
29
26
|
if (completionContext?.triggerCharacter === '-') {
|
|
30
|
-
const frontmatter = this.getComponentScriptCompletion(
|
|
27
|
+
const frontmatter = this.getComponentScriptCompletion(document, position);
|
|
31
28
|
if (frontmatter)
|
|
32
29
|
items.push(frontmatter);
|
|
33
30
|
}
|
|
@@ -35,11 +32,11 @@ class CompletionsProviderImpl {
|
|
|
35
32
|
const offset = document.offsetAt(position);
|
|
36
33
|
const node = html.findNodeAt(offset);
|
|
37
34
|
if ((0, utils_1.isInComponentStartTag)(html, offset) && !(0, utils_1.isInsideExpression)(document.getText(), node.start, offset)) {
|
|
38
|
-
const props = await this.
|
|
35
|
+
const { completions: props, componentFilePath } = await this.getPropCompletionsAndFilePath(document, position, completionContext);
|
|
39
36
|
if (props.length) {
|
|
40
37
|
items.push(...props);
|
|
41
38
|
}
|
|
42
|
-
const isAstro =
|
|
39
|
+
const isAstro = componentFilePath?.endsWith('.astro');
|
|
43
40
|
if (!isAstro) {
|
|
44
41
|
const directives = (0, utils_4.removeDataAttrCompletion)(this.directivesHTMLLang.doComplete(document, position, html).items);
|
|
45
42
|
items.push(...directives);
|
|
@@ -47,7 +44,7 @@ class CompletionsProviderImpl {
|
|
|
47
44
|
}
|
|
48
45
|
return vscode_languageserver_1.CompletionList.create(items, true);
|
|
49
46
|
}
|
|
50
|
-
getComponentScriptCompletion(document, position
|
|
47
|
+
getComponentScriptCompletion(document, position) {
|
|
51
48
|
const base = {
|
|
52
49
|
kind: vscode_languageserver_1.CompletionItemKind.Snippet,
|
|
53
50
|
label: '---',
|
|
@@ -55,7 +52,7 @@ class CompletionsProviderImpl {
|
|
|
55
52
|
preselect: true,
|
|
56
53
|
detail: 'Component script',
|
|
57
54
|
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
|
|
58
|
-
commitCharacters: [
|
|
55
|
+
commitCharacters: [],
|
|
59
56
|
};
|
|
60
57
|
const prefix = document.getLineUntilOffset(document.offsetAt(position));
|
|
61
58
|
if (document.astroMeta.frontmatter.state === null) {
|
|
@@ -78,25 +75,25 @@ class CompletionsProviderImpl {
|
|
|
78
75
|
}
|
|
79
76
|
return null;
|
|
80
77
|
}
|
|
81
|
-
async
|
|
78
|
+
async getPropCompletionsAndFilePath(document, position, completionContext) {
|
|
82
79
|
const offset = document.offsetAt(position);
|
|
83
80
|
const html = document.html;
|
|
84
81
|
const node = html.findNodeAt(offset);
|
|
85
82
|
if (!(0, utils_2.isPossibleComponent)(node)) {
|
|
86
|
-
return [];
|
|
83
|
+
return { completions: [], componentFilePath: null };
|
|
87
84
|
}
|
|
88
85
|
const inAttribute = node.start + node.tag.length < offset;
|
|
89
86
|
if (!inAttribute) {
|
|
90
|
-
return [];
|
|
87
|
+
return { completions: [], componentFilePath: null };
|
|
91
88
|
}
|
|
92
89
|
if (completionContext?.triggerCharacter === '/' || completionContext?.triggerCharacter === '>') {
|
|
93
|
-
return [];
|
|
90
|
+
return { completions: [], componentFilePath: null };
|
|
94
91
|
}
|
|
95
92
|
// If inside of attribute value, skip.
|
|
96
93
|
if (completionContext &&
|
|
97
94
|
completionContext.triggerKind === vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter &&
|
|
98
95
|
completionContext.triggerCharacter === '"') {
|
|
99
|
-
return [];
|
|
96
|
+
return { completions: [], componentFilePath: null };
|
|
100
97
|
}
|
|
101
98
|
const componentName = node.tag;
|
|
102
99
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
@@ -106,18 +103,34 @@ class CompletionsProviderImpl {
|
|
|
106
103
|
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
107
104
|
const typeChecker = program?.getTypeChecker();
|
|
108
105
|
if (!sourceFile || !typeChecker) {
|
|
109
|
-
return [];
|
|
106
|
+
return { completions: [], componentFilePath: null };
|
|
110
107
|
}
|
|
111
108
|
// Get the import statement
|
|
112
109
|
const imp = this.getImportedSymbol(sourceFile, componentName);
|
|
113
110
|
const importType = imp && typeChecker.getTypeAtLocation(imp);
|
|
114
111
|
if (!importType) {
|
|
115
|
-
return [];
|
|
112
|
+
return { completions: [], componentFilePath: null };
|
|
113
|
+
}
|
|
114
|
+
const symbol = importType.getSymbol();
|
|
115
|
+
if (!symbol) {
|
|
116
|
+
return { completions: [], componentFilePath: null };
|
|
117
|
+
}
|
|
118
|
+
const symbolDeclaration = symbol.declarations;
|
|
119
|
+
if (!symbolDeclaration) {
|
|
120
|
+
return { completions: [], componentFilePath: null };
|
|
121
|
+
}
|
|
122
|
+
const filePath = symbolDeclaration[0].getSourceFile().fileName;
|
|
123
|
+
const componentSnapshot = await this.languageServiceManager.getSnapshot(filePath);
|
|
124
|
+
if (this.lastCompletion) {
|
|
125
|
+
if (this.lastCompletion.tag === componentName &&
|
|
126
|
+
this.lastCompletion.documentVersion == componentSnapshot.version) {
|
|
127
|
+
return { completions: this.lastCompletion.completions, componentFilePath: filePath };
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
130
|
// Get the component's props type
|
|
118
|
-
const componentType = this.getPropType(
|
|
131
|
+
const componentType = this.getPropType(symbolDeclaration, typeChecker);
|
|
119
132
|
if (!componentType) {
|
|
120
|
-
return [];
|
|
133
|
+
return { completions: [], componentFilePath: null };
|
|
121
134
|
}
|
|
122
135
|
let completionItems = [];
|
|
123
136
|
// Add completions for this component's props type properties
|
|
@@ -127,11 +140,12 @@ class CompletionsProviderImpl {
|
|
|
127
140
|
let completionItem = this.getCompletionItemForProperty(property, typeChecker, type);
|
|
128
141
|
completionItems.push(completionItem);
|
|
129
142
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
this.lastCompletion = {
|
|
144
|
+
tag: componentName,
|
|
145
|
+
documentVersion: componentSnapshot.version,
|
|
146
|
+
completions: completionItems,
|
|
147
|
+
};
|
|
148
|
+
return { completions: completionItems, componentFilePath: filePath };
|
|
135
149
|
}
|
|
136
150
|
getImportedSymbol(sourceFile, identifier) {
|
|
137
151
|
for (let list of sourceFile.getChildren()) {
|
|
@@ -159,24 +173,20 @@ class CompletionsProviderImpl {
|
|
|
159
173
|
}
|
|
160
174
|
return null;
|
|
161
175
|
}
|
|
162
|
-
getPropType(
|
|
163
|
-
const
|
|
164
|
-
if (!sym) {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
for (const decl of sym?.getDeclarations() || []) {
|
|
176
|
+
getPropType(declarations, typeChecker) {
|
|
177
|
+
for (const decl of declarations) {
|
|
168
178
|
const fileName = (0, utils_3.toVirtualFilePath)(decl.getSourceFile().fileName);
|
|
169
|
-
if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx')) {
|
|
170
|
-
if (!typescript_1.default.isFunctionDeclaration(decl)) {
|
|
171
|
-
console.error(`We only support
|
|
179
|
+
if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx') || fileName.endsWith('.d.ts')) {
|
|
180
|
+
if (!typescript_1.default.isFunctionDeclaration(decl) && !typescript_1.default.isFunctionTypeNode(decl)) {
|
|
181
|
+
console.error(`We only support functions declarations at the moment`);
|
|
172
182
|
continue;
|
|
173
183
|
}
|
|
174
184
|
const fn = decl;
|
|
175
185
|
if (!fn.parameters.length)
|
|
176
186
|
continue;
|
|
177
187
|
const param1 = fn.parameters[0];
|
|
178
|
-
const
|
|
179
|
-
return
|
|
188
|
+
const propType = typeChecker.getTypeAtLocation(param1);
|
|
189
|
+
return propType;
|
|
180
190
|
}
|
|
181
191
|
}
|
|
182
192
|
return null;
|
|
@@ -201,7 +211,15 @@ class CompletionsProviderImpl {
|
|
|
201
211
|
insertText: insertText,
|
|
202
212
|
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
|
|
203
213
|
commitCharacters: [],
|
|
214
|
+
// Ensure that props shows up first as a completion, despite this plugin being ran after the HTML one
|
|
215
|
+
sortText: '\0',
|
|
204
216
|
};
|
|
217
|
+
if (mem.flags & typescript_1.default.SymbolFlags.Optional) {
|
|
218
|
+
item.filterText = item.label;
|
|
219
|
+
item.label += '?';
|
|
220
|
+
// Put optional props at a lower priority
|
|
221
|
+
item.sortText = '_';
|
|
222
|
+
}
|
|
205
223
|
mem.getDocumentationComment(typeChecker);
|
|
206
224
|
let description = mem
|
|
207
225
|
.getDocumentationComment(typeChecker)
|
|
@@ -216,28 +234,5 @@ class CompletionsProviderImpl {
|
|
|
216
234
|
}
|
|
217
235
|
return item;
|
|
218
236
|
}
|
|
219
|
-
async isAstroComponent(document, node) {
|
|
220
|
-
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
221
|
-
// Get the source file
|
|
222
|
-
const tsFilePath = (0, utils_3.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
223
|
-
const program = lang.getProgram();
|
|
224
|
-
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
225
|
-
const typeChecker = program?.getTypeChecker();
|
|
226
|
-
if (!sourceFile || !typeChecker) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const componentName = node.tag;
|
|
230
|
-
const imp = this.getImportedSymbol(sourceFile, componentName);
|
|
231
|
-
const importType = imp && typeChecker.getTypeAtLocation(imp);
|
|
232
|
-
if (!importType) {
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
const symbolDeclaration = importType.getSymbol()?.declarations;
|
|
236
|
-
if (symbolDeclaration) {
|
|
237
|
-
const fileName = symbolDeclaration[0].getSourceFile().fileName;
|
|
238
|
-
return fileName.endsWith('.astro');
|
|
239
|
-
}
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
237
|
}
|
|
243
238
|
exports.CompletionsProviderImpl = CompletionsProviderImpl;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompletionList, Position, FoldingRange, Hover, SymbolInformation } from 'vscode-languageserver';
|
|
1
|
+
import { CompletionList, Position, TextEdit, FoldingRange, Hover, SymbolInformation, FormattingOptions } from 'vscode-languageserver';
|
|
2
2
|
import type { Plugin } from '../interfaces';
|
|
3
3
|
import { ConfigManager } from '../../core/config/ConfigManager';
|
|
4
4
|
import { AstroDocument } from '../../core/documents/AstroDocument';
|
|
@@ -15,6 +15,7 @@ export declare class HTMLPlugin implements Plugin {
|
|
|
15
15
|
* Get HTML completions
|
|
16
16
|
*/
|
|
17
17
|
getCompletions(document: AstroDocument, position: Position): Promise<CompletionList | null>;
|
|
18
|
+
formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
|
|
18
19
|
getFoldingRanges(document: AstroDocument): FoldingRange[] | null;
|
|
19
20
|
doTagComplete(document: AstroDocument, position: Position): Promise<string | null>;
|
|
20
21
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
@@ -80,6 +80,24 @@ class HTMLPlugin {
|
|
|
80
80
|
// Emmet completions change on every keystroke, so they are never complete
|
|
81
81
|
emmetResults.items.length > 0);
|
|
82
82
|
}
|
|
83
|
+
async formatDocument(document, options) {
|
|
84
|
+
const start = document.positionAt(document.astroMeta.frontmatter.state === 'closed' ? document.astroMeta.frontmatter.endOffset + 3 : 0);
|
|
85
|
+
if (document.astroMeta.frontmatter.state === 'closed') {
|
|
86
|
+
start.line += 1;
|
|
87
|
+
start.character = 0;
|
|
88
|
+
}
|
|
89
|
+
const end = document.positionAt(document.getTextLength());
|
|
90
|
+
const htmlFormatConfig = (await this.configManager.getConfig('html.format', document.uri)) ?? {};
|
|
91
|
+
// The HTML plugin can't format script tags properly, we'll handle those inside the TypeScript plugin
|
|
92
|
+
if (htmlFormatConfig.contentUnformatted) {
|
|
93
|
+
htmlFormatConfig.contentUnformatted = htmlFormatConfig.contentUnformatted + ',script';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
htmlFormatConfig.contentUnformatted = 'script';
|
|
97
|
+
}
|
|
98
|
+
const edits = this.lang.format(document, vscode_languageserver_1.Range.create(start, end), { ...htmlFormatConfig, ...options });
|
|
99
|
+
return edits;
|
|
100
|
+
}
|
|
83
101
|
getFoldingRanges(document) {
|
|
84
102
|
const html = document.html;
|
|
85
103
|
if (!html) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Diagnostic, FoldingRange, Hover, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
|
|
1
|
+
import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Diagnostic, FoldingRange, FormattingOptions, Hover, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextEdit, 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';
|
|
@@ -16,9 +16,11 @@ export declare class TypeScriptPlugin implements Plugin {
|
|
|
16
16
|
private readonly documentSymbolsProvider;
|
|
17
17
|
private readonly semanticTokensProvider;
|
|
18
18
|
private readonly foldingRangesProvider;
|
|
19
|
+
private readonly formattingProvider;
|
|
19
20
|
constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
|
|
20
21
|
doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
|
|
21
22
|
rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
23
|
+
formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
|
|
22
24
|
getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
|
|
23
25
|
getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
24
26
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
@@ -17,6 +17,7 @@ const SemanticTokenProvider_1 = require("./features/SemanticTokenProvider");
|
|
|
17
17
|
const FoldingRangesProvider_1 = require("./features/FoldingRangesProvider");
|
|
18
18
|
const CodeActionsProvider_1 = require("./features/CodeActionsProvider");
|
|
19
19
|
const DefinitionsProvider_1 = require("./features/DefinitionsProvider");
|
|
20
|
+
const FormattingProvider_1 = require("./features/FormattingProvider");
|
|
20
21
|
class TypeScriptPlugin {
|
|
21
22
|
constructor(docManager, configManager, workspaceUris) {
|
|
22
23
|
this.__name = 'typescript';
|
|
@@ -31,6 +32,7 @@ class TypeScriptPlugin {
|
|
|
31
32
|
this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
|
|
32
33
|
this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
|
|
33
34
|
this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
|
|
35
|
+
this.formattingProvider = new FormattingProvider_1.FormattingProviderImpl(this.languageServiceManager, this.configManager);
|
|
34
36
|
}
|
|
35
37
|
async doHover(document, position) {
|
|
36
38
|
if (!(await this.featureEnabled(document, 'hover'))) {
|
|
@@ -61,6 +63,9 @@ class TypeScriptPlugin {
|
|
|
61
63
|
});
|
|
62
64
|
return edit;
|
|
63
65
|
}
|
|
66
|
+
async formatDocument(document, options) {
|
|
67
|
+
return this.formattingProvider.formatDocument(document, options);
|
|
68
|
+
}
|
|
64
69
|
async getFoldingRanges(document) {
|
|
65
70
|
return this.foldingRangesProvider.getFoldingRanges(document);
|
|
66
71
|
}
|