@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
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
|
package/dist/apply.d.ts
ADDED
|
@@ -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":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAuDlD;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAmB;IAC7C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;gBACjB,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;qBACzD,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC9B,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnE,CAAC;qBAAM,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACvE,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,IAAmB,EAAE,KAAqB;IAClE,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,uBAAuB,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAYD;;;;;;;;GAQG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,2EAA2E;AAC3E,SAAS,gBAAgB,CAAC,UAAoB,EAAE,UAAkB,EAAE,GAAgB;IAClF,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC;IACrD,OAAO,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;AACnC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAoB;IAC/D,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,GAAgB,EAAE,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpF,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACxF,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAmB,EAAE,KAAqB;IAC1E,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;gBACjB,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;qBACnF,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAC3B,OAAO,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC;wBAC9B,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC;qBACjC,CAAC,CAAC;qBACA,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAC3B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC;oBACxC,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM;iBAC3B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC;gBACxB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC,MAAM;aACrC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAmB,EACnB,IAAmC;IAEnC,IAAI,CAAC,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QAClD,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,sBAAsB;QACxD,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC,CAAC,qDAAqD;QAC9E,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,CAAC,uCAAuC;QAC9E,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACrC,0DAA0D;QAC1D,OAAO,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAQD,4EAA4E;AAC5E,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC1F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,WAAW,CAAC,EAAgB;IACnC,MAAM,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAChC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;QAC1B,4EAA4E;QAC5E,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,wEAAwE;IACxE,4EAA4E;IAC5E,oDAAoD;IACpD,IAAI,CAAC;QACH,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc;gBAAE,OAAO,SAAS,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,gDAAgD,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,EAAgB;IACnC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACpC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;QAC1B,uEAAuE;QACvE,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACpD,CAAC;IACD,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,2EAA2E;IAC3E,0EAA0E;IAC1E,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;QACnB,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc;YAAE,OAAO,SAAS,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,oBAAoB,EAAE,gDAAgD,CAAC,CAAC;IAC1F,CAAC;IACD,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACpD,CAAC;AAED,2EAA2E;AAC3E,SAAS,WAAW,CAAC,EAAgB;IACnC,MAAM,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,iBAAiB;QAAE,OAAO,SAAS,CAAC;IACtE,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,EAAE,SAAS,IAAI,KAAK,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACrC,CAAC;AAED,kDAAkD;AAClD,SAAS,cAAc,CAAC,CAAY;IAClC,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAC3D,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAmB,EAAE,KAAqB;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,oBAAoB;IACpB,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,0DAA0D;QAC1D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;gBACjB,MAAM,MAAM,GACV,EAAE,CAAC,IAAI,KAAK,QAAQ;oBAClB,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;oBACjB,CAAC,CAAC,EAAE,CAAC,IAAI,KAAK,QAAQ;wBACpB,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;wBACjB,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;gBACxB,IAAI,MAAM;oBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,4EAA4E;QAC5E,yEAAyE;QACzE,MAAM,MAAM,GAAgB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;aAClD,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAQ,CAAC,GAAG,CAAE,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;YACnD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM;SACtB,CAAC,CAAC,CAAC;QACJ,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|