@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,327 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { Language } from '../ls_config.js';
|
|
6
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
7
|
+
import { SolidLanguageServer, registerLanguageServer } from '../ls.js';
|
|
8
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
9
|
+
const DEFAULT_REQUEST_TIMEOUT_SECONDS = 120;
|
|
10
|
+
const ERLANG_IGNORED_DIRECTORIES = [
|
|
11
|
+
'_build',
|
|
12
|
+
'deps',
|
|
13
|
+
'ebin',
|
|
14
|
+
'.rebar3',
|
|
15
|
+
'logs',
|
|
16
|
+
'node_modules',
|
|
17
|
+
'_checkouts',
|
|
18
|
+
'cover'
|
|
19
|
+
];
|
|
20
|
+
const READINESS_KEYWORDS = [
|
|
21
|
+
'Started Erlang LS',
|
|
22
|
+
'server started',
|
|
23
|
+
'initialized',
|
|
24
|
+
'ready to serve requests',
|
|
25
|
+
'compilation finished',
|
|
26
|
+
'indexing complete'
|
|
27
|
+
];
|
|
28
|
+
export class ErlangLanguageServer extends SolidLanguageServer {
|
|
29
|
+
handler;
|
|
30
|
+
initialized = false;
|
|
31
|
+
readyPromise;
|
|
32
|
+
readyResolver = null;
|
|
33
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
34
|
+
const requestTimeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_SECONDS;
|
|
35
|
+
const runtime = ensureErlangRuntime();
|
|
36
|
+
const augmentedConfig = {
|
|
37
|
+
...config,
|
|
38
|
+
ignoredPaths: mergeIgnoredPaths(config.ignoredPaths, ERLANG_IGNORED_DIRECTORIES)
|
|
39
|
+
};
|
|
40
|
+
const providedHandler = options.handler;
|
|
41
|
+
if (providedHandler && !(providedHandler instanceof NodeLanguageServerHandler)) {
|
|
42
|
+
throw new TypeError('ErlangLanguageServer requires a NodeLanguageServerHandler when supplying a custom handler.');
|
|
43
|
+
}
|
|
44
|
+
const handlerInstance = providedHandler ?? new NodeLanguageServerHandler({
|
|
45
|
+
cmd: [runtime.binaryPath, '--transport', 'stdio'],
|
|
46
|
+
cwd: repositoryRootPath
|
|
47
|
+
}, {
|
|
48
|
+
requestTimeoutSeconds: requestTimeout
|
|
49
|
+
});
|
|
50
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
51
|
+
timeout: requestTimeout,
|
|
52
|
+
solidlspSettings: options?.solidlspSettings,
|
|
53
|
+
handler: handlerInstance
|
|
54
|
+
});
|
|
55
|
+
if (runtime.erlangVersion) {
|
|
56
|
+
this.logger.info(`Detected Erlang runtime: ${runtime.erlangVersion}`);
|
|
57
|
+
}
|
|
58
|
+
if (!runtime.hasRebar3) {
|
|
59
|
+
this.logger.warn('rebar3 command not found. Some Erlang LS features may be degraded.');
|
|
60
|
+
}
|
|
61
|
+
this.handler = handlerInstance;
|
|
62
|
+
this.readyPromise = this.createReadyPromise();
|
|
63
|
+
this.registerHandlers();
|
|
64
|
+
}
|
|
65
|
+
start() {
|
|
66
|
+
const shouldInitialize = !this.initialized;
|
|
67
|
+
super.start();
|
|
68
|
+
if (shouldInitialize) {
|
|
69
|
+
this.initializeLanguageServer();
|
|
70
|
+
this.initialized = true;
|
|
71
|
+
}
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
stop(shutdownTimeout = 2.0) {
|
|
75
|
+
super.stop(shutdownTimeout);
|
|
76
|
+
this.initialized = false;
|
|
77
|
+
this.readyPromise = this.createReadyPromise();
|
|
78
|
+
}
|
|
79
|
+
initializeLanguageServer() {
|
|
80
|
+
this.logger.info('Initializing Erlang LS');
|
|
81
|
+
const params = this.buildInitializeParams();
|
|
82
|
+
const response = this.handler.sendRequest('initialize', params);
|
|
83
|
+
this.verifyCapabilities(response?.capabilities ?? null);
|
|
84
|
+
this.handler.notify.initialized({});
|
|
85
|
+
this.waitForReadiness();
|
|
86
|
+
}
|
|
87
|
+
buildInitializeParams() {
|
|
88
|
+
const rootUri = pathToFileURL(this.repositoryRootPath).href;
|
|
89
|
+
return {
|
|
90
|
+
processId: process.pid,
|
|
91
|
+
rootPath: this.repositoryRootPath,
|
|
92
|
+
rootUri,
|
|
93
|
+
locale: 'en',
|
|
94
|
+
capabilities: buildClientCapabilities(),
|
|
95
|
+
workspaceFolders: [
|
|
96
|
+
{
|
|
97
|
+
uri: rootUri,
|
|
98
|
+
name: path.basename(this.repositoryRootPath)
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
verifyCapabilities(capabilities) {
|
|
104
|
+
if (!capabilities) {
|
|
105
|
+
throw new Error('Erlang LS initialize response is missing capabilities.');
|
|
106
|
+
}
|
|
107
|
+
const textDocumentCaps = capabilities.textDocument;
|
|
108
|
+
if (!textDocumentCaps) {
|
|
109
|
+
throw new Error('Erlang LS did not report textDocument capabilities.');
|
|
110
|
+
}
|
|
111
|
+
const requiredKeys = ['synchronization', 'completion', 'definition', 'references', 'documentSymbol', 'hover'];
|
|
112
|
+
for (const key of requiredKeys) {
|
|
113
|
+
if (!(key in textDocumentCaps)) {
|
|
114
|
+
throw new Error(`Erlang LS capabilities missing '${key}' under textDocument.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
registerHandlers() {
|
|
119
|
+
const noop = () => undefined;
|
|
120
|
+
this.handler.onRequest('client/registerCapability', noop);
|
|
121
|
+
this.handler.onNotification('window/logMessage', (payload) => {
|
|
122
|
+
this.handleWindowLogMessage(payload);
|
|
123
|
+
});
|
|
124
|
+
this.handler.onNotification('$/progress', (payload) => {
|
|
125
|
+
this.handleProgressNotification(payload);
|
|
126
|
+
});
|
|
127
|
+
this.handler.onNotification('window/workDoneProgress/create', noop);
|
|
128
|
+
this.handler.onNotification('$/workDoneProgress', noop);
|
|
129
|
+
this.handler.onNotification('textDocument/publishDiagnostics', noop);
|
|
130
|
+
}
|
|
131
|
+
handleWindowLogMessage(payload) {
|
|
132
|
+
const message = extractMessage(payload);
|
|
133
|
+
if (!message) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.logger.info(`Erlang LS: ${message}`);
|
|
137
|
+
const normalized = message.toLowerCase();
|
|
138
|
+
if (READINESS_KEYWORDS.some((keyword) => normalized.includes(keyword.toLowerCase()))) {
|
|
139
|
+
this.logger.info(`Erlang LS readiness signal detected: ${message}`);
|
|
140
|
+
this.markServerReady();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
handleProgressNotification(payload) {
|
|
144
|
+
if (!payload || typeof payload !== 'object') {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const params = payload;
|
|
148
|
+
const progress = params.value;
|
|
149
|
+
if (progress?.kind?.toLowerCase() === 'end') {
|
|
150
|
+
const message = progress.message ?? '';
|
|
151
|
+
if (containsReadinessKeyword(message)) {
|
|
152
|
+
this.logger.info('Erlang LS initialization progress reported completion.');
|
|
153
|
+
this.markServerReady();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
waitForReadiness() {
|
|
158
|
+
const isCi = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
|
|
159
|
+
const isMacOs = process.platform === 'darwin';
|
|
160
|
+
const readyTimeoutSeconds = isCi ? (isMacOs ? 240 : 180) : 60;
|
|
161
|
+
const settlingSeconds = isCi ? 15 : 5;
|
|
162
|
+
const fallbackSettlingSeconds = isCi ? 20 : 10;
|
|
163
|
+
this.logger.info(`Waiting up to ${readyTimeoutSeconds} seconds for Erlang LS readiness (${isCi ? (isMacOs ? 'macOS CI' : 'CI') : 'local'} environment).`);
|
|
164
|
+
void (async () => {
|
|
165
|
+
const readinessOutcome = await Promise.race([
|
|
166
|
+
this.readyPromise.then(() => 'ready'),
|
|
167
|
+
delay(readyTimeoutSeconds * 1000).then(() => 'timeout')
|
|
168
|
+
]);
|
|
169
|
+
if (readinessOutcome === 'ready') {
|
|
170
|
+
this.logger.info('Erlang LS reported readiness. Allowing additional settling time.');
|
|
171
|
+
await delay(settlingSeconds * 1000);
|
|
172
|
+
this.logger.info('Erlang LS settling period complete.');
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.logger.warn(`Erlang LS readiness timeout reached after ${readyTimeoutSeconds}s, proceeding anyway (common in CI environments).`);
|
|
176
|
+
this.markServerReady();
|
|
177
|
+
await delay(fallbackSettlingSeconds * 1000);
|
|
178
|
+
this.logger.info('Basic Erlang LS initialization period complete.');
|
|
179
|
+
}
|
|
180
|
+
})().catch((error) => {
|
|
181
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
182
|
+
this.logger.warn(`Erlang LS readiness watcher encountered an error: ${message}`);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
markServerReady() {
|
|
186
|
+
const resolver = this.readyResolver;
|
|
187
|
+
if (!resolver) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.readyResolver = null;
|
|
191
|
+
resolver();
|
|
192
|
+
}
|
|
193
|
+
createReadyPromise() {
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
this.readyResolver = () => {
|
|
196
|
+
this.readyResolver = null;
|
|
197
|
+
resolve();
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function ensureErlangRuntime() {
|
|
203
|
+
if (shouldAssumeInstalled()) {
|
|
204
|
+
return {
|
|
205
|
+
binaryPath: getAssumedBinaryPath(),
|
|
206
|
+
hasRebar3: true,
|
|
207
|
+
erlangVersion: null
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const binaryPath = findExecutable(['erlang_ls', process.platform === 'win32' ? 'erlang_ls.cmd' : null]);
|
|
211
|
+
if (!binaryPath) {
|
|
212
|
+
throw new Error('Erlang LS not found. Install from https://github.com/erlang-ls/erlang_ls and ensure it is on PATH.');
|
|
213
|
+
}
|
|
214
|
+
if (!commandSucceeds('erl', ['-version'])) {
|
|
215
|
+
throw new Error('Erlang/OTP not found. Install from https://www.erlang.org/downloads and ensure it is on PATH.');
|
|
216
|
+
}
|
|
217
|
+
const erlangVersion = captureCommandOutput('erl', ['-version']);
|
|
218
|
+
const hasRebar3 = commandSucceeds('rebar3', ['version']);
|
|
219
|
+
return {
|
|
220
|
+
binaryPath,
|
|
221
|
+
hasRebar3,
|
|
222
|
+
erlangVersion: erlangVersion ?? null
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function shouldAssumeInstalled() {
|
|
226
|
+
return process.env.SERENA_ASSUME_ERLANG === '1' || process.env.SERENA_ASSUME_ERLANG_LS === '1';
|
|
227
|
+
}
|
|
228
|
+
function getAssumedBinaryPath() {
|
|
229
|
+
const override = process.env.SERENA_ERLANG_LS_PATH;
|
|
230
|
+
if (override) {
|
|
231
|
+
return override;
|
|
232
|
+
}
|
|
233
|
+
if (process.platform === 'win32') {
|
|
234
|
+
return 'erlang_ls.cmd';
|
|
235
|
+
}
|
|
236
|
+
return 'erlang_ls';
|
|
237
|
+
}
|
|
238
|
+
function commandSucceeds(command, args) {
|
|
239
|
+
const result = spawnSync(command, args, ensureDefaultSubprocessOptions({ stdio: 'ignore' }));
|
|
240
|
+
return result.status === 0;
|
|
241
|
+
}
|
|
242
|
+
function captureCommandOutput(command, args) {
|
|
243
|
+
const result = spawnSync(command, args, ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
244
|
+
if (result.status !== 0) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
const stdout = typeof result.stdout === 'string' ? result.stdout.trim() : '';
|
|
248
|
+
const stderr = typeof result.stderr === 'string' ? result.stderr.trim() : '';
|
|
249
|
+
return stdout || stderr || null;
|
|
250
|
+
}
|
|
251
|
+
function findExecutable(candidates) {
|
|
252
|
+
for (const candidate of candidates) {
|
|
253
|
+
if (!candidate) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const overridePath = process.env.SERENA_ERLANG_LS_PATH;
|
|
257
|
+
if (overridePath && fs.existsSync(overridePath)) {
|
|
258
|
+
return overridePath;
|
|
259
|
+
}
|
|
260
|
+
const resolved = whichBinary(candidate);
|
|
261
|
+
if (resolved) {
|
|
262
|
+
return resolved;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
function whichBinary(command) {
|
|
268
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
269
|
+
const result = spawnSync(locator, [command], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
270
|
+
if (result.status === 0 && typeof result.stdout === 'string') {
|
|
271
|
+
const [first] = result.stdout.split(/\r?\n/u);
|
|
272
|
+
if (first && first.trim().length > 0) {
|
|
273
|
+
return first.trim();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
function mergeIgnoredPaths(existing, additions) {
|
|
279
|
+
const merged = new Set(existing ?? []);
|
|
280
|
+
for (const entry of additions) {
|
|
281
|
+
merged.add(entry);
|
|
282
|
+
merged.add(`**/${entry}/**`);
|
|
283
|
+
}
|
|
284
|
+
return Array.from(merged);
|
|
285
|
+
}
|
|
286
|
+
function buildClientCapabilities() {
|
|
287
|
+
return {
|
|
288
|
+
textDocument: {
|
|
289
|
+
synchronization: { didSave: true, dynamicRegistration: true },
|
|
290
|
+
completion: { dynamicRegistration: true },
|
|
291
|
+
definition: { dynamicRegistration: true },
|
|
292
|
+
references: { dynamicRegistration: true },
|
|
293
|
+
documentSymbol: { dynamicRegistration: true },
|
|
294
|
+
hover: { dynamicRegistration: true }
|
|
295
|
+
},
|
|
296
|
+
workspace: {
|
|
297
|
+
workspaceFolders: true,
|
|
298
|
+
configuration: true
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function extractMessage(payload) {
|
|
303
|
+
if (!payload || typeof payload !== 'object') {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const raw = payload.message;
|
|
307
|
+
return typeof raw === 'string' ? raw : null;
|
|
308
|
+
}
|
|
309
|
+
function containsReadinessKeyword(message) {
|
|
310
|
+
const normalized = message.toLowerCase();
|
|
311
|
+
return READINESS_KEYWORDS.some((keyword) => normalized.includes(keyword.toLowerCase()));
|
|
312
|
+
}
|
|
313
|
+
function delay(ms) {
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
const timer = globalThis.setTimeout(resolve, ms);
|
|
316
|
+
if (isUnrefableTimeout(timer)) {
|
|
317
|
+
timer.unref();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function isUnrefableTimeout(value) {
|
|
322
|
+
if (!value || typeof value !== 'object') {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
return 'unref' in value && typeof value.unref === 'function';
|
|
326
|
+
}
|
|
327
|
+
registerLanguageServer(Language.ERLANG, ErlangLanguageServer);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SolidLanguageServer, type LanguageServerConfigLike, type SolidLspSettingsInit } from '../ls.js';
|
|
2
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
3
|
+
export declare class GoplsLanguageServer extends SolidLanguageServer {
|
|
4
|
+
protected readonly handler: NodeLanguageServerHandler;
|
|
5
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
6
|
+
level?: number;
|
|
7
|
+
} | null, repositoryRootPath: string, options?: {
|
|
8
|
+
timeout?: number | null;
|
|
9
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
10
|
+
});
|
|
11
|
+
private registerHandlers;
|
|
12
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { Language } from '../ls_config.js';
|
|
3
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
4
|
+
import { SolidLanguageServer, registerLanguageServer } from '../ls.js';
|
|
5
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
6
|
+
function commandExists(command) {
|
|
7
|
+
if (process.env.SERENA_ASSUME_GOPLS === '1') {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
11
|
+
return spawnSync(locator, [command], ensureDefaultSubprocessOptions({ stdio: 'ignore' })).status === 0;
|
|
12
|
+
}
|
|
13
|
+
function ensureGoRuntime() {
|
|
14
|
+
if (!commandExists('go')) {
|
|
15
|
+
throw new Error('Go is not installed. Install Go from https://golang.org/doc/install and ensure it is in PATH.');
|
|
16
|
+
}
|
|
17
|
+
if (!commandExists('gopls')) {
|
|
18
|
+
throw new Error('gopls is not installed. Install it via `go install golang.org/x/tools/gopls@latest` and ensure it is available in PATH.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function mergeIgnored(existing) {
|
|
22
|
+
const merged = new Set(existing ?? []);
|
|
23
|
+
['vendor', 'node_modules', 'dist', 'build'].forEach((entry) => merged.add(entry));
|
|
24
|
+
return Array.from(merged);
|
|
25
|
+
}
|
|
26
|
+
export class GoplsLanguageServer extends SolidLanguageServer {
|
|
27
|
+
handler;
|
|
28
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
29
|
+
ensureGoRuntime();
|
|
30
|
+
const augmentedConfig = {
|
|
31
|
+
...config,
|
|
32
|
+
ignoredPaths: mergeIgnored(config.ignoredPaths)
|
|
33
|
+
};
|
|
34
|
+
const handler = new NodeLanguageServerHandler({
|
|
35
|
+
cmd: 'gopls',
|
|
36
|
+
cwd: repositoryRootPath
|
|
37
|
+
});
|
|
38
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
39
|
+
...options,
|
|
40
|
+
handler,
|
|
41
|
+
solidlspSettings: options?.solidlspSettings
|
|
42
|
+
});
|
|
43
|
+
this.handler = handler;
|
|
44
|
+
this.registerHandlers();
|
|
45
|
+
}
|
|
46
|
+
registerHandlers() {
|
|
47
|
+
const noop = () => undefined;
|
|
48
|
+
this.handler.onNotification('window/logMessage', (payload) => {
|
|
49
|
+
if (payload && typeof payload === 'object' && 'message' in payload) {
|
|
50
|
+
this.logger.info(`gopls: ${payload.message ?? ''}`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.handler.onNotification('$/progress', noop);
|
|
54
|
+
this.handler.onNotification('textDocument/publishDiagnostics', noop);
|
|
55
|
+
this.handler.onRequest('client/registerCapability', noop);
|
|
56
|
+
this.handler.onRequest('workspace/executeClientCommand', () => []);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
registerLanguageServer(Language.GO, GoplsLanguageServer);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type LogLevel } from '../../serena/util/logging.js';
|
|
2
|
+
import { SolidLanguageServer, type LanguageServerConfigLike, type SolidLspSettingsInit } from '../ls.js';
|
|
3
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
4
|
+
export declare class IntelephenseLanguageServer extends SolidLanguageServer {
|
|
5
|
+
protected readonly handler: NodeLanguageServerHandler;
|
|
6
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
7
|
+
level?: number | LogLevel;
|
|
8
|
+
} | null, repositoryRootPath: string, options?: {
|
|
9
|
+
timeout?: number | null;
|
|
10
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
11
|
+
});
|
|
12
|
+
private registerHandlers;
|
|
13
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createSerenaLogger } from '../../serena/util/logging.js';
|
|
5
|
+
import { Language } from '../ls_config.js';
|
|
6
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
7
|
+
import { SolidLanguageServer, SolidLspSettings, registerLanguageServer, coerceLogLevel } from '../ls.js';
|
|
8
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
9
|
+
import { RuntimeDependencyCollection, quoteWindowsPath } from './common.js';
|
|
10
|
+
const INTELEPHENSE_DEPENDENCIES = [
|
|
11
|
+
{
|
|
12
|
+
id: 'intelephense',
|
|
13
|
+
command: 'npm install --prefix ./ intelephense@1.14.4',
|
|
14
|
+
platformId: 'any'
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
function commandExists(command) {
|
|
18
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
19
|
+
return spawnSync(locator, [command], ensureDefaultSubprocessOptions({ stdio: 'ignore' })).status === 0;
|
|
20
|
+
}
|
|
21
|
+
function ensureNodeTooling() {
|
|
22
|
+
if (process.env.SERENA_ASSUME_INTELEPHENSE === '1') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!commandExists('node')) {
|
|
26
|
+
throw new Error('Node.js is not installed or not in PATH. Install it from https://nodejs.org/ and retry.');
|
|
27
|
+
}
|
|
28
|
+
if (!commandExists('npm')) {
|
|
29
|
+
throw new Error('npm is not installed or not in PATH. Install npm (bundled with Node.js) and retry.');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function resolveRuntimeDirectory(settings) {
|
|
33
|
+
const dir = path.join(settings.languageServersStaticDir, 'php-lsp');
|
|
34
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
+
return dir;
|
|
36
|
+
}
|
|
37
|
+
function resolveBinaryPath(runtimeDir) {
|
|
38
|
+
const base = path.join(runtimeDir, 'node_modules', '.bin');
|
|
39
|
+
if (process.platform === 'win32') {
|
|
40
|
+
const cmdPath = path.join(base, 'intelephense.cmd');
|
|
41
|
+
if (fs.existsSync(cmdPath)) {
|
|
42
|
+
return cmdPath;
|
|
43
|
+
}
|
|
44
|
+
const ps1Path = path.join(base, 'intelephense.ps1');
|
|
45
|
+
if (fs.existsSync(ps1Path)) {
|
|
46
|
+
return ps1Path;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return path.join(base, 'intelephense');
|
|
50
|
+
}
|
|
51
|
+
function ensureIntelephenseBinary(runtimeDir, dependencies, loggerLevel) {
|
|
52
|
+
const { logger } = createSerenaLogger({
|
|
53
|
+
name: 'solidlsp.language_servers.intelephense',
|
|
54
|
+
emitToConsole: false,
|
|
55
|
+
level: loggerLevel === undefined ? undefined : coerceLogLevel(loggerLevel)
|
|
56
|
+
});
|
|
57
|
+
const binaryPath = resolveBinaryPath(runtimeDir);
|
|
58
|
+
if (fs.existsSync(binaryPath)) {
|
|
59
|
+
return binaryPath;
|
|
60
|
+
}
|
|
61
|
+
if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
|
|
62
|
+
throw new Error(`Intelephense executable not found at ${binaryPath}. Allow downloads by unsetting SERENA_SKIP_RUNTIME_INSTALL or install manually.`);
|
|
63
|
+
}
|
|
64
|
+
ensureNodeTooling();
|
|
65
|
+
logger.info('Installing intelephense via npm');
|
|
66
|
+
dependencies.install(logger, runtimeDir);
|
|
67
|
+
if (!fs.existsSync(binaryPath)) {
|
|
68
|
+
throw new Error(`Intelephense installation completed but executable missing at ${binaryPath}. Verify npm installation succeeded.`);
|
|
69
|
+
}
|
|
70
|
+
if (process.platform !== 'win32') {
|
|
71
|
+
try {
|
|
72
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignore chmod failures
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return binaryPath;
|
|
79
|
+
}
|
|
80
|
+
function mergeIgnoredPaths(existing) {
|
|
81
|
+
const merged = new Set(existing ?? []);
|
|
82
|
+
['node_modules', 'vendor', 'cache'].forEach((entry) => merged.add(entry));
|
|
83
|
+
return Array.from(merged);
|
|
84
|
+
}
|
|
85
|
+
export class IntelephenseLanguageServer extends SolidLanguageServer {
|
|
86
|
+
handler;
|
|
87
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
88
|
+
const augmentedConfig = {
|
|
89
|
+
...config,
|
|
90
|
+
ignoredPaths: mergeIgnoredPaths(config.ignoredPaths)
|
|
91
|
+
};
|
|
92
|
+
const solidSettings = new SolidLspSettings(options?.solidlspSettings);
|
|
93
|
+
const runtimeDir = resolveRuntimeDirectory(solidSettings);
|
|
94
|
+
const dependencies = new RuntimeDependencyCollection(INTELEPHENSE_DEPENDENCIES);
|
|
95
|
+
const binaryPath = ensureIntelephenseBinary(runtimeDir, dependencies, loggerLike?.level);
|
|
96
|
+
const handler = new NodeLanguageServerHandler({
|
|
97
|
+
cmd: `${quoteWindowsPath(binaryPath)} --stdio`,
|
|
98
|
+
cwd: repositoryRootPath
|
|
99
|
+
});
|
|
100
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
101
|
+
...options,
|
|
102
|
+
handler,
|
|
103
|
+
solidlspSettings: options?.solidlspSettings
|
|
104
|
+
});
|
|
105
|
+
this.handler = handler;
|
|
106
|
+
this.registerHandlers();
|
|
107
|
+
}
|
|
108
|
+
registerHandlers() {
|
|
109
|
+
const noop = () => undefined;
|
|
110
|
+
this.handler.onNotification('window/logMessage', (payload) => {
|
|
111
|
+
if (payload && typeof payload === 'object' && 'message' in payload) {
|
|
112
|
+
this.logger.info(`intelephense: ${payload.message ?? ''}`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
this.handler.onNotification('$/progress', noop);
|
|
116
|
+
this.handler.onNotification('textDocument/publishDiagnostics', noop);
|
|
117
|
+
this.handler.onRequest('client/registerCapability', noop);
|
|
118
|
+
this.handler.onRequest('workspace/executeClientCommand', () => []);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
registerLanguageServer(Language.PHP, IntelephenseLanguageServer);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LogLevel } from '../../serena/util/logging.js';
|
|
2
|
+
import { SolidLanguageServer, type LanguageServerConfigLike, type SolidLspSettingsInit } from '../ls.js';
|
|
3
|
+
export declare class JediLanguageServer extends SolidLanguageServer {
|
|
4
|
+
private readonly nodeHandler;
|
|
5
|
+
private initialized;
|
|
6
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
7
|
+
level?: number | LogLevel;
|
|
8
|
+
} | null, repositoryRootPath: string, options?: {
|
|
9
|
+
timeout?: number | null;
|
|
10
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
11
|
+
});
|
|
12
|
+
start(): this;
|
|
13
|
+
stop(shutdownTimeout?: number): void;
|
|
14
|
+
private registerHandlers;
|
|
15
|
+
private initializeLanguageServer;
|
|
16
|
+
private verifyCapabilities;
|
|
17
|
+
private buildInitializeParams;
|
|
18
|
+
}
|