Skip to content

Commit e435174

Browse files
authored
Merge pull request #39 from vim-fall/fix-session-performance
fix: remove session compression to improve performance
2 parents 2c66097 + f55583b commit e435174

File tree

6 files changed

+61
-131
lines changed

6 files changed

+61
-131
lines changed

denops/fall/extension/previewer/session.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@ import type { PreviewItem } from "jsr:@vim-fall/core@^0.3.0/item";
22
import type { Previewer } from "jsr:@vim-fall/core@^0.3.0/previewer";
33
import { definePreviewer } from "jsr:@vim-fall/std@^0.10.0/previewer";
44
import type { Detail } from "../source/session.ts";
5-
import { decompressPickerSession } from "../../session.ts";
65

76
export function session(): Previewer<Detail> {
8-
return definePreviewer(async (_denops, { item }, { signal }) => {
7+
return definePreviewer((_denops, { item }, { signal }) => {
98
if (!item || signal?.aborted) {
109
return undefined;
1110
}
1211

1312
try {
14-
// Decompress the session to access its data
15-
const session = await decompressPickerSession(item.detail);
13+
// Access the session data directly
14+
const session = item.detail;
1615

1716
const lines: string[] = [];
1817

denops/fall/extension/source/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Source } from "jsr:@vim-fall/core@^0.3.0/source";
22
import type { DetailUnit, IdItem } from "jsr:@vim-fall/core@^0.3.0/item";
3-
import type { PickerSessionCompressed } from "../../session.ts";
3+
import type { PickerSession } from "../../session.ts";
44
import { listPickerSessions } from "../../session.ts";
55

6-
export type Detail = PickerSessionCompressed<DetailUnit>;
6+
export type Detail = PickerSession<DetailUnit>;
77

88
export function session(): Source<Detail> {
99
return {

denops/fall/main/picker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ async function resumePicker(
149149
// Parse filter ({name}#{indexFromLatest})
150150
const [filterName, filterNumberStr = "1"] = filter.split("#", 2);
151151
const filterNumber = Number(filterNumberStr);
152-
const session = await loadPickerSession({
152+
const session = loadPickerSession({
153153
name: filterName,
154154
number: filterNumber,
155155
});
@@ -212,12 +212,12 @@ async function startPicker<T extends Detail>(
212212
stack.defer(() => {
213213
zindex -= Picker.ZINDEX_ALLOCATION;
214214
});
215-
stack.defer(async () => {
215+
stack.defer(() => {
216216
const name = pickerParams.name;
217217
if (SESSION_EXCLUDE_SOURCES.includes(name)) {
218218
return;
219219
}
220-
await savePickerSession({
220+
savePickerSession({
221221
name,
222222
args,
223223
context: itemPicker.context,

denops/fall/session.ts

Lines changed: 14 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import type { Detail } from "jsr:@vim-fall/core@^0.3.0/item";
2-
import { brotli } from "jsr:@deno-library/compress@^0.5.6";
32

43
import type { PickerContext } from "./picker.ts";
54

65
/**
7-
* In-memory storage for compressed picker sessions.
6+
* In-memory storage for picker sessions.
87
* Sessions are stored in chronological order (oldest first).
98
*/
109
// deno-lint-ignore no-explicit-any
11-
const sessions: PickerSessionCompressed<any>[] = [];
10+
const sessions: PickerSession<any>[] = [];
1211

1312
/**
1413
* Maximum number of sessions to keep in memory.
@@ -28,88 +27,24 @@ export type PickerSession<T extends Detail> = {
2827
readonly context: PickerContext<T>;
2928
};
3029

31-
/**
32-
* Compressed version of PickerSession where the context is stored as binary data.
33-
* This reduces memory usage when storing multiple sessions.
34-
* @template T - The type of item detail in the picker
35-
*/
36-
export type PickerSessionCompressed<T extends Detail> =
37-
& Omit<PickerSession<T>, "context">
38-
& {
39-
/** Brotli-compressed binary representation of the context */
40-
context: Uint8Array;
41-
};
42-
43-
/**
44-
* Compresses a picker session by converting its context to brotli-compressed binary data.
45-
* This is used internally to reduce memory usage when storing sessions.
46-
* @template T - The type of item detail in the picker
47-
* @param session - The session to compress
48-
* @returns A promise that resolves to the compressed session
49-
*/
50-
async function compressPickerSession<T extends Detail>(
51-
session: PickerSession<T>,
52-
): Promise<PickerSessionCompressed<T>> {
53-
const encoder = new TextEncoder();
54-
// Convert Set to Array for JSON serialization
55-
const contextForSerialization = {
56-
...session.context,
57-
selection: Array.from(session.context.selection),
58-
};
59-
return {
60-
...session,
61-
context: await brotli.compress(
62-
encoder.encode(JSON.stringify(contextForSerialization)),
63-
),
64-
};
65-
}
66-
67-
/**
68-
* Decompresses a picker session by converting its binary context back to structured data.
69-
* @template T - The type of item detail in the picker
70-
* @param compressed - The compressed session to decompress
71-
* @returns A promise that resolves to the decompressed session
72-
*/
73-
export async function decompressPickerSession<T extends Detail>(
74-
compressed: PickerSessionCompressed<T>,
75-
): Promise<PickerSession<T>> {
76-
const decoder = new TextDecoder();
77-
const decompressedContext = JSON.parse(
78-
decoder.decode(await brotli.uncompress(compressed.context)),
79-
);
80-
// Convert selection array back to Set
81-
return {
82-
...compressed,
83-
context: {
84-
...decompressedContext,
85-
selection: new Set(decompressedContext.selection),
86-
},
87-
};
88-
}
89-
9030
/**
9131
* Lists all stored picker sessions in reverse chronological order (newest first).
92-
* @returns A readonly array of compressed sessions
32+
* @returns A readonly array of sessions
9333
*/
94-
export function listPickerSessions(): readonly PickerSessionCompressed<
95-
Detail
96-
>[] {
34+
export function listPickerSessions(): readonly PickerSession<Detail>[] {
9735
return sessions.slice().reverse(); // Return a copy in reverse order
9836
}
9937

10038
/**
10139
* Saves a picker session to the in-memory storage.
102-
* The session is compressed before storage to reduce memory usage.
10340
* If the storage exceeds MAX_SESSION_COUNT, the oldest session is removed.
10441
* @template T - The type of item detail in the picker
10542
* @param session - The session to save
106-
* @returns A promise that resolves when the session is saved
10743
*/
108-
export async function savePickerSession<T extends Detail>(
44+
export function savePickerSession<T extends Detail>(
10945
session: PickerSession<T>,
110-
): Promise<void> {
111-
const compressed = await compressPickerSession(session);
112-
sessions.push(compressed);
46+
): void {
47+
sessions.push(session);
11348
if (sessions.length > MAX_SESSION_COUNT) {
11449
sessions.shift(); // Keep only the last MAX_SESSION_COUNT sessions
11550
}
@@ -130,29 +65,25 @@ export type LoadPickerSessionOptions = {
13065
* @template T - The type of item detail in the picker
13166
* @param indexFromLatest - The index from the latest session (0 = most recent, 1 = second most recent, etc.)
13267
* @param options - Options to filter sessions
133-
* @returns A promise that resolves to the decompressed session, or undefined if not found
68+
* @returns The session, or undefined if not found
13469
* @example
13570
* ```ts
13671
* // Load the most recent session
137-
* const session1 = await loadPickerSession();
72+
* const session1 = loadPickerSession();
13873
*
13974
* // Load the second most recent session
140-
* const session2 = await loadPickerSession({ number: 2 });
75+
* const session2 = loadPickerSession({ number: 2 });
14176
*
14277
* // Load the most recent session with name "file"
143-
* const session3 = await loadPickerSession({ name: "file", number: 1 });
78+
* const session3 = loadPickerSession({ name: "file", number: 1 });
14479
* ```
14580
*/
146-
export async function loadPickerSession<T extends Detail>(
81+
export function loadPickerSession<T extends Detail>(
14782
{ name, number: indexFromLatest }: LoadPickerSessionOptions = {},
148-
): Promise<PickerSession<T> | undefined> {
83+
): PickerSession<T> | undefined {
14984
const filteredSessions = name
15085
? sessions.filter((s) => s.name === name)
15186
: sessions;
15287
const index = filteredSessions.length - (indexFromLatest ?? 1);
153-
const compressed = filteredSessions.at(index);
154-
if (!compressed) {
155-
return undefined;
156-
}
157-
return await decompressPickerSession(compressed);
88+
return filteredSessions.at(index) as PickerSession<T> | undefined;
15889
}

0 commit comments

Comments
 (0)