Skip to content

Commit cb81ee0

Browse files
lambdalisueclaude
andcommitted
perf: optimize processor pipeline to avoid redundant reprocessing
Modify picker event handlers to restart only the affected processor instead of restarting from the matcher. When sorter changes, restart sort processor with matcher output. When renderer changes, restart render processor with sorter output. This avoids redundant matching operations. Also implement copy-on-write for sort processor to prevent modifying the input array, ensuring pipeline integrity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 79600bd commit cb81ee0

File tree

3 files changed

+32
-36
lines changed

3 files changed

+32
-36
lines changed

denops/fall/picker.ts

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -559,13 +559,9 @@ export class Picker<T extends Detail> implements AsyncDisposable {
559559
this.#sortProcessor.sorterIndex = index;
560560
this.#listComponent.title = this.#getExtensionIndicator();
561561
reserve((denops) => {
562-
// NOTE:
563-
// We need to restart from the matcher processor because
564-
// sorters and renderers applies changes in-place thus
565-
// the items would be polluted.
566-
this.#matchProcessor.start(denops, {
567-
items: this.#collectProcessor.items,
568-
query: this.#inputComponent.cmdline,
562+
// Restart the sort processor with items from the match processor
563+
this.#sortProcessor.start(denops, {
564+
items: this.#matchProcessor.items,
569565
});
570566
});
571567
break;
@@ -574,13 +570,9 @@ export class Picker<T extends Detail> implements AsyncDisposable {
574570
this.#sortProcessor.sorterIndex = event.index;
575571
this.#listComponent.title = this.#getExtensionIndicator();
576572
reserve((denops) => {
577-
// NOTE:
578-
// We need to restart from the matcher processor because
579-
// sorters and renderers applies changes in-place thus
580-
// the items would be polluted.
581-
this.#matchProcessor.start(denops, {
582-
items: this.#collectProcessor.items,
583-
query: this.#inputComponent.cmdline,
573+
// Restart the sort processor with items from the match processor
574+
this.#sortProcessor.start(denops, {
575+
items: this.#matchProcessor.items,
584576
});
585577
});
586578
break;
@@ -596,13 +588,9 @@ export class Picker<T extends Detail> implements AsyncDisposable {
596588
this.#renderProcessor.rendererIndex = index;
597589
this.#listComponent.title = this.#getExtensionIndicator();
598590
reserve((denops) => {
599-
// NOTE:
600-
// We need to restart from the matcher processor because
601-
// sorters and renderers applies changes in-place thus
602-
// the items would be polluted.
603-
this.#matchProcessor.start(denops, {
604-
items: this.#collectProcessor.items,
605-
query: this.#inputComponent.cmdline,
591+
// Restart the render processor with items from the sort processor
592+
this.#renderProcessor.start(denops, {
593+
items: this.#sortProcessor.items,
606594
});
607595
});
608596
break;
@@ -611,13 +599,9 @@ export class Picker<T extends Detail> implements AsyncDisposable {
611599
this.#renderProcessor.rendererIndex = event.index;
612600
this.#listComponent.title = this.#getExtensionIndicator();
613601
reserve((denops) => {
614-
// NOTE:
615-
// We need to restart from the matcher processor because
616-
// sorters and renderers applies changes in-place thus
617-
// the items would be polluted.
618-
this.#matchProcessor.start(denops, {
619-
items: this.#collectProcessor.items,
620-
query: this.#inputComponent.cmdline,
602+
// Restart the render processor with items from the sort processor
603+
this.#renderProcessor.start(denops, {
604+
items: this.#sortProcessor.items,
621605
});
622606
});
623607
break;

denops/fall/processor/sort.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class SortProcessor<T extends Detail> implements Disposable {
5959
}
6060
}
6161

62-
start(denops: Denops, { items }: { items: IdItem<T>[] }): void {
62+
start(denops: Denops, { items }: { items: readonly IdItem<T>[] }): void {
6363
this.#validateAvailability();
6464
if (this.#processing) {
6565
// Keep most recent start request for later.
@@ -70,14 +70,17 @@ export class SortProcessor<T extends Detail> implements Disposable {
7070
dispatch({ type: "sort-processor-started" });
7171
const signal = this.#controller.signal;
7272

73+
// Create a shallow copy of the items array
74+
const cloned = items.slice();
75+
7376
await this.#sorter?.sort(
7477
denops,
75-
{ items },
78+
{ items: cloned },
7679
{ signal },
7780
);
7881
signal.throwIfAborted();
7982

80-
this.#items = items;
83+
this.#items = cloned;
8184
dispatch({ type: "sort-processor-succeeded" });
8285
})();
8386
this.#processing

denops/fall/processor/sort_test.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Deno.test("SortProcessor", async (t) => {
120120
);
121121

122122
await t.step(
123-
"start sort items in-place",
123+
"start sorts items without modifying original array (copy-on-write)",
124124
async () => {
125125
await using stack = new AsyncDisposableStack();
126126
stack.defer(async () => {
@@ -133,10 +133,11 @@ Deno.test("SortProcessor", async (t) => {
133133
const processor = stack.use(
134134
new SortProcessor([sorter]),
135135
);
136-
const cloned = items.slice();
137-
processor.start(denops, { items: cloned });
136+
const original = items.slice();
137+
processor.start(denops, { items: original });
138138

139-
assertEquals(cloned, [
139+
// Original array should not be modified
140+
assertEquals(original, [
140141
{ id: 0, value: "0", detail: {} },
141142
{ id: 1, value: "1", detail: {} },
142143
{ id: 2, value: "2", detail: {} },
@@ -145,7 +146,15 @@ Deno.test("SortProcessor", async (t) => {
145146
notify.notify();
146147
await flushPromises();
147148

148-
assertEquals(cloned, [
149+
// Original array should still not be modified
150+
assertEquals(original, [
151+
{ id: 0, value: "0", detail: {} },
152+
{ id: 1, value: "1", detail: {} },
153+
{ id: 2, value: "2", detail: {} },
154+
]);
155+
156+
// Processor should have sorted items
157+
assertEquals(processor.items, [
149158
{ id: 2, value: "2", detail: {} },
150159
{ id: 1, value: "1", detail: {} },
151160
{ id: 0, value: "0", detail: {} },

0 commit comments

Comments
 (0)