@lsproxy/cli 0.3.0
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 +21 -0
- package/README.md +167 -0
- package/dist/apply.d.ts +139 -0
- package/dist/apply.d.ts.map +1 -0
- package/dist/apply.js +325 -0
- package/dist/apply.js.map +1 -0
- package/dist/build-commands.d.ts +6 -0
- package/dist/build-commands.d.ts.map +1 -0
- package/dist/build-commands.js +111 -0
- package/dist/build-commands.js.map +1 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +207 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/move-file.d.ts +13 -0
- package/dist/commands/move-file.d.ts.map +1 -0
- package/dist/commands/move-file.js +84 -0
- package/dist/commands/move-file.js.map +1 -0
- package/dist/commands/move-symbol.d.ts +70 -0
- package/dist/commands/move-symbol.d.ts.map +1 -0
- package/dist/commands/move-symbol.js +154 -0
- package/dist/commands/move-symbol.js.map +1 -0
- package/dist/commands/query.d.ts +14 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +46 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/rename.d.ts +14 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +42 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/connect.d.ts +10 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +63 -0
- package/dist/connect.js.map +1 -0
- package/dist/discover.d.ts +17 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +46 -0
- package/dist/discover.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +76 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +150 -0
- package/dist/io.js.map +1 -0
- package/dist/session.d.ts +89 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +286 -0
- package/dist/session.js.map +1 -0
- package/dist/zod-to-commander.d.ts +11 -0
- package/dist/zod-to-commander.d.ts.map +1 -0
- package/dist/zod-to-commander.js +351 -0
- package/dist/zod-to-commander.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { ServerCapabilities } from '@lspeasy/core';
|
|
3
|
+
import type { RefactorSession } from './session.js';
|
|
4
|
+
import type { GlobalFlags } from './io.js';
|
|
5
|
+
export declare function buildCommandTree(program: Command, capabilities: ServerCapabilities, session: RefactorSession, flags: GlobalFlags): void;
|
|
6
|
+
//# sourceMappingURL=build-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-commands.d.ts","sourceRoot":"","sources":["../src/build-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA0D3C,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,kBAAkB,EAChC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,WAAW,GACjB,IAAI,CA4DN"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { LSPSchemas, getSchemaForMethod, getCapabilityForRequestMethod } from '@lspeasy/core';
|
|
3
|
+
import { zodToCommander, printAppliedChanges } from './zod-to-commander.js';
|
|
4
|
+
import { applyWorkspaceEdit, planWorkspaceEdit } from './apply.js';
|
|
5
|
+
function getNestedValue(obj, path) {
|
|
6
|
+
return path
|
|
7
|
+
.split('.')
|
|
8
|
+
.reduce((acc, key) => acc != null && typeof acc === 'object' ? acc[key] : undefined, obj);
|
|
9
|
+
}
|
|
10
|
+
function capabilityObject(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null ? value : null;
|
|
12
|
+
}
|
|
13
|
+
function stringArray(value) {
|
|
14
|
+
return Array.isArray(value) && value.length > 0 ? value : null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Enrich subcommand help text with runtime-discovered capability metadata.
|
|
18
|
+
* Resolves the server capability provider for the method, then scans its
|
|
19
|
+
* string and string-array fields — booleans and nested objects are skipped.
|
|
20
|
+
* Works generically for any LSP method; no per-method hardcoding required.
|
|
21
|
+
*/
|
|
22
|
+
function enrichCommandFromCapabilities(method, cmd, capabilities) {
|
|
23
|
+
const capPath = getCapabilityForRequestMethod(method);
|
|
24
|
+
if (capPath === 'alwaysOn')
|
|
25
|
+
return;
|
|
26
|
+
const obj = capabilityObject(getNestedValue(capabilities, capPath));
|
|
27
|
+
if (!obj)
|
|
28
|
+
return;
|
|
29
|
+
const lines = [];
|
|
30
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
31
|
+
const arr = stringArray(val);
|
|
32
|
+
if (arr) {
|
|
33
|
+
lines.push(`${key}: ${arr.map((v) => JSON.stringify(v)).join(' ')}`);
|
|
34
|
+
}
|
|
35
|
+
else if (typeof val === 'string' && val.length > 0) {
|
|
36
|
+
lines.push(`${key}: ${JSON.stringify(val)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (lines.length)
|
|
40
|
+
cmd.addHelpText('after', `\nCapability options:\n ${lines.join('\n ')}`);
|
|
41
|
+
}
|
|
42
|
+
// Some methods share a top-level capability path with sibling methods, so the
|
|
43
|
+
// top-level check alone is insufficient. Map those methods to a more specific
|
|
44
|
+
// sub-path that must also be truthy before exposing the command.
|
|
45
|
+
// e.g. semanticTokens/full shares 'semanticTokensProvider' with range/delta,
|
|
46
|
+
// but requires 'semanticTokensProvider.full' to actually be supported.
|
|
47
|
+
const CAPABILITY_REFINEMENTS = {
|
|
48
|
+
'textDocument/semanticTokens/full': 'semanticTokensProvider.full'
|
|
49
|
+
};
|
|
50
|
+
export function buildCommandTree(program, capabilities, session, flags) {
|
|
51
|
+
for (const method of Object.keys(LSPSchemas)) {
|
|
52
|
+
const schema = getSchemaForMethod(method);
|
|
53
|
+
if (!schema)
|
|
54
|
+
continue;
|
|
55
|
+
const capPath = getCapabilityForRequestMethod(method);
|
|
56
|
+
if (capPath === 'alwaysOn')
|
|
57
|
+
continue;
|
|
58
|
+
if (!getNestedValue(capabilities, capPath))
|
|
59
|
+
continue;
|
|
60
|
+
const refinedCapPath = CAPABILITY_REFINEMENTS[method];
|
|
61
|
+
if (refinedCapPath && !getNestedValue(capabilities, refinedCapPath))
|
|
62
|
+
continue;
|
|
63
|
+
const parts = method.split('/');
|
|
64
|
+
if (parts.length < 2)
|
|
65
|
+
continue;
|
|
66
|
+
const [namespace] = parts;
|
|
67
|
+
let nsCmd = program.commands.find((c) => c.name() === namespace);
|
|
68
|
+
if (!nsCmd) {
|
|
69
|
+
nsCmd = new Command(namespace).description(`${namespace} operations`);
|
|
70
|
+
program.addCommand(nsCmd);
|
|
71
|
+
}
|
|
72
|
+
const subCmd = zodToCommander(method, schema, session, flags);
|
|
73
|
+
enrichCommandFromCapabilities(method, subCmd, capabilities);
|
|
74
|
+
nsCmd.addCommand(subCmd);
|
|
75
|
+
}
|
|
76
|
+
program
|
|
77
|
+
.command('call <method>')
|
|
78
|
+
.description('Send any LSP request by method name with raw JSON params')
|
|
79
|
+
.option('--params <json>', 'LSP params as JSON')
|
|
80
|
+
.action(async (method, opts) => {
|
|
81
|
+
try {
|
|
82
|
+
const params = opts.params ? JSON.parse(opts.params) : {};
|
|
83
|
+
const result = await session.lsp.sendRequest(method, params);
|
|
84
|
+
const capturedEdits = session.takeCapturedEdits();
|
|
85
|
+
if (capturedEdits.length > 0) {
|
|
86
|
+
const guard = {
|
|
87
|
+
root: flags.root,
|
|
88
|
+
allowOutsideRoot: flags.allowOutsideRoot
|
|
89
|
+
};
|
|
90
|
+
const allChanges = capturedEdits.flatMap((edit) => flags.dryRun ? planWorkspaceEdit(edit, guard) : applyWorkspaceEdit(edit, guard));
|
|
91
|
+
printAppliedChanges(allChanges, method, flags.dryRun, flags.json);
|
|
92
|
+
}
|
|
93
|
+
else if (flags.json) {
|
|
94
|
+
process.stdout.write(JSON.stringify({ ok: true, method, result }) + '\n');
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
if (flags.json) {
|
|
102
|
+
process.stdout.write(JSON.stringify({ ok: false, error: String(err) }) + '\n');
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
process.stderr.write(`error: ${String(err)}\n`);
|
|
106
|
+
}
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=build-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-commands.js","sourceRoot":"","sources":["../src/build-commands.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lspeasy CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Two-pass parse: util.parseArgs extracts global flags first (no Commander),
|
|
6
|
+
* then the CLI connects to the server, reads capabilities, builds a
|
|
7
|
+
* namespace/subcommand Commander tree, and hands process.argv back to
|
|
8
|
+
* Commander for final dispatch.
|
|
9
|
+
*/
|
|
10
|
+
import { type GlobalFlags } from './io.js';
|
|
11
|
+
/** Parsed raw flag values from the first pass. */
|
|
12
|
+
export type ParsedOptionValues = {
|
|
13
|
+
server?: string;
|
|
14
|
+
root?: string;
|
|
15
|
+
'dry-run'?: boolean;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
wait?: string;
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
'allow-outside-root'?: boolean;
|
|
20
|
+
'no-proxy'?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Validate raw flag values and project them onto {@link GlobalFlags}.
|
|
24
|
+
*
|
|
25
|
+
* `--wait` must parse to a finite, non-negative number; a typo like
|
|
26
|
+
* `--wait abc` would otherwise become NaN, which setTimeout coerces to 0,
|
|
27
|
+
* silently skipping the index wait refactor requests depend on.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildFlags(values: ParsedOptionValues): GlobalFlags;
|
|
30
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AASH,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAwCjE,kDAAkD;AAClD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CAiBlE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lspeasy CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Two-pass parse: util.parseArgs extracts global flags first (no Commander),
|
|
6
|
+
* then the CLI connects to the server, reads capabilities, builds a
|
|
7
|
+
* namespace/subcommand Commander tree, and hands process.argv back to
|
|
8
|
+
* Commander for final dispatch.
|
|
9
|
+
*/
|
|
10
|
+
import { parseArgs } from 'node:util';
|
|
11
|
+
import { argv, exit } from 'node:process';
|
|
12
|
+
import { extname } from 'node:path';
|
|
13
|
+
import { realpathSync } from 'node:fs';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import { fail, resolvePathArg } from './io.js';
|
|
17
|
+
import { discoverServer } from '@lspeasy/core';
|
|
18
|
+
import { RefactorSession } from './session.js';
|
|
19
|
+
import { connectViaProxy } from './connect.js';
|
|
20
|
+
import { buildCommandTree } from './build-commands.js';
|
|
21
|
+
const STATIC_HELP = `lspeasy — LSP-driven CLI
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
lspeasy <namespace> <command> [args]
|
|
25
|
+
lspeasy call <method> --params <json>
|
|
26
|
+
|
|
27
|
+
Available commands depend on the connected server's advertised capabilities.
|
|
28
|
+
Run with a file argument to see available commands for that language:
|
|
29
|
+
lspeasy textDocument hover --help src/foo.ts
|
|
30
|
+
|
|
31
|
+
Global flags:
|
|
32
|
+
--server <cmd> LSP server launch command (overrides lsp.json discovery)
|
|
33
|
+
--root <dir> Project root (default: cwd)
|
|
34
|
+
--dry-run Print changes; do not write
|
|
35
|
+
--json Machine-readable JSON on stdout; diagnostics to stderr
|
|
36
|
+
--wait <ms> Server index wait in ms (default: 15000)
|
|
37
|
+
--verbose Progress logging to stderr
|
|
38
|
+
--allow-outside-root Allow file paths outside --root
|
|
39
|
+
--no-proxy Bypass proxy daemon; connect directly to language server
|
|
40
|
+
-h, --help Show this help
|
|
41
|
+
`;
|
|
42
|
+
const GLOBAL_OPTION_CONFIG = {
|
|
43
|
+
server: { type: 'string' },
|
|
44
|
+
root: { type: 'string' },
|
|
45
|
+
'dry-run': { type: 'boolean', default: false },
|
|
46
|
+
json: { type: 'boolean', default: false },
|
|
47
|
+
wait: { type: 'string', default: '15000' },
|
|
48
|
+
verbose: { type: 'boolean', default: false },
|
|
49
|
+
'allow-outside-root': { type: 'boolean', default: false },
|
|
50
|
+
'no-proxy': { type: 'boolean', default: false },
|
|
51
|
+
help: { type: 'boolean', short: 'h', default: false }
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Validate raw flag values and project them onto {@link GlobalFlags}.
|
|
55
|
+
*
|
|
56
|
+
* `--wait` must parse to a finite, non-negative number; a typo like
|
|
57
|
+
* `--wait abc` would otherwise become NaN, which setTimeout coerces to 0,
|
|
58
|
+
* silently skipping the index wait refactor requests depend on.
|
|
59
|
+
*/
|
|
60
|
+
export function buildFlags(values) {
|
|
61
|
+
const json = values.json === true;
|
|
62
|
+
const waitMs = Number(values.wait ?? '15000');
|
|
63
|
+
if (!Number.isFinite(waitMs) || waitMs < 0) {
|
|
64
|
+
fail(`--wait must be a non-negative number of milliseconds, got "${values.wait}"`, json);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
server: values.server ?? '',
|
|
68
|
+
root: values.root ?? process.cwd(),
|
|
69
|
+
dryRun: values['dry-run'] === true,
|
|
70
|
+
json,
|
|
71
|
+
verbose: values.verbose === true,
|
|
72
|
+
waitMs,
|
|
73
|
+
allowOutsideRoot: values['allow-outside-root'] === true,
|
|
74
|
+
noProxy: values['no-proxy'] === true,
|
|
75
|
+
overwrite: false // move-file removed; the flag is kept in GlobalFlags for io.ts compatibility
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function main() {
|
|
79
|
+
const { values, positionals } = parseArgs({
|
|
80
|
+
args: argv.slice(2),
|
|
81
|
+
options: GLOBAL_OPTION_CONFIG,
|
|
82
|
+
allowPositionals: true,
|
|
83
|
+
strict: false
|
|
84
|
+
});
|
|
85
|
+
if (!positionals.length) {
|
|
86
|
+
process.stdout.write(STATIC_HELP);
|
|
87
|
+
exit(0);
|
|
88
|
+
}
|
|
89
|
+
const flags = buildFlags(values);
|
|
90
|
+
let serverCommand;
|
|
91
|
+
let languageId = 'plaintext';
|
|
92
|
+
// positionals[0] = namespace, positionals[1] = method/command.
|
|
93
|
+
// The source file can only appear at positionals[2] (the first subcommand
|
|
94
|
+
// argument). A value is file-like only when it has an extension AND is not
|
|
95
|
+
// a JSON literal (--params values that parseArgs left in positionals).
|
|
96
|
+
// workspace/* methods (e.g. workspace/symbol) take a query string as their
|
|
97
|
+
// first argument, not a file — skip file detection for that namespace to avoid
|
|
98
|
+
// treating dotted identifiers like React.Component as file paths.
|
|
99
|
+
const isFileLike = (p) => extname(p) !== '' && !p.startsWith('{') && !p.startsWith('[') && !p.startsWith('"');
|
|
100
|
+
const firstSubArg = positionals[2];
|
|
101
|
+
const subArgFile = positionals[0] !== 'workspace' && firstSubArg !== undefined && isFileLike(firstSubArg)
|
|
102
|
+
? firstSubArg
|
|
103
|
+
: undefined;
|
|
104
|
+
if (flags.server) {
|
|
105
|
+
serverCommand = flags.server;
|
|
106
|
+
// Infer languageId from the file extension when --server bypasses discovery.
|
|
107
|
+
if (subArgFile) {
|
|
108
|
+
const ext = extname(subArgFile);
|
|
109
|
+
const discovered = discoverServer(flags.root, ext);
|
|
110
|
+
if (discovered)
|
|
111
|
+
languageId = discovered.languageId;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const ext = subArgFile ? extname(subArgFile) : '';
|
|
116
|
+
if (!ext) {
|
|
117
|
+
// No file argument — try lsp.json discovery with a wildcard lookup so
|
|
118
|
+
// file-less commands (e.g. workspace/symbol) can still find a server.
|
|
119
|
+
const discovered = discoverServer(flags.root, '');
|
|
120
|
+
if (discovered) {
|
|
121
|
+
serverCommand = discovered.serverCommand;
|
|
122
|
+
languageId = discovered.languageId;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
fail('Cannot determine language: pass a file argument or use --server <cmd>.', flags.json);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const discovered = discoverServer(flags.root, ext);
|
|
130
|
+
if (!discovered) {
|
|
131
|
+
fail(`No LSP server configured for ${ext} files.\n` +
|
|
132
|
+
'Add an lsp.json to your project (or ~/.claude/lsp.json) or use --server <cmd>.\n' +
|
|
133
|
+
'Format: { "lspServers": { "lang": { "command": "...", "args": [...], "fileExtensions": { ".ext": "languageId" } } } }', flags.json);
|
|
134
|
+
}
|
|
135
|
+
serverCommand = discovered.serverCommand;
|
|
136
|
+
languageId = discovered.languageId;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let session;
|
|
140
|
+
if (flags.noProxy || !!flags.server || !serverCommand) {
|
|
141
|
+
session = new RefactorSession({
|
|
142
|
+
serverCommand,
|
|
143
|
+
languageId,
|
|
144
|
+
root: flags.root,
|
|
145
|
+
indexWaitMs: flags.waitMs,
|
|
146
|
+
verbose: flags.verbose
|
|
147
|
+
});
|
|
148
|
+
await session.start();
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
session = await connectViaProxy({
|
|
152
|
+
root: flags.root,
|
|
153
|
+
languageId,
|
|
154
|
+
indexWaitMs: flags.waitMs,
|
|
155
|
+
verbose: flags.verbose
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
if (subArgFile && !values.help) {
|
|
160
|
+
const absPath = resolvePathArg(subArgFile, flags);
|
|
161
|
+
await session.open(absPath);
|
|
162
|
+
}
|
|
163
|
+
const program = new Command('lspeasy');
|
|
164
|
+
// Declare global options so Commander does not reject them in pass 2
|
|
165
|
+
program
|
|
166
|
+
.option('--server <cmd>')
|
|
167
|
+
.option('--root <dir>')
|
|
168
|
+
.option('--dry-run')
|
|
169
|
+
.option('--json')
|
|
170
|
+
.option('--wait <ms>')
|
|
171
|
+
.option('--verbose')
|
|
172
|
+
.option('--allow-outside-root')
|
|
173
|
+
.option('--no-proxy');
|
|
174
|
+
buildCommandTree(program, session.capabilities, session, flags);
|
|
175
|
+
await program.parseAsync(argv);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
await session.stop();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* True when this module is the process entry point (vs. imported by a test).
|
|
183
|
+
*
|
|
184
|
+
* Compares resolved real paths of import.meta.url and argv[1]. The bin
|
|
185
|
+
* (lspeasy) is installed as a symlink to dist/cli.js, so argv[1] is the
|
|
186
|
+
* symlink path while import.meta.url is Node's realpath — a plain compare
|
|
187
|
+
* would mismatch and never run main(). realpathSync on both sides makes the
|
|
188
|
+
* symlinked-bin and direct-invocation cases agree.
|
|
189
|
+
*/
|
|
190
|
+
function isEntryPoint() {
|
|
191
|
+
const entry = argv[1];
|
|
192
|
+
if (!entry)
|
|
193
|
+
return false;
|
|
194
|
+
try {
|
|
195
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(entry);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (isEntryPoint()) {
|
|
202
|
+
main().catch((err) => {
|
|
203
|
+
process.stderr.write(`fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
204
|
+
exit(1);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy move-file <oldPath> <newPath>`
|
|
3
|
+
*
|
|
4
|
+
* Asks the server for importer path updates via `workspace/willRenameFiles`,
|
|
5
|
+
* applies them, then physically moves the file. Uses `git mv` when the file is
|
|
6
|
+
* tracked (so history follows), falling back to a plain rename.
|
|
7
|
+
*/
|
|
8
|
+
import { type GlobalFlags } from '../io.js';
|
|
9
|
+
export declare function runMoveFile(args: {
|
|
10
|
+
oldPath: string;
|
|
11
|
+
newPath: string;
|
|
12
|
+
}, flags: GlobalFlags): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=move-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-file.d.ts","sourceRoot":"","sources":["../../src/commands/move-file.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAgC9E,wBAAsB,WAAW,CAC/B,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC1C,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAoDf"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy move-file <oldPath> <newPath>`
|
|
3
|
+
*
|
|
4
|
+
* Asks the server for importer path updates via `workspace/willRenameFiles`,
|
|
5
|
+
* applies them, then physically moves the file. Uses `git mv` when the file is
|
|
6
|
+
* tracked (so history follows), falling back to a plain rename.
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
9
|
+
import { existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
import { applyWorkspaceEdit, planWorkspaceEdit, stripResourceOps } from '../apply.js';
|
|
13
|
+
import { emitResult, fail, resolvePathArg } from '../io.js';
|
|
14
|
+
import { RefactorSession } from '../session.js';
|
|
15
|
+
function isGitTracked(root, path) {
|
|
16
|
+
try {
|
|
17
|
+
execFileSync('git', ['ls-files', '--error-unmatch', path], { cwd: root, stdio: 'ignore' });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function physicallyMove(root, from, to, dryRun, overwrite) {
|
|
25
|
+
if (!dryRun) {
|
|
26
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
27
|
+
if (isGitTracked(root, from)) {
|
|
28
|
+
// `git mv` refuses an existing destination; `-f` is required to clobber.
|
|
29
|
+
const gitArgs = overwrite ? ['mv', '-f', from, to] : ['mv', from, to];
|
|
30
|
+
execFileSync('git', gitArgs, { cwd: root });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
renameSync(from, to);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { kind: 'rename', path: from, toPath: to };
|
|
37
|
+
}
|
|
38
|
+
export async function runMoveFile(args, flags) {
|
|
39
|
+
const from = resolvePathArg(args.oldPath, flags);
|
|
40
|
+
const to = resolvePathArg(args.newPath, flags);
|
|
41
|
+
if (!existsSync(from))
|
|
42
|
+
fail(`source file not found: ${from}`, flags.json);
|
|
43
|
+
// Refuse to clobber an existing destination unless --overwrite was given.
|
|
44
|
+
// `renameSync` (the untracked-file path) silently destroys the destination on
|
|
45
|
+
// POSIX, and `git mv` (without -f) refuses — so guard up-front, before
|
|
46
|
+
// spawning the server, so dry-run predicts the refusal too.
|
|
47
|
+
if (existsSync(to) && !flags.overwrite) {
|
|
48
|
+
fail(`destination already exists: ${to} (pass --overwrite to replace)`, flags.json);
|
|
49
|
+
}
|
|
50
|
+
const session = new RefactorSession({
|
|
51
|
+
serverCommand: flags.server,
|
|
52
|
+
root: flags.root,
|
|
53
|
+
indexWaitMs: flags.waitMs,
|
|
54
|
+
verbose: flags.verbose
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
await session.start();
|
|
58
|
+
await session.openAndWait(from);
|
|
59
|
+
const edit = await session.requestWithRetry(() => session.lsp.sendRequest('workspace/willRenameFiles', {
|
|
60
|
+
files: [{ oldUri: pathToFileURL(from).href, newUri: pathToFileURL(to).href }]
|
|
61
|
+
}));
|
|
62
|
+
// A null edit just means no importers needed updating — still do the move.
|
|
63
|
+
// Strip ONLY the rename the server folded in that duplicates our physical
|
|
64
|
+
// move below (so `git mv` history is preserved); applying that rename here
|
|
65
|
+
// too would double-move and break a re-run with "source not found". Other
|
|
66
|
+
// resource ops the server emits (e.g. a `create shim.ts`) are preserved.
|
|
67
|
+
const importerEdit = edit ? stripResourceOps(edit, { from, to }) : undefined;
|
|
68
|
+
// Validate the SERVER-RETURNED edit against --root before applying it.
|
|
69
|
+
const guard = { root: flags.root, allowOutsideRoot: flags.allowOutsideRoot };
|
|
70
|
+
const importerChanges = importerEdit
|
|
71
|
+
? flags.dryRun
|
|
72
|
+
? planWorkspaceEdit(importerEdit, guard)
|
|
73
|
+
: applyWorkspaceEdit(importerEdit, guard)
|
|
74
|
+
: [];
|
|
75
|
+
const moveChange = physicallyMove(flags.root, from, to, flags.dryRun, flags.overwrite);
|
|
76
|
+
emitResult('move-file', [...importerChanges, moveChange], flags, {
|
|
77
|
+
importerFilesUpdated: importerChanges.length
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await session.stop();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=move-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-file.js","sourceRoot":"","sources":["../../src/commands/move-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy move-symbol <file> <line:col> <targetFile>`
|
|
3
|
+
*
|
|
4
|
+
* Moves the symbol at the given position into `targetFile`, updating all
|
|
5
|
+
* importers. Driven by `textDocument/codeAction` (kind `refactor.move`).
|
|
6
|
+
*
|
|
7
|
+
* Two server shapes are supported:
|
|
8
|
+
* 1. The code action (or its `codeAction/resolve`) returns a `WorkspaceEdit`
|
|
9
|
+
* directly — applied as-is.
|
|
10
|
+
* 2. The action carries a `command`. We execute it via
|
|
11
|
+
* `workspace/executeCommand`; many servers (notably
|
|
12
|
+
* typescript-language-server's `_typescript.applyRefactoring`) then push
|
|
13
|
+
* the edit back through a `workspace/applyEdit` request, which the session
|
|
14
|
+
* captures. For TS, we inject the destination as
|
|
15
|
+
* `interactiveRefactorArguments.targetFile` so the move targets an existing
|
|
16
|
+
* file rather than prompting for a new one.
|
|
17
|
+
*/
|
|
18
|
+
import { type WorkspaceEdit } from '../apply.js';
|
|
19
|
+
import { type GlobalFlags } from '../io.js';
|
|
20
|
+
interface LspCommand {
|
|
21
|
+
title: string;
|
|
22
|
+
command: string;
|
|
23
|
+
arguments?: unknown[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A `textDocument/codeAction` response item. Per the LSP spec the response is
|
|
27
|
+
* `(Command | CodeAction)[]`, so an item may be a full {@link CodeAction} OR a
|
|
28
|
+
* raw {@link LspCommand} — the latter carries its payload in a TOP-LEVEL
|
|
29
|
+
* `arguments` field (and `command` is then a `string`). We model both so a
|
|
30
|
+
* command-only action's `arguments` are not dropped.
|
|
31
|
+
*/
|
|
32
|
+
interface CodeAction {
|
|
33
|
+
title: string;
|
|
34
|
+
kind?: string;
|
|
35
|
+
edit?: WorkspaceEdit;
|
|
36
|
+
/** Nested command (CodeAction shape) or a command name (raw Command shape). */
|
|
37
|
+
command?: LspCommand | string;
|
|
38
|
+
/** Present only on the raw `Command` shape, alongside a string `command`. */
|
|
39
|
+
arguments?: unknown[];
|
|
40
|
+
data?: unknown;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Normalize the `command` of a code action into an {@link LspCommand}, preserving
|
|
44
|
+
* arguments for BOTH shapes:
|
|
45
|
+
* - nested object command → used as-is;
|
|
46
|
+
* - raw `Command` (string `command`) → its top-level `arguments` are retained
|
|
47
|
+
* (dropping them left command-only `_typescript.applyRefactoring` moves with
|
|
48
|
+
* an empty arg list, so the refactor could not run).
|
|
49
|
+
*/
|
|
50
|
+
export declare function toLspCommand(action: CodeAction): LspCommand | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the ordered list of {@link WorkspaceEdit}s a `refactor.move` action
|
|
53
|
+
* produces, honoring the LSP `edit` + `command` contract:
|
|
54
|
+
* - if the action has an inline `edit`, it is applied FIRST;
|
|
55
|
+
* - if it also (or instead) has a `command`, the command is executed AFTER the
|
|
56
|
+
* inline edit, and EVERY edit the server then pushes via `workspace/applyEdit`
|
|
57
|
+
* (possibly several, in sequence) is appended in arrival order.
|
|
58
|
+
*
|
|
59
|
+
* Pure with respect to disk: it only sequences edits. `execute` runs the command
|
|
60
|
+
* (injecting the move target); `drainCapturedEdits` returns all server-pushed
|
|
61
|
+
* edits captured since the last drain.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveMoveEdits(action: CodeAction, targetFile: string, execute: (cmd: LspCommand) => Promise<void>, drainCapturedEdits: () => WorkspaceEdit[]): Promise<WorkspaceEdit[]>;
|
|
64
|
+
export declare function runMoveSymbol(args: {
|
|
65
|
+
file: string;
|
|
66
|
+
position: string;
|
|
67
|
+
targetFile: string;
|
|
68
|
+
}, flags: GlobalFlags): Promise<void>;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=move-symbol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-symbol.d.ts","sourceRoot":"","sources":["../../src/commands/move-symbol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAyC,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAML,KAAK,WAAW,EAEjB,MAAM,UAAU,CAAC;AAGlB,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;CACvB;AACD;;;;;;GAMG;AACH,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAQvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,EAC3C,kBAAkB,EAAE,MAAM,aAAa,EAAE,GACxC,OAAO,CAAC,aAAa,EAAE,CAAC,CAS1B;AAyBD,wBAAsB,aAAa,CACjC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAC5D,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAmFf"}
|