@astrojs/language-server 0.19.0 → 0.19.3
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 +26 -0
- package/dist/core/config/ConfigManager.js +9 -2
- package/dist/plugins/PluginHost.d.ts +2 -1
- package/dist/plugins/PluginHost.js +4 -0
- package/dist/plugins/astro/features/CompletionsProvider.js +16 -8
- package/dist/plugins/html/HTMLPlugin.d.ts +2 -1
- package/dist/plugins/html/HTMLPlugin.js +13 -2
- package/dist/plugins/typescript/features/DiagnosticsProvider.d.ts +12 -0
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +95 -65
- package/dist/plugins/typescript/language-service.js +1 -0
- package/dist/server.js +2 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @astrojs/language-server
|
|
2
2
|
|
|
3
|
+
## 0.19.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 49ff4ef: Fixed more bugs where nonexistent server settings would result in a crash
|
|
8
|
+
- 14cbf05: Fix frontmatter completion not working when three dashes were already present
|
|
9
|
+
|
|
10
|
+
## 0.19.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 7de4967: Add better error messages for Vue and Svelte components with syntax errors
|
|
15
|
+
- Updated dependencies [7de4967]
|
|
16
|
+
- @astrojs/svelte-language-integration@0.1.6
|
|
17
|
+
- @astrojs/vue-language-integration@0.1.1
|
|
18
|
+
|
|
19
|
+
## 0.19.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- 729dff5: Add support for giving linked editing ranges
|
|
24
|
+
- 05a48c2: Fix some TypeScript diagnostics not showing up in certain cases
|
|
25
|
+
- fe2d26b: Add support for showing Svelte components documentation on hover
|
|
26
|
+
- Updated dependencies [fe2d26b]
|
|
27
|
+
- @astrojs/svelte-language-integration@0.1.5
|
|
28
|
+
|
|
3
29
|
## 0.19.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
@@ -142,8 +142,15 @@ class ConfigManager {
|
|
|
142
142
|
* Return true if a plugin and an optional feature is enabled
|
|
143
143
|
*/
|
|
144
144
|
async isEnabled(document, plugin, feature) {
|
|
145
|
-
const config = await this.getConfig('astro', document.uri);
|
|
146
|
-
|
|
145
|
+
const config = (await this.getConfig('astro', document.uri)) ?? {};
|
|
146
|
+
if (config[plugin]) {
|
|
147
|
+
let res = config[plugin].enabled;
|
|
148
|
+
if (feature && config[plugin][feature]) {
|
|
149
|
+
res = res && config[plugin][feature].enabled;
|
|
150
|
+
}
|
|
151
|
+
return res;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
147
154
|
}
|
|
148
155
|
/**
|
|
149
156
|
* Updating the global config should only be done in cases where the client doesn't support `workspace/configuration`
|
|
@@ -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, InlayHint, FormattingOptions, TextEdit } 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, InlayHint, FormattingOptions, TextEdit, LinkedEditingRanges } from 'vscode-languageserver';
|
|
2
2
|
import type { AppCompletionItem, Plugin } from './interfaces';
|
|
3
3
|
import { DocumentManager } from '../core/documents/DocumentManager';
|
|
4
4
|
interface PluginHostConfig {
|
|
@@ -22,6 +22,7 @@ export declare class PluginHost {
|
|
|
22
22
|
getFoldingRanges(textDocument: TextDocumentIdentifier): Promise<FoldingRange[] | null>;
|
|
23
23
|
getDocumentSymbols(textDocument: TextDocumentIdentifier, cancellationToken: CancellationToken): Promise<SymbolInformation[]>;
|
|
24
24
|
getSemanticTokens(textDocument: TextDocumentIdentifier, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
25
|
+
getLinkedEditingRanges(textDocument: TextDocumentIdentifier, position: Position): Promise<LinkedEditingRanges | null>;
|
|
25
26
|
getDefinitions(textDocument: TextDocumentIdentifier, position: Position): Promise<DefinitionLink[] | Location[]>;
|
|
26
27
|
rename(textDocument: TextDocumentIdentifier, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
27
28
|
getDocumentColors(textDocument: TextDocumentIdentifier): Promise<ColorInformation[]>;
|
|
@@ -94,6 +94,10 @@ class PluginHost {
|
|
|
94
94
|
const document = this.getDocument(textDocument.uri);
|
|
95
95
|
return await this.execute('getSemanticTokens', [document, range, cancellationToken], ExecuteMode.FirstNonNull);
|
|
96
96
|
}
|
|
97
|
+
async getLinkedEditingRanges(textDocument, position) {
|
|
98
|
+
const document = this.getDocument(textDocument.uri);
|
|
99
|
+
return await this.execute('getLinkedEditingRanges', [document, position], ExecuteMode.FirstNonNull);
|
|
100
|
+
}
|
|
97
101
|
async getDefinitions(textDocument, position) {
|
|
98
102
|
const document = this.getDocument(textDocument.uri);
|
|
99
103
|
const definitions = (0, lodash_1.flatten)(await this.execute('getDefinitions', [document, position], ExecuteMode.Collect));
|
|
@@ -23,15 +23,16 @@ class CompletionsProviderImpl {
|
|
|
23
23
|
}
|
|
24
24
|
async getCompletions(document, position, completionContext) {
|
|
25
25
|
let items = [];
|
|
26
|
-
|
|
26
|
+
const html = document.html;
|
|
27
|
+
const offset = document.offsetAt(position);
|
|
28
|
+
const node = html.findNodeAt(offset);
|
|
29
|
+
const insideExpression = (0, utils_1.isInsideExpression)(document.getText(), node.start, offset);
|
|
30
|
+
if (completionContext?.triggerCharacter === '-' && node.parent === undefined && !insideExpression) {
|
|
27
31
|
const frontmatter = this.getComponentScriptCompletion(document, position);
|
|
28
32
|
if (frontmatter)
|
|
29
33
|
items.push(frontmatter);
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
const offset = document.offsetAt(position);
|
|
33
|
-
const node = html.findNodeAt(offset);
|
|
34
|
-
if ((0, utils_1.isInComponentStartTag)(html, offset) && !(0, utils_1.isInsideExpression)(document.getText(), node.start, offset)) {
|
|
35
|
+
if ((0, utils_1.isInComponentStartTag)(html, offset) && !insideExpression) {
|
|
35
36
|
const { completions: props, componentFilePath } = await this.getPropCompletionsAndFilePath(document, position, completionContext);
|
|
36
37
|
if (props.length) {
|
|
37
38
|
items.push(...props);
|
|
@@ -50,7 +51,7 @@ class CompletionsProviderImpl {
|
|
|
50
51
|
label: '---',
|
|
51
52
|
sortText: '\0',
|
|
52
53
|
preselect: true,
|
|
53
|
-
detail: '
|
|
54
|
+
detail: 'Create component script block',
|
|
54
55
|
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
|
|
55
56
|
commitCharacters: [],
|
|
56
57
|
};
|
|
@@ -65,11 +66,18 @@ class CompletionsProviderImpl {
|
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
if (document.astroMeta.frontmatter.state === 'open') {
|
|
69
|
+
let insertText = '---';
|
|
70
|
+
// If the current line is a full component script starter/ender, the user expects a full frontmatter
|
|
71
|
+
// completion and not just a completion for "---" on the same line (which result in, well, nothing)
|
|
72
|
+
if (prefix === '---') {
|
|
73
|
+
insertText = '---\n$0\n---';
|
|
74
|
+
}
|
|
68
75
|
return {
|
|
69
76
|
...base,
|
|
70
|
-
insertText
|
|
77
|
+
insertText,
|
|
78
|
+
detail: insertText === '---' ? 'Close component script block' : 'Create component script block',
|
|
71
79
|
textEdit: prefix.match(/^\s*\-+/)
|
|
72
|
-
? vscode_languageserver_1.TextEdit.replace({ start: { ...position, character: 0 }, end: position },
|
|
80
|
+
? vscode_languageserver_1.TextEdit.replace({ start: { ...position, character: 0 }, end: position }, insertText)
|
|
73
81
|
: undefined,
|
|
74
82
|
};
|
|
75
83
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompletionList, Position, TextEdit, FoldingRange, Hover, SymbolInformation, FormattingOptions } from 'vscode-languageserver';
|
|
1
|
+
import { CompletionList, Position, TextEdit, FoldingRange, Hover, SymbolInformation, FormattingOptions, LinkedEditingRanges } 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';
|
|
@@ -17,6 +17,7 @@ export declare class HTMLPlugin implements Plugin {
|
|
|
17
17
|
getCompletions(document: AstroDocument, position: Position): Promise<CompletionList | null>;
|
|
18
18
|
formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
|
|
19
19
|
getFoldingRanges(document: AstroDocument): FoldingRange[] | null;
|
|
20
|
+
getLinkedEditingRanges(document: AstroDocument, position: Position): LinkedEditingRanges | null;
|
|
20
21
|
doTagComplete(document: AstroDocument, position: Position): Promise<string | null>;
|
|
21
22
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
22
23
|
/**
|
|
@@ -63,8 +63,8 @@ class HTMLPlugin {
|
|
|
63
63
|
items: [],
|
|
64
64
|
};
|
|
65
65
|
const emmetConfig = await this.configManager.getEmmetConfig(document);
|
|
66
|
-
const extensionConfig = await this.configManager.getConfig('astro', document.uri);
|
|
67
|
-
if (extensionConfig
|
|
66
|
+
const extensionConfig = (await this.configManager.getConfig('astro', document.uri)) ?? {};
|
|
67
|
+
if (extensionConfig?.html?.completions?.emmet) {
|
|
68
68
|
this.lang.setCompletionParticipants([
|
|
69
69
|
{
|
|
70
70
|
onHtmlContent: () => (emmetResults = (0, emmet_helper_1.doComplete)(document, position, 'html', emmetConfig) || emmetResults),
|
|
@@ -105,6 +105,17 @@ class HTMLPlugin {
|
|
|
105
105
|
}
|
|
106
106
|
return this.lang.getFoldingRanges(document);
|
|
107
107
|
}
|
|
108
|
+
getLinkedEditingRanges(document, position) {
|
|
109
|
+
const html = document.html;
|
|
110
|
+
if (!html) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const ranges = this.lang.findLinkedEditingRanges(document, position, html);
|
|
114
|
+
if (!ranges) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return { ranges };
|
|
118
|
+
}
|
|
108
119
|
async doTagComplete(document, position) {
|
|
109
120
|
if (!(await this.featureEnabled(document, 'tagComplete'))) {
|
|
110
121
|
return null;
|
|
@@ -3,6 +3,18 @@ import { Diagnostic } from 'vscode-languageserver-types';
|
|
|
3
3
|
import { AstroDocument } from '../../../core/documents';
|
|
4
4
|
import { DiagnosticsProvider } from '../../interfaces';
|
|
5
5
|
import { LanguageServiceManager } from '../LanguageServiceManager';
|
|
6
|
+
export declare enum DiagnosticCodes {
|
|
7
|
+
SPREAD_EXPECTED = 1005,
|
|
8
|
+
DUPLICATED_JSX_ATTRIBUTES = 17001,
|
|
9
|
+
MUST_HAVE_PARENT_ELEMENT = 2657,
|
|
10
|
+
CANNOT_IMPORT_TS_EXT = 2691,
|
|
11
|
+
CANT_RETURN_OUTSIDE_FUNC = 1108,
|
|
12
|
+
ISOLATED_MODULE_COMPILE_ERR = 1208,
|
|
13
|
+
TYPE_NOT_ASSIGNABLE = 2322,
|
|
14
|
+
JSX_NO_CLOSING_TAG = 17008,
|
|
15
|
+
NO_DECL_IMPLICIT_ANY_TYPE = 7016,
|
|
16
|
+
JSX_ELEMENT_NO_CALL = 2604
|
|
17
|
+
}
|
|
6
18
|
export declare class DiagnosticsProviderImpl implements DiagnosticsProvider {
|
|
7
19
|
private readonly languageServiceManager;
|
|
8
20
|
constructor(languageServiceManager: LanguageServiceManager);
|
|
@@ -3,11 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DiagnosticsProviderImpl = void 0;
|
|
6
|
+
exports.DiagnosticsProviderImpl = exports.DiagnosticCodes = void 0;
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
8
|
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
|
|
9
9
|
const documents_1 = require("../../../core/documents");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
+
// List of codes:
|
|
12
|
+
// https://github.com/Microsoft/TypeScript/blob/main/src/compiler/diagnosticMessages.json
|
|
13
|
+
var DiagnosticCodes;
|
|
14
|
+
(function (DiagnosticCodes) {
|
|
15
|
+
DiagnosticCodes[DiagnosticCodes["SPREAD_EXPECTED"] = 1005] = "SPREAD_EXPECTED";
|
|
16
|
+
DiagnosticCodes[DiagnosticCodes["DUPLICATED_JSX_ATTRIBUTES"] = 17001] = "DUPLICATED_JSX_ATTRIBUTES";
|
|
17
|
+
DiagnosticCodes[DiagnosticCodes["MUST_HAVE_PARENT_ELEMENT"] = 2657] = "MUST_HAVE_PARENT_ELEMENT";
|
|
18
|
+
DiagnosticCodes[DiagnosticCodes["CANNOT_IMPORT_TS_EXT"] = 2691] = "CANNOT_IMPORT_TS_EXT";
|
|
19
|
+
DiagnosticCodes[DiagnosticCodes["CANT_RETURN_OUTSIDE_FUNC"] = 1108] = "CANT_RETURN_OUTSIDE_FUNC";
|
|
20
|
+
DiagnosticCodes[DiagnosticCodes["ISOLATED_MODULE_COMPILE_ERR"] = 1208] = "ISOLATED_MODULE_COMPILE_ERR";
|
|
21
|
+
DiagnosticCodes[DiagnosticCodes["TYPE_NOT_ASSIGNABLE"] = 2322] = "TYPE_NOT_ASSIGNABLE";
|
|
22
|
+
DiagnosticCodes[DiagnosticCodes["JSX_NO_CLOSING_TAG"] = 17008] = "JSX_NO_CLOSING_TAG";
|
|
23
|
+
DiagnosticCodes[DiagnosticCodes["NO_DECL_IMPLICIT_ANY_TYPE"] = 7016] = "NO_DECL_IMPLICIT_ANY_TYPE";
|
|
24
|
+
DiagnosticCodes[DiagnosticCodes["JSX_ELEMENT_NO_CALL"] = 2604] = "JSX_ELEMENT_NO_CALL";
|
|
25
|
+
})(DiagnosticCodes = exports.DiagnosticCodes || (exports.DiagnosticCodes = {}));
|
|
11
26
|
class DiagnosticsProviderImpl {
|
|
12
27
|
constructor(languageServiceManager) {
|
|
13
28
|
this.languageServiceManager = languageServiceManager;
|
|
@@ -39,17 +54,15 @@ class DiagnosticsProviderImpl {
|
|
|
39
54
|
code: diagnostic.code,
|
|
40
55
|
tags: getDiagnosticTag(diagnostic),
|
|
41
56
|
}))
|
|
57
|
+
.filter(isNoCantEndWithTS)
|
|
42
58
|
.map(mapRange(scriptTagSnapshot, document));
|
|
43
59
|
scriptDiagnostics.push(...scriptDiagnostic);
|
|
44
60
|
});
|
|
45
61
|
const { script: scriptBoundaries } = this.getTagBoundaries(lang, filePath);
|
|
46
|
-
const syntaxDiagnostics = lang.getSyntacticDiagnostics(filePath);
|
|
47
|
-
const suggestionDiagnostics = lang.getSuggestionDiagnostics(filePath);
|
|
48
|
-
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath);
|
|
49
62
|
const diagnostics = [
|
|
50
|
-
...
|
|
51
|
-
...
|
|
52
|
-
...
|
|
63
|
+
...lang.getSyntacticDiagnostics(filePath),
|
|
64
|
+
...lang.getSuggestionDiagnostics(filePath),
|
|
65
|
+
...lang.getSemanticDiagnostics(filePath),
|
|
53
66
|
].filter((diag) => {
|
|
54
67
|
return isNoWithinBoundary(scriptBoundaries, diag);
|
|
55
68
|
});
|
|
@@ -67,14 +80,15 @@ class DiagnosticsProviderImpl {
|
|
|
67
80
|
...scriptDiagnostics,
|
|
68
81
|
]
|
|
69
82
|
.filter((diag) => {
|
|
70
|
-
return (
|
|
71
|
-
|
|
83
|
+
return (
|
|
84
|
+
// Make sure the diagnostic is inside the document and not in generated code
|
|
85
|
+
diag.range.start.line <= document.lineCount &&
|
|
86
|
+
hasNoNegativeLines(diag) &&
|
|
72
87
|
isNoJSXMustHaveOneParent(diag) &&
|
|
73
|
-
|
|
74
|
-
isNoSpreadExpected(diag) &&
|
|
75
|
-
isNoCantResolveJSONModule(diag) &&
|
|
88
|
+
isNoSpreadExpected(diag, document) &&
|
|
76
89
|
isNoCantReturnOutsideFunction(diag) &&
|
|
77
90
|
isNoIsolatedModuleError(diag) &&
|
|
91
|
+
isNoImportImplicitAnyType(diag) &&
|
|
78
92
|
isNoJsxCannotHaveMultipleAttrsError(diag));
|
|
79
93
|
})
|
|
80
94
|
.map(enhanceIfNecessary);
|
|
@@ -113,30 +127,39 @@ class DiagnosticsProviderImpl {
|
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
exports.DiagnosticsProviderImpl = DiagnosticsProviderImpl;
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
function isWithinBoundaries(boundaries, start) {
|
|
131
|
+
for (let [bstart, bend] of boundaries) {
|
|
132
|
+
if (start > bstart && start < bend) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
function diagnosticIsWithinBoundaries(sourceFile, boundaries, diagnostic) {
|
|
139
|
+
if ('start' in diagnostic) {
|
|
140
|
+
if (diagnostic.start == null)
|
|
141
|
+
return false;
|
|
142
|
+
return isWithinBoundaries(boundaries, diagnostic.start);
|
|
123
143
|
}
|
|
124
|
-
|
|
144
|
+
if (!sourceFile)
|
|
145
|
+
return false;
|
|
146
|
+
let startRange = diagnostic.range.start;
|
|
147
|
+
let pos = typescript_1.default.getPositionOfLineAndCharacter(sourceFile, startRange.line, startRange.character);
|
|
148
|
+
return isWithinBoundaries(boundaries, pos);
|
|
149
|
+
}
|
|
150
|
+
function isNoWithinBoundary(boundaries, diagnostic) {
|
|
151
|
+
return !diagnosticIsWithinBoundaries(undefined, boundaries, diagnostic);
|
|
125
152
|
}
|
|
126
153
|
function mapRange(fragment, _document) {
|
|
127
154
|
return (diagnostic) => {
|
|
128
155
|
let range = (0, documents_1.mapRangeToOriginal)(fragment, diagnostic.range);
|
|
129
|
-
if (range.start.line < 0) {
|
|
130
|
-
// Could be a props error?
|
|
131
|
-
// From svelte
|
|
132
|
-
}
|
|
133
156
|
return { ...diagnostic, range };
|
|
134
157
|
};
|
|
135
158
|
}
|
|
136
159
|
/**
|
|
137
160
|
* In some rare cases mapping of diagnostics does not work and produces negative lines.
|
|
138
161
|
* We filter out these diagnostics with negative lines because else the LSP
|
|
139
|
-
* apparently has a
|
|
162
|
+
* apparently has a hiccup and does not show any diagnostics at all.
|
|
140
163
|
*/
|
|
141
164
|
function hasNoNegativeLines(diagnostic) {
|
|
142
165
|
return diagnostic.range.start.line >= 0 && diagnostic.range.end.line >= 0;
|
|
@@ -145,56 +168,68 @@ function hasNoNegativeLines(diagnostic) {
|
|
|
145
168
|
* Astro allows multiple attributes to have the same name
|
|
146
169
|
*/
|
|
147
170
|
function isNoJsxCannotHaveMultipleAttrsError(diagnostic) {
|
|
148
|
-
return diagnostic.code !==
|
|
171
|
+
return diagnostic.code !== DiagnosticCodes.DUPLICATED_JSX_ATTRIBUTES;
|
|
149
172
|
}
|
|
150
173
|
/** Astro allows component with multiple root elements */
|
|
151
174
|
function isNoJSXMustHaveOneParent(diagnostic) {
|
|
152
|
-
return diagnostic.code !==
|
|
175
|
+
return diagnostic.code !== DiagnosticCodes.MUST_HAVE_PARENT_ELEMENT;
|
|
153
176
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return diagnostic.code !== 2691;
|
|
177
|
+
function isNoImportImplicitAnyType(diagnostic) {
|
|
178
|
+
return diagnostic.code !== DiagnosticCodes.NO_DECL_IMPLICIT_ANY_TYPE;
|
|
157
179
|
}
|
|
158
|
-
|
|
159
|
-
|
|
180
|
+
/**
|
|
181
|
+
* When using the shorthand syntax for props TSX expects you to use the spread operator
|
|
182
|
+
* Since the shorthand syntax works differently in Astro and this is not required, hide this message
|
|
183
|
+
* However, the error code used here is quite generic, as such we need to make we only ignore in valid cases
|
|
184
|
+
*/
|
|
185
|
+
function isNoSpreadExpected(diagnostic, document) {
|
|
186
|
+
if (diagnostic.code === DiagnosticCodes.SPREAD_EXPECTED &&
|
|
187
|
+
diagnostic.message.includes('...') &&
|
|
188
|
+
document.offsetAt(diagnostic.range.start) > (document.astroMeta.frontmatter.endOffset ?? 0)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
160
192
|
}
|
|
161
|
-
|
|
162
|
-
|
|
193
|
+
/** Inside script tags, Astro currently require the `.ts` file extension for imports */
|
|
194
|
+
function isNoCantEndWithTS(diagnostic) {
|
|
195
|
+
return diagnostic.code !== DiagnosticCodes.CANNOT_IMPORT_TS_EXT;
|
|
163
196
|
}
|
|
164
197
|
/**
|
|
165
198
|
* Ignore "Can't return outside of function body"
|
|
166
|
-
*
|
|
167
|
-
* to ignore this. It wasn't a problem before because users didn't need to return things but they can now with SSR
|
|
199
|
+
* Since the frontmatter is at the top level, users trying to return a Response for SSR mode run into this
|
|
168
200
|
*/
|
|
169
201
|
function isNoCantReturnOutsideFunction(diagnostic) {
|
|
170
|
-
return diagnostic.code !==
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Astro allows users to import JSON modules
|
|
174
|
-
*/
|
|
175
|
-
function isNoCantResolveJSONModule(diagnostic) {
|
|
176
|
-
return diagnostic.code !== 2732;
|
|
202
|
+
return diagnostic.code !== DiagnosticCodes.CANT_RETURN_OUTSIDE_FUNC;
|
|
177
203
|
}
|
|
178
204
|
/**
|
|
179
205
|
* When the content of the file is invalid and can't be parsed properly for TSX generation, TS will show an error about
|
|
180
206
|
* how the current module can't be compiled under --isolatedModule, this is confusing to users so let's ignore this
|
|
181
207
|
*/
|
|
182
208
|
function isNoIsolatedModuleError(diagnostic) {
|
|
183
|
-
return diagnostic.code !==
|
|
209
|
+
return diagnostic.code !== DiagnosticCodes.ISOLATED_MODULE_COMPILE_ERR;
|
|
184
210
|
}
|
|
185
211
|
/**
|
|
186
212
|
* Some diagnostics have JSX-specific nomenclature or unclear description. Enhance them for more clarity.
|
|
187
213
|
*/
|
|
188
214
|
function enhanceIfNecessary(diagnostic) {
|
|
189
215
|
// JSX element has no closing tag. JSX -> HTML
|
|
190
|
-
if (diagnostic.code ===
|
|
216
|
+
if (diagnostic.code === DiagnosticCodes.JSX_NO_CLOSING_TAG) {
|
|
191
217
|
return {
|
|
192
218
|
...diagnostic,
|
|
193
219
|
message: diagnostic.message.replace('JSX', 'HTML'),
|
|
194
220
|
};
|
|
195
221
|
}
|
|
222
|
+
// JSX Element can't be constructed or called. This happens on syntax errors / invalid components
|
|
223
|
+
if (diagnostic.code === DiagnosticCodes.JSX_ELEMENT_NO_CALL) {
|
|
224
|
+
return {
|
|
225
|
+
...diagnostic,
|
|
226
|
+
message: diagnostic.message
|
|
227
|
+
.replace('JSX element type', 'Component')
|
|
228
|
+
.replace('does not have any construct or call signatures.', 'is not a valid component.\n\nIf this is a Svelte or Vue component, it might have a syntax error that makes it impossible to parse.'),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
196
231
|
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
197
|
-
if (diagnostic.code ===
|
|
232
|
+
if (diagnostic.code === DiagnosticCodes.TYPE_NOT_ASSIGNABLE) {
|
|
198
233
|
if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
|
|
199
234
|
return {
|
|
200
235
|
...diagnostic,
|
|
@@ -202,28 +237,23 @@ function enhanceIfNecessary(diagnostic) {
|
|
|
202
237
|
};
|
|
203
238
|
}
|
|
204
239
|
}
|
|
240
|
+
// An import path cannot end with '.ts(x)' consider importing with no extension
|
|
241
|
+
// TODO: Remove this when https://github.com/withastro/astro/issues/3415 is fixed
|
|
242
|
+
if (diagnostic.code === DiagnosticCodes.CANNOT_IMPORT_TS_EXT) {
|
|
243
|
+
return {
|
|
244
|
+
...diagnostic,
|
|
245
|
+
message: diagnostic.message.replace(/\.jsx?/, ''),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
205
248
|
return diagnostic;
|
|
206
249
|
}
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
250
|
+
function getDiagnosticTag(diagnostic) {
|
|
251
|
+
const tags = [];
|
|
252
|
+
if (diagnostic.reportsUnnecessary) {
|
|
253
|
+
tags.push(vscode_languageserver_types_1.DiagnosticTag.Unnecessary);
|
|
212
254
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
function diagnosticIsWithinBoundaries(sourceFile, boundaries, diagnostic) {
|
|
216
|
-
if ('start' in diagnostic) {
|
|
217
|
-
if (diagnostic.start == null)
|
|
218
|
-
return false;
|
|
219
|
-
return isWithinBoundaries(boundaries, diagnostic.start);
|
|
255
|
+
if (diagnostic.reportsDeprecated) {
|
|
256
|
+
tags.push(vscode_languageserver_types_1.DiagnosticTag.Deprecated);
|
|
220
257
|
}
|
|
221
|
-
|
|
222
|
-
return false;
|
|
223
|
-
let startRange = diagnostic.range.start;
|
|
224
|
-
let pos = typescript_1.default.getPositionOfLineAndCharacter(sourceFile, startRange.line, startRange.character);
|
|
225
|
-
return isWithinBoundaries(boundaries, pos);
|
|
226
|
-
}
|
|
227
|
-
function isNoWithinBoundary(boundaries, diagnostic) {
|
|
228
|
-
return !diagnosticIsWithinBoundaries(undefined, boundaries, diagnostic);
|
|
258
|
+
return tags;
|
|
229
259
|
}
|
|
@@ -224,6 +224,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
224
224
|
const forcedCompilerOptions = {
|
|
225
225
|
noEmit: true,
|
|
226
226
|
declaration: false,
|
|
227
|
+
resolveJsonModule: true,
|
|
227
228
|
allowNonTsExtensions: true,
|
|
228
229
|
allowJs: true,
|
|
229
230
|
jsx: typescript_1.default.JsxEmit.Preserve,
|
package/dist/server.js
CHANGED
|
@@ -129,6 +129,7 @@ function startLanguageServer(connection) {
|
|
|
129
129
|
colorProvider: true,
|
|
130
130
|
hoverProvider: true,
|
|
131
131
|
documentSymbolProvider: true,
|
|
132
|
+
linkedEditingRangeProvider: true,
|
|
132
133
|
semanticTokensProvider: {
|
|
133
134
|
legend: (0, utils_2.getSemanticTokenLegend)(),
|
|
134
135
|
range: true,
|
|
@@ -195,6 +196,7 @@ function startLanguageServer(connection) {
|
|
|
195
196
|
connection.onDocumentSymbol((params, cancellationToken) => pluginHost.getDocumentSymbols(params.textDocument, cancellationToken));
|
|
196
197
|
connection.onRequest(vscode_languageserver_1.SemanticTokensRequest.type, (evt, cancellationToken) => pluginHost.getSemanticTokens(evt.textDocument, undefined, cancellationToken));
|
|
197
198
|
connection.onRequest(vscode_languageserver_1.SemanticTokensRangeRequest.type, (evt, cancellationToken) => pluginHost.getSemanticTokens(evt.textDocument, evt.range, cancellationToken));
|
|
199
|
+
connection.onRequest(vscode_languageserver_1.LinkedEditingRangeRequest.type, async (evt) => await pluginHost.getLinkedEditingRanges(evt.textDocument, evt.position));
|
|
198
200
|
connection.onDocumentFormatting((params) => pluginHost.formatDocument(params.textDocument, params.options));
|
|
199
201
|
connection.onDocumentColor((params) => pluginHost.getDocumentColors(params.textDocument));
|
|
200
202
|
connection.onColorPresentation((params) => pluginHost.getColorPresentations(params.textDocument, params.range, params.color));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/language-server",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"author": "withastro",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --timeout 20000 --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\""
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@astrojs/vue-language-integration": "^0.1.
|
|
23
|
-
"@astrojs/svelte-language-integration": "^0.1.
|
|
22
|
+
"@astrojs/vue-language-integration": "^0.1.1",
|
|
23
|
+
"@astrojs/svelte-language-integration": "^0.1.6",
|
|
24
24
|
"@vscode/emmet-helper": "^2.8.4",
|
|
25
25
|
"lodash": "^4.17.21",
|
|
26
26
|
"source-map": "^0.7.3",
|