@nogataka/smart-edit 0.0.14
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/LICENSE +22 -0
- package/README.md +244 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +7 -0
- package/dist/devtools/generate_prompt_factory.d.ts +5 -0
- package/dist/devtools/generate_prompt_factory.js +114 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +34 -0
- package/dist/interprompt/index.d.ts +2 -0
- package/dist/interprompt/index.js +1 -0
- package/dist/interprompt/jinja_template.d.ts +10 -0
- package/dist/interprompt/jinja_template.js +174 -0
- package/dist/interprompt/multilang_prompt.d.ts +54 -0
- package/dist/interprompt/multilang_prompt.js +302 -0
- package/dist/interprompt/prompt_factory.d.ts +16 -0
- package/dist/interprompt/prompt_factory.js +189 -0
- package/dist/interprompt/util/class_decorators.d.ts +1 -0
- package/dist/interprompt/util/class_decorators.js +1 -0
- package/dist/interprompt/util/index.d.ts +1 -0
- package/dist/interprompt/util/index.js +1 -0
- package/dist/serena/agent.d.ts +118 -0
- package/dist/serena/agent.js +675 -0
- package/dist/serena/agno.d.ts +111 -0
- package/dist/serena/agno.js +278 -0
- package/dist/serena/analytics.d.ts +24 -0
- package/dist/serena/analytics.js +119 -0
- package/dist/serena/cli.d.ts +9 -0
- package/dist/serena/cli.js +731 -0
- package/dist/serena/code_editor.d.ts +42 -0
- package/dist/serena/code_editor.js +239 -0
- package/dist/serena/config/context_mode.d.ts +41 -0
- package/dist/serena/config/context_mode.js +239 -0
- package/dist/serena/config/serena_config.d.ts +134 -0
- package/dist/serena/config/serena_config.js +718 -0
- package/dist/serena/constants.d.ts +18 -0
- package/dist/serena/constants.js +27 -0
- package/dist/serena/dashboard.d.ts +55 -0
- package/dist/serena/dashboard.js +472 -0
- package/dist/serena/generated/generated_prompt_factory.d.ts +27 -0
- package/dist/serena/generated/generated_prompt_factory.js +42 -0
- package/dist/serena/gui_log_viewer.d.ts +41 -0
- package/dist/serena/gui_log_viewer.js +436 -0
- package/dist/serena/mcp.d.ts +118 -0
- package/dist/serena/mcp.js +904 -0
- package/dist/serena/project.d.ts +62 -0
- package/dist/serena/project.js +321 -0
- package/dist/serena/prompt_factory.d.ts +20 -0
- package/dist/serena/prompt_factory.js +42 -0
- package/dist/serena/resources/config/contexts/agent.yml +8 -0
- package/dist/serena/resources/config/contexts/chatgpt.yml +28 -0
- package/dist/serena/resources/config/contexts/codex.yml +27 -0
- package/dist/serena/resources/config/contexts/context.template.yml +11 -0
- package/dist/serena/resources/config/contexts/desktop-app.yml +17 -0
- package/dist/serena/resources/config/contexts/ide-assistant.yml +26 -0
- package/dist/serena/resources/config/contexts/oaicompat-agent.yml +8 -0
- package/dist/serena/resources/config/internal_modes/jetbrains.yml +15 -0
- package/dist/serena/resources/config/modes/editing.yml +112 -0
- package/dist/serena/resources/config/modes/interactive.yml +11 -0
- package/dist/serena/resources/config/modes/mode.template.yml +7 -0
- package/dist/serena/resources/config/modes/no-onboarding.yml +8 -0
- package/dist/serena/resources/config/modes/onboarding.yml +16 -0
- package/dist/serena/resources/config/modes/one-shot.yml +15 -0
- package/dist/serena/resources/config/modes/planning.yml +15 -0
- package/dist/serena/resources/config/prompt_templates/simple_tool_outputs.yml +75 -0
- package/dist/serena/resources/config/prompt_templates/system_prompt.yml +66 -0
- package/dist/serena/resources/dashboard/dashboard.js +815 -0
- package/dist/serena/resources/dashboard/index.html +314 -0
- package/dist/serena/resources/dashboard/jquery.min.js +3 -0
- package/dist/serena/resources/dashboard/serena-icon-16.png +0 -0
- package/dist/serena/resources/dashboard/serena-icon-32.png +0 -0
- package/dist/serena/resources/dashboard/serena-icon-48.png +0 -0
- package/dist/serena/resources/dashboard/serena-logs-dark-mode.png +0 -0
- package/dist/serena/resources/dashboard/serena-logs.png +0 -0
- package/dist/serena/resources/project.template.yml +67 -0
- package/dist/serena/resources/serena_config.template.yml +85 -0
- package/dist/serena/symbol.d.ts +199 -0
- package/dist/serena/symbol.js +616 -0
- package/dist/serena/text_utils.d.ts +51 -0
- package/dist/serena/text_utils.js +267 -0
- package/dist/serena/tools/cmd_tools.d.ts +31 -0
- package/dist/serena/tools/cmd_tools.js +48 -0
- package/dist/serena/tools/config_tools.d.ts +53 -0
- package/dist/serena/tools/config_tools.js +176 -0
- package/dist/serena/tools/file_tools.d.ts +231 -0
- package/dist/serena/tools/file_tools.js +511 -0
- package/dist/serena/tools/index.d.ts +7 -0
- package/dist/serena/tools/index.js +7 -0
- package/dist/serena/tools/memory_tools.d.ts +60 -0
- package/dist/serena/tools/memory_tools.js +135 -0
- package/dist/serena/tools/symbol_tools.d.ts +165 -0
- package/dist/serena/tools/symbol_tools.js +362 -0
- package/dist/serena/tools/tools_base.d.ts +162 -0
- package/dist/serena/tools/tools_base.js +378 -0
- package/dist/serena/tools/workflow_tools.d.ts +35 -0
- package/dist/serena/tools/workflow_tools.js +161 -0
- package/dist/serena/util/class_decorators.d.ts +7 -0
- package/dist/serena/util/class_decorators.js +37 -0
- package/dist/serena/util/exception.d.ts +8 -0
- package/dist/serena/util/exception.js +53 -0
- package/dist/serena/util/file_system.d.ts +30 -0
- package/dist/serena/util/file_system.js +352 -0
- package/dist/serena/util/general.d.ts +11 -0
- package/dist/serena/util/general.js +42 -0
- package/dist/serena/util/git.d.ts +11 -0
- package/dist/serena/util/git.js +37 -0
- package/dist/serena/util/inspection.d.ts +45 -0
- package/dist/serena/util/inspection.js +221 -0
- package/dist/serena/util/logging.d.ts +46 -0
- package/dist/serena/util/logging.js +205 -0
- package/dist/serena/util/shell.d.ts +21 -0
- package/dist/serena/util/shell.js +95 -0
- package/dist/serena/util/thread.d.ts +23 -0
- package/dist/serena/util/thread.js +88 -0
- package/dist/serena/version.d.ts +1 -0
- package/dist/serena/version.js +23 -0
- package/dist/solidlsp/language_servers/autoload.d.ts +23 -0
- package/dist/solidlsp/language_servers/autoload.js +25 -0
- package/dist/solidlsp/language_servers/bash_language_server.d.ts +10 -0
- package/dist/solidlsp/language_servers/bash_language_server.js +64 -0
- package/dist/solidlsp/language_servers/clangd_language_server.d.ts +13 -0
- package/dist/solidlsp/language_servers/clangd_language_server.js +110 -0
- package/dist/solidlsp/language_servers/clojure_lsp.d.ts +13 -0
- package/dist/solidlsp/language_servers/clojure_lsp.js +137 -0
- package/dist/solidlsp/language_servers/common.d.ts +41 -0
- package/dist/solidlsp/language_servers/common.js +365 -0
- package/dist/solidlsp/language_servers/csharp_language_server.d.ts +21 -0
- package/dist/solidlsp/language_servers/csharp_language_server.js +694 -0
- package/dist/solidlsp/language_servers/dart_language_server.d.ts +10 -0
- package/dist/solidlsp/language_servers/dart_language_server.js +122 -0
- package/dist/solidlsp/language_servers/eclipse_jdtls.d.ts +24 -0
- package/dist/solidlsp/language_servers/eclipse_jdtls.js +671 -0
- package/dist/solidlsp/language_servers/erlang_language_server.d.ts +22 -0
- package/dist/solidlsp/language_servers/erlang_language_server.js +327 -0
- package/dist/solidlsp/language_servers/gopls.d.ts +12 -0
- package/dist/solidlsp/language_servers/gopls.js +59 -0
- package/dist/solidlsp/language_servers/intelephense.d.ts +13 -0
- package/dist/solidlsp/language_servers/intelephense.js +121 -0
- package/dist/solidlsp/language_servers/jedi_server.d.ts +18 -0
- package/dist/solidlsp/language_servers/jedi_server.js +234 -0
- package/dist/solidlsp/language_servers/kotlin_language_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/kotlin_language_server.js +474 -0
- package/dist/solidlsp/language_servers/lua_ls.d.ts +18 -0
- package/dist/solidlsp/language_servers/lua_ls.js +319 -0
- package/dist/solidlsp/language_servers/nixd_language_server.d.ts +17 -0
- package/dist/solidlsp/language_servers/nixd_language_server.js +341 -0
- package/dist/solidlsp/language_servers/pyright_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/pyright_server.js +180 -0
- package/dist/solidlsp/language_servers/r_language_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/r_language_server.js +184 -0
- package/dist/solidlsp/language_servers/ruby_common.d.ts +10 -0
- package/dist/solidlsp/language_servers/ruby_common.js +136 -0
- package/dist/solidlsp/language_servers/ruby_lsp.d.ts +18 -0
- package/dist/solidlsp/language_servers/ruby_lsp.js +230 -0
- package/dist/solidlsp/language_servers/rust_analyzer.d.ts +13 -0
- package/dist/solidlsp/language_servers/rust_analyzer.js +96 -0
- package/dist/solidlsp/language_servers/solargraph.d.ts +18 -0
- package/dist/solidlsp/language_servers/solargraph.js +208 -0
- package/dist/solidlsp/language_servers/sourcekit_lsp.d.ts +24 -0
- package/dist/solidlsp/language_servers/sourcekit_lsp.js +449 -0
- package/dist/solidlsp/language_servers/terraform_ls.d.ts +13 -0
- package/dist/solidlsp/language_servers/terraform_ls.js +139 -0
- package/dist/solidlsp/language_servers/typescript_language_server.d.ts +20 -0
- package/dist/solidlsp/language_servers/typescript_language_server.js +237 -0
- package/dist/solidlsp/language_servers/vts_language_server.d.ts +13 -0
- package/dist/solidlsp/language_servers/vts_language_server.js +121 -0
- package/dist/solidlsp/language_servers/zls.d.ts +20 -0
- package/dist/solidlsp/language_servers/zls.js +254 -0
- package/dist/solidlsp/ls.d.ts +197 -0
- package/dist/solidlsp/ls.js +507 -0
- package/dist/solidlsp/ls_config.d.ts +43 -0
- package/dist/solidlsp/ls_config.js +157 -0
- package/dist/solidlsp/ls_exceptions.d.ts +5 -0
- package/dist/solidlsp/ls_exceptions.js +14 -0
- package/dist/solidlsp/ls_handler.d.ts +54 -0
- package/dist/solidlsp/ls_handler.js +406 -0
- package/dist/solidlsp/ls_request.d.ts +31 -0
- package/dist/solidlsp/ls_request.js +42 -0
- package/dist/solidlsp/ls_types.d.ts +7 -0
- package/dist/solidlsp/ls_types.js +8 -0
- package/dist/solidlsp/lsp_protocol_handler/server.d.ts +61 -0
- package/dist/solidlsp/lsp_protocol_handler/server.js +68 -0
- package/dist/solidlsp/util/subprocess_util.d.ts +6 -0
- package/dist/solidlsp/util/subprocess_util.js +11 -0
- package/dist/solidlsp/util/zip.d.ts +25 -0
- package/dist/solidlsp/util/zip.js +188 -0
- package/package.json +65 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { MatchedConsecutiveLines } from '../serena/text_utils.js';
|
|
2
|
+
import { type LogLevel, type SerenaLogger } from '../serena/util/logging.js';
|
|
3
|
+
import { type PathMatcher } from '../serena/util/file_system.js';
|
|
4
|
+
import type { Language } from './ls_config.js';
|
|
5
|
+
export interface LanguageServerConfig {
|
|
6
|
+
codeLanguage: Language;
|
|
7
|
+
traceLspCommunication?: boolean;
|
|
8
|
+
startIndependentLspProcess?: boolean;
|
|
9
|
+
ignoredPaths?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface LanguageServerConfigLike {
|
|
12
|
+
codeLanguage: Language;
|
|
13
|
+
traceLspCommunication?: boolean;
|
|
14
|
+
startIndependentLspProcess?: boolean;
|
|
15
|
+
ignoredPaths?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface SolidLspSettingsInit {
|
|
18
|
+
solidlspDir?: string;
|
|
19
|
+
projectDataRelativePath?: string;
|
|
20
|
+
lsSpecificSettings?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export declare class SolidLspSettings {
|
|
23
|
+
readonly solidlspDir: string;
|
|
24
|
+
readonly projectDataRelativePath: string;
|
|
25
|
+
readonly lsSpecificSettings: Record<string, unknown>;
|
|
26
|
+
constructor(init?: SolidLspSettingsInit);
|
|
27
|
+
get languageServersStaticDir(): string;
|
|
28
|
+
}
|
|
29
|
+
export declare function coerceLogLevel(value: LogLevel | number | undefined): LogLevel;
|
|
30
|
+
export interface LspRange {
|
|
31
|
+
start?: {
|
|
32
|
+
line?: number;
|
|
33
|
+
character?: number | null;
|
|
34
|
+
} | null;
|
|
35
|
+
end?: {
|
|
36
|
+
line?: number;
|
|
37
|
+
character?: number | null;
|
|
38
|
+
} | null;
|
|
39
|
+
}
|
|
40
|
+
export interface UnifiedSymbolInformation {
|
|
41
|
+
name: string;
|
|
42
|
+
kind: number;
|
|
43
|
+
children?: UnifiedSymbolInformation[];
|
|
44
|
+
location?: {
|
|
45
|
+
relativePath?: string | null;
|
|
46
|
+
range?: LspRange | null;
|
|
47
|
+
} | null;
|
|
48
|
+
parent?: UnifiedSymbolInformation | null;
|
|
49
|
+
range?: LspRange | null;
|
|
50
|
+
selectionRange?: LspRange | null;
|
|
51
|
+
body?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface ReferenceInSymbol {
|
|
54
|
+
symbol: UnifiedSymbolInformation;
|
|
55
|
+
line: number;
|
|
56
|
+
character: number;
|
|
57
|
+
}
|
|
58
|
+
export interface DocumentSymbolsOptions {
|
|
59
|
+
includeBody?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface FullSymbolTreeOptions {
|
|
62
|
+
withinRelativePath?: string;
|
|
63
|
+
includeBody?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface ReferencingSymbolsOptions {
|
|
66
|
+
relativeFilePath: string;
|
|
67
|
+
line: number;
|
|
68
|
+
column: number;
|
|
69
|
+
includeImports?: boolean;
|
|
70
|
+
includeSelf?: boolean;
|
|
71
|
+
includeBody?: boolean;
|
|
72
|
+
includeFileSymbols?: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface DidOpenTextDocumentParams {
|
|
75
|
+
textDocument: {
|
|
76
|
+
uri: string;
|
|
77
|
+
languageId: string;
|
|
78
|
+
version: number;
|
|
79
|
+
text: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface DidChangeTextDocumentParams {
|
|
83
|
+
textDocument: {
|
|
84
|
+
uri: string;
|
|
85
|
+
version: number;
|
|
86
|
+
};
|
|
87
|
+
contentChanges: {
|
|
88
|
+
range: {
|
|
89
|
+
start: {
|
|
90
|
+
line: number;
|
|
91
|
+
character: number;
|
|
92
|
+
};
|
|
93
|
+
end: {
|
|
94
|
+
line: number;
|
|
95
|
+
character: number;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
text: string;
|
|
99
|
+
}[];
|
|
100
|
+
}
|
|
101
|
+
export interface DidCloseTextDocumentParams {
|
|
102
|
+
textDocument: {
|
|
103
|
+
uri: string;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export interface DocumentSymbolResult {
|
|
107
|
+
documentSymbols: UnifiedSymbolInformation[];
|
|
108
|
+
outlineSymbols: UnifiedSymbolInformation[];
|
|
109
|
+
}
|
|
110
|
+
export interface SolidLanguageServerNotifications {
|
|
111
|
+
initialized(params: unknown): void;
|
|
112
|
+
exit(): void;
|
|
113
|
+
didOpenTextDocument(params: DidOpenTextDocumentParams): void;
|
|
114
|
+
didChangeTextDocument(params: DidChangeTextDocumentParams): void;
|
|
115
|
+
didCloseTextDocument(params: DidCloseTextDocumentParams): void;
|
|
116
|
+
}
|
|
117
|
+
export interface SolidLanguageServerRequests {
|
|
118
|
+
documentSymbol(params: {
|
|
119
|
+
textDocument: {
|
|
120
|
+
uri: string;
|
|
121
|
+
};
|
|
122
|
+
options?: DocumentSymbolsOptions;
|
|
123
|
+
}): DocumentSymbolResult | null;
|
|
124
|
+
fullSymbolTree(params: FullSymbolTreeOptions): UnifiedSymbolInformation[] | null;
|
|
125
|
+
referencingSymbols(options: ReferencingSymbolsOptions): ReferenceInSymbol[] | null;
|
|
126
|
+
overview(relativeFilePath: string): Record<string, UnifiedSymbolInformation[]> | null;
|
|
127
|
+
shutdown(): void;
|
|
128
|
+
}
|
|
129
|
+
export interface SolidLanguageServerHandler {
|
|
130
|
+
setRequestTimeout(timeout: number | null): void;
|
|
131
|
+
isRunning(): boolean;
|
|
132
|
+
start(): void;
|
|
133
|
+
shutdown(): void;
|
|
134
|
+
dispose(): void;
|
|
135
|
+
readonly notify: SolidLanguageServerNotifications;
|
|
136
|
+
readonly send: SolidLanguageServerRequests;
|
|
137
|
+
}
|
|
138
|
+
export interface SolidLanguageServerOptions {
|
|
139
|
+
timeout?: number | null;
|
|
140
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
141
|
+
handler?: SolidLanguageServerHandler;
|
|
142
|
+
}
|
|
143
|
+
export type SolidLanguageServerConstructor = new (config: LanguageServerConfigLike, loggerLike: {
|
|
144
|
+
level?: LogLevel | number;
|
|
145
|
+
} | null, repositoryRootPath: string, options?: SolidLanguageServerOptions) => SolidLanguageServer;
|
|
146
|
+
export declare class SolidLanguageServer {
|
|
147
|
+
static readonly CACHE_FOLDER_NAME = "cache";
|
|
148
|
+
protected readonly repositoryRootPath: string;
|
|
149
|
+
protected readonly logger: SerenaLogger;
|
|
150
|
+
protected readonly language: Language;
|
|
151
|
+
private readonly languageMatcher;
|
|
152
|
+
private readonly ignoreMatcher;
|
|
153
|
+
protected readonly handler: SolidLanguageServerHandler;
|
|
154
|
+
protected readonly solidlspSettings: SolidLspSettings;
|
|
155
|
+
private readonly documentSymbolsCache;
|
|
156
|
+
private serverStarted;
|
|
157
|
+
private cacheHasChanged;
|
|
158
|
+
private readonly openFileBuffers;
|
|
159
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
160
|
+
level?: LogLevel | number;
|
|
161
|
+
} | null, repositoryRootPath: string, options?: SolidLanguageServerOptions);
|
|
162
|
+
static create(config: LanguageServerConfigLike, loggerLike: {
|
|
163
|
+
level?: LogLevel | number;
|
|
164
|
+
} | null, repositoryRootPath: string, options?: SolidLanguageServerOptions): SolidLanguageServer;
|
|
165
|
+
start(): this;
|
|
166
|
+
stop(shutdownTimeout?: number): void;
|
|
167
|
+
isRunning(): boolean;
|
|
168
|
+
setRequestTimeout(timeout: number | null): void;
|
|
169
|
+
getRepositoryRootPath(): string;
|
|
170
|
+
get ignoreSpec(): PathMatcher;
|
|
171
|
+
saveCache(): void;
|
|
172
|
+
requestDocumentSymbols(relativePath: string, options?: DocumentSymbolsOptions): DocumentSymbolResult;
|
|
173
|
+
requestFullSymbolTree(options?: FullSymbolTreeOptions): UnifiedSymbolInformation[];
|
|
174
|
+
requestReferencingSymbols(options: ReferencingSymbolsOptions): ReferenceInSymbol[];
|
|
175
|
+
requestOverview(relativePath: string): Record<string, UnifiedSymbolInformation[]>;
|
|
176
|
+
retrieveFullFileContent(relativePath: string): string;
|
|
177
|
+
retrieveContentAroundLine(relativePath: string, line: number, contextLinesBefore?: number, contextLinesAfter?: number): MatchedConsecutiveLines;
|
|
178
|
+
insertTextAtPosition(relativePath: string, line: number, column: number, text: string): {
|
|
179
|
+
line: number;
|
|
180
|
+
column: number;
|
|
181
|
+
};
|
|
182
|
+
deleteTextBetweenPositions(relativePath: string, start: {
|
|
183
|
+
line: number;
|
|
184
|
+
character: number;
|
|
185
|
+
}, end: {
|
|
186
|
+
line: number;
|
|
187
|
+
character: number;
|
|
188
|
+
}): string;
|
|
189
|
+
isIgnoredPath(relativePath: string, ignoreUnsupportedFiles?: boolean): boolean;
|
|
190
|
+
private ensureServerRunning;
|
|
191
|
+
private withOpenFile;
|
|
192
|
+
private acquireFileBuffer;
|
|
193
|
+
private releaseFileBuffer;
|
|
194
|
+
private loadCache;
|
|
195
|
+
private get cachePath();
|
|
196
|
+
}
|
|
197
|
+
export declare function registerLanguageServer(language: Language, ctor: SolidLanguageServerConstructor): void;
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import ignore from 'ignore';
|
|
6
|
+
import { MatchedConsecutiveLines } from '../serena/text_utils.js';
|
|
7
|
+
import { createSerenaLogger } from '../serena/util/logging.js';
|
|
8
|
+
import { matchPath } from '../serena/util/file_system.js';
|
|
9
|
+
import { getLanguageFilenameMatcher } from './ls_config.js';
|
|
10
|
+
export class SolidLspSettings {
|
|
11
|
+
solidlspDir;
|
|
12
|
+
projectDataRelativePath;
|
|
13
|
+
lsSpecificSettings;
|
|
14
|
+
constructor(init = {}) {
|
|
15
|
+
this.solidlspDir = init.solidlspDir ?? path.join(path.resolve(process.env.HOME ?? '.'), '.solidlsp');
|
|
16
|
+
this.projectDataRelativePath = init.projectDataRelativePath ?? '.solidlsp';
|
|
17
|
+
this.lsSpecificSettings = { ...(init.lsSpecificSettings ?? {}) };
|
|
18
|
+
fs.mkdirSync(this.solidlspDir, { recursive: true });
|
|
19
|
+
fs.mkdirSync(this.languageServersStaticDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
get languageServersStaticDir() {
|
|
22
|
+
return path.join(this.solidlspDir, 'language_servers', 'static');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function logWithLevel(logger, level, message, meta) {
|
|
26
|
+
switch (level) {
|
|
27
|
+
case 'trace':
|
|
28
|
+
logger.trace(message, meta);
|
|
29
|
+
break;
|
|
30
|
+
case 'debug':
|
|
31
|
+
logger.debug(message, meta);
|
|
32
|
+
break;
|
|
33
|
+
case 'info':
|
|
34
|
+
logger.info(message, meta);
|
|
35
|
+
break;
|
|
36
|
+
case 'warn':
|
|
37
|
+
logger.warn(message, meta);
|
|
38
|
+
break;
|
|
39
|
+
case 'error':
|
|
40
|
+
logger.error(message, meta);
|
|
41
|
+
break;
|
|
42
|
+
case 'fatal':
|
|
43
|
+
logger.fatal(message, meta);
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
logger.info(message, meta);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function coerceLogLevel(value) {
|
|
50
|
+
if (typeof value === 'string') {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === 'number') {
|
|
54
|
+
if (value <= 10) {
|
|
55
|
+
return 'trace';
|
|
56
|
+
}
|
|
57
|
+
if (value <= 20) {
|
|
58
|
+
return 'debug';
|
|
59
|
+
}
|
|
60
|
+
if (value <= 30) {
|
|
61
|
+
return 'info';
|
|
62
|
+
}
|
|
63
|
+
if (value <= 40) {
|
|
64
|
+
return 'warn';
|
|
65
|
+
}
|
|
66
|
+
if (value <= 50) {
|
|
67
|
+
return 'error';
|
|
68
|
+
}
|
|
69
|
+
return 'fatal';
|
|
70
|
+
}
|
|
71
|
+
return 'info';
|
|
72
|
+
}
|
|
73
|
+
class NullLanguageServerHandler {
|
|
74
|
+
running = false;
|
|
75
|
+
timeout = null;
|
|
76
|
+
setRequestTimeout(timeout) {
|
|
77
|
+
this.timeout = timeout ?? null;
|
|
78
|
+
}
|
|
79
|
+
isRunning() {
|
|
80
|
+
return this.running;
|
|
81
|
+
}
|
|
82
|
+
start() {
|
|
83
|
+
this.running = true;
|
|
84
|
+
}
|
|
85
|
+
shutdown() {
|
|
86
|
+
this.running = false;
|
|
87
|
+
}
|
|
88
|
+
dispose() {
|
|
89
|
+
this.timeout = null;
|
|
90
|
+
}
|
|
91
|
+
notify = {
|
|
92
|
+
initialized: (params) => {
|
|
93
|
+
void params;
|
|
94
|
+
},
|
|
95
|
+
exit: () => {
|
|
96
|
+
// no-op
|
|
97
|
+
},
|
|
98
|
+
didOpenTextDocument: (params) => {
|
|
99
|
+
void params;
|
|
100
|
+
},
|
|
101
|
+
didChangeTextDocument: (params) => {
|
|
102
|
+
void params;
|
|
103
|
+
},
|
|
104
|
+
didCloseTextDocument: (params) => {
|
|
105
|
+
void params;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
send = {
|
|
109
|
+
documentSymbol: () => null,
|
|
110
|
+
fullSymbolTree: () => null,
|
|
111
|
+
referencingSymbols: () => null,
|
|
112
|
+
overview: () => ({}),
|
|
113
|
+
shutdown: () => undefined
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
class LspFileBuffer {
|
|
117
|
+
uri;
|
|
118
|
+
contents;
|
|
119
|
+
version;
|
|
120
|
+
languageId;
|
|
121
|
+
refCount;
|
|
122
|
+
contentHash;
|
|
123
|
+
constructor(init) {
|
|
124
|
+
this.uri = init.uri;
|
|
125
|
+
this.contents = init.contents;
|
|
126
|
+
this.version = init.version;
|
|
127
|
+
this.languageId = init.languageId;
|
|
128
|
+
this.refCount = init.refCount;
|
|
129
|
+
this.contentHash = computeContentHash(this.contents);
|
|
130
|
+
}
|
|
131
|
+
updateContents(contents) {
|
|
132
|
+
this.contents = contents;
|
|
133
|
+
this.contentHash = computeContentHash(contents);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function computeContentHash(contents) {
|
|
137
|
+
return createHash('md5').update(contents, 'utf8').digest('hex');
|
|
138
|
+
}
|
|
139
|
+
function normalizeRelativePath(relativePath) {
|
|
140
|
+
const normalized = relativePath.replace(/\\/g, '/');
|
|
141
|
+
if (normalized.startsWith('./')) {
|
|
142
|
+
return normalized.slice(2);
|
|
143
|
+
}
|
|
144
|
+
if (normalized.startsWith('../')) {
|
|
145
|
+
return normalized.replace(/^\\.\\./u, '..');
|
|
146
|
+
}
|
|
147
|
+
return normalized;
|
|
148
|
+
}
|
|
149
|
+
function getIndexFromLineCol(text, line, col) {
|
|
150
|
+
let currentLine = 0;
|
|
151
|
+
let currentCol = 0;
|
|
152
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
153
|
+
if (currentLine === line && currentCol === col) {
|
|
154
|
+
return index;
|
|
155
|
+
}
|
|
156
|
+
const char = text[index];
|
|
157
|
+
if (char === '\n') {
|
|
158
|
+
currentLine += 1;
|
|
159
|
+
currentCol = 0;
|
|
160
|
+
}
|
|
161
|
+
else if (char === '\r') {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
currentCol += 1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (currentLine === line && currentCol === col) {
|
|
169
|
+
return text.length;
|
|
170
|
+
}
|
|
171
|
+
throw new RangeError(`Position (${line}, ${col}) is out of bounds.`);
|
|
172
|
+
}
|
|
173
|
+
function insertTextAtPosition(text, line, col, snippet) {
|
|
174
|
+
const index = getIndexFromLineCol(text, line, col);
|
|
175
|
+
const updated = `${text.slice(0, index)}${snippet}${text.slice(index)}`;
|
|
176
|
+
const lines = snippet.split('\n');
|
|
177
|
+
if (lines.length === 1) {
|
|
178
|
+
return { contents: updated, line, column: col + snippet.length };
|
|
179
|
+
}
|
|
180
|
+
const newLine = line + lines.length - 1;
|
|
181
|
+
const newColumn = lines[lines.length - 1].length;
|
|
182
|
+
return { contents: updated, line: newLine, column: newColumn };
|
|
183
|
+
}
|
|
184
|
+
function deleteTextBetweenPositions(text, start, end) {
|
|
185
|
+
const startIndex = getIndexFromLineCol(text, start.line, start.character);
|
|
186
|
+
const endIndex = getIndexFromLineCol(text, end.line, end.character);
|
|
187
|
+
if (endIndex < startIndex) {
|
|
188
|
+
throw new RangeError('End position must not precede start position.');
|
|
189
|
+
}
|
|
190
|
+
const deleted = text.slice(startIndex, endIndex);
|
|
191
|
+
const updated = `${text.slice(0, startIndex)}${text.slice(endIndex)}`;
|
|
192
|
+
return { contents: updated, deleted };
|
|
193
|
+
}
|
|
194
|
+
export class SolidLanguageServer {
|
|
195
|
+
static CACHE_FOLDER_NAME = 'cache';
|
|
196
|
+
repositoryRootPath;
|
|
197
|
+
logger;
|
|
198
|
+
language;
|
|
199
|
+
languageMatcher;
|
|
200
|
+
ignoreMatcher;
|
|
201
|
+
handler;
|
|
202
|
+
solidlspSettings;
|
|
203
|
+
documentSymbolsCache = new Map();
|
|
204
|
+
serverStarted = false;
|
|
205
|
+
cacheHasChanged = false;
|
|
206
|
+
openFileBuffers = new Map();
|
|
207
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
208
|
+
this.repositoryRootPath = path.resolve(repositoryRootPath);
|
|
209
|
+
this.language = config.codeLanguage;
|
|
210
|
+
this.languageMatcher = getLanguageFilenameMatcher(this.language);
|
|
211
|
+
const ignoredPatterns = Array.from(new Set(config.ignoredPaths ?? [])).map((pattern) => pattern.replace(/\\/g, '/'));
|
|
212
|
+
const ignoreInstance = ignore();
|
|
213
|
+
if (ignoredPatterns.length > 0) {
|
|
214
|
+
ignoreInstance.add(ignoredPatterns);
|
|
215
|
+
}
|
|
216
|
+
this.ignoreMatcher = {
|
|
217
|
+
ignores(candidate) {
|
|
218
|
+
const normalizedCandidate = candidate.replace(/^\/+/, '');
|
|
219
|
+
return ignoreInstance.ignores(normalizedCandidate);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const { logger } = createSerenaLogger({
|
|
223
|
+
level: coerceLogLevel(loggerLike?.level),
|
|
224
|
+
emitToConsole: false,
|
|
225
|
+
name: 'solidlsp.language_server'
|
|
226
|
+
});
|
|
227
|
+
this.logger = logger;
|
|
228
|
+
this.handler = options.handler ?? new NullLanguageServerHandler();
|
|
229
|
+
if (options.timeout !== undefined) {
|
|
230
|
+
this.handler.setRequestTimeout(options.timeout ?? null);
|
|
231
|
+
}
|
|
232
|
+
this.solidlspSettings = new SolidLspSettings(options.solidlspSettings);
|
|
233
|
+
this.loadCache();
|
|
234
|
+
}
|
|
235
|
+
static create(config, loggerLike, repositoryRootPath, options = {}) {
|
|
236
|
+
const Ctor = getLanguageServerConstructor(config.codeLanguage);
|
|
237
|
+
return new Ctor(config, loggerLike, repositoryRootPath, options);
|
|
238
|
+
}
|
|
239
|
+
start() {
|
|
240
|
+
if (this.serverStarted) {
|
|
241
|
+
return this;
|
|
242
|
+
}
|
|
243
|
+
logWithLevel(this.logger, 'info', `Starting SolidLanguageServer for ${this.repositoryRootPath}`);
|
|
244
|
+
this.handler.start();
|
|
245
|
+
this.serverStarted = true;
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
stop(shutdownTimeout = 2.0) {
|
|
249
|
+
if (!this.serverStarted) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
logWithLevel(this.logger, 'debug', `Stopping SolidLanguageServer (timeout=${shutdownTimeout}s)`);
|
|
253
|
+
for (const buffer of this.openFileBuffers.values()) {
|
|
254
|
+
this.handler.notify.didCloseTextDocument({
|
|
255
|
+
textDocument: { uri: buffer.uri }
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
this.openFileBuffers.clear();
|
|
259
|
+
this.handler.shutdown();
|
|
260
|
+
this.handler.dispose();
|
|
261
|
+
this.serverStarted = false;
|
|
262
|
+
}
|
|
263
|
+
isRunning() {
|
|
264
|
+
return this.handler.isRunning();
|
|
265
|
+
}
|
|
266
|
+
setRequestTimeout(timeout) {
|
|
267
|
+
this.handler.setRequestTimeout(timeout);
|
|
268
|
+
}
|
|
269
|
+
getRepositoryRootPath() {
|
|
270
|
+
return this.repositoryRootPath;
|
|
271
|
+
}
|
|
272
|
+
get ignoreSpec() {
|
|
273
|
+
return this.ignoreMatcher;
|
|
274
|
+
}
|
|
275
|
+
saveCache() {
|
|
276
|
+
if (!this.cacheHasChanged) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const cachePath = this.cachePath;
|
|
280
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
281
|
+
const payload = {};
|
|
282
|
+
for (const [relativePath, entry] of this.documentSymbolsCache.entries()) {
|
|
283
|
+
payload[relativePath] = {
|
|
284
|
+
hash: entry.hash,
|
|
285
|
+
data: {
|
|
286
|
+
documentSymbols: entry.data.documentSymbols,
|
|
287
|
+
outlineSymbols: entry.data.outlineSymbols
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
fs.writeFileSync(cachePath, JSON.stringify(payload, null, 2), { encoding: 'utf-8' });
|
|
292
|
+
this.cacheHasChanged = false;
|
|
293
|
+
}
|
|
294
|
+
requestDocumentSymbols(relativePath, options = {}) {
|
|
295
|
+
this.ensureServerRunning('requestDocumentSymbols');
|
|
296
|
+
return this.withOpenFile(relativePath, (buffer) => {
|
|
297
|
+
const cached = this.documentSymbolsCache.get(relativePath);
|
|
298
|
+
if (cached && cached.hash === buffer.contentHash) {
|
|
299
|
+
return {
|
|
300
|
+
documentSymbols: cloneSymbols(cached.data.documentSymbols),
|
|
301
|
+
outlineSymbols: cloneSymbols(cached.data.outlineSymbols)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const response = this.handler.send.documentSymbol({
|
|
305
|
+
textDocument: { uri: buffer.uri },
|
|
306
|
+
options
|
|
307
|
+
});
|
|
308
|
+
const result = response ?? {
|
|
309
|
+
documentSymbols: [],
|
|
310
|
+
outlineSymbols: []
|
|
311
|
+
};
|
|
312
|
+
this.documentSymbolsCache.set(relativePath, {
|
|
313
|
+
hash: buffer.contentHash,
|
|
314
|
+
data: {
|
|
315
|
+
documentSymbols: cloneSymbols(result.documentSymbols),
|
|
316
|
+
outlineSymbols: cloneSymbols(result.outlineSymbols)
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
this.cacheHasChanged = true;
|
|
320
|
+
return result;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
requestFullSymbolTree(options = {}) {
|
|
324
|
+
this.ensureServerRunning('requestFullSymbolTree');
|
|
325
|
+
const response = this.handler.send.fullSymbolTree(options);
|
|
326
|
+
return response ? cloneSymbols(response) : [];
|
|
327
|
+
}
|
|
328
|
+
requestReferencingSymbols(options) {
|
|
329
|
+
this.ensureServerRunning('requestReferencingSymbols');
|
|
330
|
+
const response = this.handler.send.referencingSymbols(options);
|
|
331
|
+
return response ? response.map((entry) => ({ ...entry, symbol: cloneSymbol(entry.symbol) })) : [];
|
|
332
|
+
}
|
|
333
|
+
requestOverview(relativePath) {
|
|
334
|
+
this.ensureServerRunning('requestOverview');
|
|
335
|
+
const response = this.handler.send.overview(relativePath) ?? {};
|
|
336
|
+
const clone = {};
|
|
337
|
+
for (const [key, symbols] of Object.entries(response)) {
|
|
338
|
+
clone[key] = cloneSymbols(symbols);
|
|
339
|
+
}
|
|
340
|
+
return clone;
|
|
341
|
+
}
|
|
342
|
+
retrieveFullFileContent(relativePath) {
|
|
343
|
+
return this.withOpenFile(relativePath, (buffer) => buffer.contents);
|
|
344
|
+
}
|
|
345
|
+
retrieveContentAroundLine(relativePath, line, contextLinesBefore = 0, contextLinesAfter = 0) {
|
|
346
|
+
const contents = this.retrieveFullFileContent(relativePath);
|
|
347
|
+
return MatchedConsecutiveLines.fromFileContents({
|
|
348
|
+
fileContents: contents,
|
|
349
|
+
line,
|
|
350
|
+
contextLinesBefore,
|
|
351
|
+
contextLinesAfter,
|
|
352
|
+
sourceFilePath: relativePath
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
insertTextAtPosition(relativePath, line, column, text) {
|
|
356
|
+
this.ensureServerRunning('insertTextAtPosition');
|
|
357
|
+
return this.withOpenFile(relativePath, (buffer) => {
|
|
358
|
+
const result = insertTextAtPosition(buffer.contents, line, column, text);
|
|
359
|
+
buffer.updateContents(result.contents);
|
|
360
|
+
buffer.version += 1;
|
|
361
|
+
this.handler.notify.didChangeTextDocument({
|
|
362
|
+
textDocument: { uri: buffer.uri, version: buffer.version },
|
|
363
|
+
contentChanges: [
|
|
364
|
+
{
|
|
365
|
+
range: {
|
|
366
|
+
start: { line, character: column },
|
|
367
|
+
end: { line, character: column }
|
|
368
|
+
},
|
|
369
|
+
text
|
|
370
|
+
}
|
|
371
|
+
]
|
|
372
|
+
});
|
|
373
|
+
return { line: result.line, column: result.column };
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
deleteTextBetweenPositions(relativePath, start, end) {
|
|
377
|
+
this.ensureServerRunning('deleteTextBetweenPositions');
|
|
378
|
+
return this.withOpenFile(relativePath, (buffer) => {
|
|
379
|
+
const { contents, deleted } = deleteTextBetweenPositions(buffer.contents, start, end);
|
|
380
|
+
buffer.updateContents(contents);
|
|
381
|
+
buffer.version += 1;
|
|
382
|
+
this.handler.notify.didChangeTextDocument({
|
|
383
|
+
textDocument: { uri: buffer.uri, version: buffer.version },
|
|
384
|
+
contentChanges: [
|
|
385
|
+
{
|
|
386
|
+
range: {
|
|
387
|
+
start,
|
|
388
|
+
end
|
|
389
|
+
},
|
|
390
|
+
text: ''
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
});
|
|
394
|
+
return deleted;
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
isIgnoredPath(relativePath, ignoreUnsupportedFiles = true) {
|
|
398
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
399
|
+
const absolutePath = path.resolve(this.repositoryRootPath, normalized);
|
|
400
|
+
if (!fs.existsSync(absolutePath)) {
|
|
401
|
+
throw new Error(`File ${absolutePath} not found, the ignore check cannot be performed`);
|
|
402
|
+
}
|
|
403
|
+
const stats = fs.statSync(absolutePath);
|
|
404
|
+
if (stats.isFile() && ignoreUnsupportedFiles) {
|
|
405
|
+
if (!this.languageMatcher.isRelevantFilename(path.basename(absolutePath))) {
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return matchPath(normalized, this.ignoreMatcher, this.repositoryRootPath);
|
|
410
|
+
}
|
|
411
|
+
ensureServerRunning(operation) {
|
|
412
|
+
if (!this.serverStarted) {
|
|
413
|
+
throw new Error(`Language server is not started; cannot call ${operation}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
withOpenFile(relativePath, handler) {
|
|
417
|
+
const buffer = this.acquireFileBuffer(relativePath);
|
|
418
|
+
try {
|
|
419
|
+
return handler(buffer);
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
this.releaseFileBuffer(buffer.uri);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
acquireFileBuffer(relativePath) {
|
|
426
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
427
|
+
const absolutePath = path.resolve(this.repositoryRootPath, normalized);
|
|
428
|
+
const uri = pathToFileURL(absolutePath).href;
|
|
429
|
+
const existing = this.openFileBuffers.get(uri);
|
|
430
|
+
if (existing) {
|
|
431
|
+
existing.refCount += 1;
|
|
432
|
+
return existing;
|
|
433
|
+
}
|
|
434
|
+
const contents = fs.readFileSync(absolutePath, { encoding: 'utf-8' });
|
|
435
|
+
const buffer = new LspFileBuffer({ uri, contents, version: 0, languageId: this.language, refCount: 1 });
|
|
436
|
+
this.openFileBuffers.set(uri, buffer);
|
|
437
|
+
this.handler.notify.didOpenTextDocument({
|
|
438
|
+
textDocument: {
|
|
439
|
+
uri,
|
|
440
|
+
languageId: this.language,
|
|
441
|
+
version: buffer.version,
|
|
442
|
+
text: contents
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
return buffer;
|
|
446
|
+
}
|
|
447
|
+
releaseFileBuffer(uri) {
|
|
448
|
+
const buffer = this.openFileBuffers.get(uri);
|
|
449
|
+
if (!buffer) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
buffer.refCount -= 1;
|
|
453
|
+
if (buffer.refCount <= 0) {
|
|
454
|
+
this.handler.notify.didCloseTextDocument({
|
|
455
|
+
textDocument: { uri }
|
|
456
|
+
});
|
|
457
|
+
this.openFileBuffers.delete(uri);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
loadCache() {
|
|
461
|
+
const cachePath = this.cachePath;
|
|
462
|
+
if (!fs.existsSync(cachePath)) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const contents = fs.readFileSync(cachePath, 'utf-8');
|
|
467
|
+
const parsed = JSON.parse(contents);
|
|
468
|
+
for (const [relativePath, entry] of Object.entries(parsed)) {
|
|
469
|
+
this.documentSymbolsCache.set(relativePath, {
|
|
470
|
+
hash: entry.hash,
|
|
471
|
+
data: {
|
|
472
|
+
documentSymbols: cloneSymbols(entry.data.documentSymbols),
|
|
473
|
+
outlineSymbols: cloneSymbols(entry.data.outlineSymbols)
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
logWithLevel(this.logger, 'warn', `Failed to load SolidLSP cache from ${cachePath}`, error);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
get cachePath() {
|
|
483
|
+
return path.join(this.repositoryRootPath, this.solidlspSettings.projectDataRelativePath, SolidLanguageServer.CACHE_FOLDER_NAME, this.language, 'document_symbols_cache.json');
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const LANGUAGE_SERVER_REGISTRY = new Map();
|
|
487
|
+
export function registerLanguageServer(language, ctor) {
|
|
488
|
+
LANGUAGE_SERVER_REGISTRY.set(language, ctor);
|
|
489
|
+
}
|
|
490
|
+
function getLanguageServerConstructor(language) {
|
|
491
|
+
return LANGUAGE_SERVER_REGISTRY.get(language) ?? SolidLanguageServer;
|
|
492
|
+
}
|
|
493
|
+
function deepClone(value) {
|
|
494
|
+
return value === undefined ? value : JSON.parse(JSON.stringify(value));
|
|
495
|
+
}
|
|
496
|
+
function cloneSymbol(symbol) {
|
|
497
|
+
return {
|
|
498
|
+
...symbol,
|
|
499
|
+
children: symbol.children ? cloneSymbols(symbol.children) : undefined,
|
|
500
|
+
parent: symbol.parent ?? undefined,
|
|
501
|
+
location: symbol.location ? deepClone(symbol.location) : undefined,
|
|
502
|
+
selectionRange: symbol.selectionRange ? deepClone(symbol.selectionRange) : undefined
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function cloneSymbols(symbols) {
|
|
506
|
+
return symbols.map((symbol) => cloneSymbol(symbol));
|
|
507
|
+
}
|