Skip to content

Commit c4e2c32

Browse files
lambdalisueclaude
andcommitted
feat: add buffer info renderer for displaying buffer metadata
Implements a renderer that appends buffer information such as modification status, file type, line count, and other properties to item labels. Supports showing buffer variables and optional colorization based on buffer state. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 905815e commit c4e2c32

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

builtin/renderer/buffer_info.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import * as fn from "@denops/std/function";
2+
import { defineRenderer, type Renderer } from "../../renderer.ts";
3+
4+
type Detail = {
5+
bufnr: number;
6+
};
7+
8+
export type BufferInfoOptions = {
9+
/**
10+
* Which buffer information to display.
11+
* @default ["modified", "type", "line_count"]
12+
*/
13+
fields?: Array<"modified" | "readonly" | "type" | "line_count" | "path">;
14+
15+
/**
16+
* Whether to show buffer variables.
17+
* @default false
18+
*/
19+
showVariables?: boolean;
20+
21+
/**
22+
* Whether to colorize output (using ANSI codes).
23+
* @default false
24+
*/
25+
colorize?: boolean;
26+
};
27+
28+
/**
29+
* Creates a Renderer that appends buffer information to item labels.
30+
*
31+
* This Renderer adds buffer metadata such as modification status,
32+
* file type, line count, and other properties to each item's label.
33+
*
34+
* @param options - Options to customize buffer info display.
35+
* @returns A Renderer that adds buffer information to item labels.
36+
*/
37+
export function bufferInfo(
38+
options: Readonly<BufferInfoOptions> = {},
39+
): Renderer<Detail> {
40+
const fields = options.fields ?? ["modified", "type", "line_count"];
41+
const showVariables = options.showVariables ?? false;
42+
const colorize = options.colorize ?? false;
43+
44+
return defineRenderer(async (denops, { items }) => {
45+
// Process items in parallel
46+
await Promise.all(
47+
items.map(async (item) => {
48+
const { bufnr } = item.detail;
49+
const parts: string[] = [];
50+
51+
try {
52+
// Get buffer information
53+
const bufinfo = await fn.getbufinfo(denops, bufnr);
54+
if (!bufinfo || bufinfo.length === 0) {
55+
return;
56+
}
57+
58+
const buf = bufinfo[0];
59+
60+
// Add requested fields
61+
for (const field of fields) {
62+
switch (field) {
63+
case "modified": {
64+
if (buf.changed) {
65+
parts.push("[+]");
66+
}
67+
break;
68+
}
69+
70+
case "readonly": {
71+
// Check if buffer is readonly
72+
const readonly = await denops.eval(
73+
`getbufvar(${bufnr}, "&readonly")`,
74+
) as number;
75+
if (readonly) {
76+
parts.push("[RO]");
77+
}
78+
break;
79+
}
80+
81+
case "type": {
82+
// Get filetype
83+
const filetype = await denops.eval(
84+
`getbufvar(${bufnr}, "&filetype")`,
85+
) as string;
86+
if (filetype) {
87+
parts.push(`(${filetype})`);
88+
}
89+
break;
90+
}
91+
92+
case "line_count": {
93+
const lineCount = buf.linecount;
94+
if (lineCount !== undefined) {
95+
parts.push(`${lineCount}L`);
96+
}
97+
break;
98+
}
99+
100+
case "path": {
101+
// Get full path
102+
const fullpath = await fn.fnamemodify(
103+
denops,
104+
buf.name,
105+
":p",
106+
) as string;
107+
if (fullpath && fullpath !== buf.name) {
108+
parts.push(fullpath);
109+
}
110+
break;
111+
}
112+
}
113+
}
114+
115+
// Add buffer variables if requested
116+
if (showVariables) {
117+
const varNames = [
118+
"&modified",
119+
"&readonly",
120+
"&modifiable",
121+
"&buflisted",
122+
];
123+
const vars: string[] = [];
124+
125+
for (const varName of varNames) {
126+
const value = await denops.eval(
127+
`getbufvar(${bufnr}, "${varName}")`,
128+
);
129+
if (value) {
130+
vars.push(`${varName.substring(1)}=${value}`);
131+
}
132+
}
133+
134+
if (vars.length > 0) {
135+
parts.push(`{${vars.join(",")}}`);
136+
}
137+
}
138+
139+
// Combine parts and append to label
140+
if (parts.length > 0) {
141+
const info = parts.join(" ");
142+
if (colorize) {
143+
// Add color based on buffer state
144+
if (buf.changed) {
145+
item.label = `${item.label} \x1b[33m${info}\x1b[0m`; // Yellow for modified
146+
} else if (
147+
fields.includes("readonly") && parts.includes("[RO]")
148+
) {
149+
item.label = `${item.label} \x1b[31m${info}\x1b[0m`; // Red for readonly
150+
} else {
151+
item.label = `${item.label} \x1b[90m${info}\x1b[0m`; // Gray for normal
152+
}
153+
} else {
154+
item.label = `${item.label} ${info}`;
155+
}
156+
}
157+
} catch {
158+
// If buffer info fails, skip
159+
}
160+
}),
161+
);
162+
});
163+
}

0 commit comments

Comments
 (0)