@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,19 @@
|
|
|
1
|
+
import type { LogLevel } from '../../serena/util/logging.js';
|
|
2
|
+
import { SolidLanguageServer, type LanguageServerConfigLike, type SolidLspSettingsInit } from '../ls.js';
|
|
3
|
+
export declare class PyrightLanguageServer extends SolidLanguageServer {
|
|
4
|
+
private readonly nodeHandler;
|
|
5
|
+
private initialized;
|
|
6
|
+
private foundSourceFiles;
|
|
7
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
8
|
+
level?: number | LogLevel;
|
|
9
|
+
} | null, repositoryRootPath: string, options?: {
|
|
10
|
+
timeout?: number | null;
|
|
11
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
12
|
+
});
|
|
13
|
+
start(): this;
|
|
14
|
+
stop(shutdownTimeout?: number): void;
|
|
15
|
+
private registerHandlers;
|
|
16
|
+
private initializeLanguageServer;
|
|
17
|
+
private verifyCapabilities;
|
|
18
|
+
private buildInitializeParams;
|
|
19
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { Language } from '../ls_config.js';
|
|
4
|
+
import { SolidLanguageServer, registerLanguageServer } from '../ls.js';
|
|
5
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
6
|
+
const PYRIGHT_IGNORED_PATTERNS = [
|
|
7
|
+
'**/__pycache__',
|
|
8
|
+
'**/.venv',
|
|
9
|
+
'**/.env',
|
|
10
|
+
'**/build',
|
|
11
|
+
'**/dist',
|
|
12
|
+
'**/.pixi'
|
|
13
|
+
];
|
|
14
|
+
function mergeIgnoredPaths(existing, additions) {
|
|
15
|
+
const merged = new Set(existing ?? []);
|
|
16
|
+
for (const entry of additions) {
|
|
17
|
+
merged.add(entry);
|
|
18
|
+
}
|
|
19
|
+
return Array.from(merged);
|
|
20
|
+
}
|
|
21
|
+
export class PyrightLanguageServer extends SolidLanguageServer {
|
|
22
|
+
nodeHandler;
|
|
23
|
+
initialized = false;
|
|
24
|
+
foundSourceFiles = false;
|
|
25
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
26
|
+
const augmentedConfig = {
|
|
27
|
+
...config,
|
|
28
|
+
ignoredPaths: mergeIgnoredPaths(config.ignoredPaths, PYRIGHT_IGNORED_PATTERNS)
|
|
29
|
+
};
|
|
30
|
+
const handler = new NodeLanguageServerHandler({
|
|
31
|
+
cmd: 'python -m pyright.langserver --stdio',
|
|
32
|
+
cwd: repositoryRootPath
|
|
33
|
+
});
|
|
34
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
35
|
+
...options,
|
|
36
|
+
handler,
|
|
37
|
+
solidlspSettings: options?.solidlspSettings
|
|
38
|
+
});
|
|
39
|
+
this.nodeHandler = handler;
|
|
40
|
+
this.registerHandlers();
|
|
41
|
+
}
|
|
42
|
+
start() {
|
|
43
|
+
const shouldInitialize = !this.initialized;
|
|
44
|
+
super.start();
|
|
45
|
+
if (shouldInitialize) {
|
|
46
|
+
this.foundSourceFiles = false;
|
|
47
|
+
this.initializeLanguageServer();
|
|
48
|
+
this.initialized = true;
|
|
49
|
+
}
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
stop(shutdownTimeout = 2.0) {
|
|
53
|
+
super.stop(shutdownTimeout);
|
|
54
|
+
this.initialized = false;
|
|
55
|
+
this.foundSourceFiles = false;
|
|
56
|
+
}
|
|
57
|
+
registerHandlers() {
|
|
58
|
+
const noop = () => undefined;
|
|
59
|
+
this.nodeHandler.onRequest('client/registerCapability', noop);
|
|
60
|
+
this.nodeHandler.onRequest('workspace/executeClientCommand', () => []);
|
|
61
|
+
this.nodeHandler.onNotification('$/progress', noop);
|
|
62
|
+
this.nodeHandler.onNotification('textDocument/publishDiagnostics', noop);
|
|
63
|
+
this.nodeHandler.onNotification('language/status', noop);
|
|
64
|
+
this.nodeHandler.onNotification('language/actionableNotification', noop);
|
|
65
|
+
this.nodeHandler.onNotification('window/logMessage', (payload) => {
|
|
66
|
+
const message = extractMessage(payload);
|
|
67
|
+
if (message) {
|
|
68
|
+
this.logger.info(`Pyright LSP message: ${message}`);
|
|
69
|
+
if (/Found \d+ source files?/i.test(message)) {
|
|
70
|
+
this.foundSourceFiles = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
this.nodeHandler.onNotification('experimental/serverStatus', (payload) => {
|
|
75
|
+
const params = payload;
|
|
76
|
+
if (params?.quiescent && !this.foundSourceFiles) {
|
|
77
|
+
this.logger.debug('Pyright reported experimental/serverStatus quiescent=true before finding sources.');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
initializeLanguageServer() {
|
|
82
|
+
const params = this.buildInitializeParams();
|
|
83
|
+
const response = this.nodeHandler.sendRequest('initialize', params);
|
|
84
|
+
if (!response || typeof response !== 'object') {
|
|
85
|
+
throw new Error('Pyright language server returned an invalid initialize response.');
|
|
86
|
+
}
|
|
87
|
+
this.verifyCapabilities(response.capabilities ?? null);
|
|
88
|
+
this.nodeHandler.notify.initialized({});
|
|
89
|
+
}
|
|
90
|
+
verifyCapabilities(capabilities) {
|
|
91
|
+
if (!capabilities) {
|
|
92
|
+
throw new Error('Pyright initialization response is missing capabilities.');
|
|
93
|
+
}
|
|
94
|
+
const hasTextDocumentSync = Object.prototype.hasOwnProperty.call(capabilities, 'textDocumentSync');
|
|
95
|
+
const hasCompletionProvider = Object.prototype.hasOwnProperty.call(capabilities, 'completionProvider');
|
|
96
|
+
const hasDefinitionProvider = Object.prototype.hasOwnProperty.call(capabilities, 'definitionProvider');
|
|
97
|
+
if (!hasTextDocumentSync || !hasCompletionProvider || !hasDefinitionProvider) {
|
|
98
|
+
throw new Error('Pyright language server does not expose required capabilities.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
buildInitializeParams() {
|
|
102
|
+
const rootUri = pathToFileURL(this.repositoryRootPath).href;
|
|
103
|
+
return {
|
|
104
|
+
processId: process.pid,
|
|
105
|
+
rootPath: this.repositoryRootPath,
|
|
106
|
+
rootUri,
|
|
107
|
+
initializationOptions: {
|
|
108
|
+
exclude: [
|
|
109
|
+
'**/__pycache__',
|
|
110
|
+
'**/.venv',
|
|
111
|
+
'**/.env',
|
|
112
|
+
'**/build',
|
|
113
|
+
'**/dist',
|
|
114
|
+
'**/.pixi'
|
|
115
|
+
],
|
|
116
|
+
reportMissingImports: 'error'
|
|
117
|
+
},
|
|
118
|
+
capabilities: {
|
|
119
|
+
workspace: {
|
|
120
|
+
workspaceEdit: { documentChanges: true },
|
|
121
|
+
didChangeConfiguration: { dynamicRegistration: true },
|
|
122
|
+
didChangeWatchedFiles: { dynamicRegistration: true },
|
|
123
|
+
symbol: {
|
|
124
|
+
dynamicRegistration: true,
|
|
125
|
+
symbolKind: { valueSet: rangeArray(1, 27) }
|
|
126
|
+
},
|
|
127
|
+
executeCommand: { dynamicRegistration: true }
|
|
128
|
+
},
|
|
129
|
+
textDocument: {
|
|
130
|
+
synchronization: {
|
|
131
|
+
dynamicRegistration: true,
|
|
132
|
+
willSave: true,
|
|
133
|
+
willSaveWaitUntil: true,
|
|
134
|
+
didSave: true
|
|
135
|
+
},
|
|
136
|
+
hover: {
|
|
137
|
+
dynamicRegistration: true,
|
|
138
|
+
contentFormat: ['markdown', 'plaintext']
|
|
139
|
+
},
|
|
140
|
+
signatureHelp: {
|
|
141
|
+
dynamicRegistration: true,
|
|
142
|
+
signatureInformation: {
|
|
143
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
144
|
+
parameterInformation: { labelOffsetSupport: true }
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
definition: { dynamicRegistration: true },
|
|
148
|
+
references: { dynamicRegistration: true },
|
|
149
|
+
documentSymbol: {
|
|
150
|
+
dynamicRegistration: true,
|
|
151
|
+
symbolKind: { valueSet: rangeArray(1, 27) },
|
|
152
|
+
hierarchicalDocumentSymbolSupport: true
|
|
153
|
+
},
|
|
154
|
+
publishDiagnostics: { relatedInformation: true }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
workspaceFolders: [
|
|
158
|
+
{
|
|
159
|
+
uri: rootUri,
|
|
160
|
+
name: path.basename(this.repositoryRootPath)
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function extractMessage(payload) {
|
|
167
|
+
if (!payload || typeof payload !== 'object') {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const message = payload.message;
|
|
171
|
+
return typeof message === 'string' ? message : null;
|
|
172
|
+
}
|
|
173
|
+
function rangeArray(start, endExclusive) {
|
|
174
|
+
const values = [];
|
|
175
|
+
for (let value = start; value < endExclusive; value += 1) {
|
|
176
|
+
values.push(value);
|
|
177
|
+
}
|
|
178
|
+
return values;
|
|
179
|
+
}
|
|
180
|
+
registerLanguageServer(Language.PYTHON, PyrightLanguageServer);
|
|
@@ -0,0 +1,19 @@
|
|
|
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 RLanguageServer extends SolidLanguageServer {
|
|
5
|
+
protected readonly handler: NodeLanguageServerHandler;
|
|
6
|
+
private initialized;
|
|
7
|
+
constructor(config: LanguageServerConfigLike, loggerLike: {
|
|
8
|
+
level?: number | LogLevel;
|
|
9
|
+
} | null, repositoryRootPath: string, options?: {
|
|
10
|
+
timeout?: number | null;
|
|
11
|
+
solidlspSettings?: SolidLspSettingsInit;
|
|
12
|
+
});
|
|
13
|
+
start(): this;
|
|
14
|
+
stop(shutdownTimeout?: number): void;
|
|
15
|
+
private registerHandlers;
|
|
16
|
+
private initializeLanguageServer;
|
|
17
|
+
private buildInitializeParams;
|
|
18
|
+
private verifyCapabilities;
|
|
19
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { Language } from '../ls_config.js';
|
|
5
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
6
|
+
import { SolidLanguageServer, registerLanguageServer } from '../ls.js';
|
|
7
|
+
import { NodeLanguageServerHandler } from '../ls_handler.js';
|
|
8
|
+
const R_ASSUME_ENV = 'SERENA_ASSUME_R';
|
|
9
|
+
const R_BINARY_ENV = 'SERENA_R_BINARY';
|
|
10
|
+
const DEFAULT_R_BINARY = process.platform === 'win32' ? 'R.exe' : 'R';
|
|
11
|
+
const R_IGNORED_PATTERNS = [
|
|
12
|
+
'**/renv',
|
|
13
|
+
'**/renv/**',
|
|
14
|
+
'**/packrat',
|
|
15
|
+
'**/packrat/**',
|
|
16
|
+
'**/.Rproj.user',
|
|
17
|
+
'**/.Rproj.user/**',
|
|
18
|
+
'**/vignettes',
|
|
19
|
+
'**/vignettes/**'
|
|
20
|
+
];
|
|
21
|
+
export class RLanguageServer extends SolidLanguageServer {
|
|
22
|
+
handler;
|
|
23
|
+
initialized = false;
|
|
24
|
+
constructor(config, loggerLike, repositoryRootPath, options = {}) {
|
|
25
|
+
ensureRRuntimeAvailable();
|
|
26
|
+
const augmentedConfig = {
|
|
27
|
+
...config,
|
|
28
|
+
ignoredPaths: mergeIgnoredPatterns(config.ignoredPaths, R_IGNORED_PATTERNS)
|
|
29
|
+
};
|
|
30
|
+
const handler = new NodeLanguageServerHandler({
|
|
31
|
+
cmd: buildRCommand(),
|
|
32
|
+
cwd: repositoryRootPath
|
|
33
|
+
}, {
|
|
34
|
+
requestTimeoutSeconds: options?.timeout ?? null
|
|
35
|
+
});
|
|
36
|
+
super(augmentedConfig, loggerLike, repositoryRootPath, {
|
|
37
|
+
...options,
|
|
38
|
+
handler,
|
|
39
|
+
solidlspSettings: options?.solidlspSettings
|
|
40
|
+
});
|
|
41
|
+
this.handler = handler;
|
|
42
|
+
this.registerHandlers();
|
|
43
|
+
}
|
|
44
|
+
start() {
|
|
45
|
+
const shouldInitialize = !this.initialized;
|
|
46
|
+
super.start();
|
|
47
|
+
if (shouldInitialize) {
|
|
48
|
+
this.initializeLanguageServer();
|
|
49
|
+
this.initialized = true;
|
|
50
|
+
}
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
stop(shutdownTimeout = 2.0) {
|
|
54
|
+
super.stop(shutdownTimeout);
|
|
55
|
+
this.initialized = false;
|
|
56
|
+
}
|
|
57
|
+
registerHandlers() {
|
|
58
|
+
const noop = () => undefined;
|
|
59
|
+
this.handler.onRequest('client/registerCapability', noop);
|
|
60
|
+
this.handler.onNotification('window/logMessage', (payload) => {
|
|
61
|
+
const message = extractWindowMessage(payload);
|
|
62
|
+
if (message) {
|
|
63
|
+
this.logger.info(`R language server: ${message}`);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.handler.onNotification('$/progress', noop);
|
|
67
|
+
this.handler.onNotification('textDocument/publishDiagnostics', noop);
|
|
68
|
+
}
|
|
69
|
+
initializeLanguageServer() {
|
|
70
|
+
this.logger.info('Initializing R language server');
|
|
71
|
+
const params = this.buildInitializeParams();
|
|
72
|
+
const response = this.handler.sendRequest('initialize', params);
|
|
73
|
+
this.verifyCapabilities(response?.capabilities ?? null);
|
|
74
|
+
this.handler.notify.initialized({});
|
|
75
|
+
}
|
|
76
|
+
buildInitializeParams() {
|
|
77
|
+
const repositoryAbsolutePath = path.resolve(this.repositoryRootPath);
|
|
78
|
+
const rootUri = pathToFileURL(repositoryAbsolutePath).href;
|
|
79
|
+
return {
|
|
80
|
+
locale: 'en',
|
|
81
|
+
capabilities: buildClientCapabilities(),
|
|
82
|
+
processId: process.pid,
|
|
83
|
+
rootPath: repositoryAbsolutePath,
|
|
84
|
+
rootUri,
|
|
85
|
+
workspaceFolders: [
|
|
86
|
+
{
|
|
87
|
+
uri: rootUri,
|
|
88
|
+
name: path.basename(repositoryAbsolutePath)
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
verifyCapabilities(capabilities) {
|
|
94
|
+
if (!capabilities || typeof capabilities !== 'object') {
|
|
95
|
+
throw new Error('R language server did not return capabilities during initialization.');
|
|
96
|
+
}
|
|
97
|
+
const textDocumentCaps = capabilities;
|
|
98
|
+
if (!Object.prototype.hasOwnProperty.call(textDocumentCaps, 'textDocumentSync')) {
|
|
99
|
+
throw new Error('R language server initialization response is missing textDocumentSync capability.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function ensureRRuntimeAvailable() {
|
|
104
|
+
if (process.env[R_ASSUME_ENV] === '1') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const binary = process.env[R_BINARY_ENV] ?? DEFAULT_R_BINARY;
|
|
108
|
+
const versionResult = spawnSync(binary, ['--version'], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
109
|
+
if (versionResult.error || versionResult.status !== 0) {
|
|
110
|
+
throw new Error('R が見つかりません。https://www.r-project.org/ から R をインストールし、PATH に追加してください。');
|
|
111
|
+
}
|
|
112
|
+
const packageCheck = spawnSync(binary, [
|
|
113
|
+
'--vanilla',
|
|
114
|
+
'--quiet',
|
|
115
|
+
'--slave',
|
|
116
|
+
'-e',
|
|
117
|
+
"if (!require('languageserver', quietly=TRUE)) quit(status=1)"
|
|
118
|
+
], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
119
|
+
if (packageCheck.error || packageCheck.status !== 0) {
|
|
120
|
+
throw new Error('R languageserver パッケージが見つかりません。`R -e "install.packages(\'languageserver\')"` でインストールしてください。');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function buildRCommand() {
|
|
124
|
+
const binary = process.env[R_BINARY_ENV] ?? DEFAULT_R_BINARY;
|
|
125
|
+
return [
|
|
126
|
+
binary,
|
|
127
|
+
'--vanilla',
|
|
128
|
+
'--quiet',
|
|
129
|
+
'--slave',
|
|
130
|
+
'-e',
|
|
131
|
+
'options(languageserver.debug_mode = FALSE); languageserver::run()'
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
function mergeIgnoredPatterns(existing, additions) {
|
|
135
|
+
const merged = new Set(existing ?? []);
|
|
136
|
+
for (const pattern of additions) {
|
|
137
|
+
merged.add(pattern);
|
|
138
|
+
}
|
|
139
|
+
return Array.from(merged);
|
|
140
|
+
}
|
|
141
|
+
function extractWindowMessage(payload) {
|
|
142
|
+
if (!payload || typeof payload !== 'object') {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const maybeMessage = payload.message;
|
|
146
|
+
return typeof maybeMessage === 'string' ? maybeMessage : null;
|
|
147
|
+
}
|
|
148
|
+
function buildClientCapabilities() {
|
|
149
|
+
const symbolKinds = Array.from({ length: 26 }, (_, index) => index + 1);
|
|
150
|
+
return {
|
|
151
|
+
textDocument: {
|
|
152
|
+
synchronization: { didSave: true, dynamicRegistration: true },
|
|
153
|
+
completion: {
|
|
154
|
+
dynamicRegistration: true,
|
|
155
|
+
completionItem: {
|
|
156
|
+
snippetSupport: true,
|
|
157
|
+
commitCharactersSupport: true,
|
|
158
|
+
documentationFormat: ['markdown', 'plaintext'],
|
|
159
|
+
deprecatedSupport: true,
|
|
160
|
+
preselectSupport: true
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
hover: { dynamicRegistration: true, contentFormat: ['markdown', 'plaintext'] },
|
|
164
|
+
definition: { dynamicRegistration: true },
|
|
165
|
+
references: { dynamicRegistration: true },
|
|
166
|
+
documentSymbol: {
|
|
167
|
+
dynamicRegistration: true,
|
|
168
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
169
|
+
symbolKind: { valueSet: symbolKinds }
|
|
170
|
+
},
|
|
171
|
+
formatting: { dynamicRegistration: true },
|
|
172
|
+
rangeFormatting: { dynamicRegistration: true }
|
|
173
|
+
},
|
|
174
|
+
workspace: {
|
|
175
|
+
workspaceFolders: true,
|
|
176
|
+
didChangeConfiguration: { dynamicRegistration: true },
|
|
177
|
+
symbol: {
|
|
178
|
+
dynamicRegistration: true,
|
|
179
|
+
symbolKind: { valueSet: symbolKinds }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
registerLanguageServer(Language.R, RLanguageServer);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SerenaLogger } from '../../serena/util/logging.js';
|
|
2
|
+
export declare const RUBY_BINARY_ENV = "SERENA_RUBY_BINARY";
|
|
3
|
+
export declare const BUNDLE_BINARY_ENV = "SERENA_BUNDLE_BINARY";
|
|
4
|
+
export declare function resolveRubyBinary(): string;
|
|
5
|
+
export declare function ensureRubyAvailable(logger: SerenaLogger, repositoryRootPath: string): void;
|
|
6
|
+
export declare function findCommand(command: string): string | null;
|
|
7
|
+
export declare function findBundleExecutable(repositoryRootPath: string): string | null;
|
|
8
|
+
export declare function gemfileLockContains(gemfileLockPath: string, gemName: string): boolean;
|
|
9
|
+
export declare function installGem(gemName: string, logger: SerenaLogger, repositoryRootPath: string): void;
|
|
10
|
+
export declare function buildRubyExcludePatterns(repositoryRootPath: string): string[];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
|
|
5
|
+
export const RUBY_BINARY_ENV = 'SERENA_RUBY_BINARY';
|
|
6
|
+
export const BUNDLE_BINARY_ENV = 'SERENA_BUNDLE_BINARY';
|
|
7
|
+
const DEFAULT_RUBY_BINARY = process.platform === 'win32' ? 'ruby.exe' : 'ruby';
|
|
8
|
+
const DEFAULT_BUNDLE_CANDIDATES = process.platform === 'win32'
|
|
9
|
+
? ['bundle.bat', 'bundle.cmd', 'bundle.exe', 'bundle']
|
|
10
|
+
: ['bundle'];
|
|
11
|
+
export function resolveRubyBinary() {
|
|
12
|
+
const raw = process.env[RUBY_BINARY_ENV];
|
|
13
|
+
const trimmed = raw?.trim();
|
|
14
|
+
return trimmed ?? DEFAULT_RUBY_BINARY;
|
|
15
|
+
}
|
|
16
|
+
export function ensureRubyAvailable(logger, repositoryRootPath) {
|
|
17
|
+
const rubyBinary = resolveRubyBinary();
|
|
18
|
+
const versionResult = spawnSync(rubyBinary, ['--version'], ensureDefaultSubprocessOptions({
|
|
19
|
+
cwd: repositoryRootPath,
|
|
20
|
+
encoding: 'utf-8'
|
|
21
|
+
}));
|
|
22
|
+
if (versionResult.error || versionResult.status !== 0) {
|
|
23
|
+
const detailSource = versionResult.stderr ??
|
|
24
|
+
versionResult.stdout ??
|
|
25
|
+
versionResult.error?.message ??
|
|
26
|
+
'';
|
|
27
|
+
const detail = detailSource.trim();
|
|
28
|
+
throw new Error(`Ruby が見つかりませんでした。${detail ? `詳細: ${detail}。` : ''}Ruby をインストールし PATH に追加してください。`);
|
|
29
|
+
}
|
|
30
|
+
const output = (versionResult.stdout || versionResult.stderr || '').trim();
|
|
31
|
+
if (output) {
|
|
32
|
+
logger.info(`検出した Ruby: ${output}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function findCommand(command) {
|
|
36
|
+
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
37
|
+
const result = spawnSync(locator, [command], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
|
|
38
|
+
if (result.error || result.status !== 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const stdout = result.stdout ?? '';
|
|
42
|
+
const line = stdout
|
|
43
|
+
.split(/\r?\n/)
|
|
44
|
+
.map((entry) => entry.trim())
|
|
45
|
+
.find((entry) => entry.length > 0);
|
|
46
|
+
return line ?? null;
|
|
47
|
+
}
|
|
48
|
+
export function findBundleExecutable(repositoryRootPath) {
|
|
49
|
+
const override = process.env[BUNDLE_BINARY_ENV]?.trim();
|
|
50
|
+
if (override) {
|
|
51
|
+
return override;
|
|
52
|
+
}
|
|
53
|
+
for (const candidate of DEFAULT_BUNDLE_CANDIDATES) {
|
|
54
|
+
const localPath = path.join(repositoryRootPath, 'bin', candidate);
|
|
55
|
+
if (fs.existsSync(localPath)) {
|
|
56
|
+
return localPath;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const candidate of DEFAULT_BUNDLE_CANDIDATES) {
|
|
60
|
+
const resolved = findCommand(candidate);
|
|
61
|
+
if (resolved) {
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
export function gemfileLockContains(gemfileLockPath, gemName) {
|
|
68
|
+
if (!fs.existsSync(gemfileLockPath)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const content = fs.readFileSync(gemfileLockPath, 'utf-8');
|
|
73
|
+
return content.toLowerCase().includes(gemName.toLowerCase());
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function installGem(gemName, logger, repositoryRootPath) {
|
|
80
|
+
if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
|
|
81
|
+
throw new Error(`${gemName} が見つかりません。SERENA_SKIP_RUNTIME_INSTALL=1 を解除するか、` +
|
|
82
|
+
`gem install ${gemName} を手動で実行してください。`);
|
|
83
|
+
}
|
|
84
|
+
logger.info(`gem install ${gemName} を試行します`);
|
|
85
|
+
const installResult = spawnSync('gem', ['install', gemName], ensureDefaultSubprocessOptions({
|
|
86
|
+
cwd: repositoryRootPath,
|
|
87
|
+
encoding: 'utf-8'
|
|
88
|
+
}));
|
|
89
|
+
if (installResult.error || installResult.status !== 0) {
|
|
90
|
+
const detail = `${installResult.stdout ?? ''}${installResult.stderr ?? ''}`.trim();
|
|
91
|
+
throw new Error(`gem install ${gemName} に失敗しました。${detail ? `詳細: ${detail}` : 'gem が利用可能か確認してください。'}`);
|
|
92
|
+
}
|
|
93
|
+
logger.info(`gem install ${gemName} が成功しました。`);
|
|
94
|
+
}
|
|
95
|
+
export function buildRubyExcludePatterns(repositoryRootPath) {
|
|
96
|
+
const basePatterns = [
|
|
97
|
+
'**/vendor/**',
|
|
98
|
+
'**/.bundle/**',
|
|
99
|
+
'**/tmp/**',
|
|
100
|
+
'**/log/**',
|
|
101
|
+
'**/coverage/**',
|
|
102
|
+
'**/.yardoc/**',
|
|
103
|
+
'**/doc/**',
|
|
104
|
+
'**/.git/**',
|
|
105
|
+
'**/node_modules/**',
|
|
106
|
+
'**/public/assets/**'
|
|
107
|
+
];
|
|
108
|
+
if (isRailsProject(repositoryRootPath)) {
|
|
109
|
+
basePatterns.push('**/app/assets/builds/**', '**/storage/**', '**/public/packs/**', '**/public/webpack/**');
|
|
110
|
+
}
|
|
111
|
+
return basePatterns;
|
|
112
|
+
}
|
|
113
|
+
function isRailsProject(repositoryRootPath) {
|
|
114
|
+
const indicators = [
|
|
115
|
+
path.join('config', 'application.rb'),
|
|
116
|
+
path.join('config', 'environment.rb'),
|
|
117
|
+
path.join('app', 'controllers', 'application_controller.rb'),
|
|
118
|
+
'Rakefile'
|
|
119
|
+
];
|
|
120
|
+
for (const relative of indicators) {
|
|
121
|
+
if (fs.existsSync(path.join(repositoryRootPath, relative))) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const gemfilePath = path.join(repositoryRootPath, 'Gemfile');
|
|
126
|
+
if (!fs.existsSync(gemfilePath)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const content = fs.readFileSync(gemfilePath, 'utf-8').toLowerCase();
|
|
131
|
+
return content.includes("gem 'rails'") || content.includes('gem "rails"');
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -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 RubyLspLanguageServer extends SolidLanguageServer {
|
|
4
|
+
private readonly handlerInstance;
|
|
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 buildInitializeParams;
|
|
17
|
+
private verifyCapabilities;
|
|
18
|
+
}
|