From c1054151f81610a7e21fabd2cdd281e327a4c6e1 Mon Sep 17 00:00:00 2001 From: mityu Date: Wed, 25 Dec 2024 23:58:13 +0900 Subject: [PATCH] :+1: Add getbufinfo() helper function When a funcref appears in buffer-local variables, calling getbufinfo() from denops world will fail due to the error on serializing its return value using json_encode(). To avoid this, we add a wrapper function of getbufinfo() to filter-out buffer-local variables from its return value. --- deno.jsonc | 1 + helper/getbufinfo.ts | 52 ++++++++++++++++++++++++++ helper/getbufinfo_test.ts | 77 +++++++++++++++++++++++++++++++++++++++ helper/mod.ts | 1 + 4 files changed, 131 insertions(+) create mode 100644 helper/getbufinfo.ts create mode 100644 helper/getbufinfo_test.ts diff --git a/deno.jsonc b/deno.jsonc index 0a135e8e..e6cf5845 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -20,6 +20,7 @@ "./helper/echo": "./helper/echo.ts", "./helper/execute": "./helper/execute.ts", "./helper/expr_string": "./helper/expr_string.ts", + "./helper/getbufinfo": "./helper/getbufinfo.ts", "./helper/input": "./helper/input.ts", "./helper/keymap": "./helper/keymap.ts", "./helper/load": "./helper/load.ts", diff --git a/helper/getbufinfo.ts b/helper/getbufinfo.ts new file mode 100644 index 00000000..67cb7687 --- /dev/null +++ b/helper/getbufinfo.ts @@ -0,0 +1,52 @@ +import type { Denops } from "../mod.ts"; +import type { BufInfo } from "../function/types.ts"; +import type { BufNameArg, GetBufInfoDictArg } from "../function/buffer.ts"; + +/** + * Get buffer information. + * + * This is same as getbufinfo() function in Vim script, but filters the + * 'variables' entry in order to avoid errors when returning values from Vim + * script world into Deno world due to appearances of Funcrefs in buffer-local + * variables. + * + * ```typescript + * import type { Entrypoint } from "jsr:@denops/std"; + * import { getbufinfo } from "jsr:@denops/std/helper/getbufinfo"; + * + * export const main: Entrypoint = async (denops) => { + * console.log( + * await getbufinfo(denops, await denops.call("bufnr") as number), + * ); + * } + * ``` + */ +export function getbufinfo( + denops: Denops, + buf?: BufNameArg, +): Promise; +export function getbufinfo( + denops: Denops, + dict?: GetBufInfoDictArg, +): Promise; +export async function getbufinfo( + denops: Denops, + ...args: unknown[] +): Promise { + const bufinfos = await denops.eval( + "map(call('getbufinfo', l:args), {_, v -> filter(v, {k -> k !=# 'variables'})})", + { + args: args, + }, + ) as Record< + keyof BufInfo, + unknown + >[]; + return bufinfos.map((bufinfo) => ({ + ...bufinfo, + changed: !!bufinfo.changed, + hidden: !!bufinfo.hidden, + listed: !!bufinfo.listed, + loaded: !!bufinfo.loaded, + } as unknown as BufInfo)); +} diff --git a/helper/getbufinfo_test.ts b/helper/getbufinfo_test.ts new file mode 100644 index 00000000..afbc0ebf --- /dev/null +++ b/helper/getbufinfo_test.ts @@ -0,0 +1,77 @@ +import { getbufinfo } from "./getbufinfo.ts"; +import { assertEquals, assertFalse } from "@std/assert"; +import { assert, is } from "@core/unknownutil"; +import { test } from "@denops/test"; +import type { BufInfo } from "../function/types.ts"; + +test({ + mode: "all", + name: "buffer", + fn: async (denops, t) => { + await t.step({ + name: "getbufinfo()", + fn: async () => { + await denops.cmd("enew!"); + await denops.cmd("new foo"); + await denops.call("setline", 1, "abcdef"); + await denops.cmd("new bar"); + await denops.cmd("hide"); + await denops.cmd("new baz"); + await denops.cmd("bunload!"); + + const actual = await getbufinfo(denops); + assert(actual, is.ArrayOf((x): x is BufInfo => is.Record(x))); + assertEquals(actual.length, 4); + assertEquals( + actual.map(({ changed, hidden, listed, loaded }) => ( + { changed, hidden, listed, loaded } + )), + [ + { + // [No Name] + changed: false, + hidden: false, + listed: true, + loaded: true, + }, + { + // foo + changed: true, + hidden: false, + listed: true, + loaded: true, + }, + { + // bar + changed: false, + hidden: true, + listed: true, + loaded: true, + }, + { + // baz + changed: false, + hidden: false, + listed: true, + loaded: false, + }, + ], + "boolean properties are invalid.", + ); + }, + }); + await denops.cmd("1,$bwipeout!"); + + await t.step({ + name: "getbufinfo() will filter buffer-local variables", + fn: async () => { + await denops.cmd("enew!"); + await denops.cmd("let b:var = 0"); + + const actual = await getbufinfo(denops); + assertEquals(actual.length, 1); + assertFalse("variables" in actual[0]); + }, + }); + }, +}); diff --git a/helper/mod.ts b/helper/mod.ts index 9f102397..c8efd1cd 100644 --- a/helper/mod.ts +++ b/helper/mod.ts @@ -6,6 +6,7 @@ export * from "./echo.ts"; export * from "./execute.ts"; export * from "./expr_string.ts"; +export * from "./getbufinfo.ts"; export * from "./input.ts"; export * from "./keymap.ts"; export * from "./load.ts";