Skip to content

Commit f0fb8b7

Browse files
committed
👍 Add argument module
1 parent 99dc457 commit f0fb8b7

File tree

9 files changed

+475
-12
lines changed

9 files changed

+475
-12
lines changed

denops_std/README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,16 @@ for more details.
5454

5555
## Index
5656

57-
| Name | Description |
58-
| -------------------------- | ---------------------------------------------------------------- |
59-
| [`anonymous`](./anonymous) | A module to provide anonymous function |
60-
| [`autocmd`](./autocmd) | A module to provide helper functions to manage `autocmd` |
61-
| [`batch`](./batch) | A module to provide wrapper functions of `denops.batch()` |
62-
| [`buffer`](./buffer) | A module to provide helper functions to manage buffers |
63-
| [`bufname`](./bufname) | A module to provide helper functions to manage Vim's buffer name |
64-
| [`function`](./function) | A module to provide functions of Vim and Neovim native functions |
65-
| [`helper`](./helper) | A module to provide helper functions |
66-
| [`mapping`](./mapping) | A module to provide helper functions to manage mappings |
67-
| [`option`](./option) | A module to provide helper functions to manage options |
68-
| [`variable`](./variable) | A module to provide helper accessor functions to variables |
57+
| Name | Description |
58+
| -------------------------- | ---------------------------------------------------------------------- |
59+
| [`anonymous`](./anonymous) | A module to provide anonymous function |
60+
| [`argument`](./argument) | A module to provide helper functions to manage Vim's command arguments |
61+
| [`autocmd`](./autocmd) | A module to provide helper functions to manage `autocmd` |
62+
| [`batch`](./batch) | A module to provide wrapper functions of `denops.batch()` |
63+
| [`buffer`](./buffer) | A module to provide helper functions to manage buffers |
64+
| [`bufname`](./bufname) | A module to provide helper functions to manage Vim's buffer name |
65+
| [`function`](./function) | A module to provide functions of Vim and Neovim native functions |
66+
| [`helper`](./helper) | A module to provide helper functions |
67+
| [`mapping`](./mapping) | A module to provide helper functions to manage mappings |
68+
| [`option`](./option) | A module to provide helper functions to manage options |
69+
| [`variable`](./variable) | A module to provide helper accessor functions to variables |

denops_std/argument/README.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# argument
2+
3+
`argument` is a module to handle Vim's command arguments.
4+
5+
- [API documentation](https://doc.deno.land/https/deno.land/x/denops_std/argument/mod.ts)
6+
7+
## Usage
8+
9+
### parse/parseOpts/parseFlags
10+
11+
Use `parse()`, `parseOpts`, or `parseFlags` to parse Vim's command arguments
12+
like:
13+
14+
```typescript
15+
import { Denops } from "../mod.ts";
16+
import { parse, parseFlags, parseOpts } from "../argument/mod.ts";
17+
18+
export async function main(denops: Denops): Promise<void> {
19+
const args = [
20+
"++enc=sjis",
21+
"++ff=dos",
22+
"-f",
23+
"--foo=foo",
24+
"--bar=bar",
25+
"--bar=baz",
26+
"hello",
27+
"world",
28+
];
29+
const [opts, flags, residues] = parse(args);
30+
// Or use parseOpts/parseFlags instead
31+
//const [opts, residues] = parseOpts(args);
32+
//const [flags, residues] = parseFlags(args);
33+
34+
console.log(opts);
35+
// { "enc": "sjis", "ff": "dos" }
36+
37+
console.log(flags);
38+
// { "f": "", "foo": "foo", "bar": ["bar", "baz"] }
39+
40+
console.log(residues);
41+
// ["hello", "world"]
42+
}
43+
```
44+
45+
### validateOpts/validateFlags
46+
47+
Use `validateOpts` or `validateFlags` to validate if `opts` or `flags` has
48+
unknown attributes like:
49+
50+
```typescript
51+
import { Denops } from "../mod.ts";
52+
import {
53+
builtinOpts,
54+
parse,
55+
validateFlags,
56+
validateOpts,
57+
} from "../argument/mod.ts";
58+
59+
export async function main(denops: Denops): Promise<void> {
60+
const args = [
61+
"++enc=sjis",
62+
"++ff=dos",
63+
"-f",
64+
"--foo=foo",
65+
"--bar=bar",
66+
"--bar=baz",
67+
"hello",
68+
"world",
69+
];
70+
const [opts, flags, residues] = parse(args);
71+
72+
validateOpts(opts, ["enc", "ff"]);
73+
// Or use `builtinOpts` to validate Vim's builtin `++opts`
74+
//validateOpts(opts, builtinOpts);
75+
76+
validateFlags(flags, ["f", "foo", "bar"]);
77+
}
78+
```
79+
80+
### formatOpt/formatFlag
81+
82+
Use `formatOpt` or `formatFlag` to format `key` and `value` pair like:
83+
84+
```typescript
85+
import { Denops } from "../mod.ts";
86+
import { formatFlag, formatOpt } from "../argument/mod.ts";
87+
88+
export async function main(denops: Denops): Promise<void> {
89+
console.log(formatOpt("enc", "sjis"));
90+
// "++enc=sjis"
91+
92+
console.log(formatFlag("f", ""));
93+
// "-f"
94+
95+
console.log(formatFlag("foo", "value"));
96+
// "--foo=value"
97+
}
98+
```
99+
100+
### formatOpts/formatFlags
101+
102+
Use `formatOpts` or `formatFlags` to format `opts` or `flags` like:
103+
104+
```typescript
105+
import { Denops } from "../mod.ts";
106+
import { formatFlags, formatOpts, parse } from "../argument/mod.ts";
107+
108+
export async function main(denops: Denops): Promise<void> {
109+
const args = [
110+
"++enc=sjis",
111+
"++ff=dos",
112+
"-f",
113+
"--foo=foo",
114+
"--bar=bar",
115+
"--bar=baz",
116+
"hello",
117+
"world",
118+
];
119+
const [opts, flags, residues] = parse(args);
120+
121+
console.log(formatOpts(opts));
122+
// "++enc=sjis ++ff=dos"
123+
124+
console.log(formatFlags(flags));
125+
// "-f --foo=foo --bar=bar --bar=baz"
126+
}
127+
```

denops_std/argument/flags.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { parsePattern } from "./util.ts";
2+
3+
export type Flags = Record<string, string | string[] | undefined>;
4+
5+
const longPattern = /^--([a-zA-Z0-9-]+)(?:=(.*))?/;
6+
const shortPattern = /^-([a-zA-Z0-9])(.*)/;
7+
8+
/**
9+
* Parse string array to extract flags (-f/--flag).
10+
*/
11+
export function parseFlags(args: string[]): [Flags, string[]] {
12+
const patterns = [longPattern, shortPattern];
13+
const flags: Flags = {};
14+
const residue: string[] = [];
15+
loop:
16+
for (let i = 0; i < args.length; i++) {
17+
const arg = args[i];
18+
if (arg === "--") {
19+
residue.push(...args.slice(i));
20+
break;
21+
}
22+
for (const pattern of patterns) {
23+
const r = parsePattern(arg, pattern);
24+
if (r) {
25+
const [k, v] = r;
26+
const b = flags[k];
27+
if (b != undefined) {
28+
flags[k] = Array.isArray(b) ? [...b, v] : [b, v];
29+
} else {
30+
flags[k] = v;
31+
}
32+
continue loop;
33+
}
34+
}
35+
residue.push(arg);
36+
}
37+
return [flags, residue];
38+
}
39+
40+
/**
41+
* Validate if `flags` has unknown attributes.
42+
*/
43+
export function validateFlags(flags: Flags, knownAttributes: string[]): void {
44+
Object.keys(flags).forEach((v) => {
45+
if (!knownAttributes.includes(v)) {
46+
if (v.length === 1) {
47+
throw new Error(`Unknown flag '-${v}' is specified.`);
48+
} else {
49+
throw new Error(`Unknown flag '--${v}' is specified.`);
50+
}
51+
}
52+
});
53+
}
54+
55+
/**
56+
* Format `key` and `value` to construct string array.
57+
*/
58+
export function formatFlag(
59+
key: string,
60+
value: string | string[] | undefined,
61+
): string[] {
62+
if (value == undefined) {
63+
return [];
64+
}
65+
value = Array.isArray(value) ? value : [value];
66+
if (key.length === 1) {
67+
return value.map((v) => v ? `-${key}${v}` : `-${key}`);
68+
} else {
69+
return value.map((v) => v ? `--${key}=${v}` : `--${key}`);
70+
}
71+
}
72+
73+
/**
74+
* Format `flags` to construct string array.
75+
*/
76+
export function formatFlags(flags: Flags, includes?: string[]): string[] {
77+
let entries = Object.entries(flags);
78+
if (includes != undefined) {
79+
entries = entries.filter(([k, _]) => includes.includes(k));
80+
}
81+
return entries.map(([k, v]) => formatFlag(k, v)).flat();
82+
}

denops_std/argument/flags_test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { assertEquals } from "https://deno.land/std@0.133.0/testing/asserts.ts";
2+
import { parseFlags } from "./flags.ts";
3+
4+
Deno.test("parseFlags", () => {
5+
const [flags, residue] = parseFlags([
6+
"Gin",
7+
"++buffer",
8+
"status",
9+
"++ff=mac",
10+
"-unormal",
11+
"++enc=utf-8",
12+
"--ignore-submodules=all",
13+
"--ignore",
14+
"-v",
15+
"autoload/gin.vim",
16+
"--",
17+
"++buffer",
18+
"-v",
19+
"--ignore",
20+
"autoload/gin/debug.vim",
21+
]);
22+
assertEquals(flags, {
23+
u: "normal",
24+
"ignore-submodules": "all",
25+
ignore: "",
26+
v: "",
27+
});
28+
assertEquals(residue, [
29+
"Gin",
30+
"++buffer",
31+
"status",
32+
"++ff=mac",
33+
"++enc=utf-8",
34+
"autoload/gin.vim",
35+
"--",
36+
"++buffer",
37+
"-v",
38+
"--ignore",
39+
"autoload/gin/debug.vim",
40+
]);
41+
});

denops_std/argument/mod.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Opts, parseOpts } from "./opts.ts";
2+
import { Flags, parseFlags } from "./flags.ts";
3+
4+
export function parse(args: string[]): [Opts, Flags, string[]] {
5+
const [opts, intermediate] = parseOpts(args);
6+
const [flags, residue] = parseFlags(intermediate);
7+
return [opts, flags, residue];
8+
}
9+
10+
export * from "./opts.ts";
11+
export * from "./flags.ts";

denops_std/argument/mod_test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { assertEquals } from "https://deno.land/std@0.133.0/testing/asserts.ts";
2+
import { parse } from "./mod.ts";
3+
4+
Deno.test("parse", () => {
5+
const [opts, flags, residue] = parse([
6+
"Gin",
7+
"++buffer",
8+
"status",
9+
"++ff=mac",
10+
"-unormal",
11+
"++enc=utf-8",
12+
"--ignore-submodules=all",
13+
"--ignore",
14+
"-v",
15+
"autoload/gin.vim",
16+
"--",
17+
"++buffer",
18+
"-v",
19+
"--ignore",
20+
"autoload/gin/debug.vim",
21+
]);
22+
assertEquals(opts, {
23+
buffer: "",
24+
ff: "mac",
25+
enc: "utf-8",
26+
});
27+
assertEquals(flags, {
28+
u: "normal",
29+
"ignore-submodules": "all",
30+
ignore: "",
31+
v: "",
32+
});
33+
assertEquals(residue, [
34+
"Gin",
35+
"status",
36+
"autoload/gin.vim",
37+
"--",
38+
"++buffer",
39+
"-v",
40+
"--ignore",
41+
"autoload/gin/debug.vim",
42+
]);
43+
});
44+
45+
Deno.test("parse (README)", () => {
46+
const [opts, flags, residue] = parse([
47+
"++enc=sjis",
48+
"++ff=dos",
49+
"-f",
50+
"--foo=foo",
51+
"--bar=bar",
52+
"--bar=baz",
53+
"hello",
54+
"world",
55+
]);
56+
assertEquals(opts, {
57+
enc: "sjis",
58+
ff: "dos",
59+
});
60+
assertEquals(flags, {
61+
f: "",
62+
foo: "foo",
63+
bar: ["bar", "baz"],
64+
});
65+
assertEquals(residue, [
66+
"hello",
67+
"world",
68+
]);
69+
});

0 commit comments

Comments
 (0)