Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3076715

Browse files
committedAug 28, 2024
👍 implement options method with class
1 parent 016970e commit 3076715

File tree

2 files changed

+116
-117
lines changed

2 files changed

+116
-117
lines changed
 

‎.scripts/gen-option/format.ts

+18-117
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,12 @@
11
import type { Option, OptionScope, OptionType } from "./types.ts";
22

3-
const denops = "@denops/core";
4-
53
const translate: Record<string, string> = {
64
"default": "defaultValue",
75
"delete": "delete_",
86
"eval": "eval_",
97
"function": "function_",
108
};
119

12-
function defaultValue(type: OptionType): string {
13-
switch (type) {
14-
case "string":
15-
return `""`;
16-
case "number":
17-
return `0`;
18-
case "boolean":
19-
return `false`;
20-
default: {
21-
const unknownType: never = type;
22-
throw new Error(`Unknown type ${unknownType}`);
23-
}
24-
}
25-
}
26-
27-
function coerceValue(expr: string, type: OptionType): string {
28-
switch (type) {
29-
case "string":
30-
return `(${expr}) as string`;
31-
case "number":
32-
return `(${expr}) as number`;
33-
case "boolean":
34-
// Vim returns (0 | 1) so coerce to boolean.
35-
return `Boolean(${expr})`;
36-
default: {
37-
const unknownType: never = type;
38-
throw new Error(`Unknown type ${unknownType}`);
39-
}
40-
}
41-
}
42-
4310
export function formatDocs(docs: string): string[] {
4411
const lines = docs.replaceAll(/\*\//g, "* /").split("\n");
4512
const normalizedLines = lines.map((v) => ` * ${v}`.trimEnd());
@@ -51,13 +18,9 @@ function formatOption(option: Option): string[] {
5118
const name = translate[option.name] ?? option.name;
5219
const lines = [
5320
...formatDocs(docs),
54-
`export const ${name}: ${getOptionTypeName(scope, type)} = {`,
55-
...formatOptionBody(name, type),
56-
...(scope.includes("global") ? formatGlobalOptionBody(name, type) : []),
57-
...(scope.includes("local") ? formatLocalOptionBody(name, type) : []),
58-
...(scope.includes("local") ? formatBufferOptionBody(name, type) : []),
59-
...(scope.includes("local") ? formatWindowOptionBody(name, type) : []),
60-
`};`,
21+
`export const ${name}: ${getOptionTypeName(scope, type)} = ${
22+
getOptionConstructor(name, type)
23+
};`,
6124
"",
6225
];
6326
return lines;
@@ -73,90 +36,28 @@ function getOptionTypeName(scope: OptionScope[], type: OptionType): string {
7336
}
7437
}
7538

76-
function formatOptionBody(name: string, type: OptionType): string[] {
77-
const lines = [
78-
` async get(denops: Denops): Promise<${type}> {`,
79-
` const result = await options.get(denops, "${name}");`,
80-
` return ${coerceValue(`result ?? ${defaultValue(type)}`, type)};`,
81-
` },`,
82-
` set(denops: Denops, value: ${type}): Promise<void> {`,
83-
` return options.set(denops, "${name}", value);`,
84-
` },`,
85-
` reset(denops: Denops): Promise<void> {`,
86-
` return options.remove(denops, "${name}");`,
87-
` },`,
88-
];
89-
return lines;
90-
}
91-
92-
function formatGlobalOptionBody(name: string, type: OptionType): string[] {
93-
const lines = [
94-
` async getGlobal(denops: Denops): Promise<${type}> {`,
95-
` const result = await globalOptions.get(denops, "${name}");`,
96-
` return ${coerceValue(`result ?? ${defaultValue(type)}`, type)};`,
97-
` },`,
98-
` setGlobal(denops: Denops, value: ${type}): Promise<void> {`,
99-
` return globalOptions.set(denops, "${name}", value);`,
100-
` },`,
101-
` resetGlobal(denops: Denops): Promise<void> {`,
102-
` return globalOptions.remove(denops, "${name}");`,
103-
` },`,
104-
];
105-
return lines;
106-
}
107-
108-
function formatLocalOptionBody(name: string, type: OptionType): string[] {
109-
const lines = [
110-
` async getLocal(denops: Denops): Promise<${type}> {`,
111-
` const result = await localOptions.get(denops, "${name}");`,
112-
` return ${coerceValue(`result ?? ${defaultValue(type)}`, type)};`,
113-
` },`,
114-
` setLocal(denops: Denops, value: ${type}): Promise<void> {`,
115-
` return localOptions.set(denops, "${name}", value);`,
116-
` },`,
117-
` resetLocal(denops: Denops): Promise<void> {`,
118-
` return localOptions.remove(denops, "${name}");`,
119-
` },`,
120-
];
121-
return lines;
122-
}
123-
124-
function formatBufferOptionBody(name: string, type: OptionType): string[] {
125-
const lines = [
126-
` async getBuffer(denops: Denops, bufnr: number): Promise<${type}> {`,
127-
` const result = await getbufvar(denops, bufnr, "&${name}");`,
128-
` return ${coerceValue(`result ?? ${defaultValue(type)}`, type)};`,
129-
` },`,
130-
` setBuffer(denops: Denops, bufnr: number, value: ${type}): Promise<void> {`,
131-
` return setbufvar(denops, bufnr, "&${name}", value);`,
132-
` },`,
133-
];
134-
return lines;
135-
}
136-
137-
function formatWindowOptionBody(name: string, type: OptionType): string[] {
138-
const lines = [
139-
` async getWindow(denops: Denops, winnr: number): Promise<${type}> {`,
140-
` const result = await getwinvar(denops, winnr, "&${name}");`,
141-
` return ${coerceValue(`result ?? ${defaultValue(type)}`, type)};`,
142-
` },`,
143-
` setWindow(denops: Denops, winnr: number, value: ${type}): Promise<void> {`,
144-
` return setwinvar(denops, winnr, "&${name}", value);`,
145-
` },`,
146-
];
147-
return lines;
39+
function getOptionConstructor(name: string, type: OptionType): string {
40+
switch (type) {
41+
case "string":
42+
return `new StringOption("${name}")`;
43+
case "number":
44+
return `new NumberOption("${name}")`;
45+
case "boolean":
46+
return `new BooleanOption("${name}")`;
47+
default: {
48+
const unknownType: never = type;
49+
throw new Error(`Unknown type ${unknownType}`);
50+
}
51+
}
14852
}
14953

15054
export function format(options: Option[], root: string): string[] {
151-
const fn = `${root}/../function/mod.ts`;
152-
const variable = `${root}/../variable/mod.ts`;
15355
const types = `${root}/types.ts`;
56+
const utils = `${root}/_utils.ts`;
15457
const lines = [
15558
"// NOTE: This file is generated. Do NOT modify it manually.",
156-
`import type { Denops } from "${denops}";`,
157-
`import { getbufvar, setbufvar, getwinvar, setwinvar } from "${fn}";`,
158-
`import { globalOptions, localOptions, options } from "${variable}";`,
15959
`import type { GlobalOption, GlobalOrLocalOption, LocalOption } from "${types}";`,
60+
`import { BooleanOption, NumberOption, StringOption } from "${utils}";`,
16061
"",
16162
...options.map(formatOption),
16263
];

‎option/_utils.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import type { Denops } from "@denops/core";
2+
import { globalOptions, localOptions, options } from "../variable/option.ts";
3+
import {
4+
getbufvar,
5+
getwinvar,
6+
setbufvar,
7+
setwinvar,
8+
} from "./../function/mod.ts";
9+
import type { GlobalOrLocalOption } from "./types.ts";
10+
11+
type Coerce<T> = (value: unknown) => T;
12+
13+
class OptionImpl<TDefault> implements GlobalOrLocalOption<TDefault> {
14+
readonly #name: string;
15+
readonly #defaultValue: TDefault;
16+
readonly #coerce: Coerce<TDefault>;
17+
18+
constructor(name: string, defaultValue: TDefault, coerce: Coerce<TDefault>) {
19+
this.#name = name;
20+
this.#defaultValue = defaultValue;
21+
this.#coerce = coerce;
22+
}
23+
24+
async get(denops: Denops): Promise<TDefault> {
25+
const result = await options.get(denops, this.#name);
26+
return this.#coerce(result ?? this.#defaultValue);
27+
}
28+
29+
set(denops: Denops, value: TDefault): Promise<void> {
30+
return options.set(denops, this.#name, value);
31+
}
32+
33+
reset(denops: Denops): Promise<void> {
34+
return options.remove(denops, this.#name);
35+
}
36+
37+
async getGlobal(denops: Denops): Promise<TDefault> {
38+
const result = await globalOptions.get(denops, this.#name);
39+
return this.#coerce(result ?? this.#defaultValue);
40+
}
41+
42+
setGlobal(denops: Denops, value: TDefault): Promise<void> {
43+
return globalOptions.set(denops, this.#name, value);
44+
}
45+
46+
resetGlobal(denops: Denops): Promise<void> {
47+
return globalOptions.remove(denops, this.#name);
48+
}
49+
50+
async getLocal(denops: Denops): Promise<TDefault> {
51+
const result = await localOptions.get(denops, this.#name);
52+
return this.#coerce(result ?? this.#defaultValue);
53+
}
54+
55+
setLocal(denops: Denops, value: TDefault): Promise<void> {
56+
return localOptions.set(denops, this.#name, value);
57+
}
58+
59+
resetLocal(denops: Denops): Promise<void> {
60+
return localOptions.remove(denops, this.#name);
61+
}
62+
63+
async getBuffer(denops: Denops, bufnr: number): Promise<TDefault> {
64+
const result = await getbufvar(denops, bufnr, `&${this.#name}`);
65+
return this.#coerce(result ?? this.#defaultValue);
66+
}
67+
68+
setBuffer(denops: Denops, bufnr: number, value: TDefault): Promise<void> {
69+
return setbufvar(denops, bufnr, `&${this.#name}`, value);
70+
}
71+
72+
async getWindow(denops: Denops, winnr: number): Promise<TDefault> {
73+
const result = await getwinvar(denops, winnr, `&${this.#name}`);
74+
return this.#coerce(result ?? this.#defaultValue);
75+
}
76+
77+
setWindow(denops: Denops, winnr: number, value: TDefault): Promise<void> {
78+
return setwinvar(denops, winnr, `&${this.#name}`, value);
79+
}
80+
}
81+
82+
export class BooleanOption extends OptionImpl<boolean> {
83+
constructor(name: string) {
84+
super(name, false, Boolean);
85+
}
86+
}
87+
88+
export class NumberOption extends OptionImpl<number> {
89+
constructor(name: string) {
90+
super(name, 0, Number);
91+
}
92+
}
93+
94+
export class StringOption extends OptionImpl<string> {
95+
constructor(name: string) {
96+
super(name, "", String);
97+
}
98+
}

0 commit comments

Comments
 (0)
Please sign in to comment.