Skip to content

Commit 9c6c452

Browse files
committed
refactor: update component modules
1 parent fc1e17d commit 9c6c452

File tree

6 files changed

+341
-47
lines changed

6 files changed

+341
-47
lines changed

denops/fall/component/_component.ts

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,125 @@ import * as popup from "jsr:@denops/std@^7.3.2/popup";
33
import type { Border } from "jsr:@vim-fall/std@^0.4.0/theme";
44
import type { Dimension } from "jsr:@vim-fall/std@^0.4.0/coordinator";
55

6+
const HIGHLIGHT_NORMAL = "FallNormal";
7+
const HIHGLIGHT_BORDER = "FallBorder";
8+
9+
/**
10+
* Properties that define the appearance and behavior of a component.
11+
*/
612
export type ComponentProperties = {
7-
title?: string;
8-
border?: Border;
9-
zindex?: number;
13+
/** The title of the component */
14+
readonly title?: string;
15+
16+
/** The border style for the component */
17+
readonly border?: Border;
18+
19+
/** The z-index of the component */
20+
readonly zindex?: number;
1021
};
1122

23+
/**
24+
* Information about the component's current state, including window ID and buffer number.
25+
*/
1226
export type ComponentInfo = {
13-
bufnr: number;
14-
winid: number;
15-
dimension: Readonly<Dimension>;
27+
/** The buffer number associated with the component */
28+
readonly bufnr: number;
29+
30+
/** The window ID associated with the component */
31+
readonly winid: number;
32+
33+
/** The dimension (size and position) of the component */
34+
readonly dimension: Readonly<Dimension>;
1635
};
1736

37+
/**
38+
* Options for interacting with the component, such as abort signals.
39+
*/
40+
export type ComponentOptions = {
41+
/** Signal used to abort operations */
42+
signal?: AbortSignal;
43+
};
44+
45+
/**
46+
* The base interface for a component that can be opened, moved, updated, rendered, and closed.
47+
* Provides methods to manipulate the component's window and update its properties.
48+
*/
1849
export type Component = AsyncDisposable & {
50+
/**
51+
* Opens the component window with the specified dimensions.
52+
* @param denops The Denops instance.
53+
* @param dimension The dimensions of the component.
54+
* @param options Additional options such as the abort signal.
55+
* @returns A disposable object to manage the component window's lifecycle.
56+
*/
1957
open(
2058
denops: Denops,
21-
dimension: Readonly<Partial<Dimension>>,
22-
option?: { signal?: AbortSignal },
59+
dimension: Readonly<Dimension>,
60+
options?: ComponentOptions,
2361
): Promise<AsyncDisposable>;
2462

63+
/**
64+
* Moves the component window to new dimensions.
65+
* @param denops The Denops instance.
66+
* @param dimension The new dimensions of the component.
67+
* @param options Additional options such as the abort signal.
68+
*/
2569
move(
2670
denops: Denops,
2771
dimension: Readonly<Partial<Dimension>>,
28-
options?: { signal?: AbortSignal },
72+
options?: ComponentOptions,
2973
): Promise<void>;
3074

75+
/**
76+
* Updates the component's properties.
77+
* @param denops The Denops instance.
78+
* @param properties The new properties of the component.
79+
* @param options Additional options such as the abort signal.
80+
*/
3181
update(
3282
denops: Denops,
3383
properties: Readonly<ComponentProperties>,
34-
options?: { signal?: AbortSignal },
84+
options?: ComponentOptions,
3585
): Promise<void>;
3686

87+
/**
88+
* Renders the component.
89+
* @param denops The Denops instance.
90+
* @param options Additional options such as the abort signal.
91+
* @returns A promise that resolves to `true` or `void` when rendering is complete.
92+
* If the component does not need to render anything, it can resolve with `void`.
93+
*/
3794
render(
3895
denops: Denops,
39-
options?: { signal?: AbortSignal },
96+
options?: ComponentOptions,
4097
): Promise<true | void>;
4198

99+
/**
100+
* Closes the component.
101+
*/
42102
close(): Promise<void>;
43103
};
44104

45105
/**
46-
* ```
47-
* width
48-
* ┌───────────────────────────────────┐
49-
* ╭─────────────────────────────────────╮
50-
* │ │ ┐
51-
* │ │ │
52-
* │ │ │height
53-
* │ │ │
54-
* │ │ ┘
55-
* ╰─────────────────────────────────────╯
56-
* ```
106+
* A base class for a component that can be opened, moved, updated, rendered, and closed.
107+
* This class uses the `popup` module to manage the window and display the component.
57108
*/
58109
export class BaseComponent implements Component {
59110
#opened?: {
60111
readonly window: popup.PopupWindow;
61112
readonly dimension: Readonly<Dimension>;
62113
};
114+
63115
protected properties: Readonly<ComponentProperties>;
64116

65117
constructor(properties: Readonly<ComponentProperties> = {}) {
66118
this.properties = properties;
67119
}
68120

121+
/**
122+
* Returns information about the component's current state (buffer number, window ID, and dimensions).
123+
* If the component is not opened, returns `undefined`.
124+
*/
69125
get info(): Readonly<ComponentInfo> | undefined {
70126
if (!this.#opened) {
71127
return undefined;
@@ -77,7 +133,7 @@ export class BaseComponent implements Component {
77133
async open(
78134
denops: Denops,
79135
dimension: Readonly<Dimension>,
80-
{ signal }: { signal?: AbortSignal } = {},
136+
{ signal }: ComponentOptions = {},
81137
): Promise<AsyncDisposable> {
82138
if (this.#opened) {
83139
return this;
@@ -90,8 +146,8 @@ export class BaseComponent implements Component {
90146
relative: "editor",
91147
anchor: "NW",
92148
highlight: {
93-
normal: "FallNormal",
94-
border: "FallBorder",
149+
normal: HIGHLIGHT_NORMAL,
150+
border: HIHGLIGHT_BORDER,
95151
},
96152
noRedraw: true,
97153
}),
@@ -103,7 +159,7 @@ export class BaseComponent implements Component {
103159
async move(
104160
denops: Denops,
105161
dimension: Readonly<Partial<Dimension>>,
106-
{ signal }: { signal?: AbortSignal } = {},
162+
{ signal }: ComponentOptions = {},
107163
): Promise<void> {
108164
if (this.#opened) {
109165
this.#opened = {
@@ -125,7 +181,7 @@ export class BaseComponent implements Component {
125181
async update(
126182
denops: Denops,
127183
properties: Readonly<ComponentProperties>,
128-
{ signal }: { signal?: AbortSignal } = {},
184+
{ signal }: ComponentOptions = {},
129185
): Promise<void> {
130186
this.properties = {
131187
...this.properties,
@@ -142,7 +198,7 @@ export class BaseComponent implements Component {
142198

143199
render(
144200
_denops: Denops,
145-
_option?: { signal?: AbortSignal },
201+
_options: ComponentOptions = {},
146202
): Promise<true | void> {
147203
return Promise.resolve(true);
148204
}

denops/fall/component/help.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,36 @@ import type { Dimension } from "jsr:@vim-fall/std@^0.4.0/coordinator";
88
import { BaseComponent } from "./_component.ts";
99
import { ItemBelt } from "../lib/item_belt.ts";
1010

11+
/**
12+
* Represents a page in the HelpComponent.
13+
* Contains content and optional decorations for the page.
14+
*/
1115
export type Page = {
12-
readonly title?: string;
16+
/** The content to be displayed on the page */
1317
readonly content: readonly string[];
18+
19+
/** The decorations to be applied on the page (optional) */
1420
readonly decorations?: readonly Decoration[];
1521
};
1622

23+
/**
24+
* A component to display help content, extendable for navigation, rendering, and updating pages.
25+
*/
1726
export class HelpComponent extends BaseComponent {
1827
readonly #pages = new ItemBelt<Page>([]);
1928
#modifiedContent = true;
2029

30+
/**
31+
* Returns the current page number (1-based).
32+
*/
2133
get page(): number {
2234
return this.#pages.index + 1;
2335
}
2436

37+
/**
38+
* Sets the page number or sets it to the last page ("$").
39+
* @param page - The page number to set or "$" to set to the last page.
40+
*/
2541
set page(page: number | "$") {
2642
if (page === "$") {
2743
page = this.#pages.count;
@@ -30,15 +46,25 @@ export class HelpComponent extends BaseComponent {
3046
this.#modifiedContent = true;
3147
}
3248

49+
/**
50+
* Returns the list of all pages.
51+
*/
3352
get pages(): readonly Page[] {
3453
return this.#pages.items;
3554
}
3655

56+
/**
57+
* Sets the list of pages and marks the content as modified.
58+
* @param pages - An array of pages to set.
59+
*/
3760
set pages(pages: readonly Page[]) {
3861
this.#pages.items = pages;
3962
this.#modifiedContent = true;
4063
}
4164

65+
/**
66+
* Forces the component to re-render by marking the content as modified.
67+
*/
4268
forceRender(): void {
4369
this.#modifiedContent = true;
4470
}

denops/fall/component/help_test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import "../lib/polyfill.ts";
2+
3+
import { test } from "jsr:@denops/test@^3.0.4";
4+
import * as fn from "jsr:@denops/std@^7.3.2/function";
5+
import { fromFileUrl } from "jsr:@std/path@^1.0.8/from-file-url";
6+
import { assertEquals } from "jsr:@std/assert@^1.0.6";
7+
8+
import { HelpComponent } from "./help.ts";
9+
10+
const dimension = {
11+
col: 1,
12+
row: 1,
13+
width: 10,
14+
height: 5,
15+
};
16+
17+
const runtimepath = fromFileUrl(new URL("../../../", import.meta.url));
18+
19+
test({
20+
mode: "all",
21+
name: "HelpComponent",
22+
prelude: [
23+
`set runtimepath+=${runtimepath}`,
24+
`runtime plugin/fall/*.vim`,
25+
],
26+
fn: async (denops, t) => {
27+
await t.step("render", async () => {
28+
await using component = new HelpComponent();
29+
await component.open(denops, dimension);
30+
await component.render(denops);
31+
await denops.cmd("redraw");
32+
33+
const info = component.info!;
34+
assertEquals(await fn.getbufline(denops, info.bufnr, 1, "$"), [
35+
"Page 1/0 ",
36+
]);
37+
38+
component.pages = [
39+
{
40+
content: [
41+
"**Help Page 1**",
42+
"**Introduction**",
43+
],
44+
},
45+
];
46+
await component.render(denops);
47+
await denops.cmd("redraw");
48+
49+
// Verify the rendered content
50+
assertEquals(await fn.getbufline(denops, info.bufnr, 1, "$"), [
51+
"Page 1/1 ",
52+
"**Help Page 1**",
53+
"**Introduction**",
54+
]);
55+
56+
component.pages = [
57+
{
58+
content: [
59+
"**Help Page 1**",
60+
"**Introduction**",
61+
],
62+
},
63+
{
64+
content: [
65+
"**Help Page 2**",
66+
"**Details**",
67+
],
68+
},
69+
];
70+
component.page = 2;
71+
await component.render(denops);
72+
await denops.cmd("redraw");
73+
74+
// Verify the content after page update
75+
assertEquals(await fn.getbufline(denops, info.bufnr, 1, "$"), [
76+
"Page 2/2 ",
77+
"**Help Page 2**",
78+
"**Details**",
79+
]);
80+
});
81+
},
82+
});

0 commit comments

Comments
 (0)