Skip to content

Commit 5b0de60

Browse files
Added more arguments
1 parent 5d5774b commit 5b0de60

8 files changed

Lines changed: 192 additions & 34 deletions

File tree

.prettierrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"tabWidth": 4,
33
"overrides": [
44
{
5-
"files": "*.yaml",
5+
"files": "*.{yaml,md}",
66
"options": {
77
"tabWidth": 2
88
}

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,48 @@
22

33
Linting and formatting tools for Talon and Cursorless.
44

5-
## Guidelines
5+
## CLI
66

7-
- Each pre-commit hook should have a matching npm binary with the same name.
8-
- Binaries ending with `-fmt` are formatters by default and turn into linters/checkers with the `--check` argument.
9-
- (Future) binaries ending with `-check` are linters by default and turn into fixers with the `--fix` argument.
7+
```sh
8+
talon-fmt [options] [file/dir/glob ...]
9+
snippet-fmt [options] [file/dir/glob ...]
10+
tree-sitter-fmt [options] [file/dir/glob ...]
11+
```
12+
13+
### Options
14+
15+
| Option | Meaning | Default |
16+
| -------------------- | -------------------------------- | ------- |
17+
| `--help` | Show help | |
18+
| `--version` | Show version | |
19+
| `--check` | Check formatting without writing | |
20+
| `--indent-tabs` | Use tabs for indentation | |
21+
| `--indent-width <n>` | Set indentation width | `4` |
22+
| `--line-width <n>` | Set preferred maximum line width | `80` |
23+
| `--column-width <n>` | Set aligned left-column width | |
1024

1125
## Exit codes
1226

1327
| Code | Information |
1428
| ---- | ----------------------------------- |
1529
| 0 | Everything formatted properly |
16-
| 1 | Something wasnt formatted properly |
30+
| 1 | Something wasn't formatted properly |
1731
| 2 | Runtime error |
32+
33+
## Pre commit
34+
35+
```yaml
36+
repos:
37+
- repo: https://github.com/cursorless-dev/talon-tools
38+
rev: v0.1.0
39+
hooks:
40+
- talon-fmt
41+
- snippet-fmt
42+
- tree-sitter-fmt
43+
```
44+
45+
## Guidelines
46+
47+
- Each pre-commit hook should have a matching npm binary with the same name.
48+
- Binaries ending with `-fmt` are formatters by default and turn into linters/checkers with the `--check` argument.
49+
- (Future) binaries ending with `-check` are linters by default and turn into fixers with the `--fix` argument.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
"prepack": "npm run build",
2929
"build": "tsx ./src/build.ts",
3030
"clean": "rm -rf out",
31-
"lint": "npm run lint:fmt && npm run lint:ts",
32-
"lint:fmt": "prettier --check .",
31+
"lint": "npm run lint:ts &&npm run lint:fmt",
3332
"lint:ts": "tsc -p . && eslint src",
34-
"fix": "npm run fix:fmt && npm run fix:ts",
35-
"fix:fmt": "prettier --write --list-different .",
33+
"lint:fmt": "prettier --check .",
34+
"fix": "npm run fix:ts && npm run fix:fmt",
3635
"fix:ts": "eslint src --fix",
36+
"fix:fmt": "prettier --write --list-different .",
3737
"test": "tsx src/test/runAllTests.ts"
3838
},
3939
"dependencies": {

src/test/cli.test.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { formatFile, formatFiles, mainFormatStdin } from "../cli/cli.js";
77
import type { CLI, ParsedArgs } from "../types.js";
88
import { EXIT_FAIL, EXIT_OK } from "../util/constants.js";
99
import { parseArgs } from "../util/parseArgs.js";
10+
import { getDefaultArguments } from "../util/getDefaultArguments.js";
1011

1112
suite("CLI", () => {
1213
test("formats a file in place", async () => {
@@ -143,35 +144,70 @@ suite("CLI", () => {
143144
});
144145

145146
test("parses check mode", () => {
146-
const expected: ParsedArgs = {
147+
const expected = getArguments({
147148
filePatterns: ["a.txt", "b.txt"],
148-
help: false,
149-
version: false,
150149
check: true,
151-
};
150+
});
152151
const actual = parseArgs(["--check", "a.txt", "b.txt"]);
153152

154153
assert.deepEqual(actual, expected);
155154
});
156155

157156
test("parses check mode and end-of-options marker", () => {
158-
const expected: ParsedArgs = {
157+
const expected = getArguments({
159158
filePatterns: ["--check"],
160-
help: false,
161-
version: false,
162159
check: true,
163-
};
160+
});
164161
const actual = parseArgs(["--check", "--", "--check"]);
165162

166163
assert.deepEqual(actual, expected);
167164
});
168165

166+
test("parses tabs and width arguments", () => {
167+
const expected = getArguments({
168+
filePatterns: ["a.txt"],
169+
help: false,
170+
version: false,
171+
check: false,
172+
indentTabs: true,
173+
indentWidth: 2,
174+
lineWidth: 80,
175+
columnWidth: 24,
176+
});
177+
const actual = parseArgs([
178+
"--indent-tabs",
179+
"--indent-width",
180+
"2",
181+
"--line-width",
182+
"80",
183+
"--column-width",
184+
"24",
185+
"a.txt",
186+
]);
187+
188+
assert.deepEqual(actual, expected);
189+
});
190+
169191
test("rejects unknown arguments", () => {
170192
assert.throws(
171193
() => parseArgs(["--check", "--write"]),
172194
/Unknown argument: --write/,
173195
);
174196
});
197+
198+
test("rejects missing width values", () => {
199+
assert.throws(
200+
() => parseArgs(["--indent-width"]),
201+
/Missing value for argument: --indent-width/,
202+
);
203+
});
204+
205+
test("rejects invalid width values", () => {
206+
assert.throws(
207+
() => parseArgs(["--line-width", "0"]),
208+
/Invalid value for --line-width: 0/,
209+
);
210+
});
175211
});
176212

177213
async function createTempFile(
@@ -230,3 +266,10 @@ async function captureStreamWrite<T>(
230266
stream.write = originalWrite;
231267
}
232268
}
269+
270+
function getArguments(args: Partial<ParsedArgs>) {
271+
return {
272+
...getDefaultArguments(),
273+
...args,
274+
};
275+
}

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ export interface ParsedArgs {
1010
help: boolean;
1111
version: boolean;
1212
check: boolean;
13+
indentTabs: boolean;
14+
indentWidth: number;
15+
lineWidth: number;
16+
columnWidth: number | undefined;
1317
}

src/util/getDefaultArguments.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { ParsedArgs } from "../types.js";
2+
3+
export function getDefaultArguments(): ParsedArgs {
4+
return {
5+
filePatterns: [],
6+
help: false,
7+
version: false,
8+
check: false,
9+
indentTabs: false,
10+
indentWidth: 4,
11+
lineWidth: 80,
12+
columnWidth: undefined,
13+
};
14+
}

src/util/parseArgs.ts

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
22

33
import type { ParsedArgs } from "../types.js";
4+
import { getDefaultArguments } from "./getDefaultArguments.js";
45

5-
export const KNOWN_ARGUMENTS = ["--help", "--version", "--check"] as const;
6+
export const KNOWN_FLAG_ARGUMENTS = [
7+
"--help",
8+
"--version",
9+
"--check",
10+
"--indent-tabs",
11+
] as const;
612

7-
type KnownArgument = (typeof KNOWN_ARGUMENTS)[number];
8-
type ArgHandler = (parsedArgs: ParsedArgs) => void;
13+
export const KNOWN_VALUE_ARGUMENTS = [
14+
"--indent-width",
15+
"--line-width",
16+
"--column-width",
17+
] as const;
918

10-
const ARG_HANDLERS: Record<KnownArgument, ArgHandler> = {
19+
type FlagArg = (typeof KNOWN_FLAG_ARGUMENTS)[number];
20+
type ValueArg = (typeof KNOWN_VALUE_ARGUMENTS)[number];
21+
type KnownArg = FlagArg | ValueArg;
22+
23+
type FlagHandler = (parsedArgs: ParsedArgs) => void;
24+
25+
type ValueHandler = (
26+
parsedArgs: ParsedArgs,
27+
argName: KnownArg,
28+
value: string,
29+
) => void;
30+
31+
const FLAG_ARG_HANDLERS: Record<FlagArg, FlagHandler> = {
1132
"--help": (parsedArgs) => {
1233
parsedArgs.help = true;
1334
},
@@ -17,15 +38,25 @@ const ARG_HANDLERS: Record<KnownArgument, ArgHandler> = {
1738
"--check": (parsedArgs) => {
1839
parsedArgs.check = true;
1940
},
41+
"--indent-tabs": (parsedArgs) => {
42+
parsedArgs.indentTabs = true;
43+
},
44+
};
45+
46+
const VALUE_ARG_HANDLERS: Record<ValueArg, ValueHandler> = {
47+
"--indent-width": (parsedArgs, argName, value) => {
48+
parsedArgs.indentWidth = parsePositiveInteger(argName, value);
49+
},
50+
"--line-width": (parsedArgs, argName, value) => {
51+
parsedArgs.lineWidth = parsePositiveInteger(argName, value);
52+
},
53+
"--column-width": (parsedArgs, argName, value) => {
54+
parsedArgs.columnWidth = parsePositiveInteger(argName, value);
55+
},
2056
};
2157

2258
export function parseArgs(argv: string[]): ParsedArgs {
23-
const result: ParsedArgs = {
24-
filePatterns: [],
25-
help: false,
26-
version: false,
27-
check: false,
28-
};
59+
const result = getDefaultArguments();
2960

3061
for (let i = 0; i < argv.length; i++) {
3162
const arg = argv[i];
@@ -36,10 +67,22 @@ export function parseArgs(argv: string[]): ParsedArgs {
3667
break;
3768
}
3869

39-
const handler = ARG_HANDLERS[arg as KnownArgument];
70+
const flagHandler = FLAG_ARG_HANDLERS[arg as FlagArg];
4071

41-
if (handler != null) {
42-
handler(result);
72+
if (flagHandler != null) {
73+
flagHandler(result);
74+
continue;
75+
}
76+
77+
const valueHandler = VALUE_ARG_HANDLERS[arg as ValueArg];
78+
79+
if (valueHandler != null) {
80+
const value = argv[i + 1];
81+
if (value == null) {
82+
throw new Error(`Missing value for argument: ${arg}`);
83+
}
84+
valueHandler(result, arg as ValueArg, value);
85+
i++;
4386
continue;
4487
}
4588

@@ -52,3 +95,13 @@ export function parseArgs(argv: string[]): ParsedArgs {
5295

5396
return result;
5497
}
98+
99+
function parsePositiveInteger(argName: KnownArg, value: string): number {
100+
const parsed = Number.parseInt(value, 10);
101+
102+
if (!Number.isInteger(parsed) || parsed <= 0) {
103+
throw new Error(`Invalid value for ${argName}: ${value}`);
104+
}
105+
106+
return parsed;
107+
}

src/util/printHelp.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
import type { CLI } from "../types.js";
2-
import { KNOWN_ARGUMENTS } from "./parseArgs.js";
2+
import { KNOWN_FLAG_ARGUMENTS, KNOWN_VALUE_ARGUMENTS } from "./parseArgs.js";
33

44
export function printHelp(cli: CLI) {
5-
const args = KNOWN_ARGUMENTS.join("|");
6-
console.log(`Usage: ${cli.binName} [${args}] [file/dir/glob ...]`);
5+
console.log(`Usage: ${cli.binName} [options] [file/dir/glob ...]`);
6+
console.log("");
7+
console.log("Flags:");
8+
9+
for (const option of KNOWN_FLAG_ARGUMENTS) {
10+
console.log(` ${option}`);
11+
}
12+
13+
console.log("");
14+
console.log("Options:");
15+
16+
for (const option of KNOWN_VALUE_ARGUMENTS) {
17+
console.log(` ${option} <n>`);
18+
}
719
}

0 commit comments

Comments
 (0)