@sagmans/dsh-tui 0.3.0 → 0.5.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 +1 -1
- package/README.md +407 -53
- package/cordis.patch.yml +14 -0
- package/lib/agent/present.js +1 -1
- package/lib/agent/present.js.map +1 -1
- package/lib/agent/projections.d.ts +22 -6
- package/lib/agent/projections.d.ts.map +1 -1
- package/lib/agent/projections.js +21 -10
- package/lib/agent/projections.js.map +1 -1
- package/lib/agent/prompt-history.d.ts +89 -0
- package/lib/agent/prompt-history.d.ts.map +1 -0
- package/lib/agent/prompt-history.js +382 -0
- package/lib/agent/prompt-history.js.map +1 -0
- package/lib/agent/status.d.ts +6 -0
- package/lib/agent/status.d.ts.map +1 -1
- package/lib/agent/status.js +1 -0
- package/lib/agent/status.js.map +1 -1
- package/lib/cards.d.ts +83 -13
- package/lib/cards.d.ts.map +1 -1
- package/lib/cards.js +96 -28
- package/lib/cards.js.map +1 -1
- package/lib/export.d.ts +3 -3
- package/lib/export.d.ts.map +1 -1
- package/lib/export.js +23 -13
- package/lib/export.js.map +1 -1
- package/lib/fold-cursor.d.ts +12 -5
- package/lib/fold-cursor.d.ts.map +1 -1
- package/lib/fold-cursor.js +16 -5
- package/lib/fold-cursor.js.map +1 -1
- package/lib/gates.d.ts.map +1 -1
- package/lib/gates.js +42 -9
- package/lib/gates.js.map +1 -1
- package/lib/herdr/client.d.ts +57 -0
- package/lib/herdr/client.d.ts.map +1 -0
- package/lib/herdr/client.js +218 -0
- package/lib/herdr/client.js.map +1 -0
- package/lib/herdr/constants.d.ts +93 -0
- package/lib/herdr/constants.d.ts.map +1 -0
- package/lib/herdr/constants.js +79 -0
- package/lib/herdr/constants.js.map +1 -0
- package/lib/herdr/reporter.d.ts +60 -0
- package/lib/herdr/reporter.d.ts.map +1 -0
- package/lib/herdr/reporter.js +217 -0
- package/lib/herdr/reporter.js.map +1 -0
- package/lib/herdr/state.d.ts +57 -0
- package/lib/herdr/state.d.ts.map +1 -0
- package/lib/herdr/state.js +62 -0
- package/lib/herdr/state.js.map +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +700 -81
- package/lib/index.js.map +1 -1
- package/lib/injection.d.ts +9 -0
- package/lib/injection.d.ts.map +1 -0
- package/lib/injection.js +128 -0
- package/lib/injection.js.map +1 -0
- package/lib/input/actions.d.ts +19 -2
- package/lib/input/actions.d.ts.map +1 -1
- package/lib/input/actions.js +60 -11
- package/lib/input/actions.js.map +1 -1
- package/lib/input/cancel.d.ts +55 -0
- package/lib/input/cancel.d.ts.map +1 -0
- package/lib/input/cancel.js +47 -0
- package/lib/input/cancel.js.map +1 -0
- package/lib/input/completion.d.ts +5 -2
- package/lib/input/completion.d.ts.map +1 -1
- package/lib/input/completion.js +79 -4
- package/lib/input/completion.js.map +1 -1
- package/lib/input/file-search.d.ts +105 -0
- package/lib/input/file-search.d.ts.map +1 -0
- package/lib/input/file-search.js +665 -0
- package/lib/input/file-search.js.map +1 -0
- package/lib/input/fuzzy.d.ts +42 -0
- package/lib/input/fuzzy.d.ts.map +1 -0
- package/lib/input/fuzzy.js +289 -0
- package/lib/input/fuzzy.js.map +1 -0
- package/lib/input/ghost.d.ts +46 -0
- package/lib/input/ghost.d.ts.map +1 -0
- package/lib/input/ghost.js +58 -0
- package/lib/input/ghost.js.map +1 -0
- package/lib/input/keymap.d.ts +3 -1
- package/lib/input/keymap.d.ts.map +1 -1
- package/lib/input/keymap.js +16 -3
- package/lib/input/keymap.js.map +1 -1
- package/lib/input/match.d.ts +12 -4
- package/lib/input/match.d.ts.map +1 -1
- package/lib/input/match.js +50 -51
- package/lib/input/match.js.map +1 -1
- package/lib/input/submission.d.ts +34 -1
- package/lib/input/submission.d.ts.map +1 -1
- package/lib/input/submission.js +33 -5
- package/lib/input/submission.js.map +1 -1
- package/lib/keys-command.d.ts +19 -3
- package/lib/keys-command.d.ts.map +1 -1
- package/lib/keys-command.js +34 -31
- package/lib/keys-command.js.map +1 -1
- package/lib/settings-notice.d.ts +5 -0
- package/lib/settings-notice.d.ts.map +1 -1
- package/lib/settings-notice.js +9 -7
- package/lib/settings-notice.js.map +1 -1
- package/lib/stash/lock.d.ts +72 -0
- package/lib/stash/lock.d.ts.map +1 -0
- package/lib/stash/lock.js +374 -0
- package/lib/stash/lock.js.map +1 -0
- package/lib/stash/paths.d.ts +22 -0
- package/lib/stash/paths.d.ts.map +1 -0
- package/lib/stash/paths.js +84 -0
- package/lib/stash/paths.js.map +1 -0
- package/lib/stash/private-fs.d.ts +61 -0
- package/lib/stash/private-fs.d.ts.map +1 -0
- package/lib/stash/private-fs.js +379 -0
- package/lib/stash/private-fs.js.map +1 -0
- package/lib/stash/schema.d.ts +56 -0
- package/lib/stash/schema.d.ts.map +1 -0
- package/lib/stash/schema.js +108 -0
- package/lib/stash/schema.js.map +1 -0
- package/lib/stash/store.d.ts +92 -0
- package/lib/stash/store.d.ts.map +1 -0
- package/lib/stash/store.js +262 -0
- package/lib/stash/store.js.map +1 -0
- package/lib/stash.d.ts +122 -0
- package/lib/stash.d.ts.map +1 -0
- package/lib/stash.js +353 -0
- package/lib/stash.js.map +1 -0
- package/lib/terminal/external-editor.d.ts +93 -0
- package/lib/terminal/external-editor.d.ts.map +1 -0
- package/lib/terminal/external-editor.js +263 -0
- package/lib/terminal/external-editor.js.map +1 -0
- package/lib/terminal/host-writes.d.ts +39 -0
- package/lib/terminal/host-writes.d.ts.map +1 -0
- package/lib/terminal/host-writes.js +136 -0
- package/lib/terminal/host-writes.js.map +1 -0
- package/lib/terminal/signals.d.ts +25 -0
- package/lib/terminal/signals.d.ts.map +1 -0
- package/lib/terminal/signals.js +39 -0
- package/lib/terminal/signals.js.map +1 -0
- package/lib/terminal/warning-screen.d.ts +19 -1
- package/lib/terminal/warning-screen.d.ts.map +1 -1
- package/lib/terminal/warning-screen.js +37 -1
- package/lib/terminal/warning-screen.js.map +1 -1
- package/lib/terminal-text.d.ts +72 -0
- package/lib/terminal-text.d.ts.map +1 -0
- package/lib/terminal-text.js +603 -0
- package/lib/terminal-text.js.map +1 -0
- package/lib/text.d.ts +45 -6
- package/lib/text.d.ts.map +1 -1
- package/lib/text.js +106 -46
- package/lib/text.js.map +1 -1
- package/lib/theme-command.d.ts +7 -4
- package/lib/theme-command.d.ts.map +1 -1
- package/lib/theme-command.js +32 -12
- package/lib/theme-command.js.map +1 -1
- package/lib/theme-files.d.ts +110 -0
- package/lib/theme-files.d.ts.map +1 -0
- package/lib/theme-files.js +352 -0
- package/lib/theme-files.js.map +1 -0
- package/lib/theme-schema.d.ts +121 -0
- package/lib/theme-schema.d.ts.map +1 -0
- package/lib/theme-schema.js +87 -0
- package/lib/theme-schema.js.map +1 -0
- package/lib/theme-settings.d.ts +72 -10
- package/lib/theme-settings.d.ts.map +1 -1
- package/lib/theme-settings.js +162 -45
- package/lib/theme-settings.js.map +1 -1
- package/lib/theme-tokens.d.ts +39 -7
- package/lib/theme-tokens.d.ts.map +1 -1
- package/lib/theme-tokens.js +104 -13
- package/lib/theme-tokens.js.map +1 -1
- package/lib/theme.d.ts +24 -2
- package/lib/theme.d.ts.map +1 -1
- package/lib/theme.js +49 -6
- package/lib/theme.js.map +1 -1
- package/lib/todo-guard.d.ts +116 -0
- package/lib/todo-guard.d.ts.map +1 -0
- package/lib/todo-guard.js +259 -0
- package/lib/todo-guard.js.map +1 -0
- package/lib/tool-display.d.ts +57 -0
- package/lib/tool-display.d.ts.map +1 -0
- package/lib/tool-display.js +51 -0
- package/lib/tool-display.js.map +1 -0
- package/lib/transcript.d.ts +28 -0
- package/lib/transcript.d.ts.map +1 -1
- package/lib/transcript.js +126 -37
- package/lib/transcript.js.map +1 -1
- package/lib/ui/diff.d.ts +49 -0
- package/lib/ui/diff.d.ts.map +1 -0
- package/lib/ui/diff.js +208 -0
- package/lib/ui/diff.js.map +1 -0
- package/lib/ui/dock.d.ts.map +1 -1
- package/lib/ui/dock.js +9 -6
- package/lib/ui/dock.js.map +1 -1
- package/lib/ui/editor.d.ts +41 -1
- package/lib/ui/editor.d.ts.map +1 -1
- package/lib/ui/editor.js +100 -4
- package/lib/ui/editor.js.map +1 -1
- package/lib/ui/frame.d.ts +16 -5
- package/lib/ui/frame.d.ts.map +1 -1
- package/lib/ui/frame.js +32 -13
- package/lib/ui/frame.js.map +1 -1
- package/lib/ui/history-picker.d.ts +15 -0
- package/lib/ui/history-picker.d.ts.map +1 -0
- package/lib/ui/history-picker.js +26 -0
- package/lib/ui/history-picker.js.map +1 -0
- package/lib/ui/keymap-picker.d.ts +16 -0
- package/lib/ui/keymap-picker.d.ts.map +1 -0
- package/lib/ui/keymap-picker.js +41 -0
- package/lib/ui/keymap-picker.js.map +1 -0
- package/lib/ui/layout.d.ts +33 -0
- package/lib/ui/layout.d.ts.map +1 -0
- package/lib/ui/layout.js +33 -0
- package/lib/ui/layout.js.map +1 -0
- package/lib/ui/markdown.d.ts +25 -3
- package/lib/ui/markdown.d.ts.map +1 -1
- package/lib/ui/markdown.js +12 -8
- package/lib/ui/markdown.js.map +1 -1
- package/lib/ui/picker-card.d.ts +42 -0
- package/lib/ui/picker-card.d.ts.map +1 -0
- package/lib/ui/picker-card.js +148 -0
- package/lib/ui/picker-card.js.map +1 -0
- package/lib/ui/picker.d.ts +50 -2
- package/lib/ui/picker.d.ts.map +1 -1
- package/lib/ui/picker.js +66 -7
- package/lib/ui/picker.js.map +1 -1
- package/lib/ui/prompt.d.ts +12 -2
- package/lib/ui/prompt.d.ts.map +1 -1
- package/lib/ui/prompt.js +25 -2
- package/lib/ui/prompt.js.map +1 -1
- package/lib/ui/rows.d.ts +8 -6
- package/lib/ui/rows.d.ts.map +1 -1
- package/lib/ui/rows.js +10 -18
- package/lib/ui/rows.js.map +1 -1
- package/lib/ui/stash-picker.d.ts +43 -0
- package/lib/ui/stash-picker.d.ts.map +1 -0
- package/lib/ui/stash-picker.js +78 -0
- package/lib/ui/stash-picker.js.map +1 -0
- package/lib/ui/status.d.ts +2 -0
- package/lib/ui/status.d.ts.map +1 -1
- package/lib/ui/status.js +25 -16
- package/lib/ui/status.js.map +1 -1
- package/lib/ui/theme-picker.d.ts +18 -0
- package/lib/ui/theme-picker.d.ts.map +1 -0
- package/lib/ui/theme-picker.js +68 -0
- package/lib/ui/theme-picker.js.map +1 -0
- package/lib/ui/view.d.ts +146 -22
- package/lib/ui/view.d.ts.map +1 -1
- package/lib/ui/view.js +461 -154
- package/lib/ui/view.js.map +1 -1
- package/lib/work.d.ts +3 -1
- package/lib/work.d.ts.map +1 -1
- package/lib/work.js +9 -1
- package/lib/work.js.map +1 -1
- package/package.json +7 -1
- package/themes/deepseek-blue.yaml +183 -0
- package/themes/violet-orbit.yaml +178 -0
package/lib/stash.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// User-facing prompt-stash operations, independent of the terminal surface.
|
|
2
|
+
//
|
|
3
|
+
// Every command funnels through one serial queue, so a chord and a typed command
|
|
4
|
+
// can never interleave a read-modify-write on the same bank. The surface is
|
|
5
|
+
// reached through a small host seam, which keeps these operations testable
|
|
6
|
+
// without a terminal and keeps the editor's own text rules — an expanded paste
|
|
7
|
+
// and an empty bar — in exactly one place.
|
|
8
|
+
import { FileTooLargeError } from "./stash/private-fs.js";
|
|
9
|
+
import { StashCommittedError } from "./stash/lock.js";
|
|
10
|
+
import { resolveStashPaths, stashBaseDir } from "./stash/paths.js";
|
|
11
|
+
import { loadStashStore, UnsupportedStashSchemaError, } from "./stash/store.js";
|
|
12
|
+
const EDITOR_BLOCKED_MESSAGE = 'clear or stash the current draft before applying or popping';
|
|
13
|
+
const EDITOR_BUSY_MESSAGE = 'the prompt bar is answering a question; finish or cancel it first';
|
|
14
|
+
const NOTHING_TO_STASH_MESSAGE = 'nothing to stash';
|
|
15
|
+
const NO_DRAFTS_MESSAGE = 'no stashed drafts';
|
|
16
|
+
const CORRUPT_RECOVERY_MESSAGE = 'corrupt stash data was quarantined to';
|
|
17
|
+
const CORRUPT_RECOVERY_UNSYNCED_MESSAGE = ' (its directory could not be synced, so the copy may not survive a crash)';
|
|
18
|
+
const SESSION_CHANGED_MESSAGE = 'the session changed; the stash stayed with the session it belonged to';
|
|
19
|
+
const stashedMessage = (index) => `Stashed [${index}]`;
|
|
20
|
+
const appliedMessage = (index) => `Applied [${index}]`;
|
|
21
|
+
const poppedMessage = (index) => `Popped [${index}]`;
|
|
22
|
+
const droppedMessage = (index) => `Dropped [${index}]`;
|
|
23
|
+
const clearedMessage = (count) => `Cleared ${count} draft${count === 1 ? '' : 's'}`;
|
|
24
|
+
const selectorMissingMessage = (selector) => `no stash matching "${selector}"`;
|
|
25
|
+
const committedMessage = (error, done) => error.releaseFailure === undefined
|
|
26
|
+
? `${done}, but the ${error.failure.phase} step failed`
|
|
27
|
+
: `${done}, but the ${error.failure.phase} and ${error.releaseFailure.phase} steps failed`;
|
|
28
|
+
const removalFailedMessage = (error, done) => `${done}, but removing the entry failed: ${error instanceof Error ? error.message : 'unknown error'}`;
|
|
29
|
+
function describeFailure(error) {
|
|
30
|
+
const detail = error instanceof Error ? error.message : 'unknown error';
|
|
31
|
+
return error instanceof UnsupportedStashSchemaError || error instanceof FileTooLargeError
|
|
32
|
+
? `stash unavailable: ${detail}`
|
|
33
|
+
: `stash failed: ${detail}`;
|
|
34
|
+
}
|
|
35
|
+
export class PromptStash {
|
|
36
|
+
host;
|
|
37
|
+
options;
|
|
38
|
+
baseDir;
|
|
39
|
+
now;
|
|
40
|
+
write;
|
|
41
|
+
store;
|
|
42
|
+
/** The session `store` was read for, so a switch can be told from a re-read. */
|
|
43
|
+
scope;
|
|
44
|
+
count = 0;
|
|
45
|
+
/** The queue a second command waits behind, so writes never interleave. */
|
|
46
|
+
tail = Promise.resolve();
|
|
47
|
+
constructor(host, options) {
|
|
48
|
+
this.host = host;
|
|
49
|
+
this.options = options;
|
|
50
|
+
this.baseDir = options.baseDir ?? stashBaseDir();
|
|
51
|
+
this.now = options.now;
|
|
52
|
+
this.write = options.write;
|
|
53
|
+
}
|
|
54
|
+
/** How many drafts this session holds, as last read. */
|
|
55
|
+
get entryCount() {
|
|
56
|
+
return this.count;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read the bank of the session in force, so the status count is real before a
|
|
60
|
+
* command and after the surface moves to another session. A read creates
|
|
61
|
+
* nothing on disk, so a session that never stashes leaves no file behind.
|
|
62
|
+
*/
|
|
63
|
+
open() {
|
|
64
|
+
return this.enqueue(async () => {
|
|
65
|
+
try {
|
|
66
|
+
await this.ensure(this.options.sessionId());
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
this.host.notice(describeFailure(error));
|
|
70
|
+
}
|
|
71
|
+
this.sync();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Persist the draft the reader is looking at, or the one named on the command
|
|
76
|
+
* line.
|
|
77
|
+
*
|
|
78
|
+
* An argument is written into the editor before the write is attempted —
|
|
79
|
+
* before the bank is even opened — because the command line it came from is
|
|
80
|
+
* already gone: a refused write has to leave the draft where the reader can
|
|
81
|
+
* retry it instead of losing the only copy. A command that names nothing — the
|
|
82
|
+
* chord, or a bare `/stash` — parks whatever the bar is holding.
|
|
83
|
+
*/
|
|
84
|
+
stashEditor(typed) {
|
|
85
|
+
const sessionId = this.options.sessionId();
|
|
86
|
+
const named = typed !== undefined && typed.trim() !== '';
|
|
87
|
+
// The draft goes back into the bar before anything can fail, including
|
|
88
|
+
// opening the bank: the command line it was typed on is already gone, so
|
|
89
|
+
// this is the only copy until a write lands.
|
|
90
|
+
const available = this.host.editorIsAvailable();
|
|
91
|
+
if (named && available)
|
|
92
|
+
this.host.setEditorText(typed);
|
|
93
|
+
// The bar is read now, not when the queue reaches this command: a session
|
|
94
|
+
// switch in between would otherwise park the next session's draft in this
|
|
95
|
+
// bank and clear it from the bar the reader is typing in.
|
|
96
|
+
const captured = available ? (named ? typed : this.host.getEditorText()) : undefined;
|
|
97
|
+
return this.run(sessionId, async (store) => {
|
|
98
|
+
if (captured === undefined) {
|
|
99
|
+
this.host.notice(EDITOR_BUSY_MESSAGE);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const text = captured;
|
|
103
|
+
if (text.trim() === '') {
|
|
104
|
+
this.host.notice(NOTHING_TO_STASH_MESSAGE);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
let resolved;
|
|
108
|
+
let warning;
|
|
109
|
+
try {
|
|
110
|
+
resolved = await store.add({ text });
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (!(error instanceof StashCommittedError))
|
|
114
|
+
throw error;
|
|
115
|
+
// The draft is on disk; only the step after the write failed. Reporting
|
|
116
|
+
// a failure here would invite a retry that stashes the same draft twice.
|
|
117
|
+
resolved = error.result;
|
|
118
|
+
warning = committedMessage(error, stashedMessage(resolved.index));
|
|
119
|
+
}
|
|
120
|
+
// Only clear what was actually persisted, and only while the bar is still
|
|
121
|
+
// the reader's and still this session's: a question can borrow the bar
|
|
122
|
+
// during the write, and a session switch moves the whole surface on.
|
|
123
|
+
const current = this.isCurrentSession(sessionId);
|
|
124
|
+
if (current && this.host.editorIsAvailable() && this.host.getEditorText() === text)
|
|
125
|
+
this.host.setEditorText('');
|
|
126
|
+
this.host.notice(current ? (warning ?? stashedMessage(resolved.index)) : SESSION_CHANGED_MESSAGE);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** Put a draft in an empty editor without removing it. */
|
|
130
|
+
apply(selector) {
|
|
131
|
+
const sessionId = this.options.sessionId();
|
|
132
|
+
return this.run(sessionId, async (store) => {
|
|
133
|
+
await store.refresh();
|
|
134
|
+
if (this.abandonIfMoved(sessionId))
|
|
135
|
+
return;
|
|
136
|
+
if (!this.editorIsReady())
|
|
137
|
+
return;
|
|
138
|
+
const resolved = store.find(selector);
|
|
139
|
+
if (resolved === undefined) {
|
|
140
|
+
this.host.notice(this.missingMessage(selector));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.host.setEditorText(resolved.entry.text);
|
|
144
|
+
this.host.notice(appliedMessage(resolved.index));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/** Put a draft in an empty editor and remove it from the bank. */
|
|
148
|
+
pop(selector) {
|
|
149
|
+
const sessionId = this.options.sessionId();
|
|
150
|
+
return this.run(sessionId, async (store) => {
|
|
151
|
+
// Re-read before resolving so "newest" is newest on disk, not newest as of
|
|
152
|
+
// the last command; the removal re-reads again under the lock.
|
|
153
|
+
await store.refresh();
|
|
154
|
+
await this.popFrom(store, sessionId, selector);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Open the list and pop whatever the reader takes. */
|
|
158
|
+
list(sessionLabel) {
|
|
159
|
+
const sessionId = this.options.sessionId();
|
|
160
|
+
return this.run(sessionId, async (store) => {
|
|
161
|
+
await store.refresh();
|
|
162
|
+
if (this.abandonIfMoved(sessionId))
|
|
163
|
+
return;
|
|
164
|
+
if (store.entryCount === 0) {
|
|
165
|
+
this.host.notice(NO_DRAFTS_MESSAGE);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// The list is handed each entry with the index the bank gives it, so the
|
|
169
|
+
// row a reader picks and the selector they could have typed agree.
|
|
170
|
+
const rows = store.entries.map((entry, index) => ({ entry, index }));
|
|
171
|
+
const picked = await this.host.pick(rows, sessionLabel);
|
|
172
|
+
if (picked === undefined)
|
|
173
|
+
return;
|
|
174
|
+
await this.popFrom(store, sessionId, picked);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/** Delete one draft without using it. */
|
|
178
|
+
drop(selector) {
|
|
179
|
+
const sessionId = this.options.sessionId();
|
|
180
|
+
return this.run(sessionId, async (store) => {
|
|
181
|
+
let resolved;
|
|
182
|
+
let warning;
|
|
183
|
+
try {
|
|
184
|
+
resolved = await store.drop(selector);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (!(error instanceof StashCommittedError))
|
|
188
|
+
throw error;
|
|
189
|
+
resolved = error.result;
|
|
190
|
+
if (resolved === undefined)
|
|
191
|
+
return;
|
|
192
|
+
warning = committedMessage(error, droppedMessage(resolved.index));
|
|
193
|
+
}
|
|
194
|
+
if (resolved === undefined) {
|
|
195
|
+
this.host.notice(this.missingMessage(selector));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.host.notice(warning ?? droppedMessage(resolved.index));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** Confirm, then delete every draft the confirmation named. */
|
|
202
|
+
clear() {
|
|
203
|
+
const sessionId = this.options.sessionId();
|
|
204
|
+
return this.run(sessionId, async (store) => {
|
|
205
|
+
await store.refresh();
|
|
206
|
+
if (this.abandonIfMoved(sessionId))
|
|
207
|
+
return;
|
|
208
|
+
const count = store.entryCount;
|
|
209
|
+
if (count === 0) {
|
|
210
|
+
this.host.notice(NO_DRAFTS_MESSAGE);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// The ids the reader was shown, not the ids on disk afterwards: another
|
|
214
|
+
// surface can stash while the dialog is open, and those drafts were never
|
|
215
|
+
// part of what this confirmation agreed to delete.
|
|
216
|
+
const confirmedIds = store.entries.map(entry => entry.id);
|
|
217
|
+
if (!(await this.host.confirm(confirmedIds.length)))
|
|
218
|
+
return;
|
|
219
|
+
let removed = confirmedIds.length;
|
|
220
|
+
let warning;
|
|
221
|
+
try {
|
|
222
|
+
removed = await store.clear(confirmedIds);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (!(error instanceof StashCommittedError))
|
|
226
|
+
throw error;
|
|
227
|
+
removed = error.result;
|
|
228
|
+
warning = committedMessage(error, clearedMessage(removed));
|
|
229
|
+
}
|
|
230
|
+
this.host.notice(warning ?? clearedMessage(removed));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Put one entry in an empty editor, then remove it.
|
|
235
|
+
*
|
|
236
|
+
* The editor is filled before the entry is removed, so a crash between the two
|
|
237
|
+
* leaves the draft in the bank rather than only in a terminal that is gone.
|
|
238
|
+
*/
|
|
239
|
+
async popFrom(store, sessionId, selector) {
|
|
240
|
+
if (this.abandonIfMoved(sessionId))
|
|
241
|
+
return;
|
|
242
|
+
if (!this.editorIsReady())
|
|
243
|
+
return;
|
|
244
|
+
const resolved = store.find(selector);
|
|
245
|
+
if (resolved === undefined) {
|
|
246
|
+
this.host.notice(this.missingMessage(selector));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.host.setEditorText(resolved.entry.text);
|
|
250
|
+
try {
|
|
251
|
+
const removed = await store.removeById(resolved.entry.id);
|
|
252
|
+
this.host.notice(poppedMessage(removed?.index ?? resolved.index));
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error instanceof StashCommittedError) {
|
|
256
|
+
this.host.notice(committedMessage(error, poppedMessage(resolved.index)));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
this.host.notice(removalFailedMessage(error, poppedMessage(resolved.index)));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
editorIsReady() {
|
|
263
|
+
if (!this.editorIsAvailable())
|
|
264
|
+
return false;
|
|
265
|
+
if (this.host.getEditorText().trim() === '')
|
|
266
|
+
return true;
|
|
267
|
+
this.host.notice(EDITOR_BLOCKED_MESSAGE);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Whether the bar is the reader's to write in.
|
|
272
|
+
*
|
|
273
|
+
* A question borrows the bar and empties it, so an empty check alone would
|
|
274
|
+
* let a pop write the draft into an answer, and the draft's only other copy is
|
|
275
|
+
* deleted right afterwards.
|
|
276
|
+
*/
|
|
277
|
+
editorIsAvailable() {
|
|
278
|
+
if (this.host.editorIsAvailable())
|
|
279
|
+
return true;
|
|
280
|
+
this.host.notice(EDITOR_BUSY_MESSAGE);
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
missingMessage(selector) {
|
|
284
|
+
const trimmed = selector?.trim();
|
|
285
|
+
return trimmed === undefined || trimmed === ''
|
|
286
|
+
? NO_DRAFTS_MESSAGE
|
|
287
|
+
: selectorMissingMessage(trimmed);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The store of one session, loaded on first use.
|
|
291
|
+
*
|
|
292
|
+
* The session is passed in rather than read here: a command must act on the
|
|
293
|
+
* session the reader was on when they asked for it, even when the surface
|
|
294
|
+
* moves on before the queue reaches that command.
|
|
295
|
+
*/
|
|
296
|
+
async ensure(sessionId) {
|
|
297
|
+
if (this.store !== undefined && this.scope === sessionId)
|
|
298
|
+
return this.store;
|
|
299
|
+
const store = await loadStashStore(resolveStashPaths(sessionId, this.baseDir), this.now, this.write);
|
|
300
|
+
this.store = store;
|
|
301
|
+
this.scope = sessionId;
|
|
302
|
+
return store;
|
|
303
|
+
}
|
|
304
|
+
run(sessionId, operation) {
|
|
305
|
+
return this.enqueue(async () => {
|
|
306
|
+
try {
|
|
307
|
+
await operation(await this.ensure(sessionId));
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this.host.notice(describeFailure(error));
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
this.sync();
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/** Whether the surface still shows the session a command was issued in. */
|
|
318
|
+
isCurrentSession(sessionId) {
|
|
319
|
+
return this.options.sessionId() === sessionId;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Stop a command whose session left the screen before the bar could be reached.
|
|
323
|
+
*
|
|
324
|
+
* The bank half has already run for the session that asked for it; writing its
|
|
325
|
+
* draft into whatever bar is on screen now would hand it to the wrong reader.
|
|
326
|
+
*/
|
|
327
|
+
abandonIfMoved(sessionId) {
|
|
328
|
+
if (this.isCurrentSession(sessionId))
|
|
329
|
+
return false;
|
|
330
|
+
this.host.notice(SESSION_CHANGED_MESSAGE);
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
enqueue(operation) {
|
|
334
|
+
const run = this.tail.then(operation, operation);
|
|
335
|
+
this.tail = run.catch(() => undefined);
|
|
336
|
+
return run;
|
|
337
|
+
}
|
|
338
|
+
/** Publish the bank's size and any quarantine the last read produced. */
|
|
339
|
+
sync() {
|
|
340
|
+
if (this.store !== undefined) {
|
|
341
|
+
// A store read for a session the surface has left is not the count to
|
|
342
|
+
// draw; the open that follows the switch publishes the right one.
|
|
343
|
+
this.count = this.scope === this.options.sessionId() ? this.store.entryCount : 0;
|
|
344
|
+
const quarantine = this.store.takeQuarantine();
|
|
345
|
+
if (quarantine !== undefined) {
|
|
346
|
+
const durability = quarantine.syncFailed ? CORRUPT_RECOVERY_UNSYNCED_MESSAGE : '';
|
|
347
|
+
this.host.notice(`${CORRUPT_RECOVERY_MESSAGE} ${quarantine.path}${durability}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
this.host.render();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=stash.js.map
|
package/lib/stash.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stash.js","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,2EAA2E;AAC3E,+EAA+E;AAC/E,2CAA2C;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAElE,OAAO,EACL,cAAc,EACd,2BAA2B,GAG5B,MAAM,kBAAkB,CAAA;AAEzB,MAAM,sBAAsB,GAAG,6DAA6D,CAAA;AAC5F,MAAM,mBAAmB,GAAG,mEAAmE,CAAA;AAC/F,MAAM,wBAAwB,GAAG,kBAAkB,CAAA;AACnD,MAAM,iBAAiB,GAAG,mBAAmB,CAAA;AAC7C,MAAM,wBAAwB,GAAG,uCAAuC,CAAA;AACxE,MAAM,iCAAiC,GAAG,2EAA2E,CAAA;AACrH,MAAM,uBAAuB,GAAG,uEAAuE,CAAA;AAEvG,MAAM,cAAc,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,YAAY,KAAK,GAAG,CAAA;AACtE,MAAM,cAAc,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,YAAY,KAAK,GAAG,CAAA;AACtE,MAAM,aAAa,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,WAAW,KAAK,GAAG,CAAA;AACpE,MAAM,cAAc,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,YAAY,KAAK,GAAG,CAAA;AACtE,MAAM,cAAc,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,WAAW,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;AACnG,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,sBAAsB,QAAQ,GAAG,CAAA;AAC9F,MAAM,gBAAgB,GAAG,CAAC,KAA0B,EAAE,IAAY,EAAU,EAAE,CAC5E,KAAK,CAAC,cAAc,KAAK,SAAS;IAChC,CAAC,CAAC,GAAG,IAAI,aAAa,KAAK,CAAC,OAAO,CAAC,KAAK,cAAc;IACvD,CAAC,CAAC,GAAG,IAAI,aAAa,KAAK,CAAC,OAAO,CAAC,KAAK,QAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,eAAe,CAAA;AAC9F,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAE,IAAY,EAAU,EAAE,CACpE,GAAG,IAAI,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAA;AA0CvG,SAAS,eAAe,CAAC,KAAc;IACrC,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAA;IACvE,OAAO,KAAK,YAAY,2BAA2B,IAAI,KAAK,YAAY,iBAAiB;QACvF,CAAC,CAAC,sBAAsB,MAAM,EAAE;QAChC,CAAC,CAAC,iBAAiB,MAAM,EAAE,CAAA;AAC/B,CAAC;AAED,MAAM,OAAO,WAAW;IAYH;IACA;IAZF,OAAO,CAAQ;IACf,GAAG,CAA4B;IAC/B,KAAK,CAAyB;IACvC,KAAK,CAAwB;IACrC,gFAAgF;IACxE,KAAK,CAAoB;IACzB,KAAK,GAAG,CAAC,CAAA;IACjB,2EAA2E;IACnE,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IAE/C,YACmB,IAAe,EACf,OAA2B;QAD3B,SAAI,GAAJ,IAAI,CAAW;QACf,YAAO,GAAP,OAAO,CAAoB;QAE5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,YAAY,EAAE,CAAA;QAChD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAA;QACtB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,CAAC;IAED,wDAAwD;IACxD,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAA;YAC1C,CAAC;YACD,IAAI,CAAC,IAAI,EAAE,CAAA;QACb,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAc;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;QACxD,uEAAuE;QACvE,yEAAyE;QACzE,6CAA6C;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC/C,IAAI,KAAK,IAAI,SAAS;YAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QACtD,0EAA0E;QAC1E,0EAA0E;QAC1E,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,KAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAChG,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;gBACrC,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAA;YACrB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAA;gBAC1C,OAAM;YACR,CAAC;YACD,IAAI,QAAuB,CAAA;YAC3B,IAAI,OAA2B,CAAA;YAC/B,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC;oBAAE,MAAM,KAAK,CAAA;gBACxD,wEAAwE;gBACxE,yEAAyE;gBACzE,QAAQ,GAAG,KAAK,CAAC,MAAuB,CAAA;gBACxC,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;YACnE,CAAC;YACD,0EAA0E;YAC1E,uEAAuE;YACvE,qEAAqE;YACrE,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAA;YAChD,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;YAC/G,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAA;QACnG,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,QAA4B;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;YACrB,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;gBAAE,OAAM;YAC1C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;gBAAE,OAAM;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACrC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAA;gBAC/C,OAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC5C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,kEAAkE;IAClE,GAAG,CAAC,QAA4B;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,2EAA2E;YAC3E,+DAA+D;YAC/D,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;YACrB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;QAChD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC,YAAoB;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;YACrB,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;gBAAE,OAAM;YAC1C,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAA;gBACnC,OAAM;YACR,CAAC;YACD,yEAAyE;YACzE,mEAAmE;YACnE,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;YACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;YACvD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAM;YAChC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,QAA4B;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,IAAI,QAAmC,CAAA;YACvC,IAAI,OAA2B,CAAA;YAC/B,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC;oBAAE,MAAM,KAAK,CAAA;gBACxD,QAAQ,GAAG,KAAK,CAAC,MAAmC,CAAA;gBACpD,IAAI,QAAQ,KAAK,SAAS;oBAAE,OAAM;gBAClC,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;YACnE,CAAC;YACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAA;gBAC/C,OAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,+DAA+D;IAC/D,KAAK;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;YACvC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;YACrB,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;gBAAE,OAAM;YAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAA;YAC9B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAA;gBACnC,OAAM;YACR,CAAC;YACD,wEAAwE;YACxE,0EAA0E;YAC1E,mDAAmD;YACnD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACzD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;gBAAE,OAAM;YAC3D,IAAI,OAAO,GAAG,YAAY,CAAC,MAAM,CAAA;YACjC,IAAI,OAA2B,CAAA;YAC/B,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;YAC3C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC;oBAAE,MAAM,KAAK,CAAA;gBACxD,OAAO,GAAG,KAAK,CAAC,MAAgB,CAAA;gBAChC,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;YAC5D,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;QACtD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,OAAO,CAAC,KAAiB,EAAE,SAAiB,EAAE,QAA4B;QACtF,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;YAAE,OAAM;QAC1C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;YAAE,OAAM;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAA;YAC/C,OAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC5C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACzD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;gBACzC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;gBACxE,OAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAAE,OAAO,KAAK,CAAA;QAC3C,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,IAAI,CAAA;QACxD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAA;QACxC,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;OAMG;IACK,iBAAiB;QACvB,IAAI,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAAE,OAAO,IAAI,CAAA;QAC9C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;QACrC,OAAO,KAAK,CAAA;IACd,CAAC;IAEO,cAAc,CAAC,QAA4B;QACjD,MAAM,OAAO,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAA;QAChC,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE;YAC5C,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAA;IACrC,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,MAAM,CAAC,SAAiB;QACpC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAA;QAC3E,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,iBAAiB,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACpG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,OAAO,KAAK,CAAA;IACd,CAAC;IAEO,GAAG,CAAC,SAAiB,EAAE,SAA+C;QAC5E,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;YAC/C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAA;YAC1C,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,2EAA2E;IACnE,gBAAgB,CAAC,SAAiB;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,SAAS,CAAA;IAC/C,CAAC;IAED;;;;;OAKG;IACK,cAAc,CAAC,SAAiB;QACtC,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAA;QAClD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAA;QACzC,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,OAAO,CAAC,SAA8B;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACtC,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,yEAAyE;IACjE,IAAI;QACV,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,sEAAsE;YACtE,kEAAkE;YAClE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;YAChF,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;YAC9C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAA;gBACjF,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,wBAAwB,IAAI,UAAU,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC,CAAA;YACjF,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a reader's editor is configured, most specific first.
|
|
3
|
+
*
|
|
4
|
+
* VISUAL is the full-screen editor and EDITOR the line one, which is the
|
|
5
|
+
* distinction a surface that gives up the whole screen cares about; a reader
|
|
6
|
+
* who set only one of them still gets it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const EDITOR_ENV_KEYS: readonly ["VISUAL", "EDITOR"];
|
|
9
|
+
/** A draft larger than this is left where it is rather than handed to the bar. */
|
|
10
|
+
export declare const MAX_DRAFT_BYTES = 1048576;
|
|
11
|
+
/**
|
|
12
|
+
* One configured command line as argv, or undefined when it names nothing.
|
|
13
|
+
*
|
|
14
|
+
* A reader writes an editor the way a shell would take it — `code --wait`,
|
|
15
|
+
* `emacsclient -t` — so the line is split the way a shell would split it:
|
|
16
|
+
* quotes group, and nothing else is special. No shell ever sees the result,
|
|
17
|
+
* because an environment value is data and a shell would execute a typo. An
|
|
18
|
+
* unterminated quote takes the rest of the line rather than refusing the whole
|
|
19
|
+
* binding: a missing quote should not leave the reader with no editor at all.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseEditorCommand(value: string): readonly string[] | undefined;
|
|
22
|
+
/** The editor this environment configures, or undefined when none is usable. */
|
|
23
|
+
export declare function resolveEditorCommand(env?: NodeJS.ProcessEnv): readonly string[] | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* What this module needs from the surface while a child owns the terminal.
|
|
26
|
+
*
|
|
27
|
+
* `suspend` and `resume` are the whole reason the handoff is safe — the
|
|
28
|
+
* alternate screen has to leave before the child starts and be asked for again
|
|
29
|
+
* after it ends — and only the surface owns that object. `notice` is how a
|
|
30
|
+
* refusal or a failure reaches the reader, who is looking at the transcript.
|
|
31
|
+
*/
|
|
32
|
+
export interface EditorTerminalHost {
|
|
33
|
+
suspend(): void;
|
|
34
|
+
resume(): void;
|
|
35
|
+
notice(message: string): void;
|
|
36
|
+
}
|
|
37
|
+
/** The slice of a spawned child this module waits on. */
|
|
38
|
+
export interface EditorChild {
|
|
39
|
+
on(event: 'error', listener: (error: Error) => void): void;
|
|
40
|
+
on(event: 'exit', listener: (code: number | null, signal: string | null) => void): void;
|
|
41
|
+
}
|
|
42
|
+
export interface EditorSpawnOptions {
|
|
43
|
+
readonly stdio: 'inherit';
|
|
44
|
+
readonly cwd: string;
|
|
45
|
+
readonly env: NodeJS.ProcessEnv;
|
|
46
|
+
}
|
|
47
|
+
/** The one process call this module makes, so a spec can drive it without a terminal. */
|
|
48
|
+
export type EditorSpawn = (file: string, args: readonly string[], options: EditorSpawnOptions) => EditorChild;
|
|
49
|
+
/** How the handoff runs; every part a spec would otherwise have to fake is overridable. */
|
|
50
|
+
export interface ExternalEditorOptions {
|
|
51
|
+
readonly env?: NodeJS.ProcessEnv | undefined;
|
|
52
|
+
/** Where the scratch directory is made; tests keep theirs out of the real tmpdir. */
|
|
53
|
+
readonly tempRoot?: string | undefined;
|
|
54
|
+
readonly spawn?: EditorSpawn | undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The reader's editor, opened over one draft.
|
|
58
|
+
*
|
|
59
|
+
* Every failure is reported through the host and answered with `undefined`
|
|
60
|
+
* ("keep what the bar had") rather than thrown, because the caller is a key
|
|
61
|
+
* press with nowhere to put an error.
|
|
62
|
+
*/
|
|
63
|
+
export declare class ExternalEditor {
|
|
64
|
+
private readonly host;
|
|
65
|
+
private readonly env;
|
|
66
|
+
private readonly tempRoot;
|
|
67
|
+
private readonly spawn;
|
|
68
|
+
/** Whether a child holds the terminal; a second handoff would stop it twice. */
|
|
69
|
+
private running;
|
|
70
|
+
constructor(host: EditorTerminalHost, options?: ExternalEditorOptions);
|
|
71
|
+
/** Edit `text` in the reader's editor, and return what was saved, or undefined to keep the bar. */
|
|
72
|
+
edit(text: string): Promise<string | undefined>;
|
|
73
|
+
/**
|
|
74
|
+
* What the editor saved, read through one handle.
|
|
75
|
+
*
|
|
76
|
+
* The file is opened without following a link and read through that same
|
|
77
|
+
* handle, so a replacement between a check and a read cannot slip past it, a
|
|
78
|
+
* fifo left in the draft's place cannot block the surface, and the read stops
|
|
79
|
+
* one byte past the cap so a file that grows while it is read cannot exhaust
|
|
80
|
+
* memory.
|
|
81
|
+
*/
|
|
82
|
+
private readDraft;
|
|
83
|
+
/**
|
|
84
|
+
* Run the editor over one file, and report only whether it could be started.
|
|
85
|
+
*
|
|
86
|
+
* A non-zero exit is not a failure: an editor that refused to save its buffer
|
|
87
|
+
* already told the reader why, and whatever it did save is what they meant to
|
|
88
|
+
* keep. A child that never started emits `error` and no `exit`, so either one
|
|
89
|
+
* settling the wait is the answer.
|
|
90
|
+
*/
|
|
91
|
+
private run;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=external-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-editor.d.ts","sourceRoot":"","sources":["../../src/terminal/external-editor.ts"],"names":[],"mappings":"AAmBA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,+BAAgC,CAAA;AAS5D,kFAAkF;AAClF,eAAO,MAAM,eAAe,UAAY,CAAA;AAuBxC;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CA2B/E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAQxG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;IACd,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAA;IAC1D,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAA;CACxF;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAChC;AAED,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,kBAAkB,KAAK,WAAW,CAAA;AAiB7G,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAA;IAC5C,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CACzC;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAQvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,gFAAgF;IAChF,OAAO,CAAC,OAAO,CAAQ;gBAGJ,IAAI,EAAE,kBAAkB,EACzC,OAAO,GAAE,qBAA0B;IAOrC,mGAAmG;IAC7F,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAwErD;;;;;;;;OAQG;YACW,SAAS;IAkBvB;;;;;;;OAOG;IACH,OAAO,CAAC,GAAG;CAiBZ"}
|