@danypops/pi-lector 0.11.0 → 0.11.2
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.
|
@@ -56,16 +56,27 @@ export class ExplorerComponent implements Component {
|
|
|
56
56
|
this.theme = theme;
|
|
57
57
|
this.session = session;
|
|
58
58
|
this.done = done;
|
|
59
|
-
|
|
59
|
+
this.pending = this.loadDirectory(initialRelativePath).catch((error: unknown) => this.reportError(error));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
private pending: Promise<void> = Promise.resolve();
|
|
63
63
|
|
|
64
64
|
invalidate(): void {}
|
|
65
65
|
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Fire-and-forget from the real TUI's own perspective (matching Component's void-returning
|
|
68
|
+
* contract) -- tests await settled() for deterministic assertions after triggering async work.
|
|
69
|
+
* Any rejection (a daemon call failing, a stale/incompatible daemon, a genuine bug) is caught
|
|
70
|
+
* here rather than left to become an unhandled rejection: that crashed the whole Pi process in
|
|
71
|
+
* production once already, when a stale running daemon didn't yet support workspace.listDirectory.
|
|
72
|
+
*/
|
|
67
73
|
handleInput(data: string): void {
|
|
68
|
-
this.pending = this.handleInputAsync(data);
|
|
74
|
+
this.pending = this.handleInputAsync(data).catch((error: unknown) => this.reportError(error));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private reportError(error: unknown): void {
|
|
78
|
+
this.statusMessage = `error: ${error instanceof Error ? error.message : String(error)}`;
|
|
79
|
+
this.tui.requestRender();
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
/** Resolves once every async operation triggered by the most recent handleInput() call has finished -- a test-determinism hook, not part of the Component contract. */
|
|
@@ -54,21 +54,40 @@ export class NeovimEditorComponent implements Component {
|
|
|
54
54
|
this.done = done;
|
|
55
55
|
this.extension = extname(host.filePath);
|
|
56
56
|
this.state = new EditorState(content);
|
|
57
|
-
|
|
57
|
+
this.refreshHighlightsSafely();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
invalidate(): void {
|
|
61
61
|
this.highlightCache = undefined;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Fire-and-forget from the real TUI's own perspective (matching Component's void-returning
|
|
66
|
+
* contract). Any rejection from performAction (host.save/host.hover talking to a daemon that
|
|
67
|
+
* fails or restarts mid-session) is caught here rather than left to become an unhandled
|
|
68
|
+
* rejection that crashes the whole Pi process -- the same defect class found and fixed in
|
|
69
|
+
* ExplorerComponent's own constructor.
|
|
70
|
+
*/
|
|
64
71
|
handleInput(data: string): void {
|
|
65
72
|
this.state.handleKey(data);
|
|
66
73
|
this.scrollToKeepCursorVisible();
|
|
67
74
|
const action = this.state.pendingAction;
|
|
68
|
-
if (action)
|
|
75
|
+
if (action) this.performActionSafely(action);
|
|
69
76
|
this.tui.requestRender();
|
|
70
77
|
}
|
|
71
78
|
|
|
79
|
+
private performActionSafely(action: EditorAction): void {
|
|
80
|
+
void this.performAction(action).catch((error: unknown) => {
|
|
81
|
+
this.statusMessage = `error: ${error instanceof Error ? error.message : String(error)}`;
|
|
82
|
+
this.tui.requestRender();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Highlighting is cosmetic: a failure here must never surface as a status message that stomps a real save/hover result, and must never crash the editor. */
|
|
87
|
+
private refreshHighlightsSafely(): void {
|
|
88
|
+
void this.refreshHighlights().catch(() => undefined);
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
private async performAction(action: EditorAction): Promise<void> {
|
|
73
92
|
switch (action.kind) {
|
|
74
93
|
case "save":
|
|
@@ -112,6 +131,10 @@ export class NeovimEditorComponent implements Component {
|
|
|
112
131
|
this.tui.requestRender();
|
|
113
132
|
}
|
|
114
133
|
|
|
134
|
+
private refreshHighlightsIfStale(): void {
|
|
135
|
+
if (this.highlightCache?.text !== this.state.buffer.text) this.refreshHighlightsSafely();
|
|
136
|
+
}
|
|
137
|
+
|
|
115
138
|
private scrollToKeepCursorVisible(): void {
|
|
116
139
|
const viewportHeight = Math.max(1, this.tui.terminal.rows - 2);
|
|
117
140
|
if (this.state.cursorLine < this.scrollTop) this.scrollTop = this.state.cursorLine;
|
|
@@ -119,7 +142,7 @@ export class NeovimEditorComponent implements Component {
|
|
|
119
142
|
}
|
|
120
143
|
|
|
121
144
|
render(width: number): string[] {
|
|
122
|
-
|
|
145
|
+
this.refreshHighlightsIfStale();
|
|
123
146
|
|
|
124
147
|
const viewportHeight = Math.max(1, this.tui.terminal.rows - 2);
|
|
125
148
|
const gutterWidth = Math.max(3, String(this.state.buffer.lineCount).length) + 1;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JobSnapshot, PopulateSymbolGraphResult, WorkspaceCacheStatus } from "@danypops/lector";
|
|
2
|
-
import { lectorClient, withWorkspace,
|
|
2
|
+
import { lectorClient, withWorkspace, workspaceForProjectDirectory } from "../lector-client.ts";
|
|
3
3
|
|
|
4
4
|
export interface WorkspaceCacheOperations {
|
|
5
5
|
status(directory: string, maxFiles: number, maxSymbolsPerFile: number): Promise<WorkspaceCacheStatus>;
|
|
@@ -11,7 +11,7 @@ export function createWorkspaceCacheOperations(): WorkspaceCacheOperations {
|
|
|
11
11
|
return {
|
|
12
12
|
status(directory, maxFiles, maxSymbolsPerFile) {
|
|
13
13
|
return withWorkspace(
|
|
14
|
-
() =>
|
|
14
|
+
() => workspaceForProjectDirectory(directory),
|
|
15
15
|
async ({ workspaceId }) => {
|
|
16
16
|
const client = await lectorClient();
|
|
17
17
|
return client.call("workspace.cacheStatus", { workspaceId, maxFiles, maxSymbolsPerFile });
|
|
@@ -20,7 +20,7 @@ export function createWorkspaceCacheOperations(): WorkspaceCacheOperations {
|
|
|
20
20
|
},
|
|
21
21
|
submit(directory, maxFiles, maxSymbolsPerFile) {
|
|
22
22
|
return withWorkspace(
|
|
23
|
-
() =>
|
|
23
|
+
() => workspaceForProjectDirectory(directory),
|
|
24
24
|
async ({ workspaceId }) => {
|
|
25
25
|
const client = await lectorClient();
|
|
26
26
|
const { job } = await client.callOnce("job.submit", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-lector",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Pi host adapter for Lector: overrides read/write/edit with a daemon-backed, hash-guarded filesystem",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"malevich-tui-components": "^0.19.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
+
"@danypops/pi-extension-harness": "^0.2.0",
|
|
26
27
|
"@danypops/pi-tui-harness": "^0.0.1",
|
|
27
28
|
"@earendil-works/pi-ai": "^0.81.1",
|
|
28
29
|
"@earendil-works/pi-coding-agent": "^0.81.1",
|