Skip to content

Commit

Permalink
docs: more examples
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoshihou514 committed Aug 8, 2024
1 parent c122927 commit 95698f3
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 51 deletions.
235 changes: 235 additions & 0 deletions CUSTOMIZE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# Creating new configuration
A tool is specified as follows:

```lua
{
-- specify an executable
cmd -- string: tool command
args -- string[]: command arguments
fname -- boolean: insert filename to args tail
stdin -- boolean: pass buffer contents into stdin

-- or provide your own logic
fn -- function: write your own logic for formatting / linting, more in ADVANCED.md

-- running condition
ignore_patterns -- string|string[]: don't run formatter when pattern match against file name
ignore_error -- boolean: when has lsp error ignore format
find -- string|string[]: format if the file is found in the lsp root dir

-- misc
env -- table<string, string>?: environment variables passed to cmd (key value pair)
timeout -- integer

-- special
parse -- function: linter only, parses linter output to neovim diagnostic
}
```

guard also tries to require them if you have `guard-collection`.

## Examples: formatters
Let's see a few formatters from `guard-collection`:

```lua
rustfmt = {
cmd = 'rustfmt',
args = { '--edition', '2021', '--emit', 'stdout' },
stdin = true,
}
```
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.

```lua
eslint_d = {
cmd = 'npx',
args = { 'eslint_d', '--fix-to-stdout', '--stdin', '--stdin-filename' },
fname = true,
stdin = true,
}

black = {
cmd = 'black',
args = { '--quiet', '-' },
stdin = true,
}
```

## Examples: linters
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`).

This sounds very cumbersome! Fortunately guard provides some api to make it smoother.

Let's see an example: clang-tidy
```bash
clang-tidy /tmp/test.c
```
<details>
<summary>Output</summary>
```
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "/tmp/test.c"
No compilation database found in /tmp or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
1 warning generated.
/tmp/test.c:6:20: warning: Division by zero [clang-analyzer-core.DivideZero]
6 | printf("%d", x / y);
| ~~^~~
/tmp/test.c:5:5: note: 'y' initialized to 0
5 | int y = 0;
| ^~~~~
/tmp/test.c:6:20: note: Division by zero
6 | printf("%d", x / y);
| ~~^~~
```
</details>

In this case we are most interested in this line:

```
/tmp/test.c:6:20: warning: Division by zero [clang-analyzer-core.DivideZero]
```
And we can identify some elements:
```
lnum = 6
col = 20
severity = warning
message = Division by zero
code = clang-analyzer-core.DivideZero
```
The following regex will give us the elements, and we just need them in a table
```lua
local xs = { line:match(":(%d+):(%d+):%s+(%w+):%s+(.-)%s+%[(.-)%]") }
local lookup = { ... } -- omitted
local result = {
lnum = xs[1],
col = xs[2],
-- The severity is a string, but neovim expects `:h vim.diagnostic.severity`
severity = lookup[xs[3]],
message = xs[4],
code = xs[5]
}
```

This pattern is encapsulated by `require("guard.lint").from_regex`

```lua
clang_tidy = {
cmd = 'clang-tidy',
args = { '--quiet' },
parse = lint.from_regex({
source = 'clang-tidy',
regex = ':(%d+):(%d+):%s+(%w+):%s+(.-)%s+%[(.-)%]',
groups = { 'lnum', 'col', 'severity', 'message', 'code' },
severities = {
information = lint.severities.info,
hint = lint.severities.info,
note = lint.severities.style,
},
}),
}
```
Another example:
```lua
ktlint = {
cmd = 'ktlint',
args = { '--log-level=error' },
fname = true,
parse = lint.from_regex({
source = 'ktlint',
regex = ':(%d+):(%d+): (.+) %((.-)%)',
groups = { 'lnum', 'col', 'message', 'code' },
-- severities defaults to info, warning, error, style
}),
}
```
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.

This pattern is encapsulated by `require("guard.lint").from_json`, an example:
```bash
cat /tmp/test.py | pylint --from-stdin true --output-format json
```
<details>
<summary>Output</summary>
```
[
{
"type": "convention",
"module": "true",
"obj": "",
"line": 89,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "true",
"symbol": "line-too-long",
"message": "Line too long (125/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "true",
"obj": "",
"line": 215,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "true",
"symbol": "line-too-long",
"message": "Line too long (108/100)",
"message-id": "C0301"
}
]
```
</details>

```lua
pylint = {
cmd = 'pylint',
args = { '--from-stdin', '--output-format', 'json' },
stdin = true,
parse = lint.from_json({
attributes = {
severity = 'type',
code = 'symbol',
},
severities = {
convention = lint.severities.info,
refactor = lint.severities.info,
informational = lint.severities.info,
fatal = lint.severities.error,
},
source = 'pylint',
}),
}
```

Another example:
```lua
ruff = {
cmd = 'ruff',
args = { '-n', '-e', '--output-format', 'json', '-', '--stdin-filename' },
stdin = true,
fname = true,
parse = lint.from_json({
attributes = {
severity = 'type',
-- if the json is very complex, pass a function
lnum = function(js)
return js['location']['row']
end,
col = function(js)
return js['location']['column']
end,
},
severities = {
E = lint.severities.error, -- pycodestyle errors
-- other severities omitted
},
source = 'ruff',
}),
}
```
56 changes: 5 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ require('guard').setup({
- `GuardDisable` disables auto format for the current buffer, you can also `GuardDisable 16` (the buffer number)
- Use `GuardEnable` to re-enable auto format, usage is the same as `GuardDisable`

### Example Configuration

Format c files with clang-format and lint with clang-tidy:

```lua
Expand Down Expand Up @@ -82,54 +80,10 @@ Lint all your files with `codespell`
ft('*'):lint('codespell')
```

### Custom Configuration

Easily setup your custom tool if not in the defaults or you do not want guard-collection bundled:

```
{
-- specify an executable
cmd -- string: tool command
args -- string[]: command arguments
fname -- boolean: insert filename to args tail
stdin -- boolean: pass buffer contents into stdin
-- or provide your own logic
fn -- function: write your own logic for formatting / linting, more in ADVANCED.md
-- running condition
ignore_patterns -- string|string[]: don't run formatter when pattern match against file name
ignore_error -- boolean: when has lsp error ignore format
find -- string|string[]: format if the file is found in the lsp root dir
-- misc
env -- table<string, string>?: environment variables passed to cmd (key value pair)
timeout -- integer
-- special
parse -- function: linter only, parses linter output to neovim diagnostic
}
```

For example, format your assembly with [asmfmt](https://github.com/klauspost/asmfmt):

```lua
ft('asm'):fmt({
cmd = 'asmfmt',
stdin = true
})
```

Consult the [builtin tools](https://github.com/nvimdev/guard-collection/tree/main/lua/guard-collection) if needed.

### Advanced configuration

[ADVANCED.md](https://github.com/nvimdev/guard.nvim/blob/main/ADVANCED.md) contains tiny tutorials to:

- Write your own formatting logic using the fn field
- Write your own linting logic using the fn field
- leverage guard's autocmds, say showing formatting status?
You can also easily create your own configuration that's not in `guard-collection`, see [CUSTOMIZE.md](./CUSTOMIZE.md).

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

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

0 comments on commit 95698f3

Please sign in to comment.