@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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/dist/apply.d.ts +139 -0
  4. package/dist/apply.d.ts.map +1 -0
  5. package/dist/apply.js +325 -0
  6. package/dist/apply.js.map +1 -0
  7. package/dist/build-commands.d.ts +6 -0
  8. package/dist/build-commands.d.ts.map +1 -0
  9. package/dist/build-commands.js +111 -0
  10. package/dist/build-commands.js.map +1 -0
  11. package/dist/cli.d.ts +30 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +207 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/commands/move-file.d.ts +13 -0
  16. package/dist/commands/move-file.d.ts.map +1 -0
  17. package/dist/commands/move-file.js +84 -0
  18. package/dist/commands/move-file.js.map +1 -0
  19. package/dist/commands/move-symbol.d.ts +70 -0
  20. package/dist/commands/move-symbol.d.ts.map +1 -0
  21. package/dist/commands/move-symbol.js +154 -0
  22. package/dist/commands/move-symbol.js.map +1 -0
  23. package/dist/commands/query.d.ts +14 -0
  24. package/dist/commands/query.d.ts.map +1 -0
  25. package/dist/commands/query.js +46 -0
  26. package/dist/commands/query.js.map +1 -0
  27. package/dist/commands/rename.d.ts +14 -0
  28. package/dist/commands/rename.d.ts.map +1 -0
  29. package/dist/commands/rename.js +42 -0
  30. package/dist/commands/rename.js.map +1 -0
  31. package/dist/connect.d.ts +10 -0
  32. package/dist/connect.d.ts.map +1 -0
  33. package/dist/connect.js +63 -0
  34. package/dist/connect.js.map +1 -0
  35. package/dist/discover.d.ts +17 -0
  36. package/dist/discover.d.ts.map +1 -0
  37. package/dist/discover.js +46 -0
  38. package/dist/discover.js.map +1 -0
  39. package/dist/index.d.ts +15 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +12 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/io.d.ts +76 -0
  44. package/dist/io.d.ts.map +1 -0
  45. package/dist/io.js +150 -0
  46. package/dist/io.js.map +1 -0
  47. package/dist/session.d.ts +89 -0
  48. package/dist/session.d.ts.map +1 -0
  49. package/dist/session.js +286 -0
  50. package/dist/session.js.map +1 -0
  51. package/dist/zod-to-commander.d.ts +11 -0
  52. package/dist/zod-to-commander.d.ts.map +1 -0
  53. package/dist/zod-to-commander.js +351 -0
  54. package/dist/zod-to-commander.js.map +1 -0
  55. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Pradeep Mouli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # @lsproxy/cli
2
+
3
+ LSP-driven CLI that connects to any Language Server Protocol server and exposes
4
+ its capabilities as typed subcommands — hover, rename, format, find-references,
5
+ code actions, and more. The command surface is built at runtime from the server's
6
+ advertised capabilities, so it works with any LSP server out of the box.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npx @lsproxy/cli textDocument hover src/foo.ts 12:7 # zero-install
12
+ pnpm add -g @lsproxy/cli # or install globally
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```
18
+ lspeasy <namespace> <command> [args] [flags]
19
+ lspeasy call <method> --params <json>
20
+ ```
21
+
22
+ Commands are built from the server's capabilities at startup:
23
+
24
+ ```bash
25
+ lspeasy textDocument hover src/foo.ts 12:7
26
+ lspeasy textDocument rename src/foo.ts 12:7 newName
27
+ lspeasy textDocument references src/foo.ts 12:7
28
+ lspeasy textDocument definition src/foo.ts 12:7
29
+ lspeasy textDocument formatting src/foo.ts
30
+ lspeasy textDocument rangeFormatting src/foo.ts 1:1-50:1
31
+ lspeasy textDocument codeAction src/foo.ts 12:1-12:20
32
+ lspeasy textDocument onTypeFormatting src/foo.ts 12:7 --ch ";" --on-type-formatting-tab-size 2 --on-type-formatting-insert-spaces true
33
+ lspeasy workspace symbol MyClass
34
+ lspeasy call textDocument/semanticTokens/full --params '{"textDocument":{"uri":"file:///…"}}'
35
+ ```
36
+
37
+ Positions are **1-based** (`line:col`, editor-style).
38
+ Write-side commands (`rename`, `formatting`, code actions that produce edits)
39
+ apply changes to disk automatically. Pass `--dry-run` to preview instead.
40
+
41
+ ## Server discovery — `lsp.json`
42
+
43
+ Without `--server`, the CLI discovers which server to launch by looking for an
44
+ `lsp.json` file, walking up from `--root` (default: cwd) to the filesystem root,
45
+ then falling back to `~/.claude/lsp.json`.
46
+
47
+ **Search order within each directory:**
48
+ 1. `lsp.json`
49
+ 2. `.claude/lsp.json`
50
+ 3. `.github/lsp.json`
51
+
52
+ ### Format
53
+
54
+ ```json
55
+ {
56
+ "lspServers": {
57
+ "<name>": {
58
+ "command": "<binary>",
59
+ "args": ["<arg>", "…"],
60
+ "fileExtensions": {
61
+ ".<ext>": "<languageId>"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### Example — multi-language project
69
+
70
+ ```json
71
+ {
72
+ "lspServers": {
73
+ "typescript": {
74
+ "command": "typescript-language-server",
75
+ "args": ["--stdio"],
76
+ "fileExtensions": {
77
+ ".ts": "typescript",
78
+ ".tsx": "typescriptreact",
79
+ ".js": "javascript",
80
+ ".jsx": "javascriptreact"
81
+ }
82
+ },
83
+ "rust": {
84
+ "command": "rust-analyzer",
85
+ "args": [],
86
+ "fileExtensions": {
87
+ ".rs": "rust"
88
+ }
89
+ },
90
+ "python": {
91
+ "command": "pylsp",
92
+ "args": [],
93
+ "fileExtensions": {
94
+ ".py": "python"
95
+ }
96
+ },
97
+ "tailwind": {
98
+ "command": "tailwindcss-language-server",
99
+ "args": ["--stdio"],
100
+ "fileExtensions": {
101
+ ".css": "css",
102
+ ".html": "html",
103
+ ".tsx": "typescriptreact",
104
+ ".jsx": "javascriptreact",
105
+ ".svelte": "svelte",
106
+ ".vue": "vue"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ > **Note:** install language servers separately — e.g.
114
+ > `npm i -g typescript-language-server typescript`
115
+ > `rustup component add rust-analyzer`
116
+ > `pip install python-lsp-server`
117
+ > `npm i -g @tailwindcss/language-server`
118
+
119
+ The first entry whose `fileExtensions` map contains the file's extension wins.
120
+ Use `--server <cmd>` to bypass discovery entirely.
121
+
122
+ ## Global flags
123
+
124
+ | Flag | Default | Meaning |
125
+ |---|---|---|
126
+ | `--server <cmd>` | — | LSP server launch command (overrides `lsp.json`) |
127
+ | `--root <dir>` | cwd | Project root used for server discovery and path resolution |
128
+ | `--dry-run` | off | Print changes; do not write to disk |
129
+ | `--json` | off | Machine-readable JSON on stdout; diagnostics to stderr |
130
+ | `--wait <ms>` | `15000` | Index wait before sending requests |
131
+ | `--verbose` | off | Progress logging to stderr |
132
+ | `--allow-outside-root` | off | Allow file-path args that resolve outside `--root` |
133
+ | `--no-proxy` | off | Bypass the proxy daemon and spawn the server directly |
134
+
135
+ ### Path resolution
136
+
137
+ Relative paths are resolved against `--root`, not cwd. Any path resolving
138
+ outside `--root` is rejected unless `--allow-outside-root` is set.
139
+
140
+ ## Proxy daemon
141
+
142
+ By default the CLI connects through `@lsproxy/proxy` — a background daemon that holds
143
+ warm LSP server connections. The daemon is started automatically on first use and exits
144
+ after 30 minutes of idle time.
145
+
146
+ ```bash
147
+ # First invocation — daemon spawns, performs the initialize handshake (~1-3s)
148
+ lspeasy textDocument hover src/foo.ts 1:1
149
+
150
+ # Subsequent invocations — reconnects via Unix socket (<100ms)
151
+ lspeasy textDocument hover src/foo.ts 2:5
152
+
153
+ # Bypass the daemon entirely
154
+ lspeasy --no-proxy textDocument hover src/foo.ts 1:1
155
+ ```
156
+
157
+ Socket path: `~/.lsproxy/<hash(root)>.sock`
158
+
159
+ ## Programmatic use
160
+
161
+ ```ts
162
+ import { RefactorSession, applyWorkspaceEdit, planWorkspaceEdit } from '@lsproxy/cli';
163
+ ```
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Apply an LSP {@link WorkspaceEdit} to disk.
3
+ *
4
+ * Handles both representations a server may return:
5
+ * - `changes`: a `{ uri -> TextEdit[] }` map (text-only, unordered → applied as
6
+ * a transactional batch).
7
+ * - `documentChanges`: an ORDERED array that may interleave `TextDocumentEdit`
8
+ * entries with resource operations (`create` / `rename` / `delete`). The array
9
+ * order is significant and is honored literally (sequential application).
10
+ *
11
+ * TextEdits are applied per file by converting each `{line, character}` to an
12
+ * absolute offset and splicing in REVERSE offset order so earlier edits do not
13
+ * invalidate the offsets of later ones.
14
+ */
15
+ export interface LspPosition {
16
+ line: number;
17
+ character: number;
18
+ }
19
+ export interface LspRange {
20
+ start: LspPosition;
21
+ end: LspPosition;
22
+ }
23
+ export interface LspTextEdit {
24
+ range: LspRange;
25
+ newText: string;
26
+ }
27
+ interface TextDocumentEdit {
28
+ textDocument: {
29
+ uri: string;
30
+ version?: number | null;
31
+ };
32
+ edits: LspTextEdit[];
33
+ }
34
+ interface CreateFileOp {
35
+ kind: 'create';
36
+ uri: string;
37
+ options?: {
38
+ overwrite?: boolean;
39
+ ignoreIfExists?: boolean;
40
+ };
41
+ }
42
+ interface RenameFileOp {
43
+ kind: 'rename';
44
+ oldUri: string;
45
+ newUri: string;
46
+ options?: {
47
+ overwrite?: boolean;
48
+ ignoreIfExists?: boolean;
49
+ };
50
+ }
51
+ interface DeleteFileOp {
52
+ kind: 'delete';
53
+ uri: string;
54
+ options?: {
55
+ recursive?: boolean;
56
+ ignoreIfNotExists?: boolean;
57
+ };
58
+ }
59
+ type DocumentChange = TextDocumentEdit | CreateFileOp | RenameFileOp | DeleteFileOp;
60
+ export interface WorkspaceEdit {
61
+ changes?: Record<string, LspTextEdit[]>;
62
+ documentChanges?: DocumentChange[];
63
+ }
64
+ /**
65
+ * Root-boundary inputs threaded into the apply pipeline so SERVER-RETURNED
66
+ * edits are validated against `--root` before anything touches disk.
67
+ */
68
+ export interface BoundaryGuard {
69
+ root: string;
70
+ allowOutsideRoot: boolean;
71
+ }
72
+ /** A single change the apply pipeline performed, for reporting / dry-run output. */
73
+ export interface AppliedChange {
74
+ kind: 'edit' | 'create' | 'rename' | 'delete';
75
+ path: string;
76
+ /** For `edit`, the number of text edits applied. */
77
+ editCount?: number;
78
+ /** For `rename`, the destination path. */
79
+ toPath?: string;
80
+ }
81
+ /**
82
+ * Apply text edits to a string, splicing in reverse offset order so earlier
83
+ * edits do not invalidate the offsets of later ones.
84
+ *
85
+ * `lineStarts` is computed once from the original `text` and reused for both the
86
+ * sort and the splice loop. This is correct because edits apply in reverse
87
+ * offset order: a later splice never shifts the line map of an earlier (smaller)
88
+ * offset, so the original line map stays valid for every remaining edit.
89
+ */
90
+ export declare function applyTextEdits(text: string, edits: LspTextEdit[]): string;
91
+ /**
92
+ * Normalize a {@link WorkspaceEdit} into an ordered list of changes without
93
+ * touching disk. Useful for `--dry-run` and `--json` reporting.
94
+ */
95
+ export declare function planWorkspaceEdit(edit: WorkspaceEdit, guard?: BoundaryGuard): AppliedChange[];
96
+ /**
97
+ * Return a copy of `edit` with ONLY the rename resource op that duplicates the
98
+ * CLI-owned physical move removed — preserving every other resource op (e.g. a
99
+ * server's `create shim.ts`) and its position in the ordered array.
100
+ *
101
+ * Used by `move-file`: the CLI performs the single physical move itself (via
102
+ * `git mv` when possible), so the matching rename a server folds into the
103
+ * `willRenameFiles` result must be dropped to avoid a double-move (which would
104
+ * leave the source missing on a second run). The client advertises support for
105
+ * `create` / `rename` / `delete`, so a server may legitimately emit OTHER
106
+ * resource ops alongside the move; dropping those broke valid edits (a `create
107
+ * shim.ts` stripped out, then the text edit for `shim.ts` failed to read it).
108
+ *
109
+ * Match is on the resolved old/new paths (via `fileURLToPath`), not raw URI
110
+ * strings, so encoding differences don't defeat it.
111
+ */
112
+ export declare function stripResourceOps(edit: WorkspaceEdit, move?: {
113
+ from: string;
114
+ to: string;
115
+ }): WorkspaceEdit;
116
+ /**
117
+ * Apply a {@link WorkspaceEdit} to disk and return what was changed.
118
+ *
119
+ * ### `documentChanges` — sequential, order significant
120
+ * Per the LSP spec the `documentChanges` array order is SIGNIFICANT: each entry
121
+ * (text edit or resource op) is applied in the exact order the server emitted
122
+ * it. This is the only correct interpretation — e.g. `[rename old→new, textEdit
123
+ * on new]` requires the rename to run first so the edit can read `new`. (An
124
+ * earlier three-phase model hoisted all resource ops into separate passes,
125
+ * which silently reordered a server's ops and broke exactly that shape.)
126
+ *
127
+ * Because order is honored literally, a failure partway through `documentChanges`
128
+ * (e.g. a text edit keyed to a not-yet-created path) may leave earlier ops
129
+ * applied — that is inherent to respecting server-specified order.
130
+ *
131
+ * ### `changes` map — unordered, transactional
132
+ * The `changes` map carries no resource ops and no defined ordering, so it is
133
+ * applied as a batch: every target is read and its new content computed in
134
+ * memory BEFORE any write. If a read fails the function throws before any write,
135
+ * so a failed edit never leaves the tree half-applied.
136
+ */
137
+ export declare function applyWorkspaceEdit(edit: WorkspaceEdit, guard?: BoundaryGuard): AppliedChange[];
138
+ export {};
139
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7D;AACD,UAAU,YAAY;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7D;AACD,UAAU,YAAY;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAChE;AAED,KAAK,cAAc,GAAG,gBAAgB,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAEpF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAuCD,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAWzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CAmC7F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,aAAa,EACnB,IAAI,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAClC,aAAa,CAYf;AA+FD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CA2C9F"}
package/dist/apply.js ADDED
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Apply an LSP {@link WorkspaceEdit} to disk.
3
+ *
4
+ * Handles both representations a server may return:
5
+ * - `changes`: a `{ uri -> TextEdit[] }` map (text-only, unordered → applied as
6
+ * a transactional batch).
7
+ * - `documentChanges`: an ORDERED array that may interleave `TextDocumentEdit`
8
+ * entries with resource operations (`create` / `rename` / `delete`). The array
9
+ * order is significant and is honored literally (sequential application).
10
+ *
11
+ * TextEdits are applied per file by converting each `{line, character}` to an
12
+ * absolute offset and splicing in REVERSE offset order so earlier edits do not
13
+ * invalidate the offsets of later ones.
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
16
+ import { dirname } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { assertTargetsInsideRoot } from './io.js';
19
+ /**
20
+ * Collect every absolute filesystem path a {@link WorkspaceEdit} would touch —
21
+ * text-edit targets, `create` uri, `rename` old AND new uri, and `delete` uri —
22
+ * so the root-boundary guard can refuse the whole edit before applying any op.
23
+ */
24
+ function collectEditTargets(edit) {
25
+ const targets = [];
26
+ if (edit.documentChanges) {
27
+ for (const dc of edit.documentChanges) {
28
+ if ('kind' in dc) {
29
+ if (dc.kind === 'create')
30
+ targets.push(fileURLToPath(dc.uri));
31
+ else if (dc.kind === 'rename') {
32
+ targets.push(fileURLToPath(dc.oldUri), fileURLToPath(dc.newUri));
33
+ }
34
+ else if (dc.kind === 'delete')
35
+ targets.push(fileURLToPath(dc.uri));
36
+ }
37
+ else {
38
+ targets.push(fileURLToPath(dc.textDocument.uri));
39
+ }
40
+ }
41
+ }
42
+ if (edit.changes) {
43
+ for (const uri of Object.keys(edit.changes))
44
+ targets.push(fileURLToPath(uri));
45
+ }
46
+ return targets;
47
+ }
48
+ /**
49
+ * Pre-flight a server-returned {@link WorkspaceEdit} against the root boundary,
50
+ * refusing (throw) if any planned target escapes `--root`. Runs BEFORE any
51
+ * mutation so a single out-of-root op cannot slip through after earlier ops have
52
+ * already hit disk. Shared by {@link planWorkspaceEdit} and
53
+ * {@link applyWorkspaceEdit} so dry-run predicts the same refusal.
54
+ */
55
+ function guardEditTargets(edit, guard) {
56
+ if (!guard)
57
+ return;
58
+ assertTargetsInsideRoot(collectEditTargets(edit), guard);
59
+ }
60
+ /**
61
+ * Compute the absolute offset of each line start (the index just past every
62
+ * `\n`, plus `0` for line 0). Built ONCE per file so offset lookups are O(1);
63
+ * rebuilding it per call made {@link applyTextEdits} O(N·E) on the file length.
64
+ *
65
+ * Column values are LSP-spec UTF-16 code units, which is exactly how JavaScript
66
+ * strings are indexed, so `lineStart + character` is correct even across
67
+ * surrogate pairs — no extra code-point translation is needed.
68
+ */
69
+ function computeLineStarts(text) {
70
+ const lineStarts = [0];
71
+ for (let i = 0; i < text.length; i++) {
72
+ if (text[i] === '\n')
73
+ lineStarts.push(i + 1);
74
+ }
75
+ return lineStarts;
76
+ }
77
+ /** Convert a `{line, character}` position to an absolute string offset. */
78
+ function positionToOffset(lineStarts, textLength, pos) {
79
+ const lineStart = lineStarts[pos.line] ?? textLength;
80
+ return lineStart + pos.character;
81
+ }
82
+ /**
83
+ * Apply text edits to a string, splicing in reverse offset order so earlier
84
+ * edits do not invalidate the offsets of later ones.
85
+ *
86
+ * `lineStarts` is computed once from the original `text` and reused for both the
87
+ * sort and the splice loop. This is correct because edits apply in reverse
88
+ * offset order: a later splice never shifts the line map of an earlier (smaller)
89
+ * offset, so the original line map stays valid for every remaining edit.
90
+ */
91
+ export function applyTextEdits(text, edits) {
92
+ const lineStarts = computeLineStarts(text);
93
+ const offset = (pos) => positionToOffset(lineStarts, text.length, pos);
94
+ const sorted = [...edits].sort((a, b) => offset(b.range.start) - offset(a.range.start));
95
+ let out = text;
96
+ for (const e of sorted) {
97
+ const start = offset(e.range.start);
98
+ const end = offset(e.range.end);
99
+ out = out.slice(0, start) + e.newText + out.slice(end);
100
+ }
101
+ return out;
102
+ }
103
+ /**
104
+ * Normalize a {@link WorkspaceEdit} into an ordered list of changes without
105
+ * touching disk. Useful for `--dry-run` and `--json` reporting.
106
+ */
107
+ export function planWorkspaceEdit(edit, guard) {
108
+ guardEditTargets(edit, guard);
109
+ const changes = [];
110
+ if (edit.documentChanges) {
111
+ for (const dc of edit.documentChanges) {
112
+ if ('kind' in dc) {
113
+ if (dc.kind === 'create')
114
+ changes.push({ kind: 'create', path: fileURLToPath(dc.uri) });
115
+ else if (dc.kind === 'rename')
116
+ changes.push({
117
+ kind: 'rename',
118
+ path: fileURLToPath(dc.oldUri),
119
+ toPath: fileURLToPath(dc.newUri)
120
+ });
121
+ else if (dc.kind === 'delete')
122
+ changes.push({ kind: 'delete', path: fileURLToPath(dc.uri) });
123
+ }
124
+ else {
125
+ changes.push({
126
+ kind: 'edit',
127
+ path: fileURLToPath(dc.textDocument.uri),
128
+ editCount: dc.edits.length
129
+ });
130
+ }
131
+ }
132
+ }
133
+ else if (edit.changes) {
134
+ for (const uri of Object.keys(edit.changes).sort()) {
135
+ changes.push({
136
+ kind: 'edit',
137
+ path: fileURLToPath(uri),
138
+ editCount: edit.changes[uri].length
139
+ });
140
+ }
141
+ }
142
+ return changes;
143
+ }
144
+ /**
145
+ * Return a copy of `edit` with ONLY the rename resource op that duplicates the
146
+ * CLI-owned physical move removed — preserving every other resource op (e.g. a
147
+ * server's `create shim.ts`) and its position in the ordered array.
148
+ *
149
+ * Used by `move-file`: the CLI performs the single physical move itself (via
150
+ * `git mv` when possible), so the matching rename a server folds into the
151
+ * `willRenameFiles` result must be dropped to avoid a double-move (which would
152
+ * leave the source missing on a second run). The client advertises support for
153
+ * `create` / `rename` / `delete`, so a server may legitimately emit OTHER
154
+ * resource ops alongside the move; dropping those broke valid edits (a `create
155
+ * shim.ts` stripped out, then the text edit for `shim.ts` failed to read it).
156
+ *
157
+ * Match is on the resolved old/new paths (via `fileURLToPath`), not raw URI
158
+ * strings, so encoding differences don't defeat it.
159
+ */
160
+ export function stripResourceOps(edit, move) {
161
+ if (!edit.documentChanges)
162
+ return edit;
163
+ const filtered = edit.documentChanges.filter((dc) => {
164
+ if (!('kind' in dc))
165
+ return true; // keep all text edits
166
+ if (!move)
167
+ return false; // legacy: no move identity → strip every resource op
168
+ if (dc.kind !== 'rename')
169
+ return true; // preserve unrelated create/delete ops
170
+ const old = fileURLToPath(dc.oldUri);
171
+ const neu = fileURLToPath(dc.newUri);
172
+ // Drop only the rename that duplicates our physical move.
173
+ return !(old === move.from && neu === move.to);
174
+ });
175
+ return { documentChanges: filtered };
176
+ }
177
+ /** Read a text-edit target, throwing a clear error if it cannot be read. */
178
+ function readForEdit(path) {
179
+ try {
180
+ return readFileSync(path, 'utf8');
181
+ }
182
+ catch (err) {
183
+ throw new Error(`cannot read ${path} for text edits: ${err instanceof Error ? err.message : String(err)}`);
184
+ }
185
+ }
186
+ /**
187
+ * Perform a single `create` resource op, honoring LSP `overwrite` /
188
+ * `ignoreIfExists` precedence. Returns the {@link AppliedChange} when the file
189
+ * was created, or `undefined` when the op was skipped per `ignoreIfExists`.
190
+ *
191
+ * Per the LSP spec, `overwrite` takes precedence over `ignoreIfExists`. With
192
+ * neither flag set, a `create` op on an EXISTING path is an error — silently
193
+ * skipping it (the old behaviour) let a later text edit run against the
194
+ * pre-existing file the server expected to have freshly created.
195
+ */
196
+ function applyCreate(op) {
197
+ const p = fileURLToPath(op.uri);
198
+ mkdirSync(dirname(p), { recursive: true });
199
+ if (op.options?.overwrite) {
200
+ // overwrite wins over ignoreIfExists: truncate (or create) unconditionally.
201
+ writeFileSync(p, '');
202
+ return { kind: 'create', path: p };
203
+ }
204
+ // Atomic exclusive create — no check-then-act TOCTOU window. The kernel
205
+ // rejects the open with EEXIST if the path appeared between calls, which we
206
+ // then map to LSP `ignoreIfExists` (skip) vs error.
207
+ try {
208
+ writeFileSync(p, '', { flag: 'wx' });
209
+ return { kind: 'create', path: p };
210
+ }
211
+ catch (err) {
212
+ if (err.code === 'EEXIST') {
213
+ if (op.options?.ignoreIfExists)
214
+ return undefined;
215
+ throw new Error(`cannot create ${p}: already exists (no overwrite/ignoreIfExists)`);
216
+ }
217
+ throw err;
218
+ }
219
+ }
220
+ /**
221
+ * Perform a single `rename` resource op, honoring LSP `overwrite` /
222
+ * `ignoreIfExists` precedence. A rename whose destination already exists is an
223
+ * error unless `overwrite` is set (clobber) or `ignoreIfExists` is set (skip).
224
+ * The old code called `renameSync` unconditionally, which clobbers on POSIX.
225
+ */
226
+ function applyRename(op) {
227
+ const from = fileURLToPath(op.oldUri);
228
+ const to = fileURLToPath(op.newUri);
229
+ mkdirSync(dirname(to), { recursive: true });
230
+ if (op.options?.overwrite) {
231
+ // overwrite requested: renameSync replaces the destination atomically.
232
+ renameSync(from, to);
233
+ return { kind: 'rename', path: from, toPath: to };
234
+ }
235
+ // No-overwrite rename. Node exposes no atomic no-clobber rename (there is no
236
+ // binding for renameat2/RENAME_NOREPLACE), so a residual TOCTOU window between
237
+ // this existence check and renameSync is unavoidable in pure Node. We keep the
238
+ // window as small as possible (check immediately precedes the syscall) and
239
+ // accept it: this is a local CLI refactor tool, not a privilege boundary.
240
+ if (existsSync(to)) {
241
+ if (op.options?.ignoreIfExists)
242
+ return undefined;
243
+ throw new Error(`cannot rename to ${to}: already exists (no overwrite/ignoreIfExists)`);
244
+ }
245
+ renameSync(from, to);
246
+ return { kind: 'rename', path: from, toPath: to };
247
+ }
248
+ /** Perform a single `delete` resource op, honoring `ignoreIfNotExists`. */
249
+ function applyDelete(op) {
250
+ const p = fileURLToPath(op.uri);
251
+ if (!existsSync(p) && op.options?.ignoreIfNotExists)
252
+ return undefined;
253
+ rmSync(p, { recursive: op.options?.recursive ?? false });
254
+ return { kind: 'delete', path: p };
255
+ }
256
+ /** Apply a single resolved text write to disk. */
257
+ function applyTextWrite(w) {
258
+ const after = applyTextEdits(readForEdit(w.path), w.edits);
259
+ writeFileSync(w.path, after);
260
+ return { kind: 'edit', path: w.path, editCount: w.edits.length };
261
+ }
262
+ /**
263
+ * Apply a {@link WorkspaceEdit} to disk and return what was changed.
264
+ *
265
+ * ### `documentChanges` — sequential, order significant
266
+ * Per the LSP spec the `documentChanges` array order is SIGNIFICANT: each entry
267
+ * (text edit or resource op) is applied in the exact order the server emitted
268
+ * it. This is the only correct interpretation — e.g. `[rename old→new, textEdit
269
+ * on new]` requires the rename to run first so the edit can read `new`. (An
270
+ * earlier three-phase model hoisted all resource ops into separate passes,
271
+ * which silently reordered a server's ops and broke exactly that shape.)
272
+ *
273
+ * Because order is honored literally, a failure partway through `documentChanges`
274
+ * (e.g. a text edit keyed to a not-yet-created path) may leave earlier ops
275
+ * applied — that is inherent to respecting server-specified order.
276
+ *
277
+ * ### `changes` map — unordered, transactional
278
+ * The `changes` map carries no resource ops and no defined ordering, so it is
279
+ * applied as a batch: every target is read and its new content computed in
280
+ * memory BEFORE any write. If a read fails the function throws before any write,
281
+ * so a failed edit never leaves the tree half-applied.
282
+ */
283
+ export function applyWorkspaceEdit(edit, guard) {
284
+ // Pre-flight EVERY target against the root boundary before mutating anything,
285
+ // so an out-of-root op late in documentChanges can't run after earlier ops
286
+ // already hit disk.
287
+ guardEditTargets(edit, guard);
288
+ const applied = [];
289
+ if (edit.documentChanges) {
290
+ // Sequential: honor the server-specified order literally.
291
+ for (const dc of edit.documentChanges) {
292
+ if ('kind' in dc) {
293
+ const change = dc.kind === 'create'
294
+ ? applyCreate(dc)
295
+ : dc.kind === 'rename'
296
+ ? applyRename(dc)
297
+ : applyDelete(dc);
298
+ if (change)
299
+ applied.push(change);
300
+ }
301
+ else {
302
+ applied.push(applyTextWrite({ path: fileURLToPath(dc.textDocument.uri), edits: dc.edits }));
303
+ }
304
+ }
305
+ return applied;
306
+ }
307
+ if (edit.changes) {
308
+ // Unordered text-only map: read every target and compute new content BEFORE
309
+ // writing anything, so a failed read aborts without a half-applied tree.
310
+ const writes = Object.keys(edit.changes)
311
+ .sort()
312
+ .map((uri) => ({ path: fileURLToPath(uri), edits: edit.changes[uri] }));
313
+ const pending = writes.map((w) => ({
314
+ path: w.path,
315
+ after: applyTextEdits(readForEdit(w.path), w.edits),
316
+ count: w.edits.length
317
+ }));
318
+ for (const w of pending) {
319
+ writeFileSync(w.path, w.after);
320
+ applied.push({ kind: 'edit', path: w.path, editCount: w.count });
321
+ }
322
+ }
323
+ return applied;
324
+ }
325
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"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"}