- Notifications
You must be signed in to change notification settings - Fork750
Alint.kakrc script is provided by default in Kakoune.
Its goal is to run an externallinter program asynchronously and parseits output, to:
List code diagnostics (warning, errors, other) in a new
*lint-output*
bufferAdd colored flags in thegutter column for each line that requiresyour attention
Display diagnostic messages and notes in inline information popups onthe relevant lines
In order to make the lint operations work, you must instruct Kakounehow to run your external linter by setting thelintcmd
option.
Kakoune expects a linter to output diagnostics in a certain formaton stdout. If messages are printed onstderr, they won’tbe read by the editor. The expected output format is documented inlint.kak:
declare-option \ -docstring %{ The shell command used by lint-buffer and lint-selections. It will be given thepath to a file containing the text to be linted, and must produce output in the format: {filename}:{line}:{column}: {kind}: {message} If the'kind' field contains'error', the message is treated as an error, otherwise it is assumed to be a warning. } \str lintcmd
You can set a different linter for any specific languages, using hooks. Forexample, for JavaScript, you can append the following snippet to yourkakrc
:
hookglobalWinSetOptionfiletype=javascript %{set-optionwindow lintcmd'eslint --format unix'}
In the above code, we seteslint as ourlintcmd
command. In order foreslint
to output diagnosticsthat can be understood by kakoune, use set the outputted format option tounix
Make also sure that eventual formatting capabilities of the linter are disabled.The purpose of a linting tool within the context of the:lint
command is tofocus on reporting errors and warnings, it is therefore not expected to modify the buffer.
The following sections document values for thelintcmd
option that workwith different linters.
Thelint.kak
script also offers the following commands:
# main linting functionsdef lint-buffer -docstring "Check the current buffer with a linter"def lint-selections -docstring "Check each selection with a linter"alias global lint lint-buffer# switchesdef lint-enable -docstring "Activate automatic diagnostics of the code"def lint-disable -docstring "Disable automatic diagnostics of the code"# navigation commandsdef lint-next-message -docstring "Jump to the next line that contains a lint message"def lint-prev-message -docstring "Jump to the previous line that contains a lint message"
hookglobalWinSetOptionfiletype=asciidoc %{set-optionwindow lintcmd"proselint"}
Note: Uses bazel_build filetype as defined inkakoune-extra-filetypes
hookglobalWinSetOptionfiletype=(bazel_build) %{set-optionwindow lintcmd %{ run() { cat $1 | buildifier -mode check -lint warn -format json $kak_buffile | jq -r'.files | .[0] | .warnings | .[] | "\(.start.line):\(.start.column): warning: \(.message)"' | sed -e"s|^|$kak_buffile:|" ; } && run }}
hookglobalWinSetOptionfiletype=c %{set-optionwindow lintcmd"cppcheck --language=c --enable=warning,style,information --template='{file}:{line}:{column}: {severity}: {message}' --suppress='*:*.h' 2>&1"}
hookglobalWinSetOptionfiletype=cpp %{set-optionwindow lintcmd"cppcheck --language=c++ --enable=warning,style,information --template='{file}:{line}:{column}: {severity}: {message}' --suppress='*:*.h' --suppress='*:*.hh' 2>&1"}
Using this linter requires theclj-kakoune-joker wrapper.
hookglobalWinSetOptionfiletype=clojure %{set-optionwindow lintcmd"clj-kj.sh"}
hookglobalWinSetOptionfiletype=css %{set-optionwindow lintcmd"npx stylelint --formatter unix --stdin-filename='%val{buffile}'"}
hookglobalWinSetOptionfiletype=d %{set-optionwindow lintcmd"dscanner -S --errorFormat '{filepath}:{line}:{column}: {type}: {message}'"}
Requirescredo
to be a dependency of your mix project. E.g. like so:
# mix.exsdefpdepsdo[# ...{:credo,"~> 1.4",only::dev},# ...]end
hookglobalWinSetOptionfiletype=elixir %{# NOTE: The `Elixir.CredoNaming.Check.Consistency.ModuleFilename` rule is# not supported because Kakoune moves the file to a temporary directory# before linting.set-optionwindow lintcmd"mix credo list --config-file=.credo.exs --format=flycheck --ignore-checks='Elixir.CredoNaming.Check.Consistency.ModuleFilename'"}
eslint-plugin-ember is required for eslint to work.To lint both the js code and the template code in the file:
hookglobalWinSetOptionfiletype=gjs %{setbuffer lintcmd'run () { cat "$1" | yarn --silent eslint --stdin --stdin-filename "$kak_buffile" --config .eslintrc.js -f unix; cat "$1" | yarn --silent ember-template-lint --format kakoune --no-ignore-pattern; } && run'}
hookglobalWinSetOptionfiletype=html %{set-optionwindow lintcmd"tidy -e --gnu-emacs yes --quiet yes 2>&1"}
hookglobalWinSetOptionfiletype=javascript %{set-optionwindow lintcmd'run() { cat "$1" |npx eslint -f unix --stdin --stdin-filename "$kak_buffile";} && run'}
I find the configuration above to be simpler, in case you find the two below to be overly-complex. Have been using this with .js, .vue, .svelte without issues.
hookglobalWinSetOptionfiletype=javascript %{set-optionwindow lintcmd'run() { cat "$1" | npx eslint -f unix --stdin --stdin-filename "$kak_buffile";} && run'# using npx to run local eslint over global# formatting with prettier `npm i prettier --save-dev`set-optionwindow formatcmd'npx prettier --stdin-filepath=${kak_buffile}'aliaswindow fix format2# the patched version, renamed to `format2`. lint-enable}# Formatting with eslint:define-command eslint-fix %{evaluate-commands -draft -no-hooks -save-regs'|'%sh{path_file_tmp=$(mktemp kak-formatter-XXXXXX)printf %s\\n"write -sync\"${path_file_tmp}\"nop %sh{ npx eslint --fix\"${path_file_tmp}\" }execute-keys '%|cat<space>$path_file_tmp<ret>'nop %sh{rm -f"${path_file_tmp}"}"}}
The above formatting command copies the contents of the buffer into a temporary file outside your project directory which may lead to wrong settings foreslint
. If you havejq available in your shell (or another JSON processor), you can do the following instead. It pipes the buffer intoeslint
onstdin
and replaces it with the fixed output fromeslint
:
define-command format-eslint -docstring %{ Formats thecurrentbuffer using eslint. Respects your local project setup in eslintrc.} %{evaluate-commands -draft -no-hooks -save-regs'|' %{# Select all to formatexecute-keys'%'# eslint does a fix-dry-run with a json formatter which results in a JSON output to stdout that includes the fixed file.# jq then extracts the fixed file output from the JSON. -j returns the raw output without any escaping.set-register'|' %{ format_out="$(mktemp)" cat | \ npx eslint --format json \ --fix-dry-run \ --stdin \ --stdin-filename"$kak_buffile" | \ jq -j".[].output" >"$format_out" if [ $? -eq0 ] && [ $(wc -c <"$format_out") -gt4 ]; then cat"$format_out" else printf'eval -client %s %%{ fail eslint formatter returned an error %s }\n'"$kak_client""$?" | kak -p"$kak_session" printf"%s""$kak_quoted_selection" fi rm -f"$format_out" }# Replace all with content from register:execute-keys'|<ret>' }}
hookglobalWinSetOptionfiletype=json %{set-optionwindow lintcmd %{ run() { cat --"$1" | jq2>&1 | awk -v filename="$1"'/ at line [0-9]+, column [0-9]+$/ { line=$(NF - 2); column=$NF; sub(/ at line [0-9]+, column [0-9]+$/, ""); printf "%s:%d:%d: error: %s", filename, line, column, $0; }'; } && run }}
hookglobalWinSetOptionfiletype=markdown %{set-optionwindow lintcmd"proselint"}
hookglobalWinSetOptionfiletype=perl %{set-optionwindow lintcmd %{ perlcritic --quiet --verbose'%f:%l:%c: severity %s: %m [%p]\n'"}
perlcritic doesn’t necessarily have the criteria of "warning" or "error".Instead, things it points out are given severity numbers.lint.kak
will classify all of these aswarnings, since "error:" doesn’t exist in the line.
You might wish to change its output to distinguish between warnings and errors with sed.
hookglobalWinSetOptionfiletype=perl %{set-optionwindow lintcmd %{ \ pc() { \ perlcritic --quiet --verbose'%f:%l:%c: severity %s: %m [%p]\n'"$1" \ | sed \ -e'/: severity 5:/ s/: severity 5:/: error:/' \ -e'/: severity [0-4]:/ s/: severity [0-4]:/: warning:/'; \ } && pc \ }}
hookglobalWinSetOptionfiletype=python %{set-optionwindow lintcmd %{ run() { pylint --msg-template='{path}:{line}:{column}: {category}: {msg_id}: {msg} ({symbol})'"$1" | awk -F:'BEGIN { OFS=":" } { if (NF == 6) { $3 += 1; print } }'; } && run }}
where we needed to modifypylint
output format to use 1-based columns and emit an error category that is parseable bylint.kak
.
hookglobalWinSetOptionfiletype=python %{set-optionwindow lintcmd"flake8 --filename='*' --format='%%(path)s:%%(row)d:%%(col)d: error: %%(text)s' --ignore=E121,E123,E126,E226,E242,E704,W503,W504,E501,E221,E127,E128,E129,F405"}
hookglobalWinSetOptionfiletype=python %{set-optionwindow lintcmd"mypy --show-column-numbers"}
When using Ruby version management, it might be convenient to set link torubocop in/usr/local/bin/
like sosudo ln -s path/to/rubocop /usr/local/bin/rubocop
. If usingrvm, usewrappers
folder to locate rubocop binary:~/.rvm/gems/ruby-x.x.x/wrappers/rubocop
hookglobalWinSetOptionfiletype=ruby %{set-optionwindow lintcmd'rubocop -l --format emacs'}
If your application usesbundler, you can also execute rubocop withbundle
:
hookglobalWinSetOptionfiletype=ruby %{set-optionwindow lintcmd %{ run() { cat"${1}" | bundleexec rubocop --format emacs -s"${kak_buffile}"2>&1; } && run }}
hookglobalWinSetOptionfiletype=sh %{set-optionwindow lintcmd"shellcheck -fgcc -Cnever"}
# yaml linter hook global WinSetOption filetype=yaml %{# set-option window lintcmd "yamllint -f parsable"set-optionwindow lintcmd %{ run() {# change [message-type] to message-type: yamllint -f parsable"$1" | sed's/ \[\(.*\)\] / \1: /' } && run } }
- Normal mode commands
- Avoid the escape key
- Implementing user mode (Leader key)
- Kakoune explain
- Kakoune TV