Skip to content

Commit 95698f3

Browse files
committed
docs: more examples
1 parent c122927 commit 95698f3

File tree

2 files changed

+240
-51
lines changed

2 files changed

+240
-51
lines changed

CUSTOMIZE.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Creating new configuration
2+
A tool is specified as follows:
3+
4+
```lua
5+
{
6+
-- specify an executable
7+
cmd -- string: tool command
8+
args -- string[]: command arguments
9+
fname -- boolean: insert filename to args tail
10+
stdin -- boolean: pass buffer contents into stdin
11+
12+
-- or provide your own logic
13+
fn -- function: write your own logic for formatting / linting, more in ADVANCED.md
14+
15+
-- running condition
16+
ignore_patterns -- string|string[]: don't run formatter when pattern match against file name
17+
ignore_error -- boolean: when has lsp error ignore format
18+
find -- string|string[]: format if the file is found in the lsp root dir
19+
20+
-- misc
21+
env -- table<string, string>?: environment variables passed to cmd (key value pair)
22+
timeout -- integer
23+
24+
-- special
25+
parse -- function: linter only, parses linter output to neovim diagnostic
26+
}
27+
```
28+
29+
guard also tries to require them if you have `guard-collection`.
30+
31+
## Examples: formatters
32+
Let's see a few formatters from `guard-collection`:
33+
34+
```lua
35+
rustfmt = {
36+
cmd = 'rustfmt',
37+
args = { '--edition', '2021', '--emit', 'stdout' },
38+
stdin = true,
39+
}
40+
```
41+
NB: Sometimes you may wish to run multiple formatters sequentially, this is only possible if you have `stdin` set ([or if you are using fn](./ADVANCED.md)) for all your formatters. This design is intentional for keeping buffer contents "valid". However, if you only wish to use one formatter, then letting the tool itself to do the IO _may be_ faster.
42+
43+
```lua
44+
eslint_d = {
45+
cmd = 'npx',
46+
args = { 'eslint_d', '--fix-to-stdout', '--stdin', '--stdin-filename' },
47+
fname = true,
48+
stdin = true,
49+
}
50+
51+
black = {
52+
cmd = 'black',
53+
args = { '--quiet', '-' },
54+
stdin = true,
55+
}
56+
```
57+
58+
## Examples: linters
59+
In addition to all the formatter fields, a linter needs to provide a `parse` function that takes the linter output and turns it into neovim diagnostics (`:h vim.Diagnostic`).
60+
61+
This sounds very cumbersome! Fortunately guard provides some api to make it smoother.
62+
63+
Let's see an example: clang-tidy
64+
```bash
65+
clang-tidy /tmp/test.c
66+
```
67+
<details>
68+
<summary>Output</summary>
69+
```
70+
Error while trying to load a compilation database:
71+
Could not auto-detect compilation database for file "/tmp/test.c"
72+
No compilation database found in /tmp or any parent directory
73+
fixed-compilation-database: Error while opening fixed database: No such file or directory
74+
json-compilation-database: Error while opening JSON database: No such file or directory
75+
Running without flags.
76+
1 warning generated.
77+
/tmp/test.c:6:20: warning: Division by zero [clang-analyzer-core.DivideZero]
78+
6 | printf("%d", x / y);
79+
| ~~^~~
80+
/tmp/test.c:5:5: note: 'y' initialized to 0
81+
5 | int y = 0;
82+
| ^~~~~
83+
/tmp/test.c:6:20: note: Division by zero
84+
6 | printf("%d", x / y);
85+
| ~~^~~
86+
```
87+
</details>
88+
89+
In this case we are most interested in this line:
90+
91+
```
92+
/tmp/test.c:6:20: warning: Division by zero [clang-analyzer-core.DivideZero]
93+
```
94+
And we can identify some elements:
95+
```
96+
lnum = 6
97+
col = 20
98+
severity = warning
99+
message = Division by zero
100+
code = clang-analyzer-core.DivideZero
101+
```
102+
The following regex will give us the elements, and we just need them in a table
103+
```lua
104+
local xs = { line:match(":(%d+):(%d+):%s+(%w+):%s+(.-)%s+%[(.-)%]") }
105+
local lookup = { ... } -- omitted
106+
local result = {
107+
lnum = xs[1],
108+
col = xs[2],
109+
-- The severity is a string, but neovim expects `:h vim.diagnostic.severity`
110+
severity = lookup[xs[3]],
111+
message = xs[4],
112+
code = xs[5]
113+
}
114+
```
115+
116+
This pattern is encapsulated by `require("guard.lint").from_regex`
117+
118+
```lua
119+
clang_tidy = {
120+
cmd = 'clang-tidy',
121+
args = { '--quiet' },
122+
parse = lint.from_regex({
123+
source = 'clang-tidy',
124+
regex = ':(%d+):(%d+):%s+(%w+):%s+(.-)%s+%[(.-)%]',
125+
groups = { 'lnum', 'col', 'severity', 'message', 'code' },
126+
severities = {
127+
information = lint.severities.info,
128+
hint = lint.severities.info,
129+
note = lint.severities.style,
130+
},
131+
}),
132+
}
133+
```
134+
Another example:
135+
```lua
136+
ktlint = {
137+
cmd = 'ktlint',
138+
args = { '--log-level=error' },
139+
fname = true,
140+
parse = lint.from_regex({
141+
source = 'ktlint',
142+
regex = ':(%d+):(%d+): (.+) %((.-)%)',
143+
groups = { 'lnum', 'col', 'message', 'code' },
144+
-- severities defaults to info, warning, error, style
145+
}),
146+
}
147+
```
148+
Figuring out the patterns can take a while, so for tools that support json output, it's usually easier to take the json, put it into a table, and get the respective key.
149+
150+
This pattern is encapsulated by `require("guard.lint").from_json`, an example:
151+
```bash
152+
cat /tmp/test.py | pylint --from-stdin true --output-format json
153+
```
154+
<details>
155+
<summary>Output</summary>
156+
```
157+
[
158+
{
159+
"type": "convention",
160+
"module": "true",
161+
"obj": "",
162+
"line": 89,
163+
"column": 0,
164+
"endLine": null,
165+
"endColumn": null,
166+
"path": "true",
167+
"symbol": "line-too-long",
168+
"message": "Line too long (125/100)",
169+
"message-id": "C0301"
170+
},
171+
{
172+
"type": "convention",
173+
"module": "true",
174+
"obj": "",
175+
"line": 215,
176+
"column": 0,
177+
"endLine": null,
178+
"endColumn": null,
179+
"path": "true",
180+
"symbol": "line-too-long",
181+
"message": "Line too long (108/100)",
182+
"message-id": "C0301"
183+
}
184+
]
185+
186+
```
187+
</details>
188+
189+
```lua
190+
pylint = {
191+
cmd = 'pylint',
192+
args = { '--from-stdin', '--output-format', 'json' },
193+
stdin = true,
194+
parse = lint.from_json({
195+
attributes = {
196+
severity = 'type',
197+
code = 'symbol',
198+
},
199+
severities = {
200+
convention = lint.severities.info,
201+
refactor = lint.severities.info,
202+
informational = lint.severities.info,
203+
fatal = lint.severities.error,
204+
},
205+
source = 'pylint',
206+
}),
207+
}
208+
```
209+
210+
Another example:
211+
```lua
212+
ruff = {
213+
cmd = 'ruff',
214+
args = { '-n', '-e', '--output-format', 'json', '-', '--stdin-filename' },
215+
stdin = true,
216+
fname = true,
217+
parse = lint.from_json({
218+
attributes = {
219+
severity = 'type',
220+
-- if the json is very complex, pass a function
221+
lnum = function(js)
222+
return js['location']['row']
223+
end,
224+
col = function(js)
225+
return js['location']['column']
226+
end,
227+
},
228+
severities = {
229+
E = lint.severities.error, -- pycodestyle errors
230+
-- other severities omitted
231+
},
232+
source = 'ruff',
233+
}),
234+
}
235+
```

README.md

Lines changed: 5 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ require('guard').setup({
5252
- `GuardDisable` disables auto format for the current buffer, you can also `GuardDisable 16` (the buffer number)
5353
- Use `GuardEnable` to re-enable auto format, usage is the same as `GuardDisable`
5454

55-
### Example Configuration
56-
5755
Format c files with clang-format and lint with clang-tidy:
5856

5957
```lua
@@ -82,54 +80,10 @@ Lint all your files with `codespell`
8280
ft('*'):lint('codespell')
8381
```
8482

85-
### Custom Configuration
86-
87-
Easily setup your custom tool if not in the defaults or you do not want guard-collection bundled:
88-
89-
```
90-
{
91-
-- specify an executable
92-
cmd -- string: tool command
93-
args -- string[]: command arguments
94-
fname -- boolean: insert filename to args tail
95-
stdin -- boolean: pass buffer contents into stdin
96-
97-
-- or provide your own logic
98-
fn -- function: write your own logic for formatting / linting, more in ADVANCED.md
99-
100-
-- running condition
101-
ignore_patterns -- string|string[]: don't run formatter when pattern match against file name
102-
ignore_error -- boolean: when has lsp error ignore format
103-
find -- string|string[]: format if the file is found in the lsp root dir
104-
105-
-- misc
106-
env -- table<string, string>?: environment variables passed to cmd (key value pair)
107-
timeout -- integer
108-
109-
-- special
110-
parse -- function: linter only, parses linter output to neovim diagnostic
111-
}
112-
```
113-
114-
For example, format your assembly with [asmfmt](https://github.com/klauspost/asmfmt):
115-
116-
```lua
117-
ft('asm'):fmt({
118-
cmd = 'asmfmt',
119-
stdin = true
120-
})
121-
```
122-
123-
Consult the [builtin tools](https://github.com/nvimdev/guard-collection/tree/main/lua/guard-collection) if needed.
124-
125-
### Advanced configuration
126-
127-
[ADVANCED.md](https://github.com/nvimdev/guard.nvim/blob/main/ADVANCED.md) contains tiny tutorials to:
128-
129-
- Write your own formatting logic using the fn field
130-
- Write your own linting logic using the fn field
131-
- leverage guard's autocmds, say showing formatting status?
83+
You can also easily create your own configuration that's not in `guard-collection`, see [CUSTOMIZE.md](./CUSTOMIZE.md).
13284

133-
### Supported Tools
85+
For more niche use cases, [ADVANCED.md](./ADVANCED.md) demonstrates how to:
13486

135-
See [here](https://github.com/nvimdev/guard-collection) for an exhaustive list.
87+
- Write your own formatting logic using the `fn` field
88+
- Write your own linting logic using the `fn` field
89+
- leverage guard's autocmds to create a status line component

0 commit comments

Comments
 (0)