Skip to content

Commit 6e24d80

Browse files
authored
Merge pull request #36 from vim-fall/session
2 parents c2fa4f8 + f9e0964 commit 6e24d80

File tree

26 files changed

+1225
-105
lines changed

26 files changed

+1225
-105
lines changed

autoload/fall/command/Fall.vim

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ function! fall#command#Fall#call(args) abort
22
if denops#plugin#wait('fall') isnot# 0
33
return
44
endif
5-
let l:laststatus_saved = &laststatus
65
try
7-
call s:hide()
8-
call fall#internal#mapping#store()
6+
call fall#internal#picker#setup()
97
call denops#request('fall', 'picker:command', [a:args])
108
finally
11-
call s:show()
12-
call fall#internal#tolerant#call({ -> fall#internal#mapping#restore() })
13-
call fall#internal#tolerant#call({ -> fall#internal#popup#closeall() })
9+
call fall#internal#picker#teardown()
1410
endtry
1511
endfunction
1612

@@ -20,13 +16,3 @@ function! fall#command#Fall#complete(arglead, cmdline, cursorpos) abort
2016
endif
2117
return denops#request('fall', 'picker:command:complete', [a:arglead, a:cmdline, a:cursorpos])
2218
endfunction
23-
24-
function! s:hide() abort
25-
call fall#internal#tolerant#call({ -> fall#internal#msgarea#hide() })
26-
call fall#internal#tolerant#call({ -> fall#internal#cursor#hide() })
27-
endfunction
28-
29-
function! s:show() abort
30-
call fall#internal#tolerant#call({ -> fall#internal#msgarea#show() })
31-
call fall#internal#tolerant#call({ -> fall#internal#cursor#show() })
32-
endfunction

autoload/fall/command/FallResume.vim

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function! fall#command#FallResume#call(filter) abort
2+
if denops#plugin#wait('fall') isnot# 0
3+
return
4+
endif
5+
try
6+
call fall#internal#picker#setup()
7+
call denops#request('fall', 'picker:resume:command', [a:filter])
8+
finally
9+
call fall#internal#picker#teardown()
10+
endtry
11+
endfunction
12+
13+
function! fall#command#FallResume#complete(arglead, cmdline, cursorpos) abort
14+
if denops#plugin#wait('fall') isnot# 0
15+
return []
16+
endif
17+
return denops#request('fall', 'picker:resume:command:complete', [a:arglead, a:cmdline, a:cursorpos])
18+
endfunction

autoload/fall/command/FallSession.vim

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function! fall#command#FallSession#call() abort
2+
if denops#plugin#wait('fall') isnot# 0
3+
return
4+
endif
5+
try
6+
call fall#internal#picker#setup()
7+
call denops#request('fall', 'picker:session:command', [])
8+
finally
9+
call fall#internal#picker#teardown()
10+
endtry
11+
endfunction

autoload/fall/internal/picker.vim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
function! fall#internal#picker#setup() abort
2+
call fall#internal#tolerant#call({ -> fall#internal#msgarea#hide() })
3+
call fall#internal#tolerant#call({ -> fall#internal#cursor#hide() })
4+
call fall#internal#mapping#store()
5+
endfunction
6+
7+
function! fall#internal#picker#teardown() abort
8+
call fall#internal#tolerant#call({ -> fall#internal#msgarea#show() })
9+
call fall#internal#tolerant#call({ -> fall#internal#cursor#show() })
10+
call fall#internal#tolerant#call({ -> fall#internal#mapping#restore() })
11+
call fall#internal#tolerant#call({ -> fall#internal#popup#closeall() })
12+
endfunction

denops/fall/component/input.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export type InputComponentParams = ComponentProperties & {
2424
/** The title of the input component */
2525
readonly title?: string;
2626

27+
/** The command line input text */
28+
readonly cmdline?: string;
29+
2730
/** Optional spinner sequence to show during processing */
2831
readonly spinner?: readonly string[];
2932

@@ -77,11 +80,18 @@ export class InputComponent extends BaseComponent {
7780
#modifiedContent = true;
7881

7982
constructor(
80-
{ title, spinner, headSymbol, failSymbol, ...params }:
81-
InputComponentParams = {},
83+
{
84+
title,
85+
cmdline,
86+
spinner,
87+
headSymbol,
88+
failSymbol,
89+
...params
90+
}: InputComponentParams = {},
8291
) {
8392
super(params);
8493
this.#title = title ?? "";
94+
this.#cmdline = cmdline ?? "";
8595
this.#spinner = new Spinner(spinner ?? SPINNER);
8696
this.#headSymbol = headSymbol ?? HEAD_SYMBOL;
8797
this.#failSymbol = failSymbol ?? FAIL_SYMBOL;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Action } from "jsr:@vim-fall/core@^0.3.0/action";
2+
import type { Detail } from "../source/session.ts";
3+
4+
export const defaultSessionActions = {
5+
resume: {
6+
invoke: async (denops, { item }) => {
7+
if (!item) {
8+
return;
9+
}
10+
// we need to use timer_start to avoid nesting pickers
11+
await denops.cmd(
12+
`call timer_start(0, { -> execute('FallResume ${item.value}') })`,
13+
);
14+
},
15+
},
16+
} satisfies Record<string, Action<Detail>>;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { PreviewItem } from "jsr:@vim-fall/core@^0.3.0/item";
2+
import type { Previewer } from "jsr:@vim-fall/core@^0.3.0/previewer";
3+
import { definePreviewer } from "jsr:@vim-fall/std@^0.10.0/previewer";
4+
import type { Detail } from "../source/session.ts";
5+
import { decompressPickerSession } from "../../session.ts";
6+
7+
export function session(): Previewer<Detail> {
8+
return definePreviewer(async (_denops, { item }, { signal }) => {
9+
if (!item || signal?.aborted) {
10+
return undefined;
11+
}
12+
13+
try {
14+
// Decompress the session to access its data
15+
const session = await decompressPickerSession(item.detail);
16+
17+
const lines: string[] = [];
18+
19+
// Add session info
20+
lines.push(`# Session: ${item.value}`);
21+
lines.push("");
22+
lines.push(`Source: ${session.name}`);
23+
lines.push(`Query: ${session.context.query || "(empty)"}`);
24+
lines.push(`Total items: ${session.context.collectedItems.length}`);
25+
lines.push(`Filtered items: ${session.context.filteredItems.length}`);
26+
lines.push("");
27+
lines.push("## Filtered Items:");
28+
lines.push("");
29+
30+
// Show filtered items with selection status
31+
const selection = session.context.selection;
32+
for (const filteredItem of session.context.filteredItems) {
33+
const isSelected = selection.has(filteredItem.id);
34+
const prefix = isSelected ? "[x]" : "[ ]";
35+
lines.push(`${prefix} ${filteredItem.value}`);
36+
}
37+
38+
if (session.context.filteredItems.length === 0) {
39+
lines.push("(no items)");
40+
}
41+
42+
return {
43+
content: lines,
44+
filetype: "markdown",
45+
} satisfies PreviewItem;
46+
} catch (error) {
47+
return {
48+
content: [`Error loading session preview: ${error}`],
49+
} satisfies PreviewItem;
50+
}
51+
});
52+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { Denops } from "jsr:@denops/std@^7.3.2";
2+
import type { Renderer } from "jsr:@vim-fall/core@^0.3.0/renderer";
3+
import type { DisplayItem } from "jsr:@vim-fall/core@^0.3.0/item";
4+
import type { Detail } from "../source/session.ts";
5+
6+
export function session(): Renderer<Detail> {
7+
return {
8+
render(
9+
_denops: Denops,
10+
{ items }: { items: DisplayItem<Detail>[] },
11+
{ signal }: { signal?: AbortSignal },
12+
): void {
13+
for (const item of items) {
14+
if (signal?.aborted) break;
15+
item.label = [
16+
item.value,
17+
item.detail.name,
18+
...item.detail.args,
19+
].join(" ");
20+
}
21+
},
22+
};
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Source } from "jsr:@vim-fall/core@^0.3.0/source";
2+
import type { DetailUnit, IdItem } from "jsr:@vim-fall/core@^0.3.0/item";
3+
import type { PickerSessionCompressed } from "../../session.ts";
4+
import { listPickerSessions } from "../../session.ts";
5+
6+
export type Detail = PickerSessionCompressed<DetailUnit>;
7+
8+
export function session(): Source<Detail> {
9+
return {
10+
collect: async function* (): AsyncIterableIterator<IdItem<Detail>> {
11+
const sessions = listPickerSessions();
12+
yield* sessions.map((session, index) => {
13+
const number = index + 1;
14+
return {
15+
id: index,
16+
value: `#${number}`,
17+
detail: session,
18+
};
19+
});
20+
},
21+
};
22+
}

denops/fall/lib/item_belt.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
1+
/**
2+
* Options for configuring an ItemBelt instance.
3+
*/
4+
export type ItemBeltOptions = {
5+
/**
6+
* The initial index to set for the ItemBelt.
7+
* If not provided, defaults to 0.
8+
*/
9+
index?: number;
10+
};
11+
112
/**
213
* A class representing an item belt, which provides functionality for managing
314
* and selecting items in a list with wrap-around and bounded selection.
415
*
516
* @template T The type of items contained in the belt.
617
*/
718
export class ItemBelt<T> {
8-
#index = 0;
19+
#index: number = 0;
920
#items: readonly T[];
1021

1122
/**
1223
* Creates an instance of ItemBelt with the specified items.
1324
*
1425
* @param items An array of items of type T to initialize the belt.
26+
* @param options Optional configuration for the ItemBelt.
27+
* @param options.index The initial index to set (default is 0).
1528
*/
16-
constructor(items: readonly T[]) {
29+
constructor(items: readonly T[], options?: ItemBeltOptions) {
1730
this.#items = items;
31+
this.index = options?.index ?? 0;
1832
}
1933

2034
/**
@@ -73,7 +87,9 @@ export class ItemBelt<T> {
7387
* to the nearest valid value.
7488
*/
7589
set index(index: number) {
76-
if (index >= this.#items.length) {
90+
if (this.#items.length === 0) {
91+
index = 0;
92+
} else if (index >= this.#items.length) {
7793
index = this.#items.length - 1;
7894
} else if (index < 0) {
7995
index = 0;
@@ -85,8 +101,8 @@ export class ItemBelt<T> {
85101
* Selects a new item based on the current index, with an optional offset.
86102
* The selection can optionally cycle through the items list.
87103
*
104+
* @param offset The number of positions to move the index (default is 1).
88105
* @param options Optional configuration for the selection.
89-
* @param options.offset The number of positions to move the index (default is 1).
90106
* @param options.cycle Whether to cycle through the list (default is `false`).
91107
*/
92108
select(offset = 1, { cycle = false } = {}): void {

0 commit comments

Comments
 (0)