Skip to content

Commit 7a390c9

Browse files
lambdalisueclaude
andcommitted
feat(source): add command source for listing user-defined commands
Implements a new source that lists all user-defined Vim commands with their definitions, attributes, and completion settings. Optionally includes buffer-local and builtin commands. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a45992e commit 7a390c9

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

builtin/source/command.ts

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import * as fn from "@denops/std/function";
2+
3+
import { defineSource, type Source } from "../../source.ts";
4+
5+
type Detail = {
6+
/**
7+
* Command name
8+
*/
9+
name: string;
10+
11+
/**
12+
* Command definition/replacement text
13+
*/
14+
definition: string;
15+
16+
/**
17+
* Command attributes (bang, range, etc.)
18+
*/
19+
attributes: string;
20+
21+
/**
22+
* Whether the command is buffer-local
23+
*/
24+
bufferLocal: boolean;
25+
26+
/**
27+
* Number of arguments the command accepts
28+
*/
29+
nargs: string;
30+
31+
/**
32+
* Completion type
33+
*/
34+
complete?: string;
35+
};
36+
37+
export type CommandOptions = {
38+
/**
39+
* Whether to include buffer-local commands.
40+
* @default true
41+
*/
42+
includeBufferLocal?: boolean;
43+
44+
/**
45+
* Whether to include builtin commands.
46+
* @default false
47+
*/
48+
includeBuiltin?: boolean;
49+
};
50+
51+
/**
52+
* Creates a Source that generates items from user-defined Vim commands.
53+
*
54+
* This Source retrieves all user-defined commands and generates items
55+
* for each one, showing their definition and attributes.
56+
*
57+
* @param options - Options to customize command listing.
58+
* @returns A Source that generates items representing commands.
59+
*/
60+
export function command(
61+
options: Readonly<CommandOptions> = {},
62+
): Source<Detail> {
63+
const includeBufferLocal = options.includeBufferLocal ?? true;
64+
const includeBuiltin = options.includeBuiltin ?? false;
65+
66+
return defineSource(async function* (denops, _params, { signal }) {
67+
// Get user commands
68+
const commandOutput = await denops.call("execute", "command") as string;
69+
signal?.throwIfAborted();
70+
71+
// Parse command output
72+
const lines = commandOutput.trim().split("\n").filter((line) =>
73+
line.trim()
74+
);
75+
let items: Array<{
76+
id: number;
77+
value: string;
78+
detail: Detail;
79+
}> = [];
80+
81+
let id = 0;
82+
for (const line of lines) {
83+
// Skip header line
84+
if (line.includes("Name") && line.includes("Args")) {
85+
continue;
86+
}
87+
88+
// Parse command line
89+
// Format: " Name Args Address Complete Definition"
90+
const match = line.match(
91+
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S*)\s+(.*)$/,
92+
);
93+
if (!match) {
94+
continue;
95+
}
96+
97+
const [, name, nargs, address, complete, definition] = match;
98+
99+
// Check if it's a buffer-local command
100+
const bufferLocal = name.startsWith("b:");
101+
102+
// Skip buffer-local commands if not included
103+
if (bufferLocal && !includeBufferLocal) {
104+
continue;
105+
}
106+
107+
// Skip builtin commands if not included (they start with uppercase)
108+
if (!includeBuiltin && /^[A-Z]/.test(name) && !name.includes(":")) {
109+
continue;
110+
}
111+
112+
// Build attributes string
113+
const attributes: string[] = [];
114+
if (nargs !== "0") {
115+
attributes.push(`nargs=${nargs}`);
116+
}
117+
if (address !== ".") {
118+
attributes.push(`addr=${address}`);
119+
}
120+
if (complete) {
121+
attributes.push(`complete=${complete}`);
122+
}
123+
124+
// Format display value
125+
const attrStr = attributes.length > 0
126+
? ` (${attributes.join(", ")})`
127+
: "";
128+
const localStr = bufferLocal ? " [buffer]" : "";
129+
const truncatedDef = definition.length > 50
130+
? definition.substring(0, 47) + "..."
131+
: definition;
132+
133+
items.push({
134+
id: id++,
135+
value: `:${name}${localStr}${attrStr}${truncatedDef}`,
136+
detail: {
137+
name,
138+
definition,
139+
attributes: attrStr,
140+
bufferLocal,
141+
nargs,
142+
complete: complete || undefined,
143+
},
144+
});
145+
}
146+
147+
// Get completion list if needed
148+
if (includeBuiltin) {
149+
const builtinCommands = await fn.getcompletion(
150+
denops,
151+
"",
152+
"command",
153+
) as string[];
154+
155+
for (const cmd of builtinCommands) {
156+
// Skip if already in the list
157+
if (items.some((item) => item.detail.name === cmd)) {
158+
continue;
159+
}
160+
161+
items.push({
162+
id: id++,
163+
value: `:${cmd} [builtin]`,
164+
detail: {
165+
name: cmd,
166+
definition: "(builtin command)",
167+
attributes: "",
168+
bufferLocal: false,
169+
nargs: "?",
170+
complete: undefined,
171+
},
172+
});
173+
}
174+
}
175+
176+
yield* items;
177+
});
178+
}

builtin/source/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is generated by gen-mod.ts
22
export * from "./buffer.ts";
33
export * from "./colorscheme.ts";
4+
export * from "./command.ts";
45
export * from "./file.ts";
56
export * from "./helptag.ts";
67
export * from "./highlight.ts";

0 commit comments

Comments
 (0)