@astrojs/language-server 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @astrojs/language-server
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a97b9a4: Add support for Inlay Hints. Minimum VS Code version supported starting from this update is 1.67.0 (April 2022)
8
+
9
+ ## 0.18.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 666739a: Revert update to latest LSP and inlay hints support
14
+
15
+ ## 0.18.0
16
+
17
+ ### Minor Changes
18
+
19
+ - d3c6fd8: Add support for formatting
20
+ - 09e1163: Updated language server to latest version of LSP, added support for Inlay Hints
21
+ - fcaba8e: Add support for completions and type checking for Vue props
22
+
23
+ ### Patch Changes
24
+
25
+ - 4138005: Fix frontmatter folding not working properly when last few lines of frontmatter are empty
26
+ - 76ff46a: Add `?` in the label of completions of optional parameters (including component props)
27
+
3
28
  ## 0.17.0
4
29
 
5
30
  ### Minor Changes
@@ -1,8 +1,8 @@
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
- import { FormatCodeSettings, UserPreferences } from 'typescript';
5
+ import { FormatCodeSettings, InlayHintsOptions, UserPreferences } from 'typescript';
6
6
  export declare const defaultLSConfig: LSConfig;
7
7
  declare type DeepPartial<T> = T extends Record<string, unknown> ? {
8
8
  [P in keyof T]?: DeepPartial<T[P]>;
@@ -22,16 +22,21 @@ 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
- getTSFormatConfig(document: TextDocument): Promise<FormatCodeSettings>;
25
+ getAstroFormatConfig(document: TextDocument): Promise<LSFormatConfig>;
26
+ getTSFormatConfig(document: TextDocument, vscodeOptions?: FormattingOptions): Promise<FormatCodeSettings>;
26
27
  getTSPreferences(document: TextDocument): Promise<UserPreferences>;
28
+ getTSInlayHintsPreferences(document: TextDocument): Promise<InlayHintsOptions>;
27
29
  /**
28
30
  * Return true if a plugin and an optional feature is enabled
29
31
  */
30
32
  isEnabled(document: TextDocument, plugin: keyof LSConfig, feature?: keyof LSTypescriptConfig | keyof LSCSSConfig | keyof LSHTMLConfig): Promise<boolean>;
31
33
  /**
32
34
  * Updating the global config should only be done in cases where the client doesn't support `workspace/configuration`
33
- * or inside of tests
35
+ * or inside of tests.
36
+ *
37
+ * The `outsideAstro` parameter can be set to true to change configurations in the global scope.
38
+ * For example, to change TypeScript settings
34
39
  */
35
- updateGlobalConfig(config: DeepPartial<LSConfig>): void;
40
+ updateGlobalConfig(config: DeepPartial<LSConfig> | any, outsideAstro?: boolean): void;
36
41
  }
37
42
  export {};
@@ -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 getTSFormatConfig(document) {
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,
@@ -110,6 +124,20 @@ class ConfigManager {
110
124
  includeCompletionsWithInsertText: true,
111
125
  };
112
126
  }
127
+ async getTSInlayHintsPreferences(document) {
128
+ const config = (await this.getConfig('typescript', document.uri)) ?? {};
129
+ const tsPreferences = this.getTSPreferences(document);
130
+ return {
131
+ ...tsPreferences,
132
+ includeInlayParameterNameHints: getInlayParameterNameHintsPreference(config),
133
+ includeInlayParameterNameHintsWhenArgumentMatchesName: !(config.inlayHints?.parameterNames?.suppressWhenArgumentMatchesName ?? true),
134
+ includeInlayFunctionParameterTypeHints: config.inlayHints?.parameterTypes?.enabled ?? false,
135
+ includeInlayVariableTypeHints: config.inlayHints?.variableTypes?.enabled ?? false,
136
+ includeInlayPropertyDeclarationTypeHints: config.inlayHints?.propertyDeclarationTypes?.enabled ?? false,
137
+ includeInlayFunctionLikeReturnTypeHints: config.inlayHints?.functionLikeReturnTypes?.enabled ?? false,
138
+ includeInlayEnumMemberValueHints: config.inlayHints?.enumMemberValues?.enabled ?? false,
139
+ };
140
+ }
113
141
  /**
114
142
  * Return true if a plugin and an optional feature is enabled
115
143
  */
@@ -119,10 +147,18 @@ class ConfigManager {
119
147
  }
120
148
  /**
121
149
  * Updating the global config should only be done in cases where the client doesn't support `workspace/configuration`
122
- * or inside of tests
150
+ * or inside of tests.
151
+ *
152
+ * The `outsideAstro` parameter can be set to true to change configurations in the global scope.
153
+ * For example, to change TypeScript settings
123
154
  */
124
- updateGlobalConfig(config) {
125
- this.globalConfig.astro = (0, lodash_1.merge)({}, exports.defaultLSConfig, this.globalConfig.astro, config);
155
+ updateGlobalConfig(config, outsideAstro) {
156
+ if (outsideAstro) {
157
+ this.globalConfig = (0, lodash_1.merge)({}, this.globalConfig, config);
158
+ }
159
+ else {
160
+ this.globalConfig.astro = (0, lodash_1.merge)({}, exports.defaultLSConfig, this.globalConfig.astro, config);
161
+ }
126
162
  }
127
163
  }
128
164
  exports.ConfigManager = ConfigManager;
@@ -160,3 +196,15 @@ function getImportModuleSpecifierEndingPreference(config) {
160
196
  return 'auto';
161
197
  }
162
198
  }
199
+ function getInlayParameterNameHintsPreference(config) {
200
+ switch (config.inlayHints?.parameterNames?.enabled) {
201
+ case 'none':
202
+ return 'none';
203
+ case 'literals':
204
+ return 'literals';
205
+ case 'all':
206
+ return 'all';
207
+ default:
208
+ return undefined;
209
+ }
210
+ }
@@ -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;
@@ -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, InlayHint, 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>;
@@ -24,6 +25,7 @@ export declare class PluginHost {
24
25
  getDefinitions(textDocument: TextDocumentIdentifier, position: Position): Promise<DefinitionLink[] | Location[]>;
25
26
  rename(textDocument: TextDocumentIdentifier, position: Position, newName: string): Promise<WorkspaceEdit | null>;
26
27
  getDocumentColors(textDocument: TextDocumentIdentifier): Promise<ColorInformation[]>;
28
+ getInlayHints(textDocument: TextDocumentIdentifier, range: Range, cancellationToken: CancellationToken): Promise<InlayHint[]>;
27
29
  getColorPresentations(textDocument: TextDocumentIdentifier, range: Range, color: Color): Promise<ColorPresentation[]>;
28
30
  getSignatureHelp(textDocument: TextDocumentIdentifier, position: Position, context: SignatureHelpContext | undefined, cancellationToken: CancellationToken): Promise<SignatureHelp | null>;
29
31
  onWatchFileChanges(onWatchFileChangesParams: any[]): void;
@@ -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));
@@ -108,6 +112,10 @@ class PluginHost {
108
112
  const document = this.getDocument(textDocument.uri);
109
113
  return (0, lodash_1.flatten)(await this.execute('getDocumentColors', [document], ExecuteMode.Collect));
110
114
  }
115
+ async getInlayHints(textDocument, range, cancellationToken) {
116
+ const document = this.getDocument(textDocument.uri);
117
+ return (0, lodash_1.flatten)(await this.execute('getInlayHints', [document, range], ExecuteMode.FirstNonNull));
118
+ }
111
119
  async getColorPresentations(textDocument, range, color) {
112
120
  const document = this.getDocument(textDocument.uri);
113
121
  return (0, lodash_1.flatten)(await this.execute('getColorPresentations', [document, range, color], ExecuteMode.Collect));
@@ -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
- const start = document.positionAt(frontmatter.startOffset);
25
- const end = document.positionAt(frontmatter.endOffset - 3);
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,
@@ -140,10 +140,6 @@ class CompletionsProviderImpl {
140
140
  let completionItem = this.getCompletionItemForProperty(property, typeChecker, type);
141
141
  completionItems.push(completionItem);
142
142
  });
143
- // Ensure that props shows up first as a completion, despite this plugin being ran after the HTML one
144
- completionItems = completionItems.map((item) => {
145
- return { ...item, sortText: '_' };
146
- });
147
143
  this.lastCompletion = {
148
144
  tag: componentName,
149
145
  documentVersion: componentSnapshot.version,
@@ -215,7 +211,15 @@ class CompletionsProviderImpl {
215
211
  insertText: insertText,
216
212
  insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
217
213
  commitCharacters: [],
214
+ // Ensure that props shows up first as a completion, despite this plugin being ran after the HTML one
215
+ sortText: '\0',
218
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
+ }
219
223
  mem.getDocumentationComment(typeChecker);
220
224
  let description = mem
221
225
  .getDocumentationComment(typeChecker)
@@ -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 { CodeAction, CodeActionContext, Color, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FileChangeType, FoldingRange, FormattingOptions, Hover, LinkedEditingRanges, Position, Range, ReferenceContext, SelectionRange, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextEdit, WorkspaceEdit } from 'vscode-languageserver';
1
+ import { CodeAction, CodeActionContext, Color, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FileChangeType, FoldingRange, FormattingOptions, Hover, InlayHint, LinkedEditingRanges, Position, Range, ReferenceContext, SelectionRange, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextEdit, WorkspaceEdit } from 'vscode-languageserver';
2
2
  import { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  export declare type Resolvable<T> = T | Promise<T>;
4
4
  export interface AppCompletionItem<T extends TextDocumentIdentifier = any> extends CompletionItem {
@@ -52,6 +52,9 @@ export interface FileRename {
52
52
  export interface UpdateImportsProvider {
53
53
  updateImports(fileRename: FileRename): Resolvable<WorkspaceEdit | null>;
54
54
  }
55
+ export interface InlayHintsProvider {
56
+ getInlayHints(document: TextDocument, range: Range): Resolvable<InlayHint[]>;
57
+ }
55
58
  export interface RenameProvider {
56
59
  rename(document: TextDocument, position: Position, newName: string): Resolvable<WorkspaceEdit | null>;
57
60
  prepareRename(document: TextDocument, position: Position): Resolvable<Range | null>;
@@ -81,7 +84,7 @@ export interface OnWatchFileChangesProvider {
81
84
  export interface UpdateNonAstroFile {
82
85
  updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
83
86
  }
84
- declare type ProviderBase = DiagnosticsProvider & HoverProvider & CompletionsProvider & DefinitionsProvider & FormattingProvider & FoldingRangesProvider & TagCompleteProvider & DocumentColorsProvider & ColorPresentationsProvider & DocumentSymbolsProvider & UpdateImportsProvider & CodeActionsProvider & FindReferencesProvider & RenameProvider & SignatureHelpProvider & SemanticTokensProvider & SelectionRangeProvider & OnWatchFileChangesProvider & LinkedEditingRangesProvider & UpdateNonAstroFile;
87
+ declare type ProviderBase = DiagnosticsProvider & HoverProvider & CompletionsProvider & DefinitionsProvider & FormattingProvider & FoldingRangesProvider & TagCompleteProvider & DocumentColorsProvider & ColorPresentationsProvider & DocumentSymbolsProvider & UpdateImportsProvider & CodeActionsProvider & FindReferencesProvider & RenameProvider & SignatureHelpProvider & SemanticTokensProvider & SelectionRangeProvider & OnWatchFileChangesProvider & LinkedEditingRangesProvider & InlayHintsProvider & UpdateNonAstroFile;
85
88
  export declare type LSProvider = ProviderBase;
86
89
  export declare type Plugin = Partial<ProviderBase> & {
87
90
  __name: string;
@@ -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, InlayHint, 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';
@@ -14,17 +14,21 @@ export declare class TypeScriptPlugin implements Plugin {
14
14
  private readonly signatureHelpProvider;
15
15
  private readonly diagnosticsProvider;
16
16
  private readonly documentSymbolsProvider;
17
+ private readonly inlayHintsProvider;
17
18
  private readonly semanticTokensProvider;
18
19
  private readonly foldingRangesProvider;
20
+ private readonly formattingProvider;
19
21
  constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
20
22
  doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
21
23
  rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
24
+ formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
22
25
  getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
23
26
  getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
24
27
  getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
25
28
  getCodeActions(document: AstroDocument, range: Range, context: CodeActionContext, cancellationToken?: CancellationToken): Promise<CodeAction[]>;
26
29
  getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
27
30
  resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
31
+ getInlayHints(document: AstroDocument, range: Range): Promise<InlayHint[]>;
28
32
  getDefinitions(document: AstroDocument, position: Position): Promise<DefinitionLink[]>;
29
33
  getDiagnostics(document: AstroDocument, cancellationToken?: CancellationToken): Promise<Diagnostic[]>;
30
34
  onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesParam[]): Promise<void>;
@@ -17,6 +17,8 @@ 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 InlayHintsProvider_1 = require("./features/InlayHintsProvider");
21
+ const FormattingProvider_1 = require("./features/FormattingProvider");
20
22
  class TypeScriptPlugin {
21
23
  constructor(docManager, configManager, workspaceUris) {
22
24
  this.__name = 'typescript';
@@ -30,7 +32,9 @@ class TypeScriptPlugin {
30
32
  this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
31
33
  this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
32
34
  this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
35
+ this.inlayHintsProvider = new InlayHintsProvider_1.InlayHintsProviderImpl(this.languageServiceManager, this.configManager);
33
36
  this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
37
+ this.formattingProvider = new FormattingProvider_1.FormattingProviderImpl(this.languageServiceManager, this.configManager);
34
38
  }
35
39
  async doHover(document, position) {
36
40
  if (!(await this.featureEnabled(document, 'hover'))) {
@@ -61,6 +65,9 @@ class TypeScriptPlugin {
61
65
  });
62
66
  return edit;
63
67
  }
68
+ async formatDocument(document, options) {
69
+ return this.formattingProvider.formatDocument(document, options);
70
+ }
64
71
  async getFoldingRanges(document) {
65
72
  return this.foldingRangesProvider.getFoldingRanges(document);
66
73
  }
@@ -93,6 +100,9 @@ class TypeScriptPlugin {
93
100
  async resolveCompletion(document, completionItem, cancellationToken) {
94
101
  return this.completionProvider.resolveCompletion(document, completionItem, cancellationToken);
95
102
  }
103
+ async getInlayHints(document, range) {
104
+ return this.inlayHintsProvider.getInlayHints(document, range);
105
+ }
96
106
  async getDefinitions(document, position) {
97
107
  return this.definitionsProvider.getDefinitions(document, position);
98
108
  }
@@ -159,6 +159,10 @@ class CompletionsProviderImpl {
159
159
  );
160
160
  if (detail) {
161
161
  const { detail: itemDetail, documentation: itemDocumentation } = this.getCompletionDocument(detail);
162
+ // TODO: Add support for labelDetails
163
+ // if (data.originalItem.source) {
164
+ // item.labelDetails = { description: data.originalItem.source };
165
+ // }
162
166
  item.detail = itemDetail;
163
167
  item.documentation = itemDocumentation;
164
168
  }
@@ -209,14 +213,23 @@ class CompletionsProviderImpl {
209
213
  }
210
214
  if (comp.kindModifiers) {
211
215
  const kindModifiers = new Set(comp.kindModifiers.split(/,|\s+/g));
216
+ if (kindModifiers.has(typescript_1.ScriptElementKindModifier.optionalModifier)) {
217
+ if (!item.insertText) {
218
+ item.insertText = item.label;
219
+ }
220
+ if (!item.filterText) {
221
+ item.filterText = item.label;
222
+ }
223
+ item.label += '?';
224
+ }
212
225
  if (kindModifiers.has(typescript_1.ScriptElementKindModifier.deprecatedModifier)) {
213
226
  item.tags = [vscode_languageserver_1.CompletionItemTag.Deprecated];
214
227
  }
215
228
  }
216
- // Label details are currently unsupported, however, they'll be supported in the next version of LSP
217
- if (comp.sourceDisplay) {
218
- item.labelDetails = { description: typescript_1.default.displayPartsToString(comp.sourceDisplay) };
219
- }
229
+ // TODO: Add support for labelDetails
230
+ // if (comp.sourceDisplay) {
231
+ // item.labelDetails = { description: ts.displayPartsToString(comp.sourceDisplay) };
232
+ // }
220
233
  item.commitCharacters = (0, utils_2.getCommitCharactersForScriptElement)(comp.kind);
221
234
  item.sortText = comp.sortText;
222
235
  item.preselect = comp.isRecommended;
@@ -0,0 +1,11 @@
1
+ import { FormattingOptions, TextEdit } from 'vscode-languageserver-types';
2
+ import { ConfigManager } from '../../../core/config';
3
+ import { AstroDocument } from '../../../core/documents';
4
+ import { FormattingProvider } from '../../interfaces';
5
+ import { LanguageServiceManager } from '../LanguageServiceManager';
6
+ export declare class FormattingProviderImpl implements FormattingProvider {
7
+ private languageServiceManager;
8
+ private configManager;
9
+ constructor(languageServiceManager: LanguageServiceManager, configManager: ConfigManager);
10
+ formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
11
+ }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FormattingProviderImpl = void 0;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const vscode_languageserver_types_1 = require("vscode-languageserver-types");
9
+ const utils_1 = require("../utils");
10
+ class FormattingProviderImpl {
11
+ constructor(languageServiceManager, configManager) {
12
+ this.languageServiceManager = languageServiceManager;
13
+ this.configManager = configManager;
14
+ }
15
+ async formatDocument(document, options) {
16
+ const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
17
+ const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
18
+ const formatConfig = await this.configManager.getTSFormatConfig(document, options);
19
+ let frontmatterEdits = [];
20
+ let scriptTagsEdits = [];
21
+ if (document.astroMeta.frontmatter.state === 'closed') {
22
+ const start = document.positionAt(document.astroMeta.frontmatter.startOffset + 3);
23
+ start.line += 1;
24
+ start.character = 0;
25
+ const startOffset = document.offsetAt(start);
26
+ const endOffset = document.astroMeta.frontmatter.endOffset;
27
+ const astroFormatConfig = await this.configManager.getAstroFormatConfig(document);
28
+ const settings = {
29
+ ...formatConfig,
30
+ baseIndentSize: astroFormatConfig.indentFrontmatter ? formatConfig.tabSize ?? 0 : undefined,
31
+ };
32
+ frontmatterEdits = lang.getFormattingEditsForRange(filePath, startOffset, endOffset, settings);
33
+ if (astroFormatConfig.newLineAfterFrontmatter) {
34
+ const templateStart = document.positionAt(endOffset + 3);
35
+ templateStart.line += 1;
36
+ templateStart.character = 0;
37
+ frontmatterEdits.push({
38
+ span: { start: document.offsetAt(templateStart), length: 0 },
39
+ newText: '\n',
40
+ });
41
+ }
42
+ }
43
+ document.scriptTags.forEach((scriptTag) => {
44
+ const { filePath: scriptFilePath, snapshot: scriptTagSnapshot } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
45
+ const startLine = document.offsetAt(vscode_languageserver_types_1.Position.create(scriptTag.startPos.line, 0));
46
+ const initialIndentLevel = computeInitialIndent(document, startLine, options);
47
+ const baseIndent = (formatConfig.tabSize ?? 0) * (initialIndentLevel + 1);
48
+ const formatSettings = {
49
+ baseIndentSize: baseIndent,
50
+ indentStyle: typescript_1.default.IndentStyle.Smart,
51
+ ...formatConfig,
52
+ };
53
+ let edits = lang.getFormattingEditsForDocument(scriptFilePath, formatSettings);
54
+ if (edits) {
55
+ edits = edits
56
+ .map((edit) => {
57
+ edit.span.start = document.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(edit.span.start)));
58
+ return edit;
59
+ })
60
+ .filter((edit) => {
61
+ return (scriptTagSnapshot.isInGenerated(document.positionAt(edit.span.start)) &&
62
+ scriptTag.end !== edit.span.start &&
63
+ // Don't format the last line of the file as it's in most case the indentation
64
+ scriptTag.endPos.line !== document.positionAt(edit.span.start).line);
65
+ });
66
+ const endLine = document.getLineUntilOffset(document.offsetAt(scriptTag.endPos));
67
+ if (isWhitespaceOnly(endLine)) {
68
+ const endLineStartOffset = document.offsetAt(vscode_languageserver_types_1.Position.create(scriptTag.endPos.line, 0));
69
+ const lastLineIndentRange = vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(scriptTag.endPos.line, 0), scriptTag.endPos);
70
+ const newText = generateIndent(initialIndentLevel, options);
71
+ if (endLine !== newText) {
72
+ edits.push({
73
+ span: {
74
+ start: endLineStartOffset,
75
+ length: lastLineIndentRange.end.character,
76
+ },
77
+ newText,
78
+ });
79
+ }
80
+ }
81
+ }
82
+ scriptTagsEdits.push(...edits);
83
+ });
84
+ return [...frontmatterEdits, ...scriptTagsEdits].map((edit) => ({
85
+ range: (0, utils_1.convertRange)(document, edit.span),
86
+ newText: edit.newText,
87
+ }));
88
+ }
89
+ }
90
+ exports.FormattingProviderImpl = FormattingProviderImpl;
91
+ function computeInitialIndent(document, lineStart, options) {
92
+ let content = document.getText();
93
+ let i = lineStart;
94
+ let nChars = 0;
95
+ let tabSize = options.tabSize || 4;
96
+ while (i < content.length) {
97
+ let ch = content.charAt(i);
98
+ if (ch === ' ') {
99
+ nChars++;
100
+ }
101
+ else if (ch === '\t') {
102
+ nChars += tabSize;
103
+ }
104
+ else {
105
+ break;
106
+ }
107
+ i++;
108
+ }
109
+ return Math.floor(nChars / tabSize);
110
+ }
111
+ function generateIndent(level, options) {
112
+ if (options.insertSpaces) {
113
+ return repeat(' ', level * options.tabSize);
114
+ }
115
+ else {
116
+ return repeat('\t', level);
117
+ }
118
+ }
119
+ function repeat(value, count) {
120
+ let s = '';
121
+ while (count > 0) {
122
+ if ((count & 1) === 1) {
123
+ s += value;
124
+ }
125
+ value += value;
126
+ count = count >>> 1;
127
+ }
128
+ return s;
129
+ }
130
+ function isWhitespaceOnly(str) {
131
+ return /^\s*$/.test(str);
132
+ }
@@ -0,0 +1,12 @@
1
+ import { InlayHint } from 'vscode-languageserver';
2
+ import { AstroDocument } from '../../../core/documents';
3
+ import { InlayHintsProvider } from '../../interfaces';
4
+ import { LanguageServiceManager } from '../LanguageServiceManager';
5
+ import { Range } from 'vscode-languageserver-types';
6
+ import { ConfigManager } from '../../../core/config';
7
+ export declare class InlayHintsProviderImpl implements InlayHintsProvider {
8
+ private languageServiceManager;
9
+ private configManager;
10
+ constructor(languageServiceManager: LanguageServiceManager, configManager: ConfigManager);
11
+ getInlayHints(document: AstroDocument, range: Range): Promise<InlayHint[]>;
12
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InlayHintsProviderImpl = void 0;
7
+ const vscode_languageserver_1 = require("vscode-languageserver");
8
+ const utils_1 = require("../utils");
9
+ const vscode_languageserver_types_1 = require("vscode-languageserver-types");
10
+ const typescript_1 = __importDefault(require("typescript"));
11
+ class InlayHintsProviderImpl {
12
+ constructor(languageServiceManager, configManager) {
13
+ this.languageServiceManager = languageServiceManager;
14
+ this.configManager = configManager;
15
+ }
16
+ async getInlayHints(document, range) {
17
+ const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
18
+ const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
19
+ const fragment = await tsDoc.createFragment();
20
+ const start = fragment.offsetAt(fragment.getGeneratedPosition(range.start));
21
+ const end = fragment.offsetAt(fragment.getGeneratedPosition(range.end));
22
+ const tsPreferences = await this.configManager.getTSInlayHintsPreferences(document);
23
+ const inlayHints = lang.provideInlayHints(filePath, { start, length: end - start }, tsPreferences);
24
+ return inlayHints.map((hint) => {
25
+ const result = vscode_languageserver_1.InlayHint.create(fragment.getOriginalPosition(fragment.positionAt(hint.position)), hint.text, hint.kind === typescript_1.default.InlayHintKind.Type
26
+ ? vscode_languageserver_types_1.InlayHintKind.Type
27
+ : hint.kind === typescript_1.default.InlayHintKind.Parameter
28
+ ? vscode_languageserver_types_1.InlayHintKind.Parameter
29
+ : undefined);
30
+ result.paddingLeft = hint.whitespaceBefore;
31
+ result.paddingRight = hint.whitespaceAfter;
32
+ return result;
33
+ });
34
+ }
35
+ }
36
+ exports.InlayHintsProviderImpl = InlayHintsProviderImpl;
@@ -10,6 +10,7 @@ const vscode_uri_1 = require("vscode-uri");
10
10
  const utils_1 = require("../utils");
11
11
  const DocumentSnapshot_1 = require("./DocumentSnapshot");
12
12
  const svelte_language_integration_1 = require("@astrojs/svelte-language-integration");
13
+ const vue_language_integration_1 = require("@astrojs/vue-language-integration");
13
14
  const utils_2 = require("../../../utils");
14
15
  // Utilities to create Snapshots from different contexts
15
16
  function createFromDocument(document) {
@@ -76,8 +77,8 @@ function createFromFrameworkFilePath(filePath, framework) {
76
77
  if (framework === 'svelte') {
77
78
  code = (0, svelte_language_integration_1.toTSX)(originalText, className);
78
79
  }
79
- else {
80
- code = `export default function ${className}__AstroComponent_(props: Record<string, any>): any {}`;
80
+ else if (framework === 'vue') {
81
+ code = (0, vue_language_integration_1.toTSX)(originalText, className);
81
82
  }
82
83
  return new DocumentSnapshot_1.TypeScriptDocumentSnapshot(0, filePath, code, typescript_1.default.ScriptKind.TSX);
83
84
  }
package/dist/server.js CHANGED
@@ -88,6 +88,7 @@ function startLanguageServer(connection) {
88
88
  foldingRangeProvider: true,
89
89
  definitionProvider: true,
90
90
  renameProvider: true,
91
+ documentFormattingProvider: true,
91
92
  codeActionProvider: {
92
93
  codeActionKinds: [
93
94
  vscode_languageserver_1.CodeActionKind.QuickFix,
@@ -133,6 +134,7 @@ function startLanguageServer(connection) {
133
134
  range: true,
134
135
  full: true,
135
136
  },
137
+ inlayHintProvider: true,
136
138
  signatureHelpProvider: {
137
139
  triggerCharacters: ['(', ',', '<'],
138
140
  retriggerCharacters: [')'],
@@ -193,8 +195,10 @@ function startLanguageServer(connection) {
193
195
  connection.onDocumentSymbol((params, cancellationToken) => pluginHost.getDocumentSymbols(params.textDocument, cancellationToken));
194
196
  connection.onRequest(vscode_languageserver_1.SemanticTokensRequest.type, (evt, cancellationToken) => pluginHost.getSemanticTokens(evt.textDocument, undefined, cancellationToken));
195
197
  connection.onRequest(vscode_languageserver_1.SemanticTokensRangeRequest.type, (evt, cancellationToken) => pluginHost.getSemanticTokens(evt.textDocument, evt.range, cancellationToken));
198
+ connection.onDocumentFormatting((params) => pluginHost.formatDocument(params.textDocument, params.options));
196
199
  connection.onDocumentColor((params) => pluginHost.getDocumentColors(params.textDocument));
197
200
  connection.onColorPresentation((params) => pluginHost.getColorPresentations(params.textDocument, params.range, params.color));
201
+ connection.onRequest(vscode_languageserver_1.InlayHintRequest.type, (params, cancellationToken) => pluginHost.getInlayHints(params.textDocument, params.range, cancellationToken));
198
202
  connection.onRequest(TagCloseRequest, (evt) => pluginHost.doTagComplete(evt.textDocument, evt.position));
199
203
  connection.onSignatureHelp((evt, cancellationToken) => pluginHost.getSignatureHelp(evt.textDocument, evt.position, evt.context, cancellationToken));
200
204
  connection.onRenameRequest((evt) => pluginHost.rename(evt.textDocument, evt.position, evt.newName));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/language-server",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "author": "withastro",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -19,18 +19,19 @@
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.0",
22
23
  "@astrojs/svelte-language-integration": "^0.1.4",
23
24
  "@vscode/emmet-helper": "^2.8.4",
24
25
  "lodash": "^4.17.21",
25
26
  "source-map": "^0.7.3",
26
- "typescript": "~4.6.2",
27
- "vscode-css-languageservice": "^5.1.13",
28
- "vscode-html-languageservice": "^4.2.2",
29
- "vscode-languageserver": "7.0.0",
30
- "vscode-languageserver-protocol": "^3.16.0",
31
- "vscode-languageserver-textdocument": "^1.0.1",
32
- "vscode-languageserver-types": "^3.16.0",
33
- "vscode-uri": "^3.0.2"
27
+ "typescript": "~4.6.4",
28
+ "vscode-css-languageservice": "^6.0.1",
29
+ "vscode-html-languageservice": "^5.0.0",
30
+ "vscode-languageserver": "^8.0.1",
31
+ "vscode-languageserver-protocol": "^3.17.1",
32
+ "vscode-languageserver-textdocument": "^1.0.4",
33
+ "vscode-languageserver-types": "^3.17.1",
34
+ "vscode-uri": "^3.0.3"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/chai": "^4.3.0",