@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,319 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { createSerenaLogger } from '../../serena/util/logging.js';
|
|
7
|
+
import { Language } from '../ls_config.js';
|
|
8
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
9
|
+
import { SolidLanguageServer, SolidLspSettings, registerLanguageServer, coerceLogLevel } from '../ls.js';
|
|
10
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
11
|
+
import { RuntimeDependencyCollection, quoteWindowsPath } from './common.js';
|
|
12
|
+
const LUA_LS_VERSION = '3.15.0';
|
|
13
|
+
const LUA_LS_DEPENDENCIES = [
|
|
14
|
+
{
|
|
15
|
+
id: 'lua-language-server',
|
|
16
|
+
platformId: 'linux-x64',
|
|
17
|
+
url: `https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-x64.tar.gz`,
|
|
18
|
+
archiveType: 'tar',
|
|
19
|
+
binaryName: `lua-language-server-${LUA_LS_VERSION}-linux-x64/bin/lua-language-server`
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'lua-language-server',
|
|
23
|
+
platformId: 'linux-arm64',
|
|
24
|
+
url: `https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-arm64.tar.gz`,
|
|
25
|
+
archiveType: 'tar',
|
|
26
|
+
binaryName: `lua-language-server-${LUA_LS_VERSION}-linux-arm64/bin/lua-language-server`
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'lua-language-server',
|
|
30
|
+
platformId: 'osx-x64',
|
|
31
|
+
url: `https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-darwin-x64.tar.gz`,
|
|
32
|
+
archiveType: 'tar',
|
|
33
|
+
binaryName: `lua-language-server-${LUA_LS_VERSION}-darwin-x64/bin/lua-language-server`
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'lua-language-server',
|
|
37
|
+
platformId: 'osx-arm64',
|
|
38
|
+
url: `https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-darwin-arm64.tar.gz`,
|
|
39
|
+
archiveType: 'tar',
|
|
40
|
+
binaryName: `lua-language-server-${LUA_LS_VERSION}-darwin-arm64/bin/lua-language-server`
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'lua-language-server',
|
|
44
|
+
platformId: 'win-x64',
|
|
45
|
+
url: `https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-win32-x64.zip`,
|
|
46
|
+
archiveType: 'zip',
|
|
47
|
+
binaryName: `lua-language-server-${LUA_LS_VERSION}-win32-x64/bin/lua-language-server.exe`
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
const LUA_IGNORED_DIRECTORIES = ['.luarocks', 'lua_modules', 'node_modules', 'build', 'dist', '.cache'];
|
|
51
|
+
export class LuaLanguageServer extends SolidLanguageServer {
|
|
52
|
+
handlerInstance;
|
|
53
|
+
initialized = false;
|
|
54
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
55
|
+
const augmentedConfig = {
|
|
56
|
+
...config,
|
|
57
|
+
ignoredPaths: mergeIgnoredPaths(config.ignoredPaths, LUA_IGNORED_DIRECTORIES)
|
|
58
|
+
};
|
|
59
|
+
const solidSettings = new SolidLspSettings(options?.solidlspSettings);
|
|
60
|
+
const runtimeDir = resolveRuntimeDirectory(solidSettings);
|
|
61
|
+
const dependencies = new RuntimeDependencyCollection(LUA_LS_DEPENDENCIES);
|
|
62
|
+
const binaryPath = ensureLuaLanguageServerBinary(runtimeDir, dependencies, loggerLike?.level);
|
|
63
|
+
const handler = new NodeLanguageServerHandler({
|
|
64
|
+
cmd: quoteWindowsPath(binaryPath),
|
|
65
|
+
cwd: repositoryRootPath
|
|
66
|
+
});
|
|
67
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
68
|
+
...options,
|
|
69
|
+
handler,
|
|
70
|
+
solidlspSettings: options?.solidlspSettings
|
|
71
|
+
});
|
|
72
|
+
this.handlerInstance = handler;
|
|
73
|
+
this.registerHandlers();
|
|
74
|
+
}
|
|
75
|
+
start() {
|
|
76
|
+
const shouldInitialize = !this.initialized;
|
|
77
|
+
super.start();
|
|
78
|
+
if (shouldInitialize) {
|
|
79
|
+
this.initializeLanguageServer();
|
|
80
|
+
this.initialized = true;
|
|
81
|
+
}
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
stop(shutdownTimeout = 2.0) {
|
|
85
|
+
super.stop(shutdownTimeout);
|
|
86
|
+
this.initialized = false;
|
|
87
|
+
}
|
|
88
|
+
registerHandlers() {
|
|
89
|
+
const noop = () => undefined;
|
|
90
|
+
this.handlerInstance.onRequest('client/registerCapability', noop);
|
|
91
|
+
this.handlerInstance.onNotification('window/logMessage', (payload) => {
|
|
92
|
+
const message = extractWindowMessage(payload);
|
|
93
|
+
if (message) {
|
|
94
|
+
this.logger.info(`lua-language-server: ${message}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
this.handlerInstance.onNotification('$/progress', noop);
|
|
98
|
+
this.handlerInstance.onNotification('textDocument/publishDiagnostics', noop);
|
|
99
|
+
}
|
|
100
|
+
initializeLanguageServer() {
|
|
101
|
+
const params = this.buildInitializeParams();
|
|
102
|
+
const response = this.handlerInstance.sendRequest('initialize', params);
|
|
103
|
+
this.verifyCapabilities(response?.capabilities ?? null);
|
|
104
|
+
this.handlerInstance.notify.initialized({});
|
|
105
|
+
}
|
|
106
|
+
buildInitializeParams() {
|
|
107
|
+
const rootUri = pathToFileURL(this.repositoryRootPath).href;
|
|
108
|
+
return {
|
|
109
|
+
processId: process.pid,
|
|
110
|
+
locale: 'en',
|
|
111
|
+
rootPath: this.repositoryRootPath,
|
|
112
|
+
rootUri,
|
|
113
|
+
capabilities: buildClientCapabilities(),
|
|
114
|
+
initializationOptions: buildInitializationOptions(),
|
|
115
|
+
workspaceFolders: [
|
|
116
|
+
{
|
|
117
|
+
uri: rootUri,
|
|
118
|
+
name: path.basename(this.repositoryRootPath)
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
verifyCapabilities(capabilities) {
|
|
124
|
+
if (!capabilities) {
|
|
125
|
+
throw new Error('Lua language server initialize response is missing capabilities.');
|
|
126
|
+
}
|
|
127
|
+
const textDocumentCaps = capabilities;
|
|
128
|
+
if (!('textDocumentSync' in textDocumentCaps)) {
|
|
129
|
+
throw new Error('Lua language server did not advertise textDocumentSync capability.');
|
|
130
|
+
}
|
|
131
|
+
if (!('definitionProvider' in textDocumentCaps)) {
|
|
132
|
+
throw new Error('Lua language server did not advertise definitionProvider capability.');
|
|
133
|
+
}
|
|
134
|
+
if (!('documentSymbolProvider' in textDocumentCaps)) {
|
|
135
|
+
throw new Error('Lua language server did not advertise documentSymbolProvider capability.');
|
|
136
|
+
}
|
|
137
|
+
if (!('referencesProvider' in textDocumentCaps)) {
|
|
138
|
+
throw new Error('Lua language server did not advertise referencesProvider capability.');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function mergeIgnoredPaths(existing, additions) {
|
|
143
|
+
const merged = new Set(existing ?? []);
|
|
144
|
+
for (const entry of additions) {
|
|
145
|
+
merged.add(entry);
|
|
146
|
+
}
|
|
147
|
+
return Array.from(merged);
|
|
148
|
+
}
|
|
149
|
+
function resolveRuntimeDirectory(settings) {
|
|
150
|
+
const runtimeDir = path.join(settings.languageServersStaticDir, 'lua-language-server');
|
|
151
|
+
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
152
|
+
return runtimeDir;
|
|
153
|
+
}
|
|
154
|
+
function ensureLuaLanguageServerBinary(runtimeDir, dependencies, loggerLevel) {
|
|
155
|
+
const overridePath = process.env.SERENA_LUA_LS_PATH;
|
|
156
|
+
if (overridePath && fs.existsSync(overridePath)) {
|
|
157
|
+
return overridePath;
|
|
158
|
+
}
|
|
159
|
+
const fromPath = whichBinary(process.platform === 'win32' ? 'lua-language-server.exe' : 'lua-language-server');
|
|
160
|
+
if (fromPath && fs.existsSync(fromPath)) {
|
|
161
|
+
return fromPath;
|
|
162
|
+
}
|
|
163
|
+
const known = locateInKnownLocations(runtimeDir);
|
|
164
|
+
if (known) {
|
|
165
|
+
return known;
|
|
166
|
+
}
|
|
167
|
+
const maybeInstalled = locateInstalledBinary(runtimeDir, dependencies);
|
|
168
|
+
if (maybeInstalled) {
|
|
169
|
+
return maybeInstalled;
|
|
170
|
+
}
|
|
171
|
+
if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
|
|
172
|
+
throw new Error('lua-language-server binary not found. Allow downloads by unsetting SERENA_SKIP_RUNTIME_INSTALL or set SERENA_LUA_LS_PATH.');
|
|
173
|
+
}
|
|
174
|
+
const { logger } = createSerenaLogger({
|
|
175
|
+
name: 'solidlsp.language_servers.lua',
|
|
176
|
+
emitToConsole: false,
|
|
177
|
+
level: loggerLevel === undefined ? undefined : coerceLogLevel(loggerLevel)
|
|
178
|
+
});
|
|
179
|
+
logger.info('Downloading lua-language-server runtime dependency.');
|
|
180
|
+
dependencies.install(logger, runtimeDir);
|
|
181
|
+
const installed = locateInstalledBinary(runtimeDir, dependencies);
|
|
182
|
+
if (!installed) {
|
|
183
|
+
throw new Error('Failed to locate lua-language-server binary after installation.');
|
|
184
|
+
}
|
|
185
|
+
if (process.platform !== 'win32') {
|
|
186
|
+
try {
|
|
187
|
+
fs.chmodSync(installed, 0o755);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// ignore chmod failures
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return installed;
|
|
194
|
+
}
|
|
195
|
+
function whichBinary(command) {
|
|
196
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
197
|
+
const result = spawnSync(locator, [command], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
198
|
+
if (result.status === 0 && result.stdout) {
|
|
199
|
+
const [firstLine] = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
200
|
+
return firstLine ?? null;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
function locateInKnownLocations(runtimeDir) {
|
|
205
|
+
const home = os.homedir();
|
|
206
|
+
const executableName = process.platform === 'win32' ? 'lua-language-server.exe' : 'lua-language-server';
|
|
207
|
+
const candidates = [
|
|
208
|
+
path.join(runtimeDir, executableName),
|
|
209
|
+
path.join(runtimeDir, 'bin', executableName),
|
|
210
|
+
path.join(home, '.local', 'bin', executableName),
|
|
211
|
+
path.join(home, '.smart-edit', 'language_servers', 'lua', 'bin', executableName),
|
|
212
|
+
path.join('/usr/local/bin', executableName),
|
|
213
|
+
path.join('/opt/lua-language-server', 'bin', executableName)
|
|
214
|
+
];
|
|
215
|
+
if (process.platform === 'win32') {
|
|
216
|
+
candidates.push(path.join(home, 'AppData', 'Local', 'lua-language-server', 'bin', 'lua-language-server.exe'), path.join(home, '.smart-edit', 'language_servers', 'lua', 'bin', 'lua-language-server.exe'));
|
|
217
|
+
}
|
|
218
|
+
for (const candidate of candidates) {
|
|
219
|
+
if (candidate && fs.existsSync(candidate)) {
|
|
220
|
+
return candidate;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
function locateInstalledBinary(runtimeDir, dependencies) {
|
|
226
|
+
const dep = dependencies.getSingleDepForCurrentPlatform();
|
|
227
|
+
const executableName = process.platform === 'win32' ? 'lua-language-server.exe' : 'lua-language-server';
|
|
228
|
+
const candidates = new Set();
|
|
229
|
+
if (dep.binaryName) {
|
|
230
|
+
candidates.add(path.join(runtimeDir, dep.binaryName));
|
|
231
|
+
}
|
|
232
|
+
candidates.add(path.join(runtimeDir, 'lua-language-server', 'bin', executableName));
|
|
233
|
+
candidates.add(path.join(runtimeDir, 'bin', executableName));
|
|
234
|
+
candidates.add(path.join(runtimeDir, executableName));
|
|
235
|
+
for (const candidate of candidates) {
|
|
236
|
+
if (fs.existsSync(candidate)) {
|
|
237
|
+
return candidate;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
function extractWindowMessage(payload) {
|
|
243
|
+
if (!payload || typeof payload !== 'object') {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
const maybeMessage = payload.message;
|
|
247
|
+
return typeof maybeMessage === 'string' ? maybeMessage : null;
|
|
248
|
+
}
|
|
249
|
+
function buildClientCapabilities() {
|
|
250
|
+
const symbolKinds = Array.from({ length: 26 }, (_, index) => index + 1);
|
|
251
|
+
return {
|
|
252
|
+
textDocument: {
|
|
253
|
+
synchronization: { didSave: true, dynamicRegistration: true },
|
|
254
|
+
definition: { dynamicRegistration: true },
|
|
255
|
+
references: { dynamicRegistration: true },
|
|
256
|
+
documentSymbol: {
|
|
257
|
+
dynamicRegistration: true,
|
|
258
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
259
|
+
symbolKind: { valueSet: symbolKinds }
|
|
260
|
+
},
|
|
261
|
+
completion: {
|
|
262
|
+
dynamicRegistration: true,
|
|
263
|
+
completionItem: {
|
|
264
|
+
snippetSupport: true,
|
|
265
|
+
commitCharactersSupport: true,
|
|
266
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
267
|
+
deprecatedSupport: true,
|
|
268
|
+
preselectSupport: true
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
hover: {
|
|
272
|
+
dynamicRegistration: true,
|
|
273
|
+
contentFormat: ['markdown', 'plaintext']
|
|
274
|
+
},
|
|
275
|
+
signatureHelp: {
|
|
276
|
+
dynamicRegistration: true,
|
|
277
|
+
signatureInformation: {
|
|
278
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
279
|
+
parameterInformation: { labelOffsetSupport: true }
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
workspace: {
|
|
284
|
+
workspaceFolders: true,
|
|
285
|
+
didChangeConfiguration: { dynamicRegistration: true },
|
|
286
|
+
configuration: true,
|
|
287
|
+
symbol: {
|
|
288
|
+
dynamicRegistration: true,
|
|
289
|
+
symbolKind: { valueSet: symbolKinds }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function buildInitializationOptions() {
|
|
295
|
+
return {
|
|
296
|
+
runtime: {
|
|
297
|
+
version: 'Lua 5.4',
|
|
298
|
+
path: ['?.lua', '?/init.lua']
|
|
299
|
+
},
|
|
300
|
+
diagnostics: {
|
|
301
|
+
enable: true,
|
|
302
|
+
globals: ['vim', 'describe', 'it', 'before_each', 'after_each']
|
|
303
|
+
},
|
|
304
|
+
workspace: {
|
|
305
|
+
library: [],
|
|
306
|
+
checkThirdParty: false,
|
|
307
|
+
userThirdParty: []
|
|
308
|
+
},
|
|
309
|
+
telemetry: {
|
|
310
|
+
enable: false
|
|
311
|
+
},
|
|
312
|
+
completion: {
|
|
313
|
+
enable: true,
|
|
314
|
+
callSnippet: 'Both',
|
|
315
|
+
keywordSnippet: 'Both'
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
registerLanguageServer(Language.LUA, LuaLanguageServer);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LogLevel } from '../../serena/util/logging.js';
|
|
2
|
+
import { type DocumentSymbolResult, type DocumentSymbolsOptions, type LanguageServerConfigLike, type SolidLanguageServerOptions, type UnifiedSymbolInformation, SolidLanguageServer } from '../ls.js';
|
|
3
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
4
|
+
export declare class NixLanguageServer extends SolidLanguageServer {
|
|
5
|
+
protected readonly handler: NodeLanguageServerHandler;
|
|
6
|
+
private initialized;
|
|
7
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
8
|
+
level?: number | LogLevel;
|
|
9
|
+
} | null, repositoryRootPath: string, options?: SolidLanguageServerOptions);
|
|
10
|
+
start(): this;
|
|
11
|
+
stop(shutdownTimeout?: number): void;
|
|
12
|
+
requestDocumentSymbols(relativePath: string, options?: DocumentSymbolsOptions): DocumentSymbolResult;
|
|
13
|
+
private registerHandlers;
|
|
14
|
+
private initializeLanguageServer;
|
|
15
|
+
private verifyCapabilities;
|
|
16
|
+
}
|
|
17
|
+
export declare function extendNixSymbolTree(symbol: UnifiedSymbolInformation, fileContents: string): UnifiedSymbolInformation;
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
7
|
+
import { Language } from '../ls_config.js';
|
|
8
|
+
import { SolidLanguageServer, registerLanguageServer } from '../ls.js';
|
|
9
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
10
|
+
const NIXD_ASSUME_ENV = 'SERENA_ASSUME_NIXD';
|
|
11
|
+
const NIXD_PATH_ENV = 'SERENA_NIXD_PATH';
|
|
12
|
+
const NIX_IGNORED_PATTERNS = [
|
|
13
|
+
'**/result',
|
|
14
|
+
'**/result/**',
|
|
15
|
+
'**/result-*',
|
|
16
|
+
'**/result-*/**',
|
|
17
|
+
'**/.direnv',
|
|
18
|
+
'**/.direnv/**'
|
|
19
|
+
];
|
|
20
|
+
export class NixLanguageServer extends SolidLanguageServer {
|
|
21
|
+
handler;
|
|
22
|
+
initialized = false;
|
|
23
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
24
|
+
const augmentedConfig = {
|
|
25
|
+
...config,
|
|
26
|
+
ignoredPaths: mergeIgnoredPatterns(config.ignoredPaths, NIX_IGNORED_PATTERNS)
|
|
27
|
+
};
|
|
28
|
+
const binaryPath = ensureNixdRuntime();
|
|
29
|
+
const providedHandler = options.handler;
|
|
30
|
+
if (providedHandler && !(providedHandler instanceof NodeLanguageServerHandler)) {
|
|
31
|
+
throw new TypeError('NixLanguageServer requires a NodeLanguageServerHandler when supplying a custom handler.');
|
|
32
|
+
}
|
|
33
|
+
const handler = providedHandler ?? new NodeLanguageServerHandler({
|
|
34
|
+
cmd: binaryPath,
|
|
35
|
+
cwd: repositoryRootPath
|
|
36
|
+
});
|
|
37
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
38
|
+
...options,
|
|
39
|
+
handler,
|
|
40
|
+
solidlspSettings: options?.solidlspSettings
|
|
41
|
+
});
|
|
42
|
+
this.handler = handler;
|
|
43
|
+
this.registerHandlers();
|
|
44
|
+
}
|
|
45
|
+
start() {
|
|
46
|
+
const shouldInitialize = !this.initialized;
|
|
47
|
+
super.start();
|
|
48
|
+
if (shouldInitialize) {
|
|
49
|
+
this.initializeLanguageServer();
|
|
50
|
+
this.initialized = true;
|
|
51
|
+
}
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
stop(shutdownTimeout = 2.0) {
|
|
55
|
+
super.stop(shutdownTimeout);
|
|
56
|
+
this.initialized = false;
|
|
57
|
+
}
|
|
58
|
+
requestDocumentSymbols(relativePath, options = {}) {
|
|
59
|
+
const result = super.requestDocumentSymbols(relativePath, options);
|
|
60
|
+
let fileContents;
|
|
61
|
+
try {
|
|
62
|
+
fileContents = this.retrieveFullFileContent(relativePath);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.logger.warn(`Failed to read file contents for ${relativePath}: ${error.message}`);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
documentSymbols: result.documentSymbols.map((symbol) => extendNixSymbolTree(symbol, fileContents)),
|
|
70
|
+
outlineSymbols: result.outlineSymbols.map((symbol) => extendNixSymbolTree(symbol, fileContents))
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
registerHandlers() {
|
|
74
|
+
const noop = () => undefined;
|
|
75
|
+
this.handler.onRequest('client/registerCapability', noop);
|
|
76
|
+
this.handler.onNotification('$/progress', noop);
|
|
77
|
+
this.handler.onNotification('textDocument/publishDiagnostics', noop);
|
|
78
|
+
this.handler.onNotification('experimental/serverStatus', noop);
|
|
79
|
+
this.handler.onNotification('window/logMessage', (payload) => {
|
|
80
|
+
const message = extractWindowMessage(payload);
|
|
81
|
+
if (message) {
|
|
82
|
+
this.logger.info(`nixd: ${message}`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
initializeLanguageServer() {
|
|
87
|
+
this.logger.info('Initializing nixd language server');
|
|
88
|
+
const params = buildInitializeParams(this.repositoryRootPath);
|
|
89
|
+
const response = this.handler.sendRequest('initialize', params);
|
|
90
|
+
this.verifyCapabilities(response?.capabilities ?? null);
|
|
91
|
+
this.handler.notify.initialized({});
|
|
92
|
+
}
|
|
93
|
+
verifyCapabilities(capabilities) {
|
|
94
|
+
if (!capabilities || typeof capabilities !== 'object') {
|
|
95
|
+
throw new Error('nixd initialization response is missing capabilities.');
|
|
96
|
+
}
|
|
97
|
+
const required = capabilities;
|
|
98
|
+
for (const key of ['textDocumentSync', 'definitionProvider', 'documentSymbolProvider', 'referencesProvider']) {
|
|
99
|
+
if (!(key in required)) {
|
|
100
|
+
throw new Error(`nixd did not advertise required capability '${key}'.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
registerLanguageServer(Language.NIX, NixLanguageServer);
|
|
106
|
+
function mergeIgnoredPatterns(existing, additions) {
|
|
107
|
+
const merged = new Set(existing ?? []);
|
|
108
|
+
for (const pattern of additions) {
|
|
109
|
+
merged.add(pattern);
|
|
110
|
+
}
|
|
111
|
+
return Array.from(merged);
|
|
112
|
+
}
|
|
113
|
+
export function extendNixSymbolTree(symbol, fileContents) {
|
|
114
|
+
const extended = extendSymbolRange(symbol, fileContents);
|
|
115
|
+
const children = Array.isArray(symbol.children) ? symbol.children : undefined;
|
|
116
|
+
return {
|
|
117
|
+
...extended,
|
|
118
|
+
children: children?.map((child) => extendNixSymbolTree(child, fileContents))
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function extendSymbolRange(symbol, fileContents) {
|
|
122
|
+
const rangeHolder = symbol;
|
|
123
|
+
const { range } = rangeHolder;
|
|
124
|
+
const endPosition = range?.end ?? null;
|
|
125
|
+
if (!endPosition) {
|
|
126
|
+
return { ...symbol };
|
|
127
|
+
}
|
|
128
|
+
const endChar = endPosition.character;
|
|
129
|
+
if (endChar == null) {
|
|
130
|
+
return { ...symbol };
|
|
131
|
+
}
|
|
132
|
+
const endLine = endPosition.line;
|
|
133
|
+
if (typeof endLine !== 'number' || typeof endChar !== 'number') {
|
|
134
|
+
return { ...symbol };
|
|
135
|
+
}
|
|
136
|
+
const lines = fileContents.split('\n');
|
|
137
|
+
if (endLine >= lines.length) {
|
|
138
|
+
return { ...symbol };
|
|
139
|
+
}
|
|
140
|
+
const line = lines[endLine];
|
|
141
|
+
if (!line || endChar >= line.length || line[endChar] !== ';') {
|
|
142
|
+
return { ...symbol };
|
|
143
|
+
}
|
|
144
|
+
const extendedRange = {
|
|
145
|
+
...(range ?? {}),
|
|
146
|
+
end: { line: endLine, character: endChar + 1 }
|
|
147
|
+
};
|
|
148
|
+
const locationValue = symbol.location;
|
|
149
|
+
let updatedLocation = locationValue ?? null;
|
|
150
|
+
if (isLocationObject(locationValue)) {
|
|
151
|
+
const locationRange = locationValue.range ?? null;
|
|
152
|
+
updatedLocation = {
|
|
153
|
+
...locationValue,
|
|
154
|
+
range: {
|
|
155
|
+
...(locationRange ?? {}),
|
|
156
|
+
end: { line: endLine, character: endChar + 1 }
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
...symbol,
|
|
162
|
+
range: extendedRange,
|
|
163
|
+
selectionRange: extendedRange,
|
|
164
|
+
location: updatedLocation
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function buildInitializeParams(repositoryRoot) {
|
|
168
|
+
const absoluteRoot = path.resolve(repositoryRoot);
|
|
169
|
+
const rootUri = pathToFileURL(absoluteRoot).href;
|
|
170
|
+
return {
|
|
171
|
+
locale: 'en',
|
|
172
|
+
processId: process.pid,
|
|
173
|
+
rootPath: absoluteRoot,
|
|
174
|
+
rootUri,
|
|
175
|
+
workspaceFolders: [
|
|
176
|
+
{
|
|
177
|
+
uri: rootUri,
|
|
178
|
+
name: path.basename(absoluteRoot)
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
capabilities: buildClientCapabilities(),
|
|
182
|
+
initializationOptions: {
|
|
183
|
+
nixpkgs: { expr: 'import <nixpkgs> { }' },
|
|
184
|
+
formatting: { command: ['nixpkgs-fmt'] },
|
|
185
|
+
options: {
|
|
186
|
+
enable: true,
|
|
187
|
+
target: {
|
|
188
|
+
installable: ''
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function buildClientCapabilities() {
|
|
195
|
+
const symbolKinds = Array.from({ length: 26 }, (_, index) => index + 1);
|
|
196
|
+
return {
|
|
197
|
+
textDocument: {
|
|
198
|
+
synchronization: { didSave: true, dynamicRegistration: true },
|
|
199
|
+
definition: { dynamicRegistration: true },
|
|
200
|
+
references: { dynamicRegistration: true },
|
|
201
|
+
documentSymbol: {
|
|
202
|
+
dynamicRegistration: true,
|
|
203
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
204
|
+
symbolKind: { valueSet: symbolKinds }
|
|
205
|
+
},
|
|
206
|
+
completion: {
|
|
207
|
+
dynamicRegistration: true,
|
|
208
|
+
completionItem: {
|
|
209
|
+
snippetSupport: true,
|
|
210
|
+
commitCharactersSupport: true,
|
|
211
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
212
|
+
deprecatedSupport: true,
|
|
213
|
+
preselectSupport: true
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
hover: {
|
|
217
|
+
dynamicRegistration: true,
|
|
218
|
+
contentFormat: ['markdown', 'plaintext']
|
|
219
|
+
},
|
|
220
|
+
signatureHelp: {
|
|
221
|
+
dynamicRegistration: true,
|
|
222
|
+
signatureInformation: {
|
|
223
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
224
|
+
parameterInformation: { labelOffsetSupport: true }
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
codeAction: {
|
|
228
|
+
dynamicRegistration: true,
|
|
229
|
+
codeActionLiteralSupport: {
|
|
230
|
+
codeActionKind: {
|
|
231
|
+
valueSet: ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports']
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
rename: { dynamicRegistration: true, prepareSupport: true }
|
|
236
|
+
},
|
|
237
|
+
workspace: {
|
|
238
|
+
workspaceFolders: true,
|
|
239
|
+
didChangeConfiguration: { dynamicRegistration: true },
|
|
240
|
+
configuration: true,
|
|
241
|
+
symbol: {
|
|
242
|
+
dynamicRegistration: true,
|
|
243
|
+
symbolKind: { valueSet: symbolKinds }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function ensureNixdRuntime() {
|
|
249
|
+
if (process.env[NIXD_ASSUME_ENV] === '1') {
|
|
250
|
+
return process.env[NIXD_PATH_ENV] ?? defaultNixdCommand();
|
|
251
|
+
}
|
|
252
|
+
if (process.platform === 'win32') {
|
|
253
|
+
throw new Error('nixd は Windows を公式サポートしていません。WSL または Linux/macOS 上で実行してください。');
|
|
254
|
+
}
|
|
255
|
+
ensureNixAvailable();
|
|
256
|
+
const detected = detectNixdBinary();
|
|
257
|
+
if (detected) {
|
|
258
|
+
verifyNixdBinary(detected);
|
|
259
|
+
return detected;
|
|
260
|
+
}
|
|
261
|
+
if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
|
|
262
|
+
throw new Error('nixd バイナリが見つかりません。SERENA_SKIP_RUNTIME_INSTALL=0 で自動インストールを許可するか、nixd を手動でインストールしてください。');
|
|
263
|
+
}
|
|
264
|
+
const installed = installNixdViaNix();
|
|
265
|
+
if (installed) {
|
|
266
|
+
verifyNixdBinary(installed);
|
|
267
|
+
return installed;
|
|
268
|
+
}
|
|
269
|
+
throw new Error('nixd (Nix Language Server) が見つかりません。`nix profile install github:nix-community/nixd` などでインストールし、PATH を更新してください。');
|
|
270
|
+
}
|
|
271
|
+
function ensureNixAvailable() {
|
|
272
|
+
const result = spawnSync('nix', ['--version'], ensureDefaultSubprocessOptions({ encoding: 'utf-8', timeout: 5000 }));
|
|
273
|
+
if (result.error || result.status !== 0) {
|
|
274
|
+
throw new Error('Nix が見つかりません。https://nixos.org/download.html の手順で Nix をセットアップしてください。');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function detectNixdBinary() {
|
|
278
|
+
const overridePath = process.env[NIXD_PATH_ENV];
|
|
279
|
+
if (overridePath) {
|
|
280
|
+
return overridePath;
|
|
281
|
+
}
|
|
282
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
283
|
+
const whichResult = spawnSync(locator, ['nixd'], ensureDefaultSubprocessOptions({ encoding: 'utf-8', timeout: 2000 }));
|
|
284
|
+
if (whichResult.status === 0 && whichResult.stdout) {
|
|
285
|
+
const firstLine = whichResult.stdout.split(/\r?\n/)[0]?.trim();
|
|
286
|
+
if (firstLine) {
|
|
287
|
+
return firstLine;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const home = os.homedir();
|
|
291
|
+
const candidates = [
|
|
292
|
+
path.join(home, '.local', 'bin', 'nixd'),
|
|
293
|
+
path.join(home, '.smart-edit', 'language_servers', 'nixd', 'nixd'),
|
|
294
|
+
path.join(home, '.nix-profile', 'bin', 'nixd'),
|
|
295
|
+
'/usr/local/bin/nixd',
|
|
296
|
+
'/run/current-system/sw/bin/nixd',
|
|
297
|
+
'/opt/homebrew/bin/nixd',
|
|
298
|
+
'/usr/local/opt/nixd/bin/nixd'
|
|
299
|
+
];
|
|
300
|
+
for (const candidate of candidates) {
|
|
301
|
+
if (fs.existsSync(candidate)) {
|
|
302
|
+
return candidate;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
function installNixdViaNix() {
|
|
308
|
+
const commands = [
|
|
309
|
+
{ cmd: 'nix', args: ['profile', 'install', 'github:nix-community/nixd'] },
|
|
310
|
+
{ cmd: 'nix-env', args: ['-iA', 'nixpkgs.nixd'] }
|
|
311
|
+
];
|
|
312
|
+
for (const command of commands) {
|
|
313
|
+
const result = spawnSync(command.cmd, command.args, ensureDefaultSubprocessOptions({ encoding: 'utf-8', timeout: 600_000 }));
|
|
314
|
+
if (result.status === 0) {
|
|
315
|
+
const detected = detectNixdBinary();
|
|
316
|
+
if (detected) {
|
|
317
|
+
return detected;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
function verifyNixdBinary(binaryPath) {
|
|
324
|
+
const result = spawnSync(binaryPath, ['--version'], ensureDefaultSubprocessOptions({ encoding: 'utf-8', timeout: 5000 }));
|
|
325
|
+
if (result.error || result.status !== 0) {
|
|
326
|
+
throw new Error(`nixd 実行ファイルの検証に失敗しました: ${result.stderr ?? result.stdout ?? ''}`.trim());
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function extractWindowMessage(payload) {
|
|
330
|
+
if (!payload || typeof payload !== 'object') {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const maybeMessage = payload.message;
|
|
334
|
+
return typeof maybeMessage === 'string' ? maybeMessage : null;
|
|
335
|
+
}
|
|
336
|
+
function defaultNixdCommand() {
|
|
337
|
+
return process.platform === 'win32' ? 'nixd.exe' : 'nixd';
|
|
338
|
+
}
|
|
339
|
+
function isLocationObject(value) {
|
|
340
|
+
return typeof value === 'object' && value !== null;
|
|
341
|
+
}
|