(pronounced /ˈpɪkᵊlz/)

Inspired by tools like ale,
null-ls,
none-ls, and
diagnostic-languageserver,
and conform, pickls
offers a
unified way to configure command-line linters, formatters and LLMs with editor
LSP integration.
- Integrate command-line linting and formatting tools with your IDE.
- Configure multiple linters and formatters for any language. This is ideal for projects with toolchains lacking native LSP integration.
- Has a built-in code action for multi-LLM "Inline Assist" which can be used to simultaneously query multiple LLMs for assistance. Currently OpenAI and Ollama are supported.
- Supports dynamic invocation of ctags to provide workspace symbol information. (See configuration notes below.)
diagnosticProvider
(spec)documentFormattingProvider
(spec)textDocumentSync
(spec)workspaceSymbolProvider
(spec)codeAction
(spec)
- You'd like to have LSP support for LLM completion as a code action.
- Avoid installing and configuring separate plugins or language servers for each tool in your workflow.
- Utilize a seamless LSP integration for command-line oriented toolchains.
Pickls is available via crates.io here.
Ensure you have a recent stable
Rust toolchain and the cargo binary directory
in your path:
cargo install pickls
Consider using pickls-debug-runner
to run from source, which is helpful for
development purposes.
Complete configuration source code is available here.
User-level configuration lives in "$XDG_CONFIG_HOME/pickls/pickls.yaml
where
$XDG_CONFIG_HOME
defaults to "$HOME"/.config
. Pickls will respect your
$XDG_CONFIG_HOME
if it is set.
Project-level configuration is not yet implemented, but is on the roadmap.
---
ai:
inline_assistants:
- provider: ollama
model: tinyllama
- provider: ollama
model: llama3.2:latest
- provider: openai
model: gpt-4o
- provider: openai
model: gpt-4o-mini
symbols:
source: universal-ctags # Currently only universal-ctags is supported.
ctags_timeout_ms: 500 # 500ms is the default timeout.
languages:
c: &c-settings
formatters:
- program: clang-format
args: ["-"]
cpp: *c-settings
dockerfile:
linters:
- program: hadolint
args:
- --no-color
- --format
- tty
- '-'
description_match: 3
line_match: 1
pattern: '-:(\d+) [^ ]+ (\w+): (.*)'
severity_match: 2
use_stderr: false
use_stdin: true
lua:
linters:
- program: luacheck
args:
- --formatter
- plain
- --ranges
- --no-color
- '-'
pattern: 'stdin:(\d+):(\d+)-(\d+): (.*)'
line_match: 1
start_col_match: 2
end_col_match: 3
description_match: 4
use_stderr: false
use_stdin: true
formatters:
- program: lua-format
args:
- "--indent-width=2"
- "--spaces-inside-table-braces"
- "--align-table-field"
- "--break-before-table-rb"
- "--chop-down-table"
markdown:
formatters:
- program: mdformat
args:
- --wrap
- '80'
- '-'
python:
root_markers:
- .git
- pyproject.toml
- setup.py
- mypy.ini
formatters:
- program: autoimport
args: ["-"]
- program: isort
args: ["-", "-d"]
- program: ruff
args: ["check", "--exit-zero", "--fix", "--stdin-filename", "$filename"]
- program: ruff
args:
- format
- --stdin-filename
- $filename
linters:
# Try out [dmypyls](https://github.com/wbbradly/dmypyls).
- program: mypy
args:
- --show-column-numbers
- --show-error-end
- --hide-error-codes
- --hide-error-context
- --no-color-output
- --no-error-summary
- --no-pretty
- --shadow-file
- $filename
- /dev/stdin
- $filename
pattern: '(.*):(\d+):(\d+):\d+:(\d+): error: (.*)'
filename_match: 1
line_match: 2
start_col_match: 3
end_col_match: 4
description_match: 5
use_stderr: false
use_stdin: true
- program: ruff
args:
- check
- --stdin-filename
- $filename
pattern: '(.*):(\d+):(\d+): (.*)'
filename_match: 1
line_match: 2
start_col_match: 3
description_match: 4
use_stderr: false
use_stdin: true
sh: &sh
linters:
- program: shellcheck
args: ["-f", "gcc", "-"]
pattern: '(.*):(\d+):(\d+): (\w+): (.*)'
filename_match: 1
line_match: 2
start_col_match: 3
severity_match: 4
description_match: 5
use_stderr: false
use_stdin: true
bash: *sh
shell script: *sh
toml:
linters:
- program: tomllint
args: ["-"]
pattern: '(.*):(\d+):(\d+): error: (.*)'
filename_match: 1
line_match: 2
start_col_match: 3
description_match: 4
use_stderr: true
use_stdin: true
yaml:
linters:
- program: yamllint
args: ["-f", "parsable", "-"]
pattern: '.*:(\d+):(\d+): \[(.*)\] (.*) \((.*)\)'
line_match: 1
start_col_match: 2
severity_match: 3
description_match: 4
use_stderr: false
use_stdin: true
Note the usage of YAML anchors and references in order to handle different language names for the same formats.
Enable pickls
for all Neovim buffers:
vim.api.nvim_create_autocmd({ "BufRead" }, {
group = vim.api.nvim_create_augroup("pickls-bufread", { clear = true }),
callback = function(_)
if vim.fn.executable("pickls") ~= 0 then
vim.lsp.start({
name = "pickls",
cmd = { "pickls", vim.api.nvim_buf_get_name(0) },
root_dir = vim.fs.root(0, { ".git", "pyproject.toml", "setup.py", "Cargo.toml", "go.mod" }),
}, {
bufnr = 0,
reuse_client = function(_, _) return false end,
})
else
vim.notify("Pickls executable not found. See pickls-debug-runner for setup instructions.")
end
end,
})
-- Invoke LSP formatting on save.
vim.api.nvim_create_autocmd("BufWritePre", {
callback = function() vim.lsp.buf.format() end
})
-- You'll want to enable a shortcut for code actions in order to trigger inline-assist.
vim.keymap.set('n', '<leader>a', function() vim.lsp.buf.code_action() end)
To use pickls
in Zed, install the
pickls-zed extension. Use the
following command:
git clone https://github.com/wbbradley/pickls-zed "$HOME"/src/pickls-zed
Note that Zed supports formatting via command-line out of the box (see
format_on_save
), so you don't really need to use pickls
for formatting in
Zed. However, I've included it in the configuration here for demonstration
purposes.
If you encounter issues with pickls
, please open an issue
here. When logging an issue,
please include the following information:
- How you have configured
pickls
in your editor. - Any relevant lines from
"$HOME"/.local/state/pickls/pickls.log
.