@ff-labs/pi-fff 0.6.5-nightly.ca7bf03 → 0.7.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/package.json +1 -1
- package/src/index.ts +97 -90
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -279,76 +279,11 @@ function createFffMentionProvider(
|
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
signal: AbortSignal,
|
|
288
|
-
) => Promise<AutocompleteItem[]>;
|
|
289
|
-
|
|
290
|
-
constructor(
|
|
291
|
-
tui: any,
|
|
292
|
-
theme: any,
|
|
293
|
-
keybindings: any,
|
|
294
|
-
getMentionItems: (
|
|
295
|
-
query: string,
|
|
296
|
-
signal: AbortSignal,
|
|
297
|
-
) => Promise<AutocompleteItem[]>,
|
|
298
|
-
) {
|
|
299
|
-
super(tui, theme, keybindings);
|
|
300
|
-
this.getMentionItems = getMentionItems;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
override setAutocompleteProvider(provider: AutocompleteProvider): void {
|
|
304
|
-
this.baseProvider = provider;
|
|
305
|
-
// Create composite provider that handles @-mentions and falls back to base
|
|
306
|
-
const mentionProvider = createFffMentionProvider(this.getMentionItems);
|
|
307
|
-
const compositeProvider: AutocompleteProvider = {
|
|
308
|
-
getSuggestions: async (lines, cursorLine, cursorCol, options) => {
|
|
309
|
-
// Try @-mention first
|
|
310
|
-
const mentionResult = await mentionProvider.getSuggestions(
|
|
311
|
-
lines,
|
|
312
|
-
cursorLine,
|
|
313
|
-
cursorCol,
|
|
314
|
-
options,
|
|
315
|
-
);
|
|
316
|
-
if (mentionResult) return mentionResult;
|
|
317
|
-
// Fall back to base provider
|
|
318
|
-
return (
|
|
319
|
-
this.baseProvider?.getSuggestions(
|
|
320
|
-
lines,
|
|
321
|
-
cursorLine,
|
|
322
|
-
cursorCol,
|
|
323
|
-
options,
|
|
324
|
-
) ?? null
|
|
325
|
-
);
|
|
326
|
-
},
|
|
327
|
-
applyCompletion: (lines, cursorLine, cursorCol, item, prefix) => {
|
|
328
|
-
// Let mention provider handle @ completions, base provider for others
|
|
329
|
-
if (prefix?.startsWith("@")) {
|
|
330
|
-
return mentionProvider.applyCompletion!(
|
|
331
|
-
lines,
|
|
332
|
-
cursorLine,
|
|
333
|
-
cursorCol,
|
|
334
|
-
item,
|
|
335
|
-
prefix,
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
return (
|
|
339
|
-
this.baseProvider?.applyCompletion?.(
|
|
340
|
-
lines,
|
|
341
|
-
cursorLine,
|
|
342
|
-
cursorCol,
|
|
343
|
-
item,
|
|
344
|
-
prefix,
|
|
345
|
-
) ?? { lines, cursorLine, cursorCol }
|
|
346
|
-
);
|
|
347
|
-
},
|
|
348
|
-
};
|
|
349
|
-
super.setAutocompleteProvider(compositeProvider);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
282
|
+
// FffEditor is defined inside fffExtension() so it can capture `getMentionItems`
|
|
283
|
+
// via closure rather than via a 4th constructor parameter. This makes the class
|
|
284
|
+
// safe to subclass via `new SubClass(tui, theme, keybindings)` -- the pattern
|
|
285
|
+
// pi-vim and pi-image-attachments use to compose editors. See:
|
|
286
|
+
// https://github.com/badlogic/pi-mono/issues/3935
|
|
352
287
|
|
|
353
288
|
// ---------------------------------------------------------------------------
|
|
354
289
|
// Extension
|
|
@@ -357,6 +292,11 @@ class FffEditor extends CustomEditor {
|
|
|
357
292
|
export default function fffExtension(pi: ExtensionAPI) {
|
|
358
293
|
let finder: FileFinder | null = null;
|
|
359
294
|
let finderCwd: string | null = null;
|
|
295
|
+
// Concurrent ensureFinder() callers share the same in-flight promise so
|
|
296
|
+
// FileFinder.create() (which takes native DB locks) runs at most once per
|
|
297
|
+
// base path at a time — otherwise parallel tool calls would race and
|
|
298
|
+
// deadlock at the native layer (issue #403).
|
|
299
|
+
let finderPromise: Promise<FileFinder> | null = null;
|
|
360
300
|
let activeCwd = process.cwd();
|
|
361
301
|
|
|
362
302
|
// Mode resolution: flag > env > default
|
|
@@ -389,28 +329,37 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
389
329
|
return currentMode !== "tools-only";
|
|
390
330
|
}
|
|
391
331
|
|
|
392
|
-
|
|
393
|
-
if (finder && !finder.isDestroyed && finderCwd === cwd)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
finder = null;
|
|
397
|
-
finderCwd = null;
|
|
398
|
-
}
|
|
332
|
+
function ensureFinder(cwd: string): Promise<FileFinder> {
|
|
333
|
+
if (finder && !finder.isDestroyed && finderCwd === cwd)
|
|
334
|
+
return Promise.resolve(finder);
|
|
335
|
+
if (finderPromise) return finderPromise;
|
|
399
336
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
337
|
+
finderPromise = (async () => {
|
|
338
|
+
if (finder && !finder.isDestroyed) {
|
|
339
|
+
finder.destroy();
|
|
340
|
+
finder = null;
|
|
341
|
+
finderCwd = null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const result = FileFinder.create({
|
|
345
|
+
basePath: cwd,
|
|
346
|
+
frecencyDbPath,
|
|
347
|
+
historyDbPath,
|
|
348
|
+
aiMode: true,
|
|
349
|
+
});
|
|
406
350
|
|
|
407
|
-
|
|
408
|
-
|
|
351
|
+
if (!result.ok)
|
|
352
|
+
throw new Error(`Failed to create FFF file finder: ${result.error}`);
|
|
409
353
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
354
|
+
finder = result.value;
|
|
355
|
+
finderCwd = cwd;
|
|
356
|
+
await finder.waitForScan(15000);
|
|
357
|
+
return finder;
|
|
358
|
+
})().finally(() => {
|
|
359
|
+
finderPromise = null;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
return finderPromise;
|
|
414
363
|
}
|
|
415
364
|
|
|
416
365
|
function destroyFinder() {
|
|
@@ -450,6 +399,64 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
450
399
|
});
|
|
451
400
|
}
|
|
452
401
|
|
|
402
|
+
// Editor wrapper that injects FFF @-mention autocomplete alongside base provider.
|
|
403
|
+
// Defined inside fffExtension() so the class methods capture `getMentionItems`
|
|
404
|
+
// via closure. Subclasses constructed as `new Sub(tui, theme, keybindings)` by
|
|
405
|
+
// composability wrappers (pi-vim, pi-image-attachments) still get a working
|
|
406
|
+
// mention provider because the closure binding is preserved across subclassing.
|
|
407
|
+
class FffEditor extends CustomEditor {
|
|
408
|
+
private baseProvider: AutocompleteProvider | undefined;
|
|
409
|
+
|
|
410
|
+
override setAutocompleteProvider(provider: AutocompleteProvider): void {
|
|
411
|
+
this.baseProvider = provider;
|
|
412
|
+
// Create composite provider that handles @-mentions and falls back to base
|
|
413
|
+
const mentionProvider = createFffMentionProvider(getMentionItems);
|
|
414
|
+
const compositeProvider: AutocompleteProvider = {
|
|
415
|
+
getSuggestions: async (lines, cursorLine, cursorCol, options) => {
|
|
416
|
+
// Try @-mention first
|
|
417
|
+
const mentionResult = await mentionProvider.getSuggestions(
|
|
418
|
+
lines,
|
|
419
|
+
cursorLine,
|
|
420
|
+
cursorCol,
|
|
421
|
+
options,
|
|
422
|
+
);
|
|
423
|
+
if (mentionResult) return mentionResult;
|
|
424
|
+
// Fall back to base provider
|
|
425
|
+
return (
|
|
426
|
+
this.baseProvider?.getSuggestions(
|
|
427
|
+
lines,
|
|
428
|
+
cursorLine,
|
|
429
|
+
cursorCol,
|
|
430
|
+
options,
|
|
431
|
+
) ?? null
|
|
432
|
+
);
|
|
433
|
+
},
|
|
434
|
+
applyCompletion: (lines, cursorLine, cursorCol, item, prefix) => {
|
|
435
|
+
// Let mention provider handle @ completions, base provider for others
|
|
436
|
+
if (prefix?.startsWith("@")) {
|
|
437
|
+
return mentionProvider.applyCompletion!(
|
|
438
|
+
lines,
|
|
439
|
+
cursorLine,
|
|
440
|
+
cursorCol,
|
|
441
|
+
item,
|
|
442
|
+
prefix,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return (
|
|
446
|
+
this.baseProvider?.applyCompletion?.(
|
|
447
|
+
lines,
|
|
448
|
+
cursorLine,
|
|
449
|
+
cursorCol,
|
|
450
|
+
item,
|
|
451
|
+
prefix,
|
|
452
|
+
) ?? { lines, cursorLine, cursorCol }
|
|
453
|
+
);
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
super.setAutocompleteProvider(compositeProvider);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
453
460
|
function applyEditorMode(ctx: {
|
|
454
461
|
ui: {
|
|
455
462
|
setEditorComponent: (
|
|
@@ -462,7 +469,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
462
469
|
} else {
|
|
463
470
|
ctx.ui.setEditorComponent(
|
|
464
471
|
(tui: any, theme: any, keybindings: any) =>
|
|
465
|
-
new FffEditor(tui, theme, keybindings
|
|
472
|
+
new FffEditor(tui, theme, keybindings),
|
|
466
473
|
);
|
|
467
474
|
}
|
|
468
475
|
}
|