@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,154 @@
|
|
|
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 { existsSync, readFileSync } from 'node:fs';
|
|
19
|
+
import { pathToFileURL } from 'node:url';
|
|
20
|
+
import { applyWorkspaceEdit, planWorkspaceEdit } from '../apply.js';
|
|
21
|
+
import { emitResult, fail, parseLineCol, resolvePathArg, toLspPosition } from '../io.js';
|
|
22
|
+
import { RefactorSession } from '../session.js';
|
|
23
|
+
/**
|
|
24
|
+
* Normalize the `command` of a code action into an {@link LspCommand}, preserving
|
|
25
|
+
* arguments for BOTH shapes:
|
|
26
|
+
* - nested object command → used as-is;
|
|
27
|
+
* - raw `Command` (string `command`) → its top-level `arguments` are retained
|
|
28
|
+
* (dropping them left command-only `_typescript.applyRefactoring` moves with
|
|
29
|
+
* an empty arg list, so the refactor could not run).
|
|
30
|
+
*/
|
|
31
|
+
export function toLspCommand(action) {
|
|
32
|
+
if (!action.command)
|
|
33
|
+
return undefined;
|
|
34
|
+
if (typeof action.command === 'string') {
|
|
35
|
+
const cmd = { title: action.title, command: action.command };
|
|
36
|
+
if (action.arguments !== undefined)
|
|
37
|
+
cmd.arguments = action.arguments;
|
|
38
|
+
return cmd;
|
|
39
|
+
}
|
|
40
|
+
return action.command;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the ordered list of {@link WorkspaceEdit}s a `refactor.move` action
|
|
44
|
+
* produces, honoring the LSP `edit` + `command` contract:
|
|
45
|
+
* - if the action has an inline `edit`, it is applied FIRST;
|
|
46
|
+
* - if it also (or instead) has a `command`, the command is executed AFTER the
|
|
47
|
+
* inline edit, and EVERY edit the server then pushes via `workspace/applyEdit`
|
|
48
|
+
* (possibly several, in sequence) is appended in arrival order.
|
|
49
|
+
*
|
|
50
|
+
* Pure with respect to disk: it only sequences edits. `execute` runs the command
|
|
51
|
+
* (injecting the move target); `drainCapturedEdits` returns all server-pushed
|
|
52
|
+
* edits captured since the last drain.
|
|
53
|
+
*/
|
|
54
|
+
export async function resolveMoveEdits(action, targetFile, execute, drainCapturedEdits) {
|
|
55
|
+
const edits = [];
|
|
56
|
+
if (action.edit)
|
|
57
|
+
edits.push(action.edit);
|
|
58
|
+
const cmd = toLspCommand(action);
|
|
59
|
+
if (cmd) {
|
|
60
|
+
await execute(injectTargetFile(cmd, targetFile));
|
|
61
|
+
edits.push(...drainCapturedEdits());
|
|
62
|
+
}
|
|
63
|
+
return edits;
|
|
64
|
+
}
|
|
65
|
+
/** Inject the move destination into a tsserver `_typescript.applyRefactoring`
|
|
66
|
+
* command argument so the symbol lands in an existing target file. */
|
|
67
|
+
function injectTargetFile(cmd, targetFile) {
|
|
68
|
+
if (cmd.command !== '_typescript.applyRefactoring' || !Array.isArray(cmd.arguments))
|
|
69
|
+
return cmd;
|
|
70
|
+
const arg0 = cmd.arguments[0];
|
|
71
|
+
if (arg0 && typeof arg0 === 'object') {
|
|
72
|
+
const obj = arg0;
|
|
73
|
+
return {
|
|
74
|
+
...cmd,
|
|
75
|
+
arguments: [
|
|
76
|
+
{
|
|
77
|
+
...obj,
|
|
78
|
+
refactor: 'Move to file',
|
|
79
|
+
action: 'Move to file',
|
|
80
|
+
interactiveRefactorArguments: { targetFile }
|
|
81
|
+
},
|
|
82
|
+
...cmd.arguments.slice(1)
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return cmd;
|
|
87
|
+
}
|
|
88
|
+
export async function runMoveSymbol(args, flags) {
|
|
89
|
+
const file = resolvePathArg(args.file, flags);
|
|
90
|
+
const targetFile = resolvePathArg(args.targetFile, flags);
|
|
91
|
+
const oneBased = parseLineCol(args.position);
|
|
92
|
+
const pos = toLspPosition(oneBased);
|
|
93
|
+
if (!existsSync(file))
|
|
94
|
+
fail(`source file not found: ${file}`, flags.json);
|
|
95
|
+
const session = new RefactorSession({
|
|
96
|
+
serverCommand: flags.server,
|
|
97
|
+
root: flags.root,
|
|
98
|
+
indexWaitMs: flags.waitMs,
|
|
99
|
+
verbose: flags.verbose
|
|
100
|
+
});
|
|
101
|
+
try {
|
|
102
|
+
await session.start();
|
|
103
|
+
await session.openAndWait(file);
|
|
104
|
+
// Also open the target so the server knows its insertion context.
|
|
105
|
+
if (existsSync(targetFile)) {
|
|
106
|
+
await session.lsp.sendNotification('textDocument/didOpen', {
|
|
107
|
+
textDocument: {
|
|
108
|
+
uri: pathToFileURL(targetFile).href,
|
|
109
|
+
languageId: 'typescript',
|
|
110
|
+
version: 1,
|
|
111
|
+
text: readFileSync(targetFile, 'utf8')
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const range = { start: pos, end: pos };
|
|
116
|
+
const actions = await session.requestWithRetry(() => session.lsp.sendRequest('textDocument/codeAction', {
|
|
117
|
+
textDocument: { uri: pathToFileURL(file).href },
|
|
118
|
+
range,
|
|
119
|
+
context: { diagnostics: [], only: ['refactor.move'] }
|
|
120
|
+
}));
|
|
121
|
+
if (!actions || actions.length === 0) {
|
|
122
|
+
fail('no refactor.move code action available at that position', flags.json);
|
|
123
|
+
}
|
|
124
|
+
// Prefer the move action; resolve it if it has no inline edit.
|
|
125
|
+
let action = actions.find((a) => a.kind?.startsWith('refactor.move')) ?? actions[0];
|
|
126
|
+
if (!action.edit && action.data !== undefined) {
|
|
127
|
+
const resolved = (await session.lsp.sendRequest('codeAction/resolve', action));
|
|
128
|
+
if (resolved)
|
|
129
|
+
action = resolved;
|
|
130
|
+
}
|
|
131
|
+
// Validate every server-returned edit against --root before applying.
|
|
132
|
+
const guard = { root: flags.root, allowOutsideRoot: flags.allowOutsideRoot };
|
|
133
|
+
const applyEdit = (e) => flags.dryRun ? planWorkspaceEdit(e, guard) : applyWorkspaceEdit(e, guard);
|
|
134
|
+
// Per the LSP spec, a CodeAction may carry BOTH an inline `edit` and a
|
|
135
|
+
// `command`: apply the edit FIRST, then execute the command (not either/or).
|
|
136
|
+
// The command may itself push further edits via workspace/applyEdit, possibly
|
|
137
|
+
// several in sequence — drain them ALL in order. (See resolveMoveEdits.)
|
|
138
|
+
const edits = await resolveMoveEdits(action, targetFile, async (cmd) => {
|
|
139
|
+
await session.lsp.sendRequest('workspace/executeCommand', {
|
|
140
|
+
command: cmd.command,
|
|
141
|
+
arguments: cmd.arguments ?? []
|
|
142
|
+
});
|
|
143
|
+
}, () => session.takeCapturedEdits());
|
|
144
|
+
if (edits.length === 0) {
|
|
145
|
+
fail('move-symbol produced no edit (server did not return or apply one)', flags.json);
|
|
146
|
+
}
|
|
147
|
+
const changes = edits.flatMap(applyEdit);
|
|
148
|
+
emitResult('move-symbol', changes, flags, { targetFile });
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
await session.stop();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=move-symbol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-symbol.js","sourceRoot":"","sources":["../../src/commands/move-symbol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy query <op> <file> <line:col>`
|
|
3
|
+
*
|
|
4
|
+
* Read-only parity ops (definition / references / hover) exposed for terminals
|
|
5
|
+
* and CI that want the same position-driven interface as the write commands.
|
|
6
|
+
* Always emits JSON on stdout (these are data ops, not edits).
|
|
7
|
+
*/
|
|
8
|
+
import { type GlobalFlags } from '../io.js';
|
|
9
|
+
export declare function runQuery(args: {
|
|
10
|
+
op: string;
|
|
11
|
+
file: string;
|
|
12
|
+
position: string;
|
|
13
|
+
}, flags: GlobalFlags): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAqD,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAK/F,wBAAsB,QAAQ,CAC5B,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EACpD,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAkCf"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy query <op> <file> <line:col>`
|
|
3
|
+
*
|
|
4
|
+
* Read-only parity ops (definition / references / hover) exposed for terminals
|
|
5
|
+
* and CI that want the same position-driven interface as the write commands.
|
|
6
|
+
* Always emits JSON on stdout (these are data ops, not edits).
|
|
7
|
+
*/
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { fail, parseLineCol, resolvePathArg, toLspPosition } from '../io.js';
|
|
10
|
+
import { RefactorSession } from '../session.js';
|
|
11
|
+
const OPS = new Set(['definition', 'references', 'hover']);
|
|
12
|
+
export async function runQuery(args, flags) {
|
|
13
|
+
if (!OPS.has(args.op))
|
|
14
|
+
fail(`unknown query op "${args.op}" (definition | references | hover)`, true);
|
|
15
|
+
const file = resolvePathArg(args.file, flags);
|
|
16
|
+
const pos = toLspPosition(parseLineCol(args.position));
|
|
17
|
+
const session = new RefactorSession({
|
|
18
|
+
serverCommand: flags.server,
|
|
19
|
+
root: flags.root,
|
|
20
|
+
indexWaitMs: flags.waitMs,
|
|
21
|
+
verbose: flags.verbose
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
await session.start();
|
|
25
|
+
await session.openAndWait(file);
|
|
26
|
+
const td = { textDocument: { uri: pathToFileURL(file).href }, position: pos };
|
|
27
|
+
let result;
|
|
28
|
+
if (args.op === 'definition') {
|
|
29
|
+
result = await session.lsp.sendRequest('textDocument/definition', td);
|
|
30
|
+
}
|
|
31
|
+
else if (args.op === 'references') {
|
|
32
|
+
result = await session.lsp.sendRequest('textDocument/references', {
|
|
33
|
+
...td,
|
|
34
|
+
context: { includeDeclaration: true }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
result = await session.lsp.sendRequest('textDocument/hover', td);
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write(JSON.stringify({ ok: true, op: args.op, result }) + '\n');
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await session.stop();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAoB,MAAM,UAAU,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAoD,EACpD,KAAkB;IAElB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,qBAAqB,IAAI,CAAC,EAAE,qCAAqC,EAAE,IAAI,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC;QAClC,aAAa,EAAE,KAAK,CAAC,MAAM;QAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAEhC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QAC9E,IAAI,MAAe,CAAC;QACpB,IAAI,IAAI,CAAC,EAAE,KAAK,YAAY,EAAE,CAAC;YAC7B,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC;QACxE,CAAC;aAAM,IAAI,IAAI,CAAC,EAAE,KAAK,YAAY,EAAE,CAAC;YACpC,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,yBAAyB,EAAE;gBAChE,GAAG,EAAE;gBACL,OAAO,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE;aACtC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACjF,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy rename <file> <line:col> <newName>`
|
|
3
|
+
*
|
|
4
|
+
* Drives `textDocument/rename` and applies the returned WorkspaceEdit. The LSP
|
|
5
|
+
* server finds every reference — including re-exports, aliased imports,
|
|
6
|
+
* type-only imports, and `{@link}` references — that a text search would miss.
|
|
7
|
+
*/
|
|
8
|
+
import { type GlobalFlags } from '../io.js';
|
|
9
|
+
export declare function runRename(args: {
|
|
10
|
+
file: string;
|
|
11
|
+
position: string;
|
|
12
|
+
newName: string;
|
|
13
|
+
}, flags: GlobalFlags): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../src/commands/rename.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAML,KAAK,WAAW,EACjB,MAAM,UAAU,CAAC;AAGlB,wBAAsB,SAAS,CAC7B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lspeasy rename <file> <line:col> <newName>`
|
|
3
|
+
*
|
|
4
|
+
* Drives `textDocument/rename` and applies the returned WorkspaceEdit. The LSP
|
|
5
|
+
* server finds every reference — including re-exports, aliased imports,
|
|
6
|
+
* type-only imports, and `{@link}` references — that a text search would miss.
|
|
7
|
+
*/
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { applyWorkspaceEdit, planWorkspaceEdit } from '../apply.js';
|
|
10
|
+
import { emitResult, fail, parseLineCol, resolvePathArg, toLspPosition } from '../io.js';
|
|
11
|
+
import { RefactorSession } from '../session.js';
|
|
12
|
+
export async function runRename(args, flags) {
|
|
13
|
+
const file = resolvePathArg(args.file, flags);
|
|
14
|
+
const pos = toLspPosition(parseLineCol(args.position));
|
|
15
|
+
const session = new RefactorSession({
|
|
16
|
+
serverCommand: flags.server,
|
|
17
|
+
root: flags.root,
|
|
18
|
+
indexWaitMs: flags.waitMs,
|
|
19
|
+
verbose: flags.verbose
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
await session.start();
|
|
23
|
+
await session.openAndWait(file);
|
|
24
|
+
const edit = await session.requestWithRetry(() => session.lsp.sendRequest('textDocument/rename', {
|
|
25
|
+
textDocument: { uri: pathToFileURL(file).href },
|
|
26
|
+
position: pos,
|
|
27
|
+
newName: args.newName
|
|
28
|
+
}));
|
|
29
|
+
if (!edit)
|
|
30
|
+
fail('rename returned no edit (symbol not renamable, or project not indexed)', flags.json);
|
|
31
|
+
// Validate the SERVER-RETURNED edit against --root: a server can return a
|
|
32
|
+
// URI/resource op outside the project; applying it unguarded would bypass
|
|
33
|
+
// the --root safety guarantee. The guard refuses unless --allow-outside-root.
|
|
34
|
+
const guard = { root: flags.root, allowOutsideRoot: flags.allowOutsideRoot };
|
|
35
|
+
const changes = flags.dryRun ? planWorkspaceEdit(edit, guard) : applyWorkspaceEdit(edit, guard);
|
|
36
|
+
emitResult('rename', changes, flags, { newName: args.newName });
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await session.stop();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.js","sourceRoot":"","sources":["../../src/commands/rename.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAsB,MAAM,aAAa,CAAC;AACxF,OAAO,EACL,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,aAAa,EAEd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAyD,EACzD,KAAkB;IAElB,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC;QAClC,aAAa,EAAE,KAAK,CAAC,MAAM;QAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAEhC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,gBAAgB,CACzC,GAAG,EAAE,CACH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,qBAAqB,EAAE;YAC7C,YAAY,EAAE,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;YAC/C,QAAQ,EAAE,GAAG;YACb,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAkC,CACtC,CAAC;QAEF,IAAI,CAAC,IAAI;YACP,IAAI,CAAC,wEAAwE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7F,0EAA0E;QAC1E,0EAA0E;QAC1E,8EAA8E;QAC9E,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChG,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAClE,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RefactorSession } from './session.js';
|
|
2
|
+
export interface ConnectOptions {
|
|
3
|
+
root: string;
|
|
4
|
+
languageId: string;
|
|
5
|
+
serverCommand?: string;
|
|
6
|
+
indexWaitMs: number;
|
|
7
|
+
verbose: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function connectViaProxy(opts: ConnectOptions): Promise<RefactorSession>;
|
|
10
|
+
//# sourceMappingURL=connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAuB,MAAM,cAAc,CAAC;AAmCpE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CA0BpF"}
|
package/dist/connect.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// apps/cli/src/connect.ts
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { createConnection } from 'node:net';
|
|
6
|
+
import { SocketTransport } from '@lspeasy/core/node';
|
|
7
|
+
import { socketPath } from '@lsproxy/proxy';
|
|
8
|
+
import { RefactorSession } from './session.js';
|
|
9
|
+
const PROXY_BIN = new URL('../../proxy/dist/main.js', import.meta.url).pathname;
|
|
10
|
+
const POLL_INTERVAL_MS = 100;
|
|
11
|
+
const POLL_TIMEOUT_MS = 5000;
|
|
12
|
+
async function tryConnect(sockPath) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const socket = createConnection({ path: sockPath });
|
|
15
|
+
socket.once('connect', () => {
|
|
16
|
+
socket.destroy();
|
|
17
|
+
resolve(true);
|
|
18
|
+
});
|
|
19
|
+
socket.once('error', () => resolve(false));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function pollForSocket(sockPath, timeoutMs) {
|
|
23
|
+
const deadline = Date.now() + timeoutMs;
|
|
24
|
+
while (Date.now() < deadline) {
|
|
25
|
+
if (existsSync(sockPath) && (await tryConnect(sockPath)))
|
|
26
|
+
return;
|
|
27
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`Proxy daemon did not start within ${timeoutMs}ms (socket: ${sockPath})`);
|
|
30
|
+
}
|
|
31
|
+
function spawnDaemon(root, sockPath) {
|
|
32
|
+
mkdirSync(dirname(sockPath), { recursive: true });
|
|
33
|
+
const child = spawn(process.execPath, [PROXY_BIN, '--root', root, '--socket', sockPath], {
|
|
34
|
+
detached: true,
|
|
35
|
+
stdio: 'ignore'
|
|
36
|
+
});
|
|
37
|
+
child.unref();
|
|
38
|
+
}
|
|
39
|
+
export async function connectViaProxy(opts) {
|
|
40
|
+
const sockPath = socketPath(opts.root);
|
|
41
|
+
const alreadyUp = existsSync(sockPath) && (await tryConnect(sockPath));
|
|
42
|
+
if (!alreadyUp) {
|
|
43
|
+
if (opts.verbose)
|
|
44
|
+
process.stderr.write(`[lspeasy] spawning proxy daemon\n`);
|
|
45
|
+
spawnDaemon(opts.root, sockPath);
|
|
46
|
+
await pollForSocket(sockPath, POLL_TIMEOUT_MS);
|
|
47
|
+
}
|
|
48
|
+
if (opts.verbose)
|
|
49
|
+
process.stderr.write(`[lspeasy] connecting via proxy ${sockPath}\n`);
|
|
50
|
+
const transport = new SocketTransport({ path: sockPath });
|
|
51
|
+
await transport.waitForConnect();
|
|
52
|
+
const sessionOpts = {
|
|
53
|
+
root: opts.root,
|
|
54
|
+
languageId: opts.languageId,
|
|
55
|
+
indexWaitMs: opts.indexWaitMs,
|
|
56
|
+
verbose: opts.verbose,
|
|
57
|
+
transport
|
|
58
|
+
};
|
|
59
|
+
const session = new RefactorSession(sessionOpts);
|
|
60
|
+
await session.start();
|
|
61
|
+
return session;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAuB,MAAM,cAAc,CAAC;AAEpE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,0BAA0B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AAChF,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,KAAK,UAAU,UAAU,CAAC,QAAgB;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;YAC1B,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,SAAiB;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAE,OAAO;QACjE,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,SAAS,eAAe,QAAQ,GAAG,CAAC,CAAC;AAC5F,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,QAAgB;IACjD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvF,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAoB;IACxD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC5E,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACjC,MAAM,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,QAAQ,IAAI,CAAC,CAAC;IAEvF,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1D,MAAM,SAAS,CAAC,cAAc,EAAE,CAAC;IAEjC,MAAM,WAAW,GAAmB;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS;KACV,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACtB,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface LspServerEntry {
|
|
2
|
+
command: string;
|
|
3
|
+
args?: string[];
|
|
4
|
+
fileExtensions: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export interface LspJson {
|
|
7
|
+
lspServers: Record<string, LspServerEntry>;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolvedServer {
|
|
10
|
+
/** Full spawn command string passed to RefactorSession as serverCommand. */
|
|
11
|
+
serverCommand: string;
|
|
12
|
+
/** languageId for textDocument/didOpen (e.g. 'typescript', 'rust'). */
|
|
13
|
+
languageId: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function selectServer(config: LspJson, fileExt: string): ResolvedServer | null;
|
|
16
|
+
export declare function discoverServer(root: string, fileExt: string): ResolvedServer | null;
|
|
17
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;CACpB;AAaD,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAkBpF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAWnF"}
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const SEARCH_PATHS = ['lsp.json', '.claude/lsp.json', '.github/lsp.json'];
|
|
5
|
+
function findLspJsonPath(root) {
|
|
6
|
+
for (const rel of SEARCH_PATHS) {
|
|
7
|
+
const full = join(root, rel);
|
|
8
|
+
if (existsSync(full))
|
|
9
|
+
return full;
|
|
10
|
+
}
|
|
11
|
+
const global = join(homedir(), '.claude', 'lsp.json');
|
|
12
|
+
return existsSync(global) ? global : null;
|
|
13
|
+
}
|
|
14
|
+
export function selectServer(config, fileExt) {
|
|
15
|
+
for (const entry of Object.values(config.lspServers)) {
|
|
16
|
+
const languageId = entry.fileExtensions[fileExt];
|
|
17
|
+
if (languageId) {
|
|
18
|
+
const parts = [entry.command, ...(entry.args ?? [])].filter(Boolean);
|
|
19
|
+
// Quote tokens containing spaces so tokenizeCommand in session.ts can
|
|
20
|
+
// round-trip them without splitting on the embedded whitespace.
|
|
21
|
+
// Escape backslashes first, then double-quotes: tokenizeCommand treats \\
|
|
22
|
+
// inside double-quotes as a literal backslash, so a trailing backslash in a
|
|
23
|
+
// Windows path like "C:\Program Files\" must become "C:\Program Files\\"
|
|
24
|
+
// to avoid the closing " being consumed as \" (an escaped quote).
|
|
25
|
+
const quoted = parts.map((t) => t.includes(' ') ? `"${t.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : t);
|
|
26
|
+
return { serverCommand: quoted.join(' '), languageId };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function discoverServer(root, fileExt) {
|
|
32
|
+
const lspJsonPath = findLspJsonPath(root);
|
|
33
|
+
if (!lspJsonPath)
|
|
34
|
+
return null;
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(readFileSync(lspJsonPath, 'utf8'));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (!parsed || typeof parsed['lspServers'] !== 'object')
|
|
43
|
+
return null;
|
|
44
|
+
return selectServer(parsed, fileExt);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmBjC,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;AAE1E,SAAS,eAAe,CAAC,IAAY;IACnC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC7B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACpC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACtD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAe,EAAE,OAAe;IAC3D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrE,sEAAsE;YACtE,gEAAgE;YAChE,0EAA0E;YAC1E,4EAA4E;YAC5E,yEAAyE;YACzE,kEAAkE;YAClE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAC3E,CAAC;YACF,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAe;IAC1D,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAQ,MAAkC,CAAC,YAAY,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAClG,OAAO,YAAY,CAAC,MAAiB,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lspeasy/cli` — programmatic entry point.
|
|
3
|
+
*
|
|
4
|
+
* Exposes the reusable refactor internals (session pipeline + WorkspaceEdit
|
|
5
|
+
* applier) so the same machinery can be embedded in scripts, not just invoked
|
|
6
|
+
* through the `lspeasy` bin.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { RefactorSession } from './session.js';
|
|
11
|
+
export type { SessionOptions } from './session.js';
|
|
12
|
+
export { applyWorkspaceEdit, applyTextEdits, planWorkspaceEdit } from './apply.js';
|
|
13
|
+
export type { WorkspaceEdit, LspTextEdit, LspRange, LspPosition, AppliedChange } from './apply.js';
|
|
14
|
+
export type { GlobalFlags } from './io.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACnF,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lspeasy/cli` — programmatic entry point.
|
|
3
|
+
*
|
|
4
|
+
* Exposes the reusable refactor internals (session pipeline + WorkspaceEdit
|
|
5
|
+
* applier) so the same machinery can be embedded in scripts, not just invoked
|
|
6
|
+
* through the `lspeasy` bin.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { RefactorSession } from './session.js';
|
|
11
|
+
export { applyWorkspaceEdit, applyTextEdits, planWorkspaceEdit } from './apply.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI I/O helpers.
|
|
3
|
+
*
|
|
4
|
+
* Convention: **stdout is reserved for command output** (human summary or, with
|
|
5
|
+
* `--json`, a single JSON document). All progress/diagnostic chatter goes to
|
|
6
|
+
* **stderr** so piped `--json` consumers get clean input.
|
|
7
|
+
*/
|
|
8
|
+
import type { AppliedChange } from './apply.js';
|
|
9
|
+
export interface GlobalFlags {
|
|
10
|
+
server: string;
|
|
11
|
+
root: string;
|
|
12
|
+
dryRun: boolean;
|
|
13
|
+
json: boolean;
|
|
14
|
+
verbose: boolean;
|
|
15
|
+
waitMs: number;
|
|
16
|
+
allowOutsideRoot: boolean;
|
|
17
|
+
/** Permit move-file to replace an existing destination (default: OFF). */
|
|
18
|
+
overwrite: boolean;
|
|
19
|
+
/** Bypass the proxy daemon and connect directly to the language server. */
|
|
20
|
+
noProxy: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Test whether an absolute path is contained within `root`, resolving symlinks
|
|
24
|
+
* in both the candidate and the root before comparing. A lexical-only check
|
|
25
|
+
* accepts `root/link/x` where `root/link -> /tmp/external`; canonicalizing the
|
|
26
|
+
* existing ancestors closes that escape.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isInsideRoot(absPath: string, root: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a file-path argument against the project `--root` and enforce that it
|
|
31
|
+
* stays inside that root.
|
|
32
|
+
*
|
|
33
|
+
* This is the single base both the server (spawn cwd / `rootUri`) and the file
|
|
34
|
+
* arguments share — they MUST agree. Relative args are resolved against `root`
|
|
35
|
+
* (NOT `process.cwd()`), so running the CLI from an unrelated directory (e.g. a
|
|
36
|
+
* different git worktree of the same project) cannot silently operate on the
|
|
37
|
+
* wrong checkout. Absolute args are used as-is.
|
|
38
|
+
*
|
|
39
|
+
* Unless `allowOutsideRoot` is set, any resolved path outside `root` is rejected
|
|
40
|
+
* (error + non-zero exit, no changes) — defense-in-depth against the wrong-repo
|
|
41
|
+
* incident relative-cwd resolution caused. Containment is symlink-aware (see
|
|
42
|
+
* {@link isInsideRoot}).
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolvePathArg(arg: string, flags: Pick<GlobalFlags, 'root' | 'json' | 'allowOutsideRoot'>): string;
|
|
45
|
+
/**
|
|
46
|
+
* Refuse a SERVER-RETURNED set of edit/resource targets that escape `--root`.
|
|
47
|
+
*
|
|
48
|
+
* The input-path guard ({@link resolvePathArg}) only validates CLI arguments;
|
|
49
|
+
* a language server can return a {@link WorkspaceEdit} whose URIs/resource ops
|
|
50
|
+
* point outside the project. Applying that unguarded would let a server
|
|
51
|
+
* modify, rename, or delete an external file — bypassing the `--root` safety
|
|
52
|
+
* guarantee. Every command routes server edits through this pre-flight check
|
|
53
|
+
* (via `apply.ts`), so all three inherit the boundary.
|
|
54
|
+
*
|
|
55
|
+
* Throws (rather than `fail()`/exit) so it composes inside the apply pipeline
|
|
56
|
+
* and is unit-testable; callers surface the message verbatim.
|
|
57
|
+
*/
|
|
58
|
+
export declare function assertTargetsInsideRoot(targets: readonly string[], flags: Pick<GlobalFlags, 'root' | 'allowOutsideRoot'>): void;
|
|
59
|
+
/** Editor-style 1-based `line:col`. */
|
|
60
|
+
export interface OneBasedPosition {
|
|
61
|
+
line: number;
|
|
62
|
+
character: number;
|
|
63
|
+
}
|
|
64
|
+
/** Parse `line:col` (1-based, editor style) into a 1-based position. */
|
|
65
|
+
export declare function parseLineCol(input: string): OneBasedPosition;
|
|
66
|
+
/** Convert a 1-based editor position to the 0-based position LSP expects. */
|
|
67
|
+
export declare function toLspPosition(p: OneBasedPosition): {
|
|
68
|
+
line: number;
|
|
69
|
+
character: number;
|
|
70
|
+
};
|
|
71
|
+
export declare function fail(message: string, json: boolean): never;
|
|
72
|
+
/** Render a list of applied/planned changes for human output. */
|
|
73
|
+
export declare function summarizeChanges(changes: AppliedChange[], root: string, dryRun: boolean): string;
|
|
74
|
+
/** Emit the result, either as JSON (stdout) or a human summary. */
|
|
75
|
+
export declare function emitResult(op: string, changes: AppliedChange[], flags: Pick<GlobalFlags, 'json' | 'root' | 'dryRun'>, extra?: Record<string, unknown>): void;
|
|
76
|
+
//# sourceMappingURL=io.d.ts.map
|
package/dist/io.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;CAClB;AA8BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAInE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,kBAAkB,CAAC,GAC7D,MAAM,CAOR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAAC,GACpD,IAAI,CAUN;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAO5D;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,CAAC,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAEtF;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,KAAK,CAI1D;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAoBhG;AAED,mEAAmE;AACnE,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,aAAa,EAAE,EACxB,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC,EACpD,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,IAAI,CAQN"}
|