@astrojs/language-server 0.21.1 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/core/config/ConfigManager.d.ts +2 -2
- package/dist/core/config/ConfigManager.js +3 -6
- package/dist/importPackage.d.ts +3 -0
- package/dist/importPackage.js +11 -1
- package/dist/plugins/PluginHost.d.ts +1 -0
- package/dist/plugins/PluginHost.js +4 -0
- package/dist/plugins/astro/AstroPlugin.d.ts +2 -1
- package/dist/plugins/astro/AstroPlugin.js +42 -0
- package/dist/plugins/css/language-service.d.ts +1 -1
- package/dist/plugins/html/HTMLPlugin.d.ts +1 -2
- package/dist/plugins/html/HTMLPlugin.js +6 -20
- package/dist/plugins/interfaces.d.ts +5 -2
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +3 -3
- package/dist/plugins/typescript/TypeScriptPlugin.js +5 -5
- package/dist/plugins/typescript/astro2tsx.js +15 -1
- package/dist/plugins/typescript/features/TypeDefinitionsProvider.d.ts +9 -0
- package/dist/plugins/typescript/features/TypeDefinitionsProvider.js +55 -0
- package/dist/server.js +4 -1
- package/package.json +4 -1
- package/dist/plugins/typescript/features/FormattingProvider.d.ts +0 -11
- package/dist/plugins/typescript/features/FormattingProvider.js +0 -132
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @astrojs/language-server
|
|
2
2
|
|
|
3
|
+
## 0.23.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 422376e: Load settings from the Prettier VS Code extension when available
|
|
8
|
+
|
|
9
|
+
## 0.23.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 1dcef68: Automatically type `Astro.props` using the Props interface when available
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- b6c95f2: Fix completions for HTML attributes not working anymore since 0.20.3
|
|
18
|
+
|
|
19
|
+
## 0.22.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- d5aafc0: Formatting is now powered by Prettier and our Prettier plugin. Going forward, this should result in a more stable and complete way of formatting Astro files
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- 61620f1: Add support for Go To Type Definition
|
|
28
|
+
- 9337f00: Fix language server not working when no initlizationOptions were passed
|
|
29
|
+
|
|
3
30
|
## 0.21.1
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VSCodeEmmetConfig } from '@vscode/emmet-helper';
|
|
2
|
-
import { LSConfig, LSCSSConfig,
|
|
2
|
+
import { LSConfig, LSCSSConfig, LSHTMLConfig, LSTypescriptConfig } from './interfaces';
|
|
3
3
|
import { Connection, FormattingOptions } from 'vscode-languageserver';
|
|
4
4
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
5
5
|
import { FormatCodeSettings, InlayHintsOptions, UserPreferences } from 'typescript';
|
|
@@ -23,7 +23,7 @@ export declare class ConfigManager {
|
|
|
23
23
|
removeDocument(scopeUri: string): void;
|
|
24
24
|
getConfig<T>(section: string, scopeUri: string): Promise<T | Record<string, any>>;
|
|
25
25
|
getEmmetConfig(document: TextDocument): Promise<VSCodeEmmetConfig>;
|
|
26
|
-
|
|
26
|
+
getPrettierVSConfig(document: TextDocument): Promise<Record<string, any>>;
|
|
27
27
|
getTSFormatConfig(document: TextDocument, vscodeOptions?: FormattingOptions): Promise<FormatCodeSettings>;
|
|
28
28
|
getTSPreferences(document: TextDocument): Promise<UserPreferences>;
|
|
29
29
|
getTSInlayHintsPreferences(document: TextDocument): Promise<InlayHintsOptions>;
|
|
@@ -90,12 +90,9 @@ class ConfigManager {
|
|
|
90
90
|
showSuggestionsAsSnippets: emmetConfig.showSuggestionsAsSnippets ?? false,
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
async
|
|
94
|
-
const
|
|
95
|
-
return
|
|
96
|
-
indentFrontmatter: astroFormatConfig.indentFrontmatter ?? exports.defaultLSConfig.format.indentFrontmatter,
|
|
97
|
-
newLineAfterFrontmatter: astroFormatConfig.newLineAfterFrontmatter ?? exports.defaultLSConfig.format.newLineAfterFrontmatter,
|
|
98
|
-
};
|
|
93
|
+
async getPrettierVSConfig(document) {
|
|
94
|
+
const prettierVSConfig = (await this.getConfig('prettier', document.uri)) ?? {};
|
|
95
|
+
return prettierVSConfig;
|
|
99
96
|
}
|
|
100
97
|
async getTSFormatConfig(document, vscodeOptions) {
|
|
101
98
|
const formatConfig = (await this.getConfig('typescript.format', document.uri)) ?? {};
|
package/dist/importPackage.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type * as svelte from '@astrojs/svelte/dist/editor.cjs';
|
|
2
2
|
import type * as vue from '@astrojs/svelte/dist/editor.cjs';
|
|
3
|
+
import type * as prettier from 'prettier';
|
|
3
4
|
export declare function setIsTrusted(_isTrusted: boolean): void;
|
|
4
5
|
export declare function getPackagePath(packageName: string, fromPath: string[]): string | undefined;
|
|
5
6
|
export declare function importSvelteIntegration(fromPath: string): typeof svelte | undefined;
|
|
6
7
|
export declare function importVueIntegration(fromPath: string): typeof vue | undefined;
|
|
8
|
+
export declare function importPrettier(fromPath: string): typeof prettier;
|
|
9
|
+
export declare function getPrettierPluginPath(fromPath: string): string;
|
package/dist/importPackage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.importVueIntegration = exports.importSvelteIntegration = exports.getPackagePath = exports.setIsTrusted = void 0;
|
|
3
|
+
exports.getPrettierPluginPath = exports.importPrettier = exports.importVueIntegration = exports.importSvelteIntegration = exports.getPackagePath = exports.setIsTrusted = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
5
|
let isTrusted = true;
|
|
6
6
|
function setIsTrusted(_isTrusted) {
|
|
@@ -42,3 +42,13 @@ function importVueIntegration(fromPath) {
|
|
|
42
42
|
return importEditorIntegration('@astrojs/vue', fromPath);
|
|
43
43
|
}
|
|
44
44
|
exports.importVueIntegration = importVueIntegration;
|
|
45
|
+
function importPrettier(fromPath) {
|
|
46
|
+
// This shouldn't ever fail, because we bundle Prettier in the extension itself
|
|
47
|
+
const prettierPkg = getPackagePath('prettier', [fromPath, __dirname]);
|
|
48
|
+
return require(prettierPkg);
|
|
49
|
+
}
|
|
50
|
+
exports.importPrettier = importPrettier;
|
|
51
|
+
function getPrettierPluginPath(fromPath) {
|
|
52
|
+
return getPackagePath('prettier-plugin-astro', [fromPath, __dirname]);
|
|
53
|
+
}
|
|
54
|
+
exports.getPrettierPluginPath = getPrettierPluginPath;
|
|
@@ -24,6 +24,7 @@ export declare class PluginHost {
|
|
|
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
|
+
getTypeDefinition(textDocument: TextDocumentIdentifier, position: Position): Promise<Location[] | null>;
|
|
27
28
|
rename(textDocument: TextDocumentIdentifier, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
28
29
|
getDocumentColors(textDocument: TextDocumentIdentifier): Promise<ColorInformation[]>;
|
|
29
30
|
getColorPresentations(textDocument: TextDocumentIdentifier, range: Range, color: Color): Promise<ColorPresentation[]>;
|
|
@@ -115,6 +115,10 @@ class PluginHost {
|
|
|
115
115
|
return definitions.map((def) => ({ range: def.targetSelectionRange, uri: def.targetUri }));
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
+
getTypeDefinition(textDocument, position) {
|
|
119
|
+
const document = this.getDocument(textDocument.uri);
|
|
120
|
+
return this.execute('getTypeDefinitions', [document, position], ExecuteMode.FirstNonNull);
|
|
121
|
+
}
|
|
118
122
|
async rename(textDocument, position, newName) {
|
|
119
123
|
const document = this.getDocument(textDocument.uri);
|
|
120
124
|
return this.execute('rename', [document, position, newName], ExecuteMode.FirstNonNull);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompletionContext, FoldingRange, Position } from 'vscode-languageserver';
|
|
1
|
+
import { CompletionContext, FoldingRange, Position, TextEdit, FormattingOptions } from 'vscode-languageserver';
|
|
2
2
|
import { ConfigManager } from '../../core/config';
|
|
3
3
|
import { AstroDocument } from '../../core/documents';
|
|
4
4
|
import { AppCompletionList, Plugin } from '../interfaces';
|
|
@@ -10,5 +10,6 @@ export declare class AstroPlugin implements Plugin {
|
|
|
10
10
|
private readonly completionProvider;
|
|
11
11
|
constructor(configManager: ConfigManager, languageServiceManager: LanguageServiceManager);
|
|
12
12
|
getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList | null>;
|
|
13
|
+
formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
|
|
13
14
|
getFoldingRanges(document: AstroDocument): FoldingRange[];
|
|
14
15
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AstroPlugin = void 0;
|
|
4
4
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
5
|
+
const importPackage_1 = require("../../importPackage");
|
|
5
6
|
const CompletionsProvider_1 = require("./features/CompletionsProvider");
|
|
6
7
|
class AstroPlugin {
|
|
7
8
|
constructor(configManager, languageServiceManager) {
|
|
@@ -14,6 +15,42 @@ class AstroPlugin {
|
|
|
14
15
|
const completions = this.completionProvider.getCompletions(document, position, completionContext);
|
|
15
16
|
return completions;
|
|
16
17
|
}
|
|
18
|
+
async formatDocument(document, options) {
|
|
19
|
+
const filePath = document.getFilePath();
|
|
20
|
+
if (!filePath) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const prettier = (0, importPackage_1.importPrettier)(filePath);
|
|
24
|
+
const prettierConfig = (await prettier.resolveConfig(filePath, { editorconfig: true, useCache: false })) ?? {};
|
|
25
|
+
const prettierVSConfig = await this.configManager.getPrettierVSConfig(document);
|
|
26
|
+
const editorFormatConfig = options !== undefined // We need to check for options existing here because some editors might not have it
|
|
27
|
+
? {
|
|
28
|
+
tabWidth: options.tabSize,
|
|
29
|
+
useTabs: !options.insertSpaces,
|
|
30
|
+
}
|
|
31
|
+
: {};
|
|
32
|
+
// Return a config with the following cascade:
|
|
33
|
+
// - Prettier config file should always win if it exists, if it doesn't:
|
|
34
|
+
// - Prettier config from the VS Code extension is used, if it doesn't exist:
|
|
35
|
+
// - Use the editor's basic configuration settings
|
|
36
|
+
const resultConfig = returnObjectIfHasKeys(prettierConfig) || returnObjectIfHasKeys(prettierVSConfig) || editorFormatConfig;
|
|
37
|
+
const fileInfo = await prettier.getFileInfo(filePath, { ignorePath: '.prettierignore' });
|
|
38
|
+
if (fileInfo.ignored) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const result = prettier.format(document.getText(), {
|
|
42
|
+
...resultConfig,
|
|
43
|
+
plugins: getAstroPrettierPlugin(),
|
|
44
|
+
parser: 'astro',
|
|
45
|
+
});
|
|
46
|
+
return document.getText() === result
|
|
47
|
+
? []
|
|
48
|
+
: [vscode_languageserver_1.TextEdit.replace(vscode_languageserver_1.Range.create(document.positionAt(0), document.positionAt(document.getTextLength())), result)];
|
|
49
|
+
function getAstroPrettierPlugin() {
|
|
50
|
+
const hasPluginLoadedAlready = prettier.getSupportInfo().languages.some((l) => l.name === 'astro');
|
|
51
|
+
return hasPluginLoadedAlready ? [] : [(0, importPackage_1.getPrettierPluginPath)(filePath)];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
17
54
|
getFoldingRanges(document) {
|
|
18
55
|
const foldingRanges = [];
|
|
19
56
|
const { frontmatter } = document.astroMeta;
|
|
@@ -41,3 +78,8 @@ class AstroPlugin {
|
|
|
41
78
|
}
|
|
42
79
|
}
|
|
43
80
|
exports.AstroPlugin = AstroPlugin;
|
|
81
|
+
function returnObjectIfHasKeys(obj) {
|
|
82
|
+
if (Object.keys(obj || {}).length > 0) {
|
|
83
|
+
return obj;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { LanguageService } from 'vscode-css-languageservice';
|
|
2
|
-
export declare function getLanguage(kind?: string): "css" | "
|
|
2
|
+
export declare function getLanguage(kind?: string): "css" | "less" | "scss";
|
|
3
3
|
export declare function getLanguageService(kind?: string): LanguageService;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompletionList, Position,
|
|
1
|
+
import { CompletionList, Position, FoldingRange, Hover, SymbolInformation, 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';
|
|
@@ -15,7 +15,6 @@ 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[]>;
|
|
19
18
|
getFoldingRanges(document: AstroDocument): FoldingRange[] | null;
|
|
20
19
|
getLinkedEditingRanges(document: AstroDocument, position: Position): LinkedEditingRanges | null;
|
|
21
20
|
doTagComplete(document: AstroDocument, position: Position): Promise<string | null>;
|
|
@@ -76,30 +76,16 @@ class HTMLPlugin {
|
|
|
76
76
|
const inTagName = (0, utils_1.isInTagName)(html, offset);
|
|
77
77
|
const results = inComponentTag && !inTagName
|
|
78
78
|
? (0, utils_2.removeDataAttrCompletion)(this.attributeOnlyLang.doComplete(document, position, html).items)
|
|
79
|
-
:
|
|
80
|
-
this.lang.doComplete(document, position, html).items.filter((item) => item.documentation !== undefined);
|
|
79
|
+
: this.lang.doComplete(document, position, html).items.filter(isNoAddedTagWithNoDocumentation);
|
|
81
80
|
const langCompletions = inComponentTag ? [] : this.getLangCompletions(results);
|
|
82
81
|
return vscode_languageserver_1.CompletionList.create([...results, ...langCompletions, ...emmetResults.items],
|
|
83
82
|
// Emmet completions change on every keystroke, so they are never complete
|
|
84
83
|
emmetResults.items.length > 0);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
start.character = 0;
|
|
91
|
-
}
|
|
92
|
-
const end = document.positionAt(document.getTextLength());
|
|
93
|
-
const htmlFormatConfig = (await this.configManager.getConfig('html.format', document.uri)) ?? {};
|
|
94
|
-
// The HTML plugin can't format script tags properly, we'll handle those inside the TypeScript plugin
|
|
95
|
-
if (htmlFormatConfig.contentUnformatted) {
|
|
96
|
-
htmlFormatConfig.contentUnformatted = htmlFormatConfig.contentUnformatted + ',script';
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
htmlFormatConfig.contentUnformatted = 'script';
|
|
100
|
-
}
|
|
101
|
-
const edits = this.lang.format(document, vscode_languageserver_1.Range.create(start, end), { ...htmlFormatConfig, ...options });
|
|
102
|
-
return edits;
|
|
84
|
+
// Filter script and style completions with no documentation to prevent duplicates
|
|
85
|
+
// due to our added definitions for those tags
|
|
86
|
+
function isNoAddedTagWithNoDocumentation(item) {
|
|
87
|
+
return !(['script', 'style'].includes(item.label) && item.documentation === undefined);
|
|
88
|
+
}
|
|
103
89
|
}
|
|
104
90
|
getFoldingRanges(document) {
|
|
105
91
|
const html = document.html;
|
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
1
|
+
import { CodeAction, CodeActionContext, Color, Location, 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 {
|
|
@@ -74,6 +74,9 @@ export interface SemanticTokensProvider {
|
|
|
74
74
|
export interface LinkedEditingRangesProvider {
|
|
75
75
|
getLinkedEditingRanges(document: TextDocument, position: Position): Resolvable<LinkedEditingRanges | null>;
|
|
76
76
|
}
|
|
77
|
+
export interface TypeDefinitionProvider {
|
|
78
|
+
getTypeDefinitions(document: TextDocument, position: Position): Resolvable<Location[] | null>;
|
|
79
|
+
}
|
|
77
80
|
export interface OnWatchFileChangesParam {
|
|
78
81
|
fileName: string;
|
|
79
82
|
changeType: FileChangeType;
|
|
@@ -84,7 +87,7 @@ export interface OnWatchFileChangesProvider {
|
|
|
84
87
|
export interface UpdateNonAstroFile {
|
|
85
88
|
updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
|
|
86
89
|
}
|
|
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;
|
|
90
|
+
declare type ProviderBase = DiagnosticsProvider & HoverProvider & CompletionsProvider & DefinitionsProvider & TypeDefinitionProvider & FormattingProvider & FoldingRangesProvider & TagCompleteProvider & DocumentColorsProvider & ColorPresentationsProvider & DocumentSymbolsProvider & UpdateImportsProvider & CodeActionsProvider & FindReferencesProvider & RenameProvider & SignatureHelpProvider & SemanticTokensProvider & SelectionRangeProvider & OnWatchFileChangesProvider & LinkedEditingRangesProvider & InlayHintsProvider & UpdateNonAstroFile;
|
|
88
91
|
export declare type LSProvider = ProviderBase;
|
|
89
92
|
export declare type Plugin = Partial<ProviderBase> & {
|
|
90
93
|
__name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Diagnostic, FoldingRange,
|
|
1
|
+
import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Location, Diagnostic, FoldingRange, Hover, InlayHint, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
|
|
2
2
|
import { ConfigManager } from '../../core/config';
|
|
3
3
|
import { AstroDocument } from '../../core/documents';
|
|
4
4
|
import { AppCompletionItem, AppCompletionList, OnWatchFileChangesParam, Plugin } from '../interfaces';
|
|
@@ -13,17 +13,16 @@ export declare class TypeScriptPlugin implements Plugin {
|
|
|
13
13
|
private readonly completionProvider;
|
|
14
14
|
private readonly hoverProvider;
|
|
15
15
|
private readonly definitionsProvider;
|
|
16
|
+
private readonly typeDefinitionsProvider;
|
|
16
17
|
private readonly signatureHelpProvider;
|
|
17
18
|
private readonly diagnosticsProvider;
|
|
18
19
|
private readonly documentSymbolsProvider;
|
|
19
20
|
private readonly inlayHintsProvider;
|
|
20
21
|
private readonly semanticTokensProvider;
|
|
21
22
|
private readonly foldingRangesProvider;
|
|
22
|
-
private readonly formattingProvider;
|
|
23
23
|
constructor(configManager: ConfigManager, languageServiceManager: LanguageServiceManager);
|
|
24
24
|
doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
|
|
25
25
|
rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
|
|
26
|
-
formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
|
|
27
26
|
getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
|
|
28
27
|
getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
|
|
29
28
|
getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
|
|
@@ -32,6 +31,7 @@ export declare class TypeScriptPlugin implements Plugin {
|
|
|
32
31
|
resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
|
|
33
32
|
getInlayHints(document: AstroDocument, range: Range): Promise<InlayHint[]>;
|
|
34
33
|
getDefinitions(document: AstroDocument, position: Position): Promise<DefinitionLink[]>;
|
|
34
|
+
getTypeDefinition(document: AstroDocument, position: Position): Promise<Location[] | null>;
|
|
35
35
|
getDiagnostics(document: AstroDocument, cancellationToken?: CancellationToken): Promise<Diagnostic[]>;
|
|
36
36
|
onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesParam[]): Promise<void>;
|
|
37
37
|
updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): Promise<void>;
|
|
@@ -17,9 +17,9 @@ const FoldingRangesProvider_1 = require("./features/FoldingRangesProvider");
|
|
|
17
17
|
const CodeActionsProvider_1 = require("./features/CodeActionsProvider");
|
|
18
18
|
const DefinitionsProvider_1 = require("./features/DefinitionsProvider");
|
|
19
19
|
const InlayHintsProvider_1 = require("./features/InlayHintsProvider");
|
|
20
|
-
const FormattingProvider_1 = require("./features/FormattingProvider");
|
|
21
20
|
const astro2tsx_1 = __importDefault(require("./astro2tsx"));
|
|
22
21
|
const utils_2 = require("./snapshots/utils");
|
|
22
|
+
const TypeDefinitionsProvider_1 = require("./features/TypeDefinitionsProvider");
|
|
23
23
|
class TypeScriptPlugin {
|
|
24
24
|
constructor(configManager, languageServiceManager) {
|
|
25
25
|
this.__name = 'typescript';
|
|
@@ -29,13 +29,13 @@ class TypeScriptPlugin {
|
|
|
29
29
|
this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager, this.configManager);
|
|
30
30
|
this.hoverProvider = new HoverProvider_1.HoverProviderImpl(this.languageServiceManager);
|
|
31
31
|
this.definitionsProvider = new DefinitionsProvider_1.DefinitionsProviderImpl(this.languageServiceManager);
|
|
32
|
+
this.typeDefinitionsProvider = new TypeDefinitionsProvider_1.TypeDefinitionsProviderImpl(this.languageServiceManager);
|
|
32
33
|
this.signatureHelpProvider = new SignatureHelpProvider_1.SignatureHelpProviderImpl(this.languageServiceManager);
|
|
33
34
|
this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
|
|
34
35
|
this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
|
|
35
36
|
this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
|
|
36
37
|
this.inlayHintsProvider = new InlayHintsProvider_1.InlayHintsProviderImpl(this.languageServiceManager, this.configManager);
|
|
37
38
|
this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
|
|
38
|
-
this.formattingProvider = new FormattingProvider_1.FormattingProviderImpl(this.languageServiceManager, this.configManager);
|
|
39
39
|
}
|
|
40
40
|
async doHover(document, position) {
|
|
41
41
|
if (!(await this.featureEnabled(document, 'hover'))) {
|
|
@@ -66,9 +66,6 @@ class TypeScriptPlugin {
|
|
|
66
66
|
});
|
|
67
67
|
return edit;
|
|
68
68
|
}
|
|
69
|
-
async formatDocument(document, options) {
|
|
70
|
-
return this.formattingProvider.formatDocument(document, options);
|
|
71
|
-
}
|
|
72
69
|
async getFoldingRanges(document) {
|
|
73
70
|
return this.foldingRangesProvider.getFoldingRanges(document);
|
|
74
71
|
}
|
|
@@ -107,6 +104,9 @@ class TypeScriptPlugin {
|
|
|
107
104
|
async getDefinitions(document, position) {
|
|
108
105
|
return this.definitionsProvider.getDefinitions(document, position);
|
|
109
106
|
}
|
|
107
|
+
async getTypeDefinition(document, position) {
|
|
108
|
+
return this.typeDefinitionsProvider.getTypeDefinitions(document, position);
|
|
109
|
+
}
|
|
110
110
|
async getDiagnostics(document, cancellationToken) {
|
|
111
111
|
if (!(await this.featureEnabled(document, 'diagnostics'))) {
|
|
112
112
|
return [];
|
|
@@ -4,10 +4,24 @@ const os_1 = require("os");
|
|
|
4
4
|
const parseAstro_1 = require("../../core/documents/parseAstro");
|
|
5
5
|
function addProps(content, className) {
|
|
6
6
|
let defaultExportType = 'Record<string, any>';
|
|
7
|
+
let shouldAddGlobal = false;
|
|
8
|
+
let astroGlobal = "type AstroGlobal = import('astro').AstroGlobal";
|
|
9
|
+
const astroGlobalConstDef = `
|
|
10
|
+
/**
|
|
11
|
+
* Astro global available in all contexts in .astro files
|
|
12
|
+
*
|
|
13
|
+
* [Astro documentation](https://docs.astro.build/reference/api-reference/#astro-global)
|
|
14
|
+
*/
|
|
15
|
+
declare const Astro: Readonly<AstroGlobal>;
|
|
16
|
+
`;
|
|
7
17
|
if (/(interface|type) Props/.test(content)) {
|
|
8
18
|
defaultExportType = 'Props';
|
|
19
|
+
shouldAddGlobal = true;
|
|
20
|
+
astroGlobal += ' & { props: Props }';
|
|
9
21
|
}
|
|
10
|
-
return os_1.EOL +
|
|
22
|
+
return (os_1.EOL +
|
|
23
|
+
(shouldAddGlobal ? astroGlobal + os_1.EOL + astroGlobalConstDef : '') +
|
|
24
|
+
`export default function ${className}__AstroComponent_(_props: ${defaultExportType}): any {}`);
|
|
11
25
|
}
|
|
12
26
|
function escapeTemplateLiteralContent(content) {
|
|
13
27
|
return content.replace(/`/g, '\\`');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Position, Location } from 'vscode-languageserver-protocol';
|
|
2
|
+
import { AstroDocument } from '../../../core/documents';
|
|
3
|
+
import { TypeDefinitionProvider } from '../../interfaces';
|
|
4
|
+
import { LanguageServiceManager } from '../LanguageServiceManager';
|
|
5
|
+
export declare class TypeDefinitionsProviderImpl implements TypeDefinitionProvider {
|
|
6
|
+
private languageServiceManager;
|
|
7
|
+
constructor(languageServiceManager: LanguageServiceManager);
|
|
8
|
+
getTypeDefinitions(document: AstroDocument, position: Position): Promise<Location[]>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypeDefinitionsProviderImpl = void 0;
|
|
4
|
+
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
5
|
+
const documents_1 = require("../../../core/documents");
|
|
6
|
+
const utils_1 = require("../../../utils");
|
|
7
|
+
const utils_2 = require("../utils");
|
|
8
|
+
const utils_3 = require("./utils");
|
|
9
|
+
class TypeDefinitionsProviderImpl {
|
|
10
|
+
constructor(languageServiceManager) {
|
|
11
|
+
this.languageServiceManager = languageServiceManager;
|
|
12
|
+
}
|
|
13
|
+
async getTypeDefinitions(document, position) {
|
|
14
|
+
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
15
|
+
const mainFragment = await tsDoc.createFragment();
|
|
16
|
+
const fragmentOffset = mainFragment.offsetAt(mainFragment.getGeneratedPosition(position));
|
|
17
|
+
const tsFilePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
18
|
+
const html = document.html;
|
|
19
|
+
const offset = document.offsetAt(position);
|
|
20
|
+
const node = html.findNodeAt(offset);
|
|
21
|
+
let typeDefs;
|
|
22
|
+
if (node.tag === 'script') {
|
|
23
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath, offset: scriptOffset, } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
24
|
+
typeDefs = lang.getTypeDefinitionAtPosition(scriptFilePath, scriptOffset);
|
|
25
|
+
if (typeDefs) {
|
|
26
|
+
typeDefs = typeDefs.map((def) => {
|
|
27
|
+
const isInSameFile = def.fileName === scriptFilePath;
|
|
28
|
+
def.fileName = isInSameFile ? tsFilePath : def.fileName;
|
|
29
|
+
if (isInSameFile) {
|
|
30
|
+
def.textSpan.start = mainFragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(def.textSpan.start)));
|
|
31
|
+
}
|
|
32
|
+
return def;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
typeDefs = lang.getTypeDefinitionAtPosition(tsFilePath, fragmentOffset);
|
|
38
|
+
}
|
|
39
|
+
const docs = new utils_3.SnapshotFragmentMap(this.languageServiceManager);
|
|
40
|
+
docs.set(tsFilePath, { fragment: mainFragment, snapshot: tsDoc });
|
|
41
|
+
if (!typeDefs) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const result = await Promise.all(typeDefs.map(async (typeDef) => {
|
|
45
|
+
const { fragment } = await docs.retrieve(typeDef.fileName);
|
|
46
|
+
const fileName = (0, utils_2.ensureRealFilePath)(typeDef.fileName);
|
|
47
|
+
const range = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, typeDef.textSpan));
|
|
48
|
+
if (range.start.line >= 0 && range.end.line >= 0) {
|
|
49
|
+
return vscode_languageserver_protocol_1.Location.create((0, utils_1.pathToUrl)(fileName), range);
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
return result.filter(utils_1.isNotNullOrUndefined);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.TypeDefinitionsProviderImpl = TypeDefinitionsProviderImpl;
|
package/dist/server.js
CHANGED
|
@@ -48,6 +48,7 @@ function startLanguageServer(connection) {
|
|
|
48
48
|
let typescriptPlugin = undefined;
|
|
49
49
|
let hasConfigurationCapability = false;
|
|
50
50
|
connection.onInitialize((params) => {
|
|
51
|
+
const environment = params.initializationOptions?.environment ?? 'node';
|
|
51
52
|
const workspaceUris = params.workspaceFolders?.map((folder) => folder.uri.toString()) ?? [params.rootUri ?? ''];
|
|
52
53
|
workspaceUris.forEach((uri) => {
|
|
53
54
|
uri = (0, utils_1.urlToPath)(uri);
|
|
@@ -73,7 +74,7 @@ function startLanguageServer(connection) {
|
|
|
73
74
|
pluginHost.registerPlugin(new HTMLPlugin_1.HTMLPlugin(configManager));
|
|
74
75
|
pluginHost.registerPlugin(new CSSPlugin_1.CSSPlugin(configManager));
|
|
75
76
|
// We don't currently support running the TypeScript and Astro plugin in the browser
|
|
76
|
-
if (
|
|
77
|
+
if (environment === 'node') {
|
|
77
78
|
const languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(documentManager, workspaceUris.map(utils_1.normalizeUri), configManager);
|
|
78
79
|
typescriptPlugin = new plugins_1.TypeScriptPlugin(configManager, languageServiceManager);
|
|
79
80
|
pluginHost.registerPlugin(new AstroPlugin_1.AstroPlugin(configManager, languageServiceManager));
|
|
@@ -90,6 +91,7 @@ function startLanguageServer(connection) {
|
|
|
90
91
|
},
|
|
91
92
|
foldingRangeProvider: true,
|
|
92
93
|
definitionProvider: true,
|
|
94
|
+
typeDefinitionProvider: true,
|
|
93
95
|
renameProvider: true,
|
|
94
96
|
documentFormattingProvider: true,
|
|
95
97
|
codeActionProvider: {
|
|
@@ -184,6 +186,7 @@ function startLanguageServer(connection) {
|
|
|
184
186
|
// Features
|
|
185
187
|
connection.onHover((params) => pluginHost.doHover(params.textDocument, params.position));
|
|
186
188
|
connection.onDefinition((evt) => pluginHost.getDefinitions(evt.textDocument, evt.position));
|
|
189
|
+
connection.onTypeDefinition((evt) => pluginHost.getTypeDefinition(evt.textDocument, evt.position));
|
|
187
190
|
connection.onFoldingRanges((evt) => pluginHost.getFoldingRanges(evt.textDocument));
|
|
188
191
|
connection.onCodeAction((evt, cancellationToken) => pluginHost.getCodeActions(evt.textDocument, evt.range, evt.context, cancellationToken));
|
|
189
192
|
connection.onCompletion(async (evt) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/language-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"author": "withastro",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@vscode/emmet-helper": "^2.8.4",
|
|
23
|
+
"prettier": "^2.7.1",
|
|
24
|
+
"prettier-plugin-astro": "^0.5.3",
|
|
23
25
|
"source-map": "^0.7.3",
|
|
24
26
|
"typescript": "~4.6.4",
|
|
25
27
|
"vscode-css-languageservice": "^6.0.1",
|
|
@@ -35,6 +37,7 @@
|
|
|
35
37
|
"@astrojs/vue": "^0.5.0",
|
|
36
38
|
"@types/chai": "^4.3.0",
|
|
37
39
|
"@types/mocha": "^9.1.0",
|
|
40
|
+
"@types/prettier": "^2.7.0",
|
|
38
41
|
"@types/sinon": "^10.0.11",
|
|
39
42
|
"astro": "^1.0.0-beta.72",
|
|
40
43
|
"astro-scripts": "0.0.1",
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
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
|
-
}
|