@astrojs/language-server 0.19.4 → 0.19.5
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 +9 -0
- package/dist/core/config/ConfigManager.d.ts +2 -1
- package/dist/core/config/ConfigManager.js +29 -11
- package/dist/core/config/interfaces.d.ts +46 -45
- package/dist/core/documents/utils.d.ts +5 -8
- package/dist/core/documents/utils.js +10 -23
- package/dist/plugins/PluginHost.d.ts +5 -6
- package/dist/plugins/PluginHost.js +30 -21
- package/dist/plugins/astro/features/CompletionsProvider.js +6 -7
- package/dist/plugins/css/CSSPlugin.js +9 -10
- package/dist/plugins/html/HTMLPlugin.js +4 -5
- package/dist/plugins/typescript/LanguageServiceManager.js +1 -0
- package/dist/plugins/typescript/features/CodeActionsProvider.js +1 -2
- package/dist/plugins/typescript/features/CompletionsProvider.js +3 -4
- package/dist/plugins/typescript/language-service.d.ts +2 -0
- package/dist/plugins/typescript/language-service.js +17 -7
- package/dist/server.js +1 -0
- package/dist/utils.d.ts +15 -8
- package/dist/utils.js +58 -14
- package/package.json +1 -3
- package/types/arbitrary-attrs.d.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @astrojs/language-server
|
|
2
2
|
|
|
3
|
+
## 0.19.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 421ab52: Added a new setting (`astro.typescript.allowArbitraryAttributes`) to enable support for arbitrary attributes
|
|
8
|
+
- 06e3c95: Updated behaviour when no settings are provided. All features are now considered enabled by default
|
|
9
|
+
- 301dcfb: Remove Lodash from the code base, significally reducing the file count of the package
|
|
10
|
+
- dd1283b: Updated Component detection so completions now work for namespaced components (for example, typing `<myMarkdown.` will now give you a completion for the Content component)
|
|
11
|
+
|
|
3
12
|
## 0.19.4
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -15,12 +15,13 @@ declare type DeepPartial<T> = T extends Record<string, unknown> ? {
|
|
|
15
15
|
export declare class ConfigManager {
|
|
16
16
|
private globalConfig;
|
|
17
17
|
private documentSettings;
|
|
18
|
+
shouldRefreshTSServices: boolean;
|
|
18
19
|
private isTrusted;
|
|
19
20
|
private connection;
|
|
20
21
|
constructor(connection?: Connection);
|
|
21
22
|
updateConfig(): void;
|
|
22
23
|
removeDocument(scopeUri: string): void;
|
|
23
|
-
getConfig<T>(section: string, scopeUri: string): Promise<T
|
|
24
|
+
getConfig<T>(section: string, scopeUri: string): Promise<T | Record<string, any>>;
|
|
24
25
|
getEmmetConfig(document: TextDocument): Promise<VSCodeEmmetConfig>;
|
|
25
26
|
getAstroFormatConfig(document: TextDocument): Promise<LSFormatConfig>;
|
|
26
27
|
getTSFormatConfig(document: TextDocument, vscodeOptions?: FormattingOptions): Promise<FormatCodeSettings>;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConfigManager = exports.defaultLSConfig = void 0;
|
|
4
|
-
const lodash_1 = require("lodash");
|
|
5
4
|
const typescript_1 = require("typescript");
|
|
5
|
+
const utils_1 = require("../../utils");
|
|
6
|
+
// The default language server configuration is used only in two cases:
|
|
7
|
+
// 1. When the client does not support `workspace/configuration` requests and as such, needs a global config
|
|
8
|
+
// 2. Inside tests, where we don't have a client connection because.. well.. we don't have a client
|
|
9
|
+
// Additionally, the default config is used to set default settings for some settings (ex: formatting settings)
|
|
6
10
|
exports.defaultLSConfig = {
|
|
7
11
|
typescript: {
|
|
8
12
|
enabled: true,
|
|
13
|
+
allowArbitraryAttributes: false,
|
|
9
14
|
diagnostics: { enabled: true },
|
|
10
15
|
hover: { enabled: true },
|
|
11
16
|
completions: { enabled: true },
|
|
@@ -31,7 +36,7 @@ exports.defaultLSConfig = {
|
|
|
31
36
|
documentSymbols: { enabled: true },
|
|
32
37
|
},
|
|
33
38
|
format: {
|
|
34
|
-
indentFrontmatter:
|
|
39
|
+
indentFrontmatter: false,
|
|
35
40
|
newLineAfterFrontmatter: true,
|
|
36
41
|
},
|
|
37
42
|
};
|
|
@@ -44,19 +49,22 @@ class ConfigManager {
|
|
|
44
49
|
constructor(connection) {
|
|
45
50
|
this.globalConfig = { astro: exports.defaultLSConfig };
|
|
46
51
|
this.documentSettings = {};
|
|
52
|
+
// If set to true, the next time we need a TypeScript language service, we'll rebuild it so it gets the new config
|
|
53
|
+
this.shouldRefreshTSServices = false;
|
|
47
54
|
this.isTrusted = true;
|
|
48
55
|
this.connection = connection;
|
|
49
56
|
}
|
|
50
57
|
updateConfig() {
|
|
51
58
|
// Reset all cached document settings
|
|
52
59
|
this.documentSettings = {};
|
|
60
|
+
this.shouldRefreshTSServices = true;
|
|
53
61
|
}
|
|
54
62
|
removeDocument(scopeUri) {
|
|
55
63
|
delete this.documentSettings[scopeUri];
|
|
56
64
|
}
|
|
57
65
|
async getConfig(section, scopeUri) {
|
|
58
66
|
if (!this.connection) {
|
|
59
|
-
return this.globalConfig
|
|
67
|
+
return (0, utils_1.get)(this.globalConfig, section) ?? {};
|
|
60
68
|
}
|
|
61
69
|
if (!this.documentSettings[scopeUri]) {
|
|
62
70
|
this.documentSettings[scopeUri] = {};
|
|
@@ -71,13 +79,22 @@ class ConfigManager {
|
|
|
71
79
|
}
|
|
72
80
|
async getEmmetConfig(document) {
|
|
73
81
|
const emmetConfig = (await this.getConfig('emmet', document.uri)) ?? {};
|
|
74
|
-
return
|
|
82
|
+
return {
|
|
83
|
+
...emmetConfig,
|
|
84
|
+
preferences: emmetConfig.preferences ?? {},
|
|
85
|
+
showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation ?? 'always',
|
|
86
|
+
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions ?? true,
|
|
87
|
+
syntaxProfiles: emmetConfig.syntaxProfiles ?? {},
|
|
88
|
+
variables: emmetConfig.variables ?? {},
|
|
89
|
+
excludeLanguages: emmetConfig.excludeLanguages ?? [],
|
|
90
|
+
showSuggestionsAsSnippets: emmetConfig.showSuggestionsAsSnippets ?? false,
|
|
91
|
+
};
|
|
75
92
|
}
|
|
76
93
|
async getAstroFormatConfig(document) {
|
|
77
94
|
const astroFormatConfig = (await this.getConfig('astro.format', document.uri)) ?? {};
|
|
78
95
|
return {
|
|
79
|
-
indentFrontmatter: astroFormatConfig.indentFrontmatter ??
|
|
80
|
-
newLineAfterFrontmatter: astroFormatConfig.newLineAfterFrontmatter ??
|
|
96
|
+
indentFrontmatter: astroFormatConfig.indentFrontmatter ?? exports.defaultLSConfig.format.indentFrontmatter,
|
|
97
|
+
newLineAfterFrontmatter: astroFormatConfig.newLineAfterFrontmatter ?? exports.defaultLSConfig.format.newLineAfterFrontmatter,
|
|
81
98
|
};
|
|
82
99
|
}
|
|
83
100
|
async getTSFormatConfig(document, vscodeOptions) {
|
|
@@ -144,13 +161,13 @@ class ConfigManager {
|
|
|
144
161
|
async isEnabled(document, plugin, feature) {
|
|
145
162
|
const config = (await this.getConfig('astro', document.uri)) ?? {};
|
|
146
163
|
if (config[plugin]) {
|
|
147
|
-
let res = config[plugin].enabled;
|
|
164
|
+
let res = config[plugin].enabled ?? true;
|
|
148
165
|
if (feature && config[plugin][feature]) {
|
|
149
|
-
res = res && config[plugin][feature].enabled;
|
|
166
|
+
res = (res && config[plugin][feature].enabled) ?? true;
|
|
150
167
|
}
|
|
151
168
|
return res;
|
|
152
169
|
}
|
|
153
|
-
return
|
|
170
|
+
return true;
|
|
154
171
|
}
|
|
155
172
|
/**
|
|
156
173
|
* Updating the global config should only be done in cases where the client doesn't support `workspace/configuration`
|
|
@@ -161,11 +178,12 @@ class ConfigManager {
|
|
|
161
178
|
*/
|
|
162
179
|
updateGlobalConfig(config, outsideAstro) {
|
|
163
180
|
if (outsideAstro) {
|
|
164
|
-
this.globalConfig = (0,
|
|
181
|
+
this.globalConfig = (0, utils_1.mergeDeep)({}, this.globalConfig, config);
|
|
165
182
|
}
|
|
166
183
|
else {
|
|
167
|
-
this.globalConfig.astro = (0,
|
|
184
|
+
this.globalConfig.astro = (0, utils_1.mergeDeep)({}, exports.defaultLSConfig, this.globalConfig.astro, config);
|
|
168
185
|
}
|
|
186
|
+
this.shouldRefreshTSServices = true;
|
|
169
187
|
}
|
|
170
188
|
}
|
|
171
189
|
exports.ConfigManager = ConfigManager;
|
|
@@ -3,74 +3,75 @@
|
|
|
3
3
|
* Make sure that this is kept in sync with the `package.json` of the VS Code extension
|
|
4
4
|
*/
|
|
5
5
|
export interface LSConfig {
|
|
6
|
-
typescript
|
|
7
|
-
html
|
|
8
|
-
css
|
|
9
|
-
format
|
|
6
|
+
typescript?: LSTypescriptConfig;
|
|
7
|
+
html?: LSHTMLConfig;
|
|
8
|
+
css?: LSCSSConfig;
|
|
9
|
+
format?: LSFormatConfig;
|
|
10
10
|
}
|
|
11
11
|
export interface LSFormatConfig {
|
|
12
|
-
indentFrontmatter
|
|
13
|
-
newLineAfterFrontmatter
|
|
12
|
+
indentFrontmatter?: boolean;
|
|
13
|
+
newLineAfterFrontmatter?: boolean;
|
|
14
14
|
}
|
|
15
15
|
export interface LSTypescriptConfig {
|
|
16
|
-
enabled
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
allowArbitraryAttributes?: boolean;
|
|
18
|
+
diagnostics?: {
|
|
19
|
+
enabled?: boolean;
|
|
19
20
|
};
|
|
20
|
-
hover
|
|
21
|
-
enabled
|
|
21
|
+
hover?: {
|
|
22
|
+
enabled?: boolean;
|
|
22
23
|
};
|
|
23
|
-
documentSymbols
|
|
24
|
-
enabled
|
|
24
|
+
documentSymbols?: {
|
|
25
|
+
enabled?: boolean;
|
|
25
26
|
};
|
|
26
|
-
completions
|
|
27
|
-
enabled
|
|
27
|
+
completions?: {
|
|
28
|
+
enabled?: boolean;
|
|
28
29
|
};
|
|
29
|
-
definitions
|
|
30
|
-
enabled
|
|
30
|
+
definitions?: {
|
|
31
|
+
enabled?: boolean;
|
|
31
32
|
};
|
|
32
|
-
codeActions
|
|
33
|
-
enabled
|
|
33
|
+
codeActions?: {
|
|
34
|
+
enabled?: boolean;
|
|
34
35
|
};
|
|
35
|
-
rename
|
|
36
|
-
enabled
|
|
36
|
+
rename?: {
|
|
37
|
+
enabled?: boolean;
|
|
37
38
|
};
|
|
38
|
-
signatureHelp
|
|
39
|
-
enabled
|
|
39
|
+
signatureHelp?: {
|
|
40
|
+
enabled?: boolean;
|
|
40
41
|
};
|
|
41
|
-
semanticTokens
|
|
42
|
-
enabled
|
|
42
|
+
semanticTokens?: {
|
|
43
|
+
enabled?: boolean;
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
export interface LSHTMLConfig {
|
|
46
|
-
enabled
|
|
47
|
-
hover
|
|
48
|
-
enabled
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
hover?: {
|
|
49
|
+
enabled?: boolean;
|
|
49
50
|
};
|
|
50
|
-
completions
|
|
51
|
-
enabled
|
|
52
|
-
emmet
|
|
51
|
+
completions?: {
|
|
52
|
+
enabled?: boolean;
|
|
53
|
+
emmet?: boolean;
|
|
53
54
|
};
|
|
54
|
-
tagComplete
|
|
55
|
-
enabled
|
|
55
|
+
tagComplete?: {
|
|
56
|
+
enabled?: boolean;
|
|
56
57
|
};
|
|
57
|
-
documentSymbols
|
|
58
|
-
enabled
|
|
58
|
+
documentSymbols?: {
|
|
59
|
+
enabled?: boolean;
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
export interface LSCSSConfig {
|
|
62
|
-
enabled
|
|
63
|
-
hover
|
|
64
|
-
enabled
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
hover?: {
|
|
65
|
+
enabled?: boolean;
|
|
65
66
|
};
|
|
66
|
-
completions
|
|
67
|
-
enabled
|
|
68
|
-
emmet
|
|
67
|
+
completions?: {
|
|
68
|
+
enabled?: boolean;
|
|
69
|
+
emmet?: boolean;
|
|
69
70
|
};
|
|
70
|
-
documentColors
|
|
71
|
-
enabled
|
|
71
|
+
documentColors?: {
|
|
72
|
+
enabled?: boolean;
|
|
72
73
|
};
|
|
73
|
-
documentSymbols
|
|
74
|
-
enabled
|
|
74
|
+
documentSymbols?: {
|
|
75
|
+
enabled?: boolean;
|
|
75
76
|
};
|
|
76
77
|
}
|
|
@@ -17,18 +17,15 @@ export declare function walk(node: Node): Generator<Node, void, unknown>;
|
|
|
17
17
|
export declare function extractStyleTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
18
18
|
export declare function extractScriptTags(source: string, html?: HTMLDocument): TagInformation[];
|
|
19
19
|
export declare function getLineAtPosition(position: Position, text: string): string;
|
|
20
|
-
/**
|
|
21
|
-
* Returns the node if offset is inside a HTML start tag
|
|
22
|
-
*/
|
|
23
|
-
export declare function getNodeIfIsInHTMLStartTag(html: HTMLDocument, offset: number): Node | undefined;
|
|
24
|
-
/**
|
|
25
|
-
* Return if a Node is a Component
|
|
26
|
-
*/
|
|
27
|
-
export declare function isComponentTag(node: Node): boolean;
|
|
28
20
|
/**
|
|
29
21
|
* Return if a given offset is inside the start tag of a component
|
|
30
22
|
*/
|
|
31
23
|
export declare function isInComponentStartTag(html: HTMLDocument, offset: number): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Return true if a specific node could be a component.
|
|
26
|
+
* This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
|
|
27
|
+
*/
|
|
28
|
+
export declare function isPossibleComponent(node: Node): boolean;
|
|
32
29
|
/**
|
|
33
30
|
* Return if the current position is in a specific tag
|
|
34
31
|
*/
|
|
@@ -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.
|
|
3
|
+
exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isPossibleComponent = exports.isInComponentStartTag = 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");
|
|
@@ -97,35 +97,22 @@ function getLineAtPosition(position, text) {
|
|
|
97
97
|
return text.substring(offsetAt({ line: position.line, character: 0 }, text), offsetAt({ line: position.line, character: Number.MAX_VALUE }, text));
|
|
98
98
|
}
|
|
99
99
|
exports.getLineAtPosition = getLineAtPosition;
|
|
100
|
-
/**
|
|
101
|
-
* Returns the node if offset is inside a HTML start tag
|
|
102
|
-
*/
|
|
103
|
-
function getNodeIfIsInHTMLStartTag(html, offset) {
|
|
104
|
-
const node = html.findNodeAt(offset);
|
|
105
|
-
if (!!node.tag && node.tag[0] === node.tag[0].toLowerCase() && (!node.startTagEnd || offset < node.startTagEnd)) {
|
|
106
|
-
return node;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
exports.getNodeIfIsInHTMLStartTag = getNodeIfIsInHTMLStartTag;
|
|
110
|
-
/**
|
|
111
|
-
* Return if a Node is a Component
|
|
112
|
-
*/
|
|
113
|
-
function isComponentTag(node) {
|
|
114
|
-
if (!node.tag) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
const firstChar = node.tag[0];
|
|
118
|
-
return /[A-Z]/.test(firstChar);
|
|
119
|
-
}
|
|
120
|
-
exports.isComponentTag = isComponentTag;
|
|
121
100
|
/**
|
|
122
101
|
* Return if a given offset is inside the start tag of a component
|
|
123
102
|
*/
|
|
124
103
|
function isInComponentStartTag(html, offset) {
|
|
125
104
|
const node = html.findNodeAt(offset);
|
|
126
|
-
return
|
|
105
|
+
return isPossibleComponent(node) && (!node.startTagEnd || offset < node.startTagEnd);
|
|
127
106
|
}
|
|
128
107
|
exports.isInComponentStartTag = isInComponentStartTag;
|
|
108
|
+
/**
|
|
109
|
+
* Return true if a specific node could be a component.
|
|
110
|
+
* This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
|
|
111
|
+
*/
|
|
112
|
+
function isPossibleComponent(node) {
|
|
113
|
+
return !!node.tag?.[0].match(/[A-Z]/) || !!node.tag?.match(/.+[.][A-Z]?/);
|
|
114
|
+
}
|
|
115
|
+
exports.isPossibleComponent = isPossibleComponent;
|
|
129
116
|
/**
|
|
130
117
|
* Return if the current position is in a specific tag
|
|
131
118
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
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, InlayHint, FormattingOptions, TextEdit, LinkedEditingRanges } from 'vscode-languageserver';
|
|
2
2
|
import type { AppCompletionItem, Plugin } from './interfaces';
|
|
3
3
|
import { DocumentManager } from '../core/documents/DocumentManager';
|
|
4
|
-
interface PluginHostConfig {
|
|
4
|
+
export interface PluginHostConfig {
|
|
5
5
|
filterIncompleteCompletions: boolean;
|
|
6
6
|
definitionLinkSupport: boolean;
|
|
7
7
|
}
|
|
@@ -17,22 +17,21 @@ export declare class PluginHost {
|
|
|
17
17
|
getDiagnostics(textDocument: TextDocumentIdentifier): Promise<Diagnostic[]>;
|
|
18
18
|
doHover(textDocument: TextDocumentIdentifier, position: Position): Promise<Hover | null>;
|
|
19
19
|
formatDocument(textDocument: TextDocumentIdentifier, options: FormattingOptions): Promise<TextEdit[]>;
|
|
20
|
-
getCodeActions(textDocument: TextDocumentIdentifier, range: Range, context: CodeActionContext, cancellationToken
|
|
20
|
+
getCodeActions(textDocument: TextDocumentIdentifier, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
|
|
21
21
|
doTagComplete(textDocument: TextDocumentIdentifier, position: Position): Promise<string | null>;
|
|
22
22
|
getFoldingRanges(textDocument: TextDocumentIdentifier): Promise<FoldingRange[] | null>;
|
|
23
|
-
getDocumentSymbols(textDocument: TextDocumentIdentifier, cancellationToken
|
|
23
|
+
getDocumentSymbols(textDocument: TextDocumentIdentifier, cancellationToken?: CancellationToken): Promise<SymbolInformation[]>;
|
|
24
24
|
getSemanticTokens(textDocument: TextDocumentIdentifier, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
25
25
|
getLinkedEditingRanges(textDocument: TextDocumentIdentifier, position: Position): Promise<LinkedEditingRanges | null>;
|
|
26
26
|
getDefinitions(textDocument: TextDocumentIdentifier, position: Position): Promise<DefinitionLink[] | Location[]>;
|
|
27
27
|
rename(textDocument: TextDocumentIdentifier, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
28
28
|
getDocumentColors(textDocument: TextDocumentIdentifier): Promise<ColorInformation[]>;
|
|
29
|
-
getInlayHints(textDocument: TextDocumentIdentifier, range: Range, cancellationToken: CancellationToken): Promise<InlayHint[]>;
|
|
30
29
|
getColorPresentations(textDocument: TextDocumentIdentifier, range: Range, color: Color): Promise<ColorPresentation[]>;
|
|
31
|
-
|
|
30
|
+
getInlayHints(textDocument: TextDocumentIdentifier, range: Range, cancellationToken?: CancellationToken): Promise<InlayHint[]>;
|
|
31
|
+
getSignatureHelp(textDocument: TextDocumentIdentifier, position: Position, context: SignatureHelpContext | undefined, cancellationToken?: CancellationToken): Promise<SignatureHelp | null>;
|
|
32
32
|
onWatchFileChanges(onWatchFileChangesParams: any[]): void;
|
|
33
33
|
updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
|
|
34
34
|
private getDocument;
|
|
35
35
|
private execute;
|
|
36
36
|
private tryExecutePlugin;
|
|
37
37
|
}
|
|
38
|
-
export {};
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PluginHost = void 0;
|
|
4
4
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
5
|
-
const lodash_1 = require("lodash");
|
|
6
5
|
const utils_1 = require("../utils");
|
|
7
6
|
const documents_1 = require("../core/documents");
|
|
8
7
|
var ExecuteMode;
|
|
@@ -38,11 +37,12 @@ class PluginHost {
|
|
|
38
37
|
const ts = completions.find((completion) => completion.plugin === 'typescript');
|
|
39
38
|
const astro = completions.find((completion) => completion.plugin === 'astro');
|
|
40
39
|
if (html && ts) {
|
|
41
|
-
|
|
40
|
+
const inComponentStartTag = (0, documents_1.isInComponentStartTag)(document.html, document.offsetAt(position));
|
|
41
|
+
if (!inComponentStartTag) {
|
|
42
42
|
ts.result.items = [];
|
|
43
43
|
}
|
|
44
44
|
// If the Astro plugin has completions for us, don't show TypeScript's as they're most likely duplicates
|
|
45
|
-
if (astro && astro.result.items.length > 0 &&
|
|
45
|
+
if (astro && astro.result.items.length > 0 && inComponentStartTag) {
|
|
46
46
|
ts.result.items = [];
|
|
47
47
|
}
|
|
48
48
|
ts.result.items = ts.result.items.map((item) => {
|
|
@@ -52,8 +52,20 @@ class PluginHost {
|
|
|
52
52
|
return item;
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
let flattenedCompletions =
|
|
55
|
+
let flattenedCompletions = completions.flatMap((completion) => completion.result.items);
|
|
56
56
|
const isIncomplete = completions.reduce((incomplete, completion) => incomplete || completion.result.isIncomplete, false);
|
|
57
|
+
// If the result is incomplete, we need to filter the results ourselves
|
|
58
|
+
// to throw out non-matching results. VSCode does filter client-side,
|
|
59
|
+
// but other IDEs might not.
|
|
60
|
+
if (isIncomplete && this.pluginHostConfig.filterIncompleteCompletions) {
|
|
61
|
+
const offset = document.offsetAt(position);
|
|
62
|
+
// Assumption for performance reasons:
|
|
63
|
+
// Noone types import names longer than 20 characters and still expects perfect autocompletion.
|
|
64
|
+
const text = document.getText().substring(Math.max(0, offset - 20), offset);
|
|
65
|
+
const start = (0, utils_1.regexLastIndexOf)(text, /[\W\s]/g) + 1;
|
|
66
|
+
const filterValue = text.substring(start).toLowerCase();
|
|
67
|
+
flattenedCompletions = flattenedCompletions.filter((comp) => comp.label.toLowerCase().includes(filterValue));
|
|
68
|
+
}
|
|
57
69
|
return vscode_languageserver_1.CompletionList.create(flattenedCompletions, isIncomplete);
|
|
58
70
|
}
|
|
59
71
|
async resolveCompletion(textDocument, completionItem) {
|
|
@@ -63,7 +75,8 @@ class PluginHost {
|
|
|
63
75
|
}
|
|
64
76
|
async getDiagnostics(textDocument) {
|
|
65
77
|
const document = this.getDocument(textDocument.uri);
|
|
66
|
-
|
|
78
|
+
const diagnostics = await this.execute('getDiagnostics', [document], ExecuteMode.Collect);
|
|
79
|
+
return diagnostics;
|
|
67
80
|
}
|
|
68
81
|
async doHover(textDocument, position) {
|
|
69
82
|
const document = this.getDocument(textDocument.uri);
|
|
@@ -71,11 +84,11 @@ class PluginHost {
|
|
|
71
84
|
}
|
|
72
85
|
async formatDocument(textDocument, options) {
|
|
73
86
|
const document = this.getDocument(textDocument.uri);
|
|
74
|
-
return
|
|
87
|
+
return await this.execute('formatDocument', [document, options], ExecuteMode.Collect);
|
|
75
88
|
}
|
|
76
89
|
async getCodeActions(textDocument, range, context, cancellationToken) {
|
|
77
90
|
const document = this.getDocument(textDocument.uri);
|
|
78
|
-
return
|
|
91
|
+
return await this.execute('getCodeActions', [document, range, context, cancellationToken], ExecuteMode.Collect);
|
|
79
92
|
}
|
|
80
93
|
async doTagComplete(textDocument, position) {
|
|
81
94
|
const document = this.getDocument(textDocument.uri);
|
|
@@ -83,12 +96,11 @@ class PluginHost {
|
|
|
83
96
|
}
|
|
84
97
|
async getFoldingRanges(textDocument) {
|
|
85
98
|
const document = this.getDocument(textDocument.uri);
|
|
86
|
-
|
|
87
|
-
return foldingRanges;
|
|
99
|
+
return await this.execute('getFoldingRanges', [document], ExecuteMode.Collect);
|
|
88
100
|
}
|
|
89
101
|
async getDocumentSymbols(textDocument, cancellationToken) {
|
|
90
102
|
const document = this.getDocument(textDocument.uri);
|
|
91
|
-
return
|
|
103
|
+
return await this.execute('getDocumentSymbols', [document, cancellationToken], ExecuteMode.Collect);
|
|
92
104
|
}
|
|
93
105
|
async getSemanticTokens(textDocument, range, cancellationToken) {
|
|
94
106
|
const document = this.getDocument(textDocument.uri);
|
|
@@ -100,7 +112,7 @@ class PluginHost {
|
|
|
100
112
|
}
|
|
101
113
|
async getDefinitions(textDocument, position) {
|
|
102
114
|
const document = this.getDocument(textDocument.uri);
|
|
103
|
-
const definitions =
|
|
115
|
+
const definitions = await this.execute('getDefinitions', [document, position], ExecuteMode.Collect);
|
|
104
116
|
if (this.pluginHostConfig.definitionLinkSupport) {
|
|
105
117
|
return definitions;
|
|
106
118
|
}
|
|
@@ -114,21 +126,18 @@ class PluginHost {
|
|
|
114
126
|
}
|
|
115
127
|
async getDocumentColors(textDocument) {
|
|
116
128
|
const document = this.getDocument(textDocument.uri);
|
|
117
|
-
return
|
|
129
|
+
return await this.execute('getDocumentColors', [document], ExecuteMode.Collect);
|
|
118
130
|
}
|
|
119
|
-
async
|
|
131
|
+
async getColorPresentations(textDocument, range, color) {
|
|
120
132
|
const document = this.getDocument(textDocument.uri);
|
|
121
|
-
return
|
|
133
|
+
return await this.execute('getColorPresentations', [document, range, color], ExecuteMode.Collect);
|
|
122
134
|
}
|
|
123
|
-
async
|
|
135
|
+
async getInlayHints(textDocument, range, cancellationToken) {
|
|
124
136
|
const document = this.getDocument(textDocument.uri);
|
|
125
|
-
return (
|
|
137
|
+
return (await this.execute('getInlayHints', [document, range], ExecuteMode.FirstNonNull)) ?? [];
|
|
126
138
|
}
|
|
127
139
|
async getSignatureHelp(textDocument, position, context, cancellationToken) {
|
|
128
140
|
const document = this.getDocument(textDocument.uri);
|
|
129
|
-
if (!document) {
|
|
130
|
-
throw new Error('Cannot call methods on an unopened document');
|
|
131
|
-
}
|
|
132
141
|
return await this.execute('getSignatureHelp', [document, position, context, cancellationToken], ExecuteMode.FirstNonNull);
|
|
133
142
|
}
|
|
134
143
|
onWatchFileChanges(onWatchFileChangesParams) {
|
|
@@ -160,10 +169,10 @@ class PluginHost {
|
|
|
160
169
|
}
|
|
161
170
|
return null;
|
|
162
171
|
case ExecuteMode.Collect:
|
|
163
|
-
return Promise.all(plugins.map((plugin) => {
|
|
172
|
+
return (await Promise.all(plugins.map((plugin) => {
|
|
164
173
|
let ret = this.tryExecutePlugin(plugin, name, args, []);
|
|
165
174
|
return ret;
|
|
166
|
-
}));
|
|
175
|
+
}))).flat();
|
|
167
176
|
case ExecuteMode.None:
|
|
168
177
|
await Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null)));
|
|
169
178
|
return;
|
|
@@ -7,11 +7,10 @@ exports.CompletionsProviderImpl = void 0;
|
|
|
7
7
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
8
8
|
const typescript_1 = __importDefault(require("typescript"));
|
|
9
9
|
const utils_1 = require("../../../core/documents/utils");
|
|
10
|
-
const utils_2 = require("
|
|
11
|
-
const utils_3 = require("../../typescript/utils");
|
|
10
|
+
const utils_2 = require("../../typescript/utils");
|
|
12
11
|
const vscode_html_languageservice_1 = require("vscode-html-languageservice");
|
|
13
12
|
const astro_attributes_1 = require("../../html/features/astro-attributes");
|
|
14
|
-
const
|
|
13
|
+
const utils_3 = require("../../html/utils");
|
|
15
14
|
class CompletionsProviderImpl {
|
|
16
15
|
constructor(languageServiceManager) {
|
|
17
16
|
this.lastCompletion = null;
|
|
@@ -39,7 +38,7 @@ class CompletionsProviderImpl {
|
|
|
39
38
|
}
|
|
40
39
|
const isAstro = componentFilePath?.endsWith('.astro');
|
|
41
40
|
if (!isAstro) {
|
|
42
|
-
const directives = (0,
|
|
41
|
+
const directives = (0, utils_3.removeDataAttrCompletion)(this.directivesHTMLLang.doComplete(document, position, html).items);
|
|
43
42
|
items.push(...directives);
|
|
44
43
|
}
|
|
45
44
|
}
|
|
@@ -87,7 +86,7 @@ class CompletionsProviderImpl {
|
|
|
87
86
|
const offset = document.offsetAt(position);
|
|
88
87
|
const html = document.html;
|
|
89
88
|
const node = html.findNodeAt(offset);
|
|
90
|
-
if (!(0,
|
|
89
|
+
if (!(0, utils_1.isPossibleComponent)(node)) {
|
|
91
90
|
return { completions: [], componentFilePath: null };
|
|
92
91
|
}
|
|
93
92
|
const inAttribute = node.start + node.tag.length < offset;
|
|
@@ -106,7 +105,7 @@ class CompletionsProviderImpl {
|
|
|
106
105
|
const componentName = node.tag;
|
|
107
106
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
108
107
|
// Get the source file
|
|
109
|
-
const tsFilePath = (0,
|
|
108
|
+
const tsFilePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
110
109
|
const program = lang.getProgram();
|
|
111
110
|
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
112
111
|
const typeChecker = program?.getTypeChecker();
|
|
@@ -183,7 +182,7 @@ class CompletionsProviderImpl {
|
|
|
183
182
|
}
|
|
184
183
|
getPropType(declarations, typeChecker) {
|
|
185
184
|
for (const decl of declarations) {
|
|
186
|
-
const fileName = (0,
|
|
185
|
+
const fileName = (0, utils_2.toVirtualFilePath)(decl.getSourceFile().fileName);
|
|
187
186
|
if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx') || fileName.endsWith('.d.ts')) {
|
|
188
187
|
if (!typescript_1.default.isFunctionDeclaration(decl) && !typescript_1.default.isFunctionTypeNode(decl)) {
|
|
189
188
|
console.error(`We only support functions declarations at the moment`);
|
|
@@ -9,7 +9,6 @@ const language_service_1 = require("./language-service");
|
|
|
9
9
|
const parseHtml_1 = require("../../core/documents/parseHtml");
|
|
10
10
|
const StyleAttributeDocument_1 = require("./StyleAttributeDocument");
|
|
11
11
|
const getIdClassCompletions_1 = require("./features/getIdClassCompletions");
|
|
12
|
-
const lodash_1 = require("lodash");
|
|
13
12
|
class CSSPlugin {
|
|
14
13
|
constructor(configManager) {
|
|
15
14
|
this.__name = 'css';
|
|
@@ -103,7 +102,7 @@ class CSSPlugin {
|
|
|
103
102
|
items: [],
|
|
104
103
|
};
|
|
105
104
|
const extensionConfig = await this.configManager.getConfig('astro', document.uri);
|
|
106
|
-
if (extensionConfig
|
|
105
|
+
if (extensionConfig?.css?.completions?.emmet ?? true) {
|
|
107
106
|
langService.setCompletionParticipants([
|
|
108
107
|
{
|
|
109
108
|
onCssProperty: (context) => {
|
|
@@ -130,7 +129,7 @@ class CSSPlugin {
|
|
|
130
129
|
if (!(await this.featureEnabled(document, 'documentColors'))) {
|
|
131
130
|
return [];
|
|
132
131
|
}
|
|
133
|
-
const allColorInfo = this.getCSSDocumentsForDocument(document).
|
|
132
|
+
const allColorInfo = this.getCSSDocumentsForDocument(document).flatMap((cssDoc) => {
|
|
134
133
|
const cssLang = extractLanguage(cssDoc);
|
|
135
134
|
const langService = (0, language_service_1.getLanguageService)(cssLang);
|
|
136
135
|
if (!isSupportedByLangService(cssLang)) {
|
|
@@ -140,13 +139,13 @@ class CSSPlugin {
|
|
|
140
139
|
.findDocumentColors(cssDoc, cssDoc.stylesheet)
|
|
141
140
|
.map((colorInfo) => (0, documents_1.mapObjWithRangeToOriginal)(cssDoc, colorInfo));
|
|
142
141
|
});
|
|
143
|
-
return
|
|
142
|
+
return allColorInfo;
|
|
144
143
|
}
|
|
145
144
|
async getColorPresentations(document, range, color) {
|
|
146
145
|
if (!(await this.featureEnabled(document, 'documentColors'))) {
|
|
147
146
|
return [];
|
|
148
147
|
}
|
|
149
|
-
const allColorPres = this.getCSSDocumentsForDocument(document).
|
|
148
|
+
const allColorPres = this.getCSSDocumentsForDocument(document).flatMap((cssDoc) => {
|
|
150
149
|
const cssLang = extractLanguage(cssDoc);
|
|
151
150
|
const langService = (0, language_service_1.getLanguageService)(cssLang);
|
|
152
151
|
if ((!cssDoc.isInGenerated(range.start) && !cssDoc.isInGenerated(range.end)) ||
|
|
@@ -157,26 +156,26 @@ class CSSPlugin {
|
|
|
157
156
|
.getColorPresentations(cssDoc, cssDoc.stylesheet, color, (0, documents_1.mapRangeToGenerated)(cssDoc, range))
|
|
158
157
|
.map((colorPres) => (0, documents_1.mapColorPresentationToOriginal)(cssDoc, colorPres));
|
|
159
158
|
});
|
|
160
|
-
return
|
|
159
|
+
return allColorPres;
|
|
161
160
|
}
|
|
162
161
|
getFoldingRanges(document) {
|
|
163
|
-
const allFoldingRanges = this.getCSSDocumentsForDocument(document).
|
|
162
|
+
const allFoldingRanges = this.getCSSDocumentsForDocument(document).flatMap((cssDoc) => {
|
|
164
163
|
const cssLang = extractLanguage(cssDoc);
|
|
165
164
|
const langService = (0, language_service_1.getLanguageService)(cssLang);
|
|
166
165
|
return langService.getFoldingRanges(cssDoc).map((foldingRange) => (0, documents_1.mapFoldingRangeToParent)(cssDoc, foldingRange));
|
|
167
166
|
});
|
|
168
|
-
return
|
|
167
|
+
return allFoldingRanges;
|
|
169
168
|
}
|
|
170
169
|
async getDocumentSymbols(document) {
|
|
171
170
|
if (!(await this.featureEnabled(document, 'documentSymbols'))) {
|
|
172
171
|
return [];
|
|
173
172
|
}
|
|
174
|
-
const allDocumentSymbols = this.getCSSDocumentsForDocument(document).
|
|
173
|
+
const allDocumentSymbols = this.getCSSDocumentsForDocument(document).flatMap((cssDoc) => {
|
|
175
174
|
return (0, language_service_1.getLanguageService)(extractLanguage(cssDoc))
|
|
176
175
|
.findDocumentSymbols(cssDoc, cssDoc.stylesheet)
|
|
177
176
|
.map((symbol) => (0, documents_1.mapSymbolInformationToOriginal)(cssDoc, symbol));
|
|
178
177
|
});
|
|
179
|
-
return
|
|
178
|
+
return allDocumentSymbols;
|
|
180
179
|
}
|
|
181
180
|
inStyleAttributeWithoutInterpolation(attrContext, text) {
|
|
182
181
|
return (attrContext.name === 'style' &&
|
|
@@ -5,9 +5,8 @@ const vscode_languageserver_1 = require("vscode-languageserver");
|
|
|
5
5
|
const emmet_helper_1 = require("@vscode/emmet-helper");
|
|
6
6
|
const vscode_html_languageservice_1 = require("vscode-html-languageservice");
|
|
7
7
|
const utils_1 = require("../../core/documents/utils");
|
|
8
|
-
const utils_2 = require("../../utils");
|
|
9
8
|
const astro_attributes_1 = require("./features/astro-attributes");
|
|
10
|
-
const
|
|
9
|
+
const utils_2 = require("./utils");
|
|
11
10
|
class HTMLPlugin {
|
|
12
11
|
constructor(configManager) {
|
|
13
12
|
this.__name = 'html';
|
|
@@ -38,7 +37,7 @@ class HTMLPlugin {
|
|
|
38
37
|
return null;
|
|
39
38
|
}
|
|
40
39
|
// If the node we're hovering on is a component, instead only provide astro-specific hover info
|
|
41
|
-
if ((0,
|
|
40
|
+
if ((0, utils_1.isPossibleComponent)(node)) {
|
|
42
41
|
return this.componentLang.doHover(document, position, html);
|
|
43
42
|
}
|
|
44
43
|
return this.lang.doHover(document, position, html);
|
|
@@ -64,7 +63,7 @@ class HTMLPlugin {
|
|
|
64
63
|
};
|
|
65
64
|
const emmetConfig = await this.configManager.getEmmetConfig(document);
|
|
66
65
|
const extensionConfig = (await this.configManager.getConfig('astro', document.uri)) ?? {};
|
|
67
|
-
if (extensionConfig?.html?.completions?.emmet) {
|
|
66
|
+
if (extensionConfig?.html?.completions?.emmet ?? true) {
|
|
68
67
|
this.lang.setCompletionParticipants([
|
|
69
68
|
{
|
|
70
69
|
onHtmlContent: () => (emmetResults = (0, emmet_helper_1.doComplete)(document, position, 'html', emmetConfig) || emmetResults),
|
|
@@ -74,7 +73,7 @@ class HTMLPlugin {
|
|
|
74
73
|
// If we're in a component starting tag, we do not want HTML language completions
|
|
75
74
|
// as HTML attributes are not valid for components
|
|
76
75
|
const results = (0, utils_1.isInComponentStartTag)(html, document.offsetAt(position))
|
|
77
|
-
? (0,
|
|
76
|
+
? (0, utils_2.removeDataAttrCompletion)(this.attributeOnlyLang.doComplete(document, position, html).items)
|
|
78
77
|
: this.lang.doComplete(document, position, html).items;
|
|
79
78
|
return vscode_languageserver_1.CompletionList.create([...results, ...this.getLangCompletions(results), ...emmetResults.items],
|
|
80
79
|
// Emmet completions change on every keystroke, so they are never complete
|
|
@@ -25,6 +25,7 @@ class LanguageServiceManager {
|
|
|
25
25
|
this.docContext = {
|
|
26
26
|
createDocument: this.createDocument,
|
|
27
27
|
globalSnapshotManager: this.globalSnapshotManager,
|
|
28
|
+
configManager: this.configManager,
|
|
28
29
|
};
|
|
29
30
|
const handleDocumentChange = (document) => {
|
|
30
31
|
this.getSnapshot(document);
|
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CodeActionsProviderImpl = exports.sortImportKind = void 0;
|
|
7
|
-
const lodash_1 = require("lodash");
|
|
8
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
9
8
|
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
|
|
10
9
|
const documents_1 = require("../../../core/documents");
|
|
@@ -144,7 +143,7 @@ class CodeActionsProviderImpl {
|
|
|
144
143
|
description: (0, utils_2.removeAstroComponentSuffix)(a.description),
|
|
145
144
|
fixName: 'import',
|
|
146
145
|
})) ?? [];
|
|
147
|
-
return
|
|
146
|
+
return completion.entries.filter((c) => c.name === name || c.name === suffixedName).flatMap(toFix);
|
|
148
147
|
}
|
|
149
148
|
async organizeSortImports(document, skipDestructiveCodeActions = false, cancellationToken) {
|
|
150
149
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
@@ -32,7 +32,6 @@ const typescript_1 = __importStar(require("typescript"));
|
|
|
32
32
|
const vscode_languageserver_2 = require("vscode-languageserver");
|
|
33
33
|
const utils_2 = require("../utils");
|
|
34
34
|
const utils_3 = require("../../../utils");
|
|
35
|
-
const lodash_1 = require("lodash");
|
|
36
35
|
const previewer_1 = require("../previewer");
|
|
37
36
|
const utils_4 = require("./utils");
|
|
38
37
|
// `import {...} from '..'` or `import ... from '..'`
|
|
@@ -113,9 +112,9 @@ class CompletionsProviderImpl {
|
|
|
113
112
|
if (!isCompletionInsideFrontmatter && node.parent && node.tag === undefined && !isCompletionInsideExpression) {
|
|
114
113
|
return null;
|
|
115
114
|
}
|
|
116
|
-
// If the current node is not a component
|
|
115
|
+
// If the current node is not a component, let's disable ourselves as the user
|
|
117
116
|
// is most likely looking for HTML completions
|
|
118
|
-
if (!isCompletionInsideFrontmatter && !(0, utils_1.
|
|
117
|
+
if (!isCompletionInsideFrontmatter && !(0, utils_1.isPossibleComponent)(node) && !isCompletionInsideExpression) {
|
|
119
118
|
return null;
|
|
120
119
|
}
|
|
121
120
|
completions = lang.getCompletionsAtPosition(filePath, offset, {
|
|
@@ -317,7 +316,7 @@ class CompletionsProviderImpl {
|
|
|
317
316
|
}
|
|
318
317
|
getExistingImports(document) {
|
|
319
318
|
const rawImports = (0, utils_3.getRegExpMatches)(scriptImportRegex, document.getText()).map((match) => (match[1] ?? match[2]).split(','));
|
|
320
|
-
const tidiedImports =
|
|
319
|
+
const tidiedImports = rawImports.flat().map((match) => match.trim());
|
|
321
320
|
return new Set(tidiedImports);
|
|
322
321
|
}
|
|
323
322
|
isAstroComponentImport(className) {
|
|
@@ -3,6 +3,7 @@ import ts from 'typescript';
|
|
|
3
3
|
import { TextDocumentContentChangeEvent } from 'vscode-languageserver';
|
|
4
4
|
import { GlobalSnapshotManager, SnapshotManager } from './snapshots/SnapshotManager';
|
|
5
5
|
import { DocumentSnapshot } from './snapshots/DocumentSnapshot';
|
|
6
|
+
import { ConfigManager } from '../../core/config';
|
|
6
7
|
export interface LanguageServiceContainer {
|
|
7
8
|
readonly tsconfigPath: string;
|
|
8
9
|
readonly compilerOptions: ts.CompilerOptions;
|
|
@@ -28,6 +29,7 @@ export interface LanguageServiceContainer {
|
|
|
28
29
|
export interface LanguageServiceDocumentContext {
|
|
29
30
|
createDocument: (fileName: string, content: string) => AstroDocument;
|
|
30
31
|
globalSnapshotManager: GlobalSnapshotManager;
|
|
32
|
+
configManager: ConfigManager;
|
|
31
33
|
}
|
|
32
34
|
export declare function getLanguageService(path: string, workspaceUris: string[], docContext: LanguageServiceDocumentContext): Promise<LanguageServiceContainer>;
|
|
33
35
|
export declare function forAllLanguageServices(cb: (service: LanguageServiceContainer) => any): Promise<void>;
|
|
@@ -53,6 +53,10 @@ exports.forAllLanguageServices = forAllLanguageServices;
|
|
|
53
53
|
*/
|
|
54
54
|
async function getLanguageServiceForTsconfig(tsconfigPath, docContext, workspaceUris) {
|
|
55
55
|
let service;
|
|
56
|
+
if (docContext.configManager.shouldRefreshTSServices) {
|
|
57
|
+
services.clear();
|
|
58
|
+
docContext.configManager.shouldRefreshTSServices = false;
|
|
59
|
+
}
|
|
56
60
|
if (services.has(tsconfigPath)) {
|
|
57
61
|
service = await services.get(tsconfigPath);
|
|
58
62
|
}
|
|
@@ -69,26 +73,32 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
69
73
|
const workspacePaths = workspaceUris.map((uri) => (0, utils_1.urlToPath)(uri));
|
|
70
74
|
const workspacePath = workspacePaths.find((uri) => tsconfigRoot.startsWith(uri)) || workspacePaths[0];
|
|
71
75
|
const astroVersion = (0, utils_1.getUserAstroVersion)(workspacePath);
|
|
76
|
+
const config = (await docContext.configManager.getConfig('astro.typescript', workspacePath)) ?? {};
|
|
77
|
+
const allowArbitraryAttrs = config.allowArbitraryAttributes ?? false;
|
|
72
78
|
// `raw` here represent the tsconfig merged with any extended config
|
|
73
79
|
const { compilerOptions, fileNames: files, raw: fullConfig } = getParsedTSConfig();
|
|
74
80
|
let projectVersion = 0;
|
|
75
81
|
const snapshotManager = new SnapshotManager_1.SnapshotManager(docContext.globalSnapshotManager, files, fullConfig, tsconfigRoot || process.cwd());
|
|
76
82
|
const astroModuleLoader = (0, module_loader_1.createAstroModuleLoader)(getScriptSnapshot, compilerOptions);
|
|
83
|
+
let languageServerDirectory;
|
|
84
|
+
try {
|
|
85
|
+
languageServerDirectory = (0, path_1.dirname)(require.resolve('@astrojs/language-server'));
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
languageServerDirectory = __dirname;
|
|
89
|
+
}
|
|
77
90
|
const scriptFileNames = [];
|
|
78
91
|
// Before Astro 1.0, JSX definitions were inside of the language-server instead of inside Astro
|
|
79
92
|
// TODO: Remove this and astro-jsx.d.ts in types when we consider having support for Astro < 1.0 unnecessary
|
|
80
93
|
if (astroVersion.major === 0 || astroVersion.full === '1.0.0-beta.0') {
|
|
81
|
-
let languageServerDirectory;
|
|
82
|
-
try {
|
|
83
|
-
languageServerDirectory = (0, path_1.dirname)(require.resolve('@astrojs/language-server'));
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
languageServerDirectory = __dirname;
|
|
87
|
-
}
|
|
88
94
|
const astroTSXFile = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/astro-jsx.d.ts'));
|
|
89
95
|
scriptFileNames.push(astroTSXFile);
|
|
90
96
|
console.warn("Version lower than 1.0 detected, using internal types instead of Astro's");
|
|
91
97
|
}
|
|
98
|
+
if (allowArbitraryAttrs) {
|
|
99
|
+
const arbitraryAttrsDTS = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/arbitrary-attrs.d.ts'));
|
|
100
|
+
scriptFileNames.push(arbitraryAttrsDTS);
|
|
101
|
+
}
|
|
92
102
|
const host = {
|
|
93
103
|
getNewLine: () => typescript_1.default.sys.newLine,
|
|
94
104
|
useCaseSensitiveFileNames: () => typescript_1.default.sys.useCaseSensitiveFileNames,
|
package/dist/server.js
CHANGED
|
@@ -155,6 +155,7 @@ function startLanguageServer(connection) {
|
|
|
155
155
|
else {
|
|
156
156
|
configManager.updateGlobalConfig(change.settings.astro || ConfigManager_1.defaultLSConfig);
|
|
157
157
|
}
|
|
158
|
+
updateAllDiagnostics();
|
|
158
159
|
});
|
|
159
160
|
// Documents
|
|
160
161
|
connection.onDidOpenTextDocument((params) => {
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Position, Range } from 'vscode-languageserver';
|
|
2
|
-
import { Node } from 'vscode-html-languageservice';
|
|
3
2
|
/** Normalizes a document URI */
|
|
4
3
|
export declare function normalizeUri(uri: string): string;
|
|
5
4
|
/**
|
|
@@ -16,6 +15,17 @@ export declare function pathToUrl(path: string): string;
|
|
|
16
15
|
* (bar or bar.astro in this example).
|
|
17
16
|
*/
|
|
18
17
|
export declare function getLastPartOfPath(path: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Return an element in an object using a path as a string (ex: `astro.typescript.format` will return astro['typescript']['format']).
|
|
20
|
+
* From: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_get
|
|
21
|
+
*/
|
|
22
|
+
export declare function get<T>(obj: Record<string, any>, path: string): T | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Performs a deep merge of objects and returns new object. Does not modify
|
|
25
|
+
* objects (immutable) and merges arrays via concatenation.
|
|
26
|
+
* From: https://stackoverflow.com/a/48218209
|
|
27
|
+
*/
|
|
28
|
+
export declare function mergeDeep(...objects: Record<string, any>[]): Record<string, any>;
|
|
19
29
|
/**
|
|
20
30
|
* Transform a string into PascalCase
|
|
21
31
|
*/
|
|
@@ -24,18 +34,15 @@ export declare function toPascalCase(string: string): string;
|
|
|
24
34
|
* Function to modify each line of a text, preserving the line break style (`\n` or `\r\n`)
|
|
25
35
|
*/
|
|
26
36
|
export declare function modifyLines(text: string, replacementFn: (line: string, lineIdx: number) => string): string;
|
|
27
|
-
/**
|
|
28
|
-
* Return true if a specific node could be a component.
|
|
29
|
-
* This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
|
|
30
|
-
*/
|
|
31
|
-
export declare function isPossibleComponent(node: Node): boolean;
|
|
32
|
-
/** Flattens an array */
|
|
33
|
-
export declare function flatten<T>(arr: T[][]): T[];
|
|
34
37
|
/** Clamps a number between min and max */
|
|
35
38
|
export declare function clamp(num: number, min: number, max: number): number;
|
|
36
39
|
export declare function isNotNullOrUndefined<T>(val: T | undefined | null): val is T;
|
|
37
40
|
export declare function isInRange(range: Range, positionToTest: Position): boolean;
|
|
38
41
|
export declare function isBeforeOrEqualToPosition(position: Position, positionToTest: Position): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Like str.lastIndexOf, but for regular expressions. Note that you need to provide the g-flag to your RegExp!
|
|
44
|
+
*/
|
|
45
|
+
export declare function regexLastIndexOf(text: string, regex: RegExp, endPos?: number): number;
|
|
39
46
|
/**
|
|
40
47
|
* Get all matches of a regexp.
|
|
41
48
|
*/
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUserAstroVersion = exports.debounceThrottle = exports.debounceSameArg = exports.getRegExpMatches = exports.isBeforeOrEqualToPosition = exports.isInRange = exports.isNotNullOrUndefined = exports.clamp = exports.
|
|
3
|
+
exports.getUserAstroVersion = exports.debounceThrottle = exports.debounceSameArg = exports.getRegExpMatches = exports.regexLastIndexOf = exports.isBeforeOrEqualToPosition = exports.isInRange = exports.isNotNullOrUndefined = exports.clamp = exports.modifyLines = exports.toPascalCase = exports.mergeDeep = exports.get = exports.getLastPartOfPath = exports.pathToUrl = exports.urlToPath = exports.normalizePath = exports.normalizeUri = void 0;
|
|
4
4
|
const vscode_uri_1 = require("vscode-uri");
|
|
5
5
|
/** Normalizes a document URI */
|
|
6
6
|
function normalizeUri(uri) {
|
|
@@ -37,6 +37,44 @@ function getLastPartOfPath(path) {
|
|
|
37
37
|
return path.replace(/\\/g, '/').split('/').pop() || '';
|
|
38
38
|
}
|
|
39
39
|
exports.getLastPartOfPath = getLastPartOfPath;
|
|
40
|
+
/**
|
|
41
|
+
* Return an element in an object using a path as a string (ex: `astro.typescript.format` will return astro['typescript']['format']).
|
|
42
|
+
* From: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_get
|
|
43
|
+
*/
|
|
44
|
+
function get(obj, path) {
|
|
45
|
+
const travel = (regexp) => String.prototype.split
|
|
46
|
+
.call(path, regexp)
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.reduce((res, key) => (res !== null && res !== undefined ? res[key] : res), obj);
|
|
49
|
+
const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/);
|
|
50
|
+
return result === undefined ? undefined : result;
|
|
51
|
+
}
|
|
52
|
+
exports.get = get;
|
|
53
|
+
/**
|
|
54
|
+
* Performs a deep merge of objects and returns new object. Does not modify
|
|
55
|
+
* objects (immutable) and merges arrays via concatenation.
|
|
56
|
+
* From: https://stackoverflow.com/a/48218209
|
|
57
|
+
*/
|
|
58
|
+
function mergeDeep(...objects) {
|
|
59
|
+
const isObject = (obj) => obj && typeof obj === 'object';
|
|
60
|
+
return objects.reduce((prev, obj) => {
|
|
61
|
+
Object.keys(obj).forEach((key) => {
|
|
62
|
+
const pVal = prev[key];
|
|
63
|
+
const oVal = obj[key];
|
|
64
|
+
if (Array.isArray(pVal) && Array.isArray(oVal)) {
|
|
65
|
+
prev[key] = pVal.concat(...oVal);
|
|
66
|
+
}
|
|
67
|
+
else if (isObject(pVal) && isObject(oVal)) {
|
|
68
|
+
prev[key] = mergeDeep(pVal, oVal);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
prev[key] = oVal;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return prev;
|
|
75
|
+
}, {});
|
|
76
|
+
}
|
|
77
|
+
exports.mergeDeep = mergeDeep;
|
|
40
78
|
/**
|
|
41
79
|
* Transform a string into PascalCase
|
|
42
80
|
*/
|
|
@@ -62,19 +100,6 @@ function modifyLines(text, replacementFn) {
|
|
|
62
100
|
.join('\r\n');
|
|
63
101
|
}
|
|
64
102
|
exports.modifyLines = modifyLines;
|
|
65
|
-
/**
|
|
66
|
-
* Return true if a specific node could be a component.
|
|
67
|
-
* This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
|
|
68
|
-
*/
|
|
69
|
-
function isPossibleComponent(node) {
|
|
70
|
-
return !!node.tag?.[0].match(/[A-Z]/) || !!node.tag?.match(/.+[.][A-Z]/);
|
|
71
|
-
}
|
|
72
|
-
exports.isPossibleComponent = isPossibleComponent;
|
|
73
|
-
/** Flattens an array */
|
|
74
|
-
function flatten(arr) {
|
|
75
|
-
return arr.reduce((all, item) => [...all, ...item], []);
|
|
76
|
-
}
|
|
77
|
-
exports.flatten = flatten;
|
|
78
103
|
/** Clamps a number between min and max */
|
|
79
104
|
function clamp(num, min, max) {
|
|
80
105
|
return Math.max(min, Math.min(max, num));
|
|
@@ -93,6 +118,25 @@ function isBeforeOrEqualToPosition(position, positionToTest) {
|
|
|
93
118
|
(positionToTest.line === position.line && positionToTest.character <= position.character));
|
|
94
119
|
}
|
|
95
120
|
exports.isBeforeOrEqualToPosition = isBeforeOrEqualToPosition;
|
|
121
|
+
/**
|
|
122
|
+
* Like str.lastIndexOf, but for regular expressions. Note that you need to provide the g-flag to your RegExp!
|
|
123
|
+
*/
|
|
124
|
+
function regexLastIndexOf(text, regex, endPos) {
|
|
125
|
+
if (endPos === undefined) {
|
|
126
|
+
endPos = text.length;
|
|
127
|
+
}
|
|
128
|
+
else if (endPos < 0) {
|
|
129
|
+
endPos = 0;
|
|
130
|
+
}
|
|
131
|
+
const stringToWorkWith = text.substring(0, endPos + 1);
|
|
132
|
+
let lastIndexOf = -1;
|
|
133
|
+
let result = null;
|
|
134
|
+
while ((result = regex.exec(stringToWorkWith)) !== null) {
|
|
135
|
+
lastIndexOf = result.index;
|
|
136
|
+
}
|
|
137
|
+
return lastIndexOf;
|
|
138
|
+
}
|
|
139
|
+
exports.regexLastIndexOf = regexLastIndexOf;
|
|
96
140
|
/**
|
|
97
141
|
* Get all matches of a regexp.
|
|
98
142
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/language-server",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.5",
|
|
4
4
|
"author": "withastro",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"@astrojs/vue-language-integration": "^0.1.1",
|
|
23
23
|
"@astrojs/svelte-language-integration": "^0.1.6",
|
|
24
24
|
"@vscode/emmet-helper": "^2.8.4",
|
|
25
|
-
"lodash": "^4.17.21",
|
|
26
25
|
"source-map": "^0.7.3",
|
|
27
26
|
"typescript": "~4.6.4",
|
|
28
27
|
"vscode-css-languageservice": "^6.0.1",
|
|
@@ -35,7 +34,6 @@
|
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
36
|
"@types/chai": "^4.3.0",
|
|
38
|
-
"@types/lodash": "^4.14.179",
|
|
39
37
|
"@types/mocha": "^9.1.0",
|
|
40
38
|
"@types/sinon": "^10.0.11",
|
|
41
39
|
"astro": "^1.0.0-beta.1",
|