| | @@ -21,17 +21,16 @@ |
| 21 | 21 | * Registers the diff pane: `/diff` once the built-in stands down, the |
| 22 | 22 | * pane's drawing and refresh, its opening on Claude's first edit, the ask. |
| 23 | 23 | * |
| 24 | | - * `session.start` binds the engine once, registers the command (refused |
| 25 | | - * because the built-in holds it, the plugin does nothing) and pins the |
| 26 | | - * backend holding the session's directory (probed again on `/diff` and |
| 27 | | - * first edit until found): what an installed backend probe finds, else git |
| 28 | | - * (backendOf). |
| 24 | + * `session.start` registers `/diff` (refused while the built-in holds it), |
| 25 | + * binds the host every pinned backend reads through, and pins the backend |
| 26 | + * (backendOf): one probe in flight, asked again on `/diff` until git answers. |
| 29 | 27 | * |
| 30 | 28 | * @param on the engine's registrar |
| 31 | 29 | */ |
| 32 | 30 | export function register(on: On) { |
| 33 | 31 | let host: Host | null = null |
| 34 | 32 | let backend: Backend.Backend | null = null |
| 33 | + let probing: Promise<boolean> | null = null |
| 35 | 34 | let sessionStartMs = 0 |
| 36 | 35 | let isPaneOpen = false |
| 37 | 36 | let hasAutoOpened = false |
| | @@ -76,19 +75,37 @@ |
| 76 | 75 | }, |
| 77 | 76 | }) |
| 78 | 77 | |
| 79 | | - async function pinBackend(engine: Host): Promise<void> { |
| 78 | + function pinBackend(engine: Host): Promise<boolean> { |
| 80 | 79 | if (backend) { |
| 81 | | - return |
| 80 | + return Promise.resolve(true) |
| 82 | 81 | } |
| 83 | 82 | |
| 83 | + probing ??= probeBackend(engine).finally(() => { |
| 84 | + probing = null |
| 85 | + }) |
| 86 | + |
| 87 | + return probing |
| 88 | + } |
| 89 | + |
| 90 | + async function probeBackend(engine: Host): Promise<boolean> { |
| 91 | + const asked = { isAnswered: true } |
| 92 | + const probeHost = backendHostOf(engine) |
| 84 | 93 | const probed = await Backend.backendOf( |
| 85 | | - backendHostOf(engine), |
| 94 | + { |
| 95 | + ...probeHost, |
| 96 | + run: (argv, init) => |
| 97 | + probeHost.run(argv, init).catch((error: unknown) => { |
| 98 | + asked.isAnswered &&= |
| 99 | + argv[0] !== 'git' || !/\baborted\b/.test(messageOf(error)) |
| 100 | + throw error |
| 101 | + }), |
| 102 | + }, |
| 86 | 103 | Backend.INSTALLED_BACKEND_PROBES, |
| 87 | 104 | ) |
| 88 | 105 | backend ??= probed |
| 89 | 106 | |
| 90 | 107 | if (!probed || backend !== probed) { |
| 91 | | - return |
| 108 | + return asked.isAnswered || backend !== null |
| 92 | 109 | } |
| 93 | 110 | |
| 94 | 111 | const stored = PaneState.baseModeOf( |
| | @@ -103,6 +120,8 @@ |
| 103 | 120 | baseModes: probed.baseModes, |
| 104 | 121 | ...(mode && { requestedMode: mode }), |
| 105 | 122 | } |
| 123 | + |
| 124 | + return true |
| 106 | 125 | } |
| 107 | 126 | |
| 108 | 127 | function redraw(engine: Host) { |
| | @@ -477,10 +496,14 @@ |
| 477 | 496 | return next(e) |
| 478 | 497 | } |
| 479 | 498 | |
| 480 | | - await pinBackend(host) |
| 499 | + const isAnswered = (await pinBackend(host)) || (await pinBackend(host)) |
| 481 | 500 | |
| 482 | 501 | if (!backend) { |
| 483 | | - return { text: Names.NOT_IN_REPOSITORY_TEXT } |
| 502 | + return { |
| 503 | + text: isAnswered |
| 504 | + ? Names.NOT_IN_REPOSITORY_TEXT |
| 505 | + : Names.GIT_UNANSWERED_TEXT, |
| 506 | + } |
| 484 | 507 | } |
| 485 | 508 | |
| 486 | 509 | const isOpening = |