Skip to content

Commit 8ca4e34

Browse files
committed
👍 Improve window resizing behavior
1 parent 0a835d7 commit 8ca4e34

File tree

13 files changed

+253
-223
lines changed

13 files changed

+253
-223
lines changed

denops/fall/component/_component.ts

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,16 @@ export type ComponentProperties = {
1010
zindex?: number;
1111
};
1212

13-
export type ComponentParams = ComponentProperties & {
14-
dimension: Readonly<Dimension>;
15-
};
16-
1713
export type ComponentInfo = {
1814
bufnr: number;
1915
winid: number;
16+
dimension: Readonly<Dimension>;
2017
};
2118

2219
export type Component = AsyncDisposable & {
2320
open(
2421
denops: Denops,
22+
dimension: Readonly<Partial<Dimension>>,
2523
option?: { signal?: AbortSignal },
2624
): Promise<AsyncDisposable>;
2725

@@ -58,67 +56,66 @@ export type Component = AsyncDisposable & {
5856
* ╰─────────────────────────────────────╯
5957
* ```
6058
*/
61-
export abstract class BaseComponent implements Component {
62-
#window?: popup.PopupWindow;
63-
#dimension: Readonly<Dimension>;
59+
export class BaseComponent implements Component {
60+
#opened?: {
61+
readonly window: popup.PopupWindow;
62+
readonly dimension: Readonly<Dimension>;
63+
};
6464
protected properties: Readonly<ComponentProperties>;
6565

66-
constructor({ dimension, ...properties }: Readonly<ComponentParams>) {
67-
this.#dimension = dimension;
66+
constructor(properties: Readonly<ComponentProperties> = {}) {
6867
this.properties = properties;
6968
}
7069

7170
get info(): Readonly<ComponentInfo> | undefined {
72-
if (!this.#window) {
71+
if (!this.#opened) {
7372
return undefined;
7473
}
75-
const { bufnr, winid } = this.#window;
76-
return { bufnr, winid };
77-
}
78-
79-
protected get dimension(): Readonly<Dimension> {
80-
return this.#dimension;
74+
const { bufnr, winid } = this.#opened.window;
75+
return { bufnr, winid, dimension: this.#opened.dimension };
8176
}
8277

8378
async open(
8479
denops: Denops,
80+
dimension: Readonly<Dimension>,
8581
{ signal }: { signal?: AbortSignal } = {},
8682
): Promise<AsyncDisposable> {
87-
if (this.#window) {
83+
if (this.#opened) {
8884
return this;
8985
}
9086
signal?.throwIfAborted();
91-
this.#window = await popup.open(denops, {
92-
...this.#dimension,
93-
...this.properties,
94-
relative: "editor",
95-
anchor: "NW",
96-
highlight: {
97-
normal: "FallNormal",
98-
border: "FallBorder",
99-
},
100-
noRedraw: true,
101-
});
87+
this.#opened = {
88+
window: await popup.open(denops, {
89+
...dimension,
90+
...this.properties,
91+
relative: "editor",
92+
anchor: "NW",
93+
highlight: {
94+
normal: "FallNormal",
95+
border: "FallBorder",
96+
},
97+
noRedraw: true,
98+
}),
99+
dimension,
100+
};
102101
return this;
103102
}
104103

105-
async close(): Promise<void> {
106-
await this.#window?.close();
107-
this.#window = undefined;
108-
}
109-
110104
async move(
111105
denops: Denops,
112106
dimension: Readonly<Partial<Dimension>>,
113107
{ signal }: { signal?: AbortSignal } = {},
114108
): Promise<void> {
115-
this.#dimension = {
116-
...this.#dimension,
117-
...dimension,
118-
};
119-
if (this.#window) {
109+
if (this.#opened) {
110+
this.#opened = {
111+
...this.#opened,
112+
dimension: {
113+
...this.#opened.dimension,
114+
...dimension,
115+
},
116+
};
120117
signal?.throwIfAborted();
121-
await popup.config(denops, this.#window.winid, {
118+
await popup.config(denops, this.#opened.window.winid, {
122119
...dimension,
123120
relative: "editor",
124121
noRedraw: true,
@@ -135,19 +132,26 @@ export abstract class BaseComponent implements Component {
135132
...this.properties,
136133
...properties,
137134
};
138-
if (this.#window) {
135+
if (this.#opened) {
139136
signal?.throwIfAborted();
140-
await popup.config(denops, this.#window.winid, {
137+
await popup.config(denops, this.#opened.window.winid, {
141138
...properties,
142139
noRedraw: true,
143140
});
144141
}
145142
}
146143

147-
abstract render(
144+
render(
148145
_denops: Denops,
149146
_option?: { signal?: AbortSignal },
150-
): Promise<true | void>;
147+
): Promise<true | void> {
148+
return Promise.resolve(true);
149+
}
150+
151+
async close(): Promise<void> {
152+
await this.#opened?.window.close();
153+
this.#opened = undefined;
154+
}
151155

152156
async [Symbol.asyncDispose]() {
153157
await this.close();

denops/fall/component/_component_test.ts

Lines changed: 46 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { Denops } from "jsr:@denops/std@^7.3.0";
21
import { test } from "jsr:@denops/test@^3.0.4";
32
import * as fn from "jsr:@denops/std@^7.3.0/function";
43
import { assertEquals, assertNotEquals } from "jsr:@std/assert@^1.0.7";
@@ -8,15 +7,6 @@ import { DOUBLE_BORDER } from "../../@fall/builtin/theme/double.ts";
87
import { screentext } from "./_testutil.ts";
98
import { BaseComponent } from "./_component.ts";
109

11-
class TestComponent extends BaseComponent {
12-
render(
13-
_denops: Denops,
14-
_option?: { signal?: AbortSignal },
15-
): Promise<true | void> {
16-
return Promise.resolve(true);
17-
}
18-
}
19-
2010
const dimension = {
2111
col: 1,
2212
row: 1,
@@ -31,31 +21,30 @@ test(
3121
await t.step(
3222
"info is `undefined` if the component is not opened",
3323
async () => {
34-
await using component = new TestComponent({ dimension });
24+
await using component = new BaseComponent();
3525
assertEquals(component.info, undefined);
3626
},
3727
);
3828

3929
await t.step("info is a value if the component is opened", async () => {
40-
await using component = new TestComponent({ dimension });
41-
await component.open(denops);
30+
await using component = new BaseComponent();
31+
await component.open(denops, dimension);
4232
assertNotEquals(component.info, undefined);
4333
});
4434
},
4535
);
4636

4737
test("nvim", "Component", async (denops, t) => {
4838
await t.step("open opens the component window", async () => {
49-
await using component = new TestComponent({
50-
dimension: {
51-
col: 1,
52-
row: 1,
53-
width: 5,
54-
height: 5,
55-
},
39+
await using component = new BaseComponent({
5640
border: SINGLE_BORDER,
5741
});
58-
await component.open(denops);
42+
await component.open(denops, {
43+
col: 1,
44+
row: 1,
45+
width: 5,
46+
height: 5,
47+
});
5948
await denops.cmd("redraw");
6049

6150
const info = component.info!;
@@ -92,33 +81,36 @@ test("nvim", "Component", async (denops, t) => {
9281
await t.step(
9382
"open does nothing if the component window is already opened",
9483
async () => {
95-
await using component = new TestComponent({
96-
dimension: {
97-
col: 1,
98-
row: 1,
99-
width: 5,
100-
height: 5,
101-
},
84+
await using component = new BaseComponent({
10285
border: SINGLE_BORDER,
10386
});
104-
await component.open(denops);
105-
await component.open(denops);
87+
await component.open(denops, {
88+
col: 1,
89+
row: 1,
90+
width: 5,
91+
height: 5,
92+
});
93+
await component.open(denops, {
94+
col: 1,
95+
row: 1,
96+
width: 5,
97+
height: 5,
98+
});
10699
},
107100
);
108101

109102
await t.step(
110103
"move moves the component window",
111104
async () => {
112-
await using component = new TestComponent({
113-
dimension: {
114-
col: 1,
115-
row: 1,
116-
width: 5,
117-
height: 5,
118-
},
105+
await using component = new BaseComponent({
119106
border: SINGLE_BORDER,
120107
});
121-
await component.open(denops);
108+
await component.open(denops, {
109+
col: 1,
110+
row: 1,
111+
width: 5,
112+
height: 5,
113+
});
122114
await component.move(denops, {
123115
col: 10,
124116
row: 10,
@@ -167,13 +159,7 @@ test("nvim", "Component", async (denops, t) => {
167159
await t.step(
168160
"move does nothing if the component window is not opened",
169161
async () => {
170-
await using component = new TestComponent({
171-
dimension: {
172-
col: 1,
173-
row: 1,
174-
width: 5,
175-
height: 5,
176-
},
162+
await using component = new BaseComponent({
177163
border: SINGLE_BORDER,
178164
});
179165
await component.move(denops, {
@@ -188,16 +174,15 @@ test("nvim", "Component", async (denops, t) => {
188174
await t.step(
189175
"update updates the component window",
190176
async () => {
191-
await using component = new TestComponent({
192-
dimension: {
193-
col: 1,
194-
row: 1,
195-
width: 5,
196-
height: 5,
197-
},
177+
await using component = new BaseComponent({
198178
border: SINGLE_BORDER,
199179
});
200-
await component.open(denops);
180+
await component.open(denops, {
181+
col: 1,
182+
row: 1,
183+
width: 5,
184+
height: 5,
185+
});
201186
await component.update(denops, {
202187
title: "Test",
203188
border: DOUBLE_BORDER,
@@ -239,13 +224,7 @@ test("nvim", "Component", async (denops, t) => {
239224
await t.step(
240225
"update does nothing if the component window is not opened",
241226
async () => {
242-
await using component = new TestComponent({
243-
dimension: {
244-
col: 1,
245-
row: 1,
246-
width: 5,
247-
height: 5,
248-
},
227+
await using component = new BaseComponent({
249228
border: SINGLE_BORDER,
250229
});
251230
await component.update(denops, {
@@ -256,16 +235,15 @@ test("nvim", "Component", async (denops, t) => {
256235
);
257236

258237
await t.step("close closes the component window", async () => {
259-
await using component = new TestComponent({
260-
dimension: {
261-
col: 1,
262-
row: 1,
263-
width: 5,
264-
height: 5,
265-
},
238+
await using component = new BaseComponent({
266239
border: SINGLE_BORDER,
267240
});
268-
await component.open(denops);
241+
await component.open(denops, {
242+
col: 1,
243+
row: 1,
244+
width: 5,
245+
height: 5,
246+
});
269247
await denops.cmd("redraw");
270248

271249
const info = component.info!;

0 commit comments

Comments
 (0)