summaryrefslogtreecommitdiff
path: root/autoload
diff options
context:
space:
mode:
Diffstat (limited to 'autoload')
m---------autoload/auto-pairs0
-rw-r--r--autoload/detection/editorconfig.kak60
-rw-r--r--autoload/detection/file.kak26
-rw-r--r--autoload/detection/modeline.kak123
-rw-r--r--autoload/filetype/apl.kak86
-rw-r--r--autoload/filetype/arch-linux.kak4
-rw-r--r--autoload/filetype/asciidoc.kak90
-rw-r--r--autoload/filetype/awk.kak88
-rw-r--r--autoload/filetype/c-family.kak476
-rw-r--r--autoload/filetype/cabal.kak88
-rw-r--r--autoload/filetype/clojure.kak237
-rw-r--r--autoload/filetype/cmake.kak36
-rw-r--r--autoload/filetype/coffee.kak92
-rw-r--r--autoload/filetype/conf.kak30
-rw-r--r--autoload/filetype/coq.kak127
-rw-r--r--autoload/filetype/crystal.kak258
-rw-r--r--autoload/filetype/css.kak186
-rw-r--r--autoload/filetype/cucumber.kak102
-rw-r--r--autoload/filetype/cue.kak168
-rw-r--r--autoload/filetype/d.kak145
-rw-r--r--autoload/filetype/dart.kak126
-rw-r--r--autoload/filetype/dhall.kak101
-rw-r--r--autoload/filetype/diff-parse.pl143
-rw-r--r--autoload/filetype/diff.kak141
-rw-r--r--autoload/filetype/dockerfile.kak55
-rw-r--r--autoload/filetype/elixir.kak128
-rw-r--r--autoload/filetype/elm.kak85
-rw-r--r--autoload/filetype/elvish.kak67
-rw-r--r--autoload/filetype/erlang.kak68
-rw-r--r--autoload/filetype/eruby.kak26
-rw-r--r--autoload/filetype/etc.kak95
-rw-r--r--autoload/filetype/exherbo.kak138
-rw-r--r--autoload/filetype/fennel.kak109
-rw-r--r--autoload/filetype/fidl.kak79
-rw-r--r--autoload/filetype/fish.kak87
-rw-r--r--autoload/filetype/fsharp.kak157
-rw-r--r--autoload/filetype/gas.kak99
-rw-r--r--autoload/filetype/gdscript.kak126
-rw-r--r--autoload/filetype/gentoo-linux.kak4
-rw-r--r--autoload/filetype/git.kak69
-rw-r--r--autoload/filetype/github.kak18
-rw-r--r--autoload/filetype/gluon.kak103
-rw-r--r--autoload/filetype/go.kak174
-rw-r--r--autoload/filetype/graphql.kak100
-rw-r--r--autoload/filetype/gren.kak7
-rw-r--r--autoload/filetype/groovy.kak92
-rw-r--r--autoload/filetype/haml.kak78
-rw-r--r--autoload/filetype/hare.kak144
-rw-r--r--autoload/filetype/haskell.kak127
-rw-r--r--autoload/filetype/hbs.kak112
-rw-r--r--autoload/filetype/html.kak87
-rw-r--r--autoload/filetype/i3.kak94
-rw-r--r--autoload/filetype/ini.kak24
-rw-r--r--autoload/filetype/janet.kak81
-rw-r--r--autoload/filetype/java.kak129
-rw-r--r--autoload/filetype/javascript.kak224
-rw-r--r--autoload/filetype/jinja.kak35
-rw-r--r--autoload/filetype/json.kak69
-rw-r--r--autoload/filetype/julia.kak42
-rw-r--r--autoload/filetype/just.kak79
-rw-r--r--autoload/filetype/kakrc.kak130
-rw-r--r--autoload/filetype/kickstart.kak38
-rw-r--r--autoload/filetype/kotlin.kak187
-rw-r--r--autoload/filetype/latex.kak144
-rw-r--r--autoload/filetype/ledger.kak144
-rw-r--r--autoload/filetype/lisp.kak81
-rw-r--r--autoload/filetype/lua.kak152
-rw-r--r--autoload/filetype/mail.kak30
-rw-r--r--autoload/filetype/makefile.kak75
-rw-r--r--autoload/filetype/markdown.kak130
-rw-r--r--autoload/filetype/mercurial.kak37
-rw-r--r--autoload/filetype/mercury.kak124
-rw-r--r--autoload/filetype/meson.kak77
-rw-r--r--autoload/filetype/mlb.kak50
-rw-r--r--autoload/filetype/moon.kak120
-rw-r--r--autoload/filetype/nim.kak132
-rw-r--r--autoload/filetype/ninja.kak106
-rw-r--r--autoload/filetype/nix.kak124
-rw-r--r--autoload/filetype/ocaml.kak130
-rw-r--r--autoload/filetype/odin.kak119
-rw-r--r--autoload/filetype/pascal.kak217
-rw-r--r--autoload/filetype/perl.kak146
-rw-r--r--autoload/filetype/php.kak107
-rw-r--r--autoload/filetype/pony.kak155
-rw-r--r--autoload/filetype/prolog.kak49
-rw-r--r--autoload/filetype/protobuf.kak109
-rw-r--r--autoload/filetype/pug.kak81
-rw-r--r--autoload/filetype/purescript.kak120
-rw-r--r--autoload/filetype/python.kak199
-rw-r--r--autoload/filetype/r.kak155
-rw-r--r--autoload/filetype/ragel.kak84
-rw-r--r--autoload/filetype/restructuredtext.kak92
-rw-r--r--autoload/filetype/ruby.kak194
-rw-r--r--autoload/filetype/rust.kak209
-rw-r--r--autoload/filetype/sass.kak86
-rw-r--r--autoload/filetype/scala.kak90
-rw-r--r--autoload/filetype/scheme.kak170
-rw-r--r--autoload/filetype/scss.kak55
-rw-r--r--autoload/filetype/sh.kak206
-rw-r--r--autoload/filetype/sml.kak86
-rw-r--r--autoload/filetype/sql.kak115
-rw-r--r--autoload/filetype/svelte.kak35
-rw-r--r--autoload/filetype/swift.kak37
-rw-r--r--autoload/filetype/systemd.kak14
-rw-r--r--autoload/filetype/taskpaper.kak63
-rw-r--r--autoload/filetype/tcl.kak79
-rw-r--r--autoload/filetype/terraform.kak120
-rw-r--r--autoload/filetype/toml.kak76
-rw-r--r--autoload/filetype/troff.kak51
-rw-r--r--autoload/filetype/tupfile.kak40
-rw-r--r--autoload/filetype/twig.kak89
-rw-r--r--autoload/filetype/vhdl.kak410
-rw-r--r--autoload/filetype/void-linux.kak4
-rw-r--r--autoload/filetype/yaml.kak73
-rw-r--r--autoload/filetype/zig.kak144
m---------autoload/kak-rainbow0
m---------autoload/kakoune-gdb0
-rw-r--r--autoload/tools/autorestore.asciidoc13
-rw-r--r--autoload/tools/autorestore.kak93
-rw-r--r--autoload/tools/autowrap.kak50
-rw-r--r--autoload/tools/clang.kak196
-rw-r--r--autoload/tools/comment.kak218
-rw-r--r--autoload/tools/ctags.kak168
-rw-r--r--autoload/tools/doc.asciidoc45
-rw-r--r--autoload/tools/doc.kak195
-rw-r--r--autoload/tools/format.kak38
-rw-r--r--autoload/tools/git.kak787
-rw-r--r--autoload/tools/go/gopls.kak98
-rw-r--r--autoload/tools/grep.kak66
-rw-r--r--autoload/tools/jump.kak70
-rw-r--r--autoload/tools/lint.asciidoc26
-rw-r--r--autoload/tools/lint.kak452
-rw-r--r--autoload/tools/make.kak92
-rw-r--r--autoload/tools/man.kak139
-rw-r--r--autoload/tools/menu.kak85
-rw-r--r--autoload/tools/patch-range.pl113
-rw-r--r--autoload/tools/patch.kak63
-rw-r--r--autoload/tools/python/jedi.kak77
-rw-r--r--autoload/tools/rust/racer.kak123
-rw-r--r--autoload/tools/spell.kak184
-rw-r--r--autoload/windowing/appleterminal.kak44
-rw-r--r--autoload/windowing/detection.kak71
-rw-r--r--autoload/windowing/iterm.kak124
-rw-r--r--autoload/windowing/kitty.kak84
-rw-r--r--autoload/windowing/new-client.kak9
-rw-r--r--autoload/windowing/repl/dtach.kak38
-rw-r--r--autoload/windowing/repl/kitty.kak56
-rw-r--r--autoload/windowing/repl/tmux.kak91
-rw-r--r--autoload/windowing/repl/x11.kak41
-rw-r--r--autoload/windowing/screen.kak75
-rw-r--r--autoload/windowing/sway.kak81
-rw-r--r--autoload/windowing/tmux.kak82
-rw-r--r--autoload/windowing/wayland.kak66
-rw-r--r--autoload/windowing/wezterm.kak70
-rw-r--r--autoload/windowing/x11.kak78
-rw-r--r--autoload/windowing/zellij.kak67
156 files changed, 16702 insertions, 0 deletions
diff --git a/autoload/auto-pairs b/autoload/auto-pairs
new file mode 160000
+Subproject e812ee6006b1c3d8c9afa45a0a90d28714636f8
diff --git a/autoload/detection/editorconfig.kak b/autoload/detection/editorconfig.kak
new file mode 100644
index 0000000..e836c3e
--- /dev/null
+++ b/autoload/detection/editorconfig.kak
@@ -0,0 +1,60 @@
+# http://editorconfig.org/#file-format-details
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](editorconfig) %{
+ set-option buffer filetype ini
+ set-option buffer static_words indent_style indent_size tab_width \
+ end_of_line charset insert_final_newline trim_trailing_whitespace root \
+ latin1 utf-8 utf-8-bom utf-16be utf-16le lf cr crlf unset space tab max_line_length
+}
+
+define-command editorconfig-load -params ..1 -docstring "editorconfig-load [file]: set formatting behavior according to editorconfig" %{
+ evaluate-commands %sh{
+ command -v editorconfig >/dev/null 2>&1 || { echo "fail editorconfig could not be found"; exit 1; }
+
+ file="${1:-$kak_buffile}"
+ case $file in
+ /*) # $kak_buffile is a full path that starts with a '/'
+ printf %s\\n "remove-hooks buffer editorconfig-hooks"
+ editorconfig "$file" | awk -v file="$file" -F= -- '
+ $1 == "indent_style" { indent_style = $2 }
+ $1 == "indent_size" { indent_size = $2 == "tab" ? 4 : $2 }
+ $1 == "tab_width" { tab_width = $2 }
+ $1 == "end_of_line" { end_of_line = $2 }
+ $1 == "charset" { charset = $2 }
+ $1 == "trim_trailing_whitespace" { trim_trailing_whitespace = $2 }
+ $1 == "max_line_length" { max_line_length = $2 }
+
+ END {
+ if (indent_style == "tab") {
+ print "set-option buffer indentwidth 0"
+ }
+ if (indent_style == "space") {
+ print "set-option buffer indentwidth " indent_size
+ }
+ if (indent_size || tab_width) {
+ print "set-option buffer tabstop " (tab_width ? tab_width : indent_size)
+ }
+ if (end_of_line == "lf" || end_of_line == "crlf") {
+ print "set-option buffer eolformat " end_of_line
+ }
+ if (charset == "utf-8-bom") {
+ print "set-option buffer BOM utf8"
+ }
+ if (trim_trailing_whitespace == "true") {
+ print "hook buffer BufWritePre \"" file "\" -group editorconfig-hooks %{ try %{ execute-keys -draft %{%s\\h+$|\\n+\\z<ret>d} } }"
+ }
+ if (max_line_length && max_line_length != "off") {
+ print "set window autowrap_column " max_line_length
+ print "autowrap-enable"
+ print "add-highlighter window/ column %sh{ echo $((" max_line_length "+1)) } default,bright-black"
+ }
+ }
+ ' ;;
+ esac
+ }
+}
+complete-command editorconfig-load file
diff --git a/autoload/detection/file.kak b/autoload/detection/file.kak
new file mode 100644
index 0000000..c707386
--- /dev/null
+++ b/autoload/detection/file.kak
@@ -0,0 +1,26 @@
+define-command -hidden file-detection %{ evaluate-commands %sh{
+ if [ -z "${kak_opt_filetype}" ]; then
+ mime=$(file -b --mime-type -L "${kak_buffile}")
+ mime=${mime%;*}
+ case "${mime}" in
+ application/*+xml) filetype="xml" ;;
+ image/*+xml) filetype="xml" ;; #SVG
+ message/rfc822) filetype="mail" ;;
+ text/x-shellscript) filetype="sh" ;;
+ text/x-script.*) filetype="${mime#text/x-script.}" ;;
+ text/x-*) filetype="${mime#text/x-}" ;;
+ text/plain) exit ;;
+ text/*) filetype="${mime#text/}" ;;
+ application/x-shellscript) filetype="sh" ;;
+ application/x-*) filetype="${mime#application/x-}" ;;
+ application/*) filetype="${mime#application/}" ;;
+ *) exit ;;
+ esac
+ if [ -n "${filetype}" ]; then
+ printf "set-option buffer filetype '%s'\n" "${filetype}"
+ fi
+ fi
+} }
+
+hook -group file-detection global BufOpenFile .* file-detection
+hook -group file-detection global BufWritePost .* file-detection
diff --git a/autoload/detection/modeline.kak b/autoload/detection/modeline.kak
new file mode 100644
index 0000000..6e067b1
--- /dev/null
+++ b/autoload/detection/modeline.kak
@@ -0,0 +1,123 @@
+##
+## modeline.kak by lenormf
+##
+
+## Currently supported modeline format: vim
+## Also supports kakoune options with a 'kak' or 'kakoune' prefix
+## Only a few options are supported, in order to prevent the
+## buffers from poking around the configuration too much
+
+declare-option -docstring "amount of lines that will be checked at the beginning and the end of the buffer" \
+ int modelines 5
+
+define-command -hidden modeline-parse-impl %{
+ evaluate-commands %sh{
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ # Translate a vim option into the corresponding kakoune one
+ translate_opt_vim() {
+ local key="$1"
+ local value="$2"
+
+ case "${key}" in
+ so|scrolloff)
+ key="scrolloff";
+ value="${value},${kak_opt_scrolloff##*,}";;
+ siso|sidescrolloff)
+ key="scrolloff";
+ value="${kak_opt_scrolloff%%,*},${value}";;
+ ts|tabstop) key="tabstop";;
+ sw|shiftwidth) key="indentwidth";;
+ tw|textwidth) key="autowrap_column";;
+ ff|fileformat)
+ key="eolformat";
+ case "${value}" in
+ unix) value="lf";;
+ dos) value="crlf";;
+ *)
+ printf '%s\n' "Unsupported file format: ${value}" >&2
+ return;;
+ esac
+ ;;
+ ft|filetype) key="filetype";;
+ bomb)
+ key="BOM";
+ value="utf8";;
+ nobomb)
+ key="BOM";
+ value="none";;
+ spelllang|spl)
+ key="spell_lang";
+ value="${value%%,*}";;
+ *)
+ printf '%s\n' "Unsupported vim variable: ${key}" >&2
+ return;;
+ esac
+
+ printf 'set-option buffer %s %s\n' "${key}" "$(kakquote "${value}")"
+ }
+
+ # Pass a few whitelisted options to kakoune directly
+ translate_opt_kakoune() {
+ local readonly key="$1"
+ local readonly value="$2"
+
+ case "${key}" in
+ scrolloff|tabstop|indentwidth|autowrap_column|eolformat|filetype|BOM|spell_lang);;
+ *) printf 'echo -debug %s' "$(kakquote "Unsupported kakoune variable: ${key}")" \
+ | kak -p "${kak_session}"
+ return;;
+ esac
+
+ printf 'set-option buffer %s %s\n' "${key}" "$(kakquote "${value}")"
+ }
+
+ case "${kak_selection}" in
+ *vi:*|*vim:*) type_selection="vim";;
+ *kak:*|*kakoune:*) type_selection="kakoune";;
+ *)
+ printf 'fail %s\n' "$(kakquote "Unsupported modeline format: ${kak_selection}")"
+ exit 1 ;;
+ esac
+
+ # The following subshell will keep the actual options of the modeline, and strip:
+ # - the text that leads the first option, according to the official vim modeline format
+ # - the trailing text after the last option, and an optional ':' sign before it
+ # It will also convert the ':' seperators beween the option=value pairs
+ # More info: http://vimdoc.sourceforge.net/htmldoc/options.html#modeline
+ printf %s "${kak_selection}" | sed \
+ -e 's/^[^:]\{1,\}://' \
+ -e 's/[ \t]*set\{0,1\}[ \t]\([^:]*\).*$/\1/' \
+ -e 's/:[^a-zA-Z0-9_=-]*$//' \
+ -e 's/:/ /g' \
+ | tr ' ' '\n' \
+ | while read -r option; do
+ name_option="${option%%=*}"
+ value_option="${option#*=}"
+
+ if [ -z "${option}" ]; then
+ continue
+ fi
+
+ case "${type_selection}" in
+ vim) translate_opt_vim "${name_option}" "${value_option}";;
+ kakoune) translate_opt_kakoune "${name_option}" "${value_option}";;
+ *) exit 1;;
+ esac
+ done
+ }
+}
+
+# Add the following function to a hook on BufOpenFile to automatically parse modelines
+# Select the first and last `modelines` lines in the buffer, only keep modelines
+# ref. options.txt (in vim `:help options`) : 2 forms of modelines:
+# [text]{white}{vi:|vim:|ex:}[white]{options}
+# [text]{white}{vi:|vim:|Vim:|ex:}[white]se[t] {options}:[text]
+define-command modeline-parse -docstring "Read and interpret vi-format modelines at the beginning/end of the buffer" %{
+ try %{ evaluate-commands -draft -save-regs ^ %{
+ execute-keys -save-regs "" gk %opt{modelines} JK x Z
+ execute-keys gj %opt{modelines} KJ x <a-z> a
+ execute-keys s^\S*?\s+?\w+:\s?[^\n]+<ret> x
+ evaluate-commands -draft -itersel modeline-parse-impl
+ } }
+}
diff --git a/autoload/filetype/apl.kak b/autoload/filetype/apl.kak
new file mode 100644
index 0000000..ff3f2f0
--- /dev/null
+++ b/autoload/filetype/apl.kak
@@ -0,0 +1,86 @@
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(apl|aplf|aplo|apln|aplc|apli|dyalog) %{
+ set-option buffer filetype apl
+
+ set-option buffer matching_pairs ( ) { } [ ]
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=apl %|
+ require-module apl
+
+ hook window InsertChar \n -group apl-insert apl-insert-on-new-line
+ hook window InsertChar \n -group apl-indent apl-indent-on-new-line
+ hook window InsertChar [}⟩\]] -group apl-indent apl-indent-on-closing
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window apl-.+ }
+|
+
+hook -group apl-highlight global WinSetOption filetype=apl %{
+ add-highlighter window/apl ref apl
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/apl }
+}
+
+provide-module apl %¹
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/apl regions
+add-highlighter shared/apl/code default-region group
+add-highlighter shared/apl/comment region "⍝" "$" fill comment
+add-highlighter shared/apl/string region "'" "'" fill string
+
+add-highlighter shared/apl/code/ regex "[{}]" 0:meta
+add-highlighter shared/apl/code/ regex "⋄" 0:meta
+add-highlighter shared/apl/code/ regex "[\[\]]" 0:magenta
+add-highlighter shared/apl/code/ regex "[()]" 0:bright-black
+add-highlighter shared/apl/code/ regex "[:;?]" 0:bright-black
+add-highlighter shared/apl/code/ regex "[←→]" 0:normal
+# Number
+add-highlighter shared/apl/code/ regex "¯?[0-9][¯0-9A-Za-z]*(?:\.[¯0-9Ee][¯0-9A-Za-z]*)*|¯?\.[0-9Ee][¯0-9A-Za-z]*" 0:value
+add-highlighter shared/apl/code/ regex "\." 0:normal
+add-highlighter shared/apl/code/ regex "[⍺⍶⍵⍹χ∇]" 0:blue # arguments
+# function
+add-highlighter shared/apl/code/ regex "[+\-×÷⌈⌊∣|⍳?*⍟○!⌹<≤=>≥≠≡≢∊⍷∪∩~∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢⍁⍂≈⌸⍯↗⊆⊇⍸√⌾…⍮]" 0:green # function
+# operator
+add-highlighter shared/apl/code/ regex "[\.\\/⌿⍀¨⍣⍨⍠⍤∘⌸&⌶@⌺⍥⍛⍢]" 0:magenta
+# system
+add-highlighter shared/apl/code/ regex "⎕[A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ][A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ¯0-9]*" 0:yellow
+add-highlighter shared/apl/code/ regex "\n^\s*\n(\n\t[A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ]\n\t[A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ¯0-9]*\n)\n(:)" 0:meta
+
+declare-user-mode apl
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden apl-insert-on-new-line %{
+ evaluate-commands -draft -itersel %[
+ # copy # comments prefix
+ try %{ execute-keys -draft <semicolon><c-s>k<a-x> s ^\h*\K#+\h* <ret> y<c-o>P<esc> }
+ ]
+}
+
+define-command -hidden apl-indent-on-new-line %`
+ evaluate-commands -draft -itersel %_
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # indent after lines ending with { [
+ try %( execute-keys -draft k<a-x> <a-k> [{\[]\h*$ <ret> j<a-gt> )
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft k<a-x> s \h+$ <ret>d }
+ _
+`
+
+define-command -hidden apl-indent-on-closing %`
+ evaluate-commands -draft -itersel %_
+ # align to opening bracket
+ try %( execute-keys -draft <a-h> <a-k> ^\h*[}\]]$ <ret> h m <a-S> 1<a-&> )
+ _
+`
+
diff --git a/autoload/filetype/arch-linux.kak b/autoload/filetype/arch-linux.kak
new file mode 100644
index 0000000..08135a7
--- /dev/null
+++ b/autoload/filetype/arch-linux.kak
@@ -0,0 +1,4 @@
+# package build description file
+hook global BufCreate (.*/)?PKGBUILD %{
+ set-option buffer filetype sh
+}
diff --git a/autoload/filetype/asciidoc.kak b/autoload/filetype/asciidoc.kak
new file mode 100644
index 0000000..453ffd4
--- /dev/null
+++ b/autoload/filetype/asciidoc.kak
@@ -0,0 +1,90 @@
+# http://asciidoc.org/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .+\.(a(scii)?doc|asc) %{
+ set-option buffer filetype asciidoc
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook -group asciidoc-highlight global WinSetOption filetype=asciidoc %{
+ require-module asciidoc
+
+ add-highlighter window/asciidoc ref asciidoc
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/asciidoc }
+}
+
+provide-module asciidoc %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/asciidoc group
+
+# Titles and headers (multi-line style)
+add-highlighter shared/asciidoc/ regex (\A|\n\n)[^\n]+\n={2,}\h*$ 0:title
+add-highlighter shared/asciidoc/ regex (\A|\n\n)[^\n]+\n-{2,}\h*$ 0:header
+add-highlighter shared/asciidoc/ regex (\A|\n\n)[^\n]+\n~{2,}\h*$ 0:header
+add-highlighter shared/asciidoc/ regex (\A|\n\n)[^\n]+\n\^{2,}\h*$ 0:header
+
+# Titles and headerss (one-line style)
+add-highlighter shared/asciidoc/ regex (\A|\n\n)=\h+[^\n]+$ 0:title
+add-highlighter shared/asciidoc/ regex (\A|\n\n)={2,}\h+[^\n]+$ 0:header
+
+# Comments
+add-highlighter shared/asciidoc/ regex ^//(?:[^\n/][^\n]*|)$ 0:comment
+add-highlighter shared/asciidoc/ regex ^(/{4,}).*?\n(/{4,})$ 0:comment
+
+# List titles
+add-highlighter shared/asciidoc/ regex ^\.[^\h\W][^\n]*$ 0:title
+
+# Bulleted lists
+add-highlighter shared/asciidoc/ regex ^\h*(?<bullet>[-\*])\h+[^\n]+$ 0:list bullet:bullet
+add-highlighter shared/asciidoc/ regex ^\h*(?<bullet>[-\*]+)\h+[^\n]+(\n\h+[^-\*\n]*)?$ 0:list bullet:bullet
+
+# Delimited blocks
+# https://docs.asciidoctor.org/asciidoc/latest/blocks/delimited/
+add-highlighter shared/asciidoc/ regex ^(-{4,})\n[^\n\h].*?\n(-{4,})$ 0:block
+add-highlighter shared/asciidoc/ regex ^(={4,})\n[^\n\h].*?\n(={4,})$ 0:block
+add-highlighter shared/asciidoc/ regex ^(~{4,})\n[^\n\h].*?\n(~{4,})$ 0:block
+add-highlighter shared/asciidoc/ regex ^(\*{4,})\n[^\n\h].*?\n(\*{4,})$ 0:block
+
+# Monospaced text
+add-highlighter shared/asciidoc/ regex \B(?:\+[^\n]+?\+|`[^\n]+?`)\B 0:mono
+
+# Bolded text
+add-highlighter shared/asciidoc/ regex \s\*[^\n\*]+\*\B 0:+b
+add-highlighter shared/asciidoc/ regex \h\*[^\n\*]+\*\B 0:+b
+add-highlighter shared/asciidoc/ regex \*{2}(?!\h)[^\n\*]+\*{2} 0:+b
+add-highlighter shared/asciidoc/ regex \h\*{2}[^\n\*]+\*{2} 0:+b
+
+# Italicized text
+# (these are simpler since they aren't able to _also_ be bullet characters.)
+add-highlighter shared/asciidoc/ regex \b_[^\n]+?_\b 0:+i
+add-highlighter shared/asciidoc/ regex __[^\n]+?__ 0:+i
+
+# Attributes
+add-highlighter shared/asciidoc/ regex ^:(?:(?<neg>!?)[-\w]+|[-\w]+(?<neg>!?)): 0:meta neg:operator
+add-highlighter shared/asciidoc/ regex [^\\](\{[-\w]+\})[^\\]? 1:meta
+
+# Options
+add-highlighter shared/asciidoc/ regex ^\[[^\n]+\]$ 0:operator
+
+# Admonition pargraphs
+add-highlighter shared/asciidoc/ regex ^(NOTE|TIP|IMPORTANT|CAUTION|WARNING): 0:block
+add-highlighter shared/asciidoc/ regex ^\[(NOTE|TIP|IMPORTANT|CAUTION|WARNING)\]$ 0:block
+
+# Links, inline macros
+add-highlighter shared/asciidoc/ regex \b((?:https?|ftp|irc://)[^\h\[]+)\[([^\n]*)?\] 1:link 2:+i
+add-highlighter shared/asciidoc/ regex (link|mailto):([^\n]+)(?:\[([^\n]*)\]) 1:keyword 2:link 3:+i
+add-highlighter shared/asciidoc/ regex (xref):([^\n]+)(?:\[([^\n]*)\]) 1:keyword 2:meta 3:+i
+add-highlighter shared/asciidoc/ regex (<<([^\n><]+)>>) 1:link 2:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+}
diff --git a/autoload/filetype/awk.kak b/autoload/filetype/awk.kak
new file mode 100644
index 0000000..536e352
--- /dev/null
+++ b/autoload/filetype/awk.kak
@@ -0,0 +1,88 @@
+# Detection
+# ---------
+
+hook global BufCreate .*\.awk %{
+ set-option buffer filetype awk
+}
+
+# Initialization
+# --------------
+
+hook global WinSetOption filetype=awk %{
+ require-module awk
+
+ hook window InsertChar \n -group awk-indent awk-indent-on-new-line
+ hook window ModeChange pop:insert:.* -group awk-trim-indent awk-trim-indent
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window awk-.+ }
+}
+
+hook -group awk-highlight global WinSetOption filetype=awk %{
+ add-highlighter window/awk ref awk
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/awk }
+}
+
+provide-module awk %@
+
+# Highlighters
+# ------------
+
+add-highlighter shared/awk regions
+add-highlighter shared/awk/code default-region group
+add-highlighter shared/awk/comment region '#' '$' fill comment
+add-highlighter shared/awk/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/awk/regex region (\A|[^\s\w)\]+-]|\bcase)\s*\K/ (?<!\\)(\\\\)*/ fill attribute
+
+add-highlighter shared/awk/code/ regex (\.\d+|\b\d+\.?\d*)([eE][+-]?\d+)?\b 0:value # Decimal/octal/scientific
+add-highlighter shared/awk/code/ regex \b0[xX][\da-fA-F]+\b 0:value # Hexadecimal
+add-highlighter shared/awk/code/ regex \$|\+|-|\*|/|%|\^|=|&&|\||!|<|>|\?|~ 0:operator
+
+evaluate-commands %sh{
+ # Grammar
+ patterns="BEGIN END BEGINFILE ENDFILE"
+ variables="BINMODE CONVFMT FIELDWIDTHS FPAT FS IGNORECASE LINT OFMT OFS
+ ORS PREC ROUNDMODE RS SUBSEP TEXTDOMAIN ARGC ARGV ARGIND ENVIRON
+ ERRNO FILENAME FNR NF FUNCTAB NR PROCINFO RLENGTH RSTART RT SYMTAB"
+ keywords="break continue delete exit function getline next print printf
+ return switch nextfile func if else while for do"
+ functions="atan2 cos exp int intdiv log rand sin sqrt srand asort asort1
+ gensub gsub index length match patsplit split sprintf strtonum sub
+ substr tolower toupper close fflush system mktime strftime systime
+ and compl lshift or rshift xor isarray typeof bindtextdomain dcgettext
+ dcngetext"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "hook global WinSetOption filetype=awk %{
+ set-option window static_words $(join "${patterns} ${variables} ${keywords} ${functions}" ' ')
+ }"
+
+ # Highlight keywords
+ printf %s\\n "add-highlighter shared/awk/code/ regex \b($(join "${patterns}" '|'))\b 0:type"
+ printf %s\\n "add-highlighter shared/awk/code/ regex \b($(join "${variables}" '|'))\b 0:meta"
+ printf %s\\n "add-highlighter shared/awk/code/ regex \b($(join "${keywords}" '|'))\b 0:keyword"
+ printf %s\\n "add-highlighter shared/awk/code/ regex \b($(join "${functions}" '|'))\b 0:function"
+}
+
+# Commands
+# --------
+
+define-command -hidden awk-indent-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # preserve previous line indent
+ try %[ execute-keys -draft <semicolon> K <a-&> ]
+ # cleanup trailing whitespaces from previous line
+ try %[ execute-keys -draft k x s \h+$ <ret> d ]
+ # indent after line ending in opening curly brace
+ try %[ execute-keys -draft kx <a-k>\{\h*(#.*)?$<ret> j<a-gt> ]
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ ]
+]
+
+define-command -hidden awk-trim-indent %{
+ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d }
+}
+
+@
diff --git a/autoload/filetype/c-family.kak b/autoload/filetype/c-family.kak
new file mode 100644
index 0000000..0677b11
--- /dev/null
+++ b/autoload/filetype/c-family.kak
@@ -0,0 +1,476 @@
+# Detection
+hook global BufCreate .*\.(cc|cpp|cxx|C|hh|hpp|hxx|H)$ %{
+ set-option buffer filetype cpp
+}
+
+hook global BufSetOption filetype=c\+\+ %{
+ hook -once buffer NormalIdle '' "set-option buffer filetype cpp"
+}
+
+hook global BufCreate .*\.c$ %{
+ set-option buffer filetype c
+}
+
+hook global BufCreate .*\.h$ %{
+ try %{
+ execute-keys -draft %{%s\b::\b|\btemplate\h*<lt>|\bclass\h+\w+|\b(typename|namespace)\b|\b(public|private|protected)\h*:<ret>}
+ set-option buffer filetype cpp
+ } catch %{
+ set-option buffer filetype c
+ }
+}
+
+hook global BufCreate .*\.m %{
+ set-option buffer filetype objc
+}
+
+hook global WinSetOption filetype=(c|cpp|objc) %[
+ require-module c-family
+
+ evaluate-commands "set-option window static_words %%opt{%val{hook_param_capture_1}_static_words}"
+
+ hook -group "%val{hook_param_capture_1}-trim-indent" window ModeChange pop:insert:.* c-family-trim-indent
+ hook -group "%val{hook_param_capture_1}-insert" window InsertChar \n c-family-insert-on-newline
+ hook -group "%val{hook_param_capture_1}-indent" window InsertChar \n c-family-indent-on-newline
+ hook -group "%val{hook_param_capture_1}-indent" window InsertChar \{ c-family-indent-on-opening-curly-brace
+ hook -group "%val{hook_param_capture_1}-indent" window InsertChar \} c-family-indent-on-closing-curly-brace
+ hook -group "%val{hook_param_capture_1}-insert" window InsertChar \} c-family-insert-on-closing-curly-brace
+
+ alias window alt "%val{hook_param_capture_1}-alternative-file"
+
+ hook -once -always window WinSetOption filetype=.* "
+ remove-hooks window %val{hook_param_capture_1}-.+
+ unalias window alt %val{hook_param_capture_1}-alternative-file
+ "
+]
+
+hook -group c-highlight global WinSetOption filetype=c %{
+ add-highlighter window/c ref c
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/c }
+}
+
+hook -group cpp-highlight global WinSetOption filetype=cpp %{
+ add-highlighter window/cpp ref cpp
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cpp }
+}
+
+hook -group objc-highlight global WinSetOption filetype=objc %{
+ add-highlighter window/objc ref objc
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/objc }
+}
+
+
+provide-module c-family %§
+
+define-command -hidden c-family-trim-indent %{
+ # remove the line if it's empty when leaving the insert mode
+ try %{ execute-keys -draft x 1s^(\h+)$<ret> d }
+}
+
+define-command -hidden c-family-indent-on-newline %< evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %<
+ # if previous line is part of a comment, do nothing
+ execute-keys -draft <a-?>/\*<ret> <a-K>^\h*[^/*\h]<ret>
+ > catch %<
+ # else if previous line closed a paren (possibly followed by words and a comment),
+ # copy indent of the opening paren line
+ execute-keys -draft kx 1s(\))(\h+\w+)*\h*(\;\h*)?(?://[^\n]+)?\n\z<ret> m<a-semicolon>J <a-S> 1<a-&>
+ > catch %<
+ # else indent new lines with the same level as the previous one
+ execute-keys -draft K <a-&>
+ >
+ # remove previous empty lines resulting from the automatic indent
+ try %< execute-keys -draft k x <a-k>^\h+$<ret> Hd >
+ # indent after an opening brace or parenthesis at end of line
+ try %< execute-keys -draft k x <a-k>[{(]\h*$<ret> j <a-gt> >
+ # indent after a label
+ try %< execute-keys -draft k x s[a-zA-Z0-9_-]+:\h*$<ret> j <a-gt> >
+ # indent after a statement not followed by an opening brace
+ try %< execute-keys -draft k x s\)\h*(?://[^\n]+)?\n\z<ret> \
+ <a-semicolon>mB <a-k>\A\b(if|for|while)\b<ret> <a-semicolon>j <a-gt> >
+ try %< execute-keys -draft k x s \belse\b\h*(?://[^\n]+)?\n\z<ret> \
+ j <a-gt> >
+ # deindent after a single line statement end
+ try %< execute-keys -draft K x <a-k>\;\h*(//[^\n]+)?$<ret> \
+ K x s\)(\h+\w+)*\h*(//[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ MB <a-k>\A\b(if|for|while)\b<ret> <a-S>1<a-&> >
+ try %< execute-keys -draft K x <a-k>\;\h*(//[^\n]+)?$<ret> \
+ K x s \belse\b\h*(?://[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ <a-S>1<a-&> >
+ # deindent closing brace(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <esc> m <a-S> 1<a-&> >
+ # align to the opening parenthesis or opening brace (whichever is first)
+ # on a previous line if its followed by text on the same line
+ try %< evaluate-commands -draft %<
+ # Go to opening parenthesis and opening brace, then select the most nested one
+ try %< execute-keys [c [({],[)}] <ret> >
+ # Validate selection and get first and last char
+ execute-keys <a-k>\A[{(](\h*\S+)+\n<ret> <a-K>"(([^"]*"){2})*<ret> <a-K>'(([^']*'){2})*<ret> <a-:><a-semicolon>L <a-S>
+ # Remove possibly incorrect indent from new line which was copied from previous line
+ try %< execute-keys -draft , <a-h> s\h+<ret> d >
+ # Now indent and align that new line with the opening parenthesis/brace
+ execute-keys 1<a-&> &
+ > >
+> >
+
+define-command -hidden c-family-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> <a-S> 1<a-&> ]
+ # align indent with opening paren when { is entered on a new line after the else
+ try %[ execute-keys -draft -itersel hK x s \belse\b\h*(?://[^\n]+)?\n\h*\{<ret> <a-S> 1<a-&> ]
+]
+
+define-command -hidden c-family-indent-on-closing-curly-brace %[
+ evaluate-commands -draft -itersel -verbatim try %[
+ # check if alone on the line and select to opening curly brace
+ execute-keys <a-h><a-:><a-k>^\h+\}$<ret>hm
+ try %[
+ # in case open curly brace follows a closing paren possibly with qualifiers, extend to opening paren
+ execute-keys -draft <a-f>) <a-k> \A\)(\h+\w+)*\h*\{\z <ret>
+ execute-keys <a-F>)M
+ ]
+ # align to selection start
+ execute-keys <a-S>1<a-&>
+ ]
+]
+
+define-command -hidden c-family-insert-on-closing-curly-brace %[
+ # add a semicolon after a closing brace if part of a class, union or struct definition
+ evaluate-commands -itersel -draft -verbatim try %[
+ execute-keys -draft hmh <a-?>\b(class|struct|union|enum)\b<ret> <a-K>\{<ret> <a-K>\)(\s+\w+)*\s*\z<ret>
+ execute-keys -draft ';i;<esc>'
+ ]
+]
+
+define-command -hidden c-family-insert-on-newline %[ evaluate-commands -itersel -draft %[
+ execute-keys <semicolon>
+ try %[
+ evaluate-commands -draft -save-regs '/"' %[
+ # copy the commenting prefix
+ execute-keys -save-regs '' k x1s^\h*(//+\h*)<ret> y
+ try %[
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*//+\h*$<ret> jxs^\h*<ret>P
+ ] catch %[
+ # if there is no text in the previous comment, remove it completely
+ execute-keys d
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft k x s\h+$<ret> d ]
+ ]
+ try %[
+ # if the previous line isn't within a comment scope, break
+ execute-keys -draft kx <a-k>^(\h*/\*|\h+\*(?!/))<ret>
+
+ # find comment opening, validate it was not closed, and check its using star prefixes
+ execute-keys -draft <a-?>/\*<ret><a-H> <a-K>\*/<ret> <a-k>\A\h*/\*([^\n]*\n\h*\*)*[^\n]*\n\h*.\z<ret>
+
+ try %[
+ # if the previous line is opening the comment, insert star preceeded by space
+ execute-keys -draft kx<a-k>^\h*/\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the next line is a comment line insert a star
+ execute-keys -draft jx<a-k>^\h+\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the previous line is an empty comment line, close the comment scope
+ execute-keys -draft kx<a-k>^\h+\*\h+$<ret> x1s\*(\h*)<ret>c/<esc>
+ ] catch %[
+ # if the previous line is a non-empty comment line, add a star
+ execute-keys -draft i*<space><esc>
+ ]
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft k x s\h+$<ret> d ]
+ # align the new star with the previous one
+ execute-keys Kx1s^[^*]*(\*)<ret>&
+ ]
+] ]
+
+# Regions definition are the same between c++ and objective-c
+evaluate-commands %sh{
+ for ft in c cpp objc; do
+ if [ "${ft}" = "objc" ]; then
+ maybe_at='@?'
+ else
+ maybe_at=''
+ fi
+
+ cat <<-EOF
+ add-highlighter shared/$ft regions
+ add-highlighter shared/$ft/code default-region group
+ add-highlighter shared/$ft/string region %{$maybe_at(?<!')(?<!'\\\\)"} %{(?<!\\\\)(?:\\\\\\\\)*"} fill string
+ add-highlighter shared/$ft/documentation_comment region /\*(\*[^/]|!) \*/ fill documentation
+ add-highlighter shared/$ft/line_documentation_comment region //[/!] $ fill documentation
+ add-highlighter shared/$ft/comment region /\\* \\*/ fill comment
+ add-highlighter shared/$ft/line_comment region // (?<!\\\\)(?=\\n) fill comment
+ add-highlighter shared/$ft/disabled region -recurse "#\\h*if(?:def)?" ^\\h*?#\\h*if\\h+(?:0|FALSE)\\b "#\\h*(?:else|elif|endif)" fill comment
+ add-highlighter shared/$ft/ifdef region %{^\\h*\\K#\\h*(?:define|elif|if)(?=\\h)} %{(?<!\\\\)(?=\\s)|(?=//)} fill meta
+ add-highlighter shared/$ft/macro region %{^\\h*#} %{(?<!\\\\)(?=\\n)|(?=//)} group
+ add-highlighter shared/$ft/macro/ regex ^\\h*(#\\h*\\S*) 1:meta
+ add-highlighter shared/$ft/macro/ regex ^\\h*#\\h*include\\h+(<[^>]*>|"(?:[^"\\\\]|\\\\.)*") 1:module
+ add-highlighter shared/$ft/macro/ regex /\\*.*?\\*/ 0:comment
+ EOF
+ done
+}
+
+# c specific
+add-highlighter shared/c/code/numbers regex %{\b-?(0[xX][0-9a-fA-F]+|\d+)([uU][lL]{0,2}|[lL]{1,2}[uU]?|[fFdDiI]|([eE][-+]?\d+))?|'((\\.)?|[^'\\])'} 0:value
+evaluate-commands %sh{
+ # Grammar
+ keywords='asm break case continue default do else for goto if return
+ sizeof switch while offsetof alignas alignof'
+ attributes='auto atomic const enum extern inline register restrict static
+ struct typedef union volatile thread_local'
+ types='char double float int long short signed unsigned void
+ complex imaginary
+ fenv_t fexcept_t
+ imaxdiv_t
+ lconv
+ float_t double_t
+ jmp_buf
+ sig_atomic_t
+ va_list
+ memory_order atomic_flag atomic_bool atomic_char atomic_schar atomic_uchar atomic_wchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_llong atomic_ulong atomic_ullong atomic_char16_t atomic_char32_t atomic_intptr_t atomic_intmax_t atomic_int8_t atomic_int16_t atomic_int32_t atomic_int64_t atomic_int_least8_t atomic_int_least16_t atomic_int_least32_t atomic_int_least64_t atomic_int_fast8_t atomic_int_fast16_t atomic_int_fast32_t atomic_int_fast64_t atomic_uintptr_t atomic_uintmax_t atomic_uint8_t atomic_uint16_t atomic_uint32_t atomic_uint64_t atomic_uint_least8_t atomic_uint_least16_t atomic_uint_least32_t atomic_uint_least64_t atomic_uint_fast8_t atomic_uint_fast16_t atomic_uint_fast32_t atomic_uint_fast64_t atomic_size_t atomic_ptrdiff_t
+ bool
+ ptrdiff_t size_t max_align_t wchar_t
+ intptr_t intmax_t int8_t int16_t int32_t int64_t int_least8_t int_least16_t int_least32_t int_least64_t int_fast8_t int_fast16_t int_fast32_t int_fast64_t uintptr_t uintmax_t uint8_t uint16_t uint32_t uint64_t uint_least8_t uint_least16_t uint_least32_t uint_least64_t uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t
+ FILE fpos_t
+ div_t ldiv_t lldiv_t
+ cnd_t thrd_t thrd_start_t tss_t tss_dtor_t mtx_t once_flag
+ clock_t time_t timespec tm
+ mbstate_t wint_t
+ wctrans_t wctype_t
+ char16_t char32_t
+ ssize_t gid_t uid_t off_t off64_t useconds_t pid_t socklen_t'
+
+ macros='assert static_assert NDEBUG
+ I
+ EDOM EILSEQ ERANGE errno
+ FE_DIVBYZERO FE_INEXACT FE_INVALID FE_OVERFLOW FE_UNDERFLOW FE_ALL_EXCEPT FE_DOWNWARD FE_TONEAREST FE_TOWARDZERO FE_UPWARD FE_DFL_ENV
+ DECIMAL_DIG FLT_ROUNDS FLT_EVAL_METHOD FLT_RADIX FLT_DIG FLT_MANT_DIG FLT_DECIMAL_DIG FLT_MIN_EXP FLT_MIN_10_EXP FLT_MAX_EXP FLT_MAX FLT_EPSILON FLT_TRUE_MIN FLT_HAS_SUBNORM DBL_DIG DBL_MANT_DIG DBL_DECIMAL_DIG DBL_MIN_EXP DBL_MIN_10_EXP DBL_MAX_EXP DBL_MAX DBL_EPSILON DBL_TRUE_MIN DBL_HAS_SUBNORM LDBL_DIG LDBL_MANT_DIG LDBL_DECIMAL_DIG LDBL_MIN_EXP LDBL_MIN_10_EXP LDBL_MAX_EXP LDBL_MAX LDBL_EPSILON LDBL_TRUE_MIN LDBL_HAS_SUBNORM
+ PRIdMAX PRIdPTR PRId8 PRId16 PRId32 PRId64 PRIdLEAST8 PRIdLEAST16 PRIdLEAST32 PRIdLEAST64 PRIdFAST8 PRIdFAST16 PRIdFAST32 PRIdFAST64 PRIiMAX PRIiPTR PRIi8 PRIi16 PRIi32 PRIi64 PRIiLEAST8 PRIiLEAST16 PRIiLEAST32 PRIiLEAST64 PRIiFAST8 PRIiFAST16 PRIiFAST32 PRIiFAST64 PRIoMAX PRIoPTR PRIo8 PRIo16 PRIo32 PRIo64 PRIoLEAST8 PRIoLEAST16 PRIoLEAST32 PRIoLEAST64 PRIoFAST8 PRIoFAST16 PRIoFAST32 PRIoFAST64 PRIuMAX PRIuPTR PRIu8 PRIu16 PRIu32 PRIu64 PRIuLEAST8 PRIuLEAST16 PRIuLEAST32 PRIuLEAST64 PRIuFAST8 PRIuFAST16 PRIuFAST32 PRIuFAST64 PRIxMAX PRIxPTR PRIx8 PRIx16 PRIx32 PRIx64 PRIxLEAST8 PRIxLEAST16 PRIxLEAST32 PRIxLEAST64 PRIxFAST8 PRIxFAST16 PRIxFAST32 PRIxFAST64 PRIXMAX PRIXPTR PRIX8 PRIX16 PRIX32 PRIX64 PRIXLEAST8 PRIXLEAST16 PRIXLEAST32 PRIXLEAST64 PRIXFAST8 PRIXFAST16 PRIXFAST32 PRIXFAST64 SCNdMAX SCNdPTR SCNd8 SCNd16 SCNd32 SCNd64 SCNdLEAST8 SCNdLEAST16 SCNdLEAST32 SCNdLEAST64 SCNdFAST8 SCNdFAST16 SCNdFAST32 SCNdFAST64 SCNiMAX SCNiPTR SCNi8 SCNi16 SCNi32 SCNi64 SCNiLEAST8 SCNiLEAST16 SCNiLEAST32 SCNiLEAST64 SCNiFAST8 SCNiFAST16 SCNiFAST32 SCNiFAST64 SCNoMAX SCNoPTR SCNo8 SCNo16 SCNo32 SCNo64 SCNoLEAST8 SCNoLEAST16 SCNoLEAST32 SCNoLEAST64 SCNoFAST8 SCNoFAST16 SCNoFAST32 SCNoFAST64 SCNuMAX SCNuPTR SCNu8 SCNu16 SCNu32 SCNu64 SCNuLEAST8 SCNuLEAST16 SCNuLEAST32 SCNuLEAST64 SCNuFAST8 SCNuFAST16 SCNuFAST32 SCNuFAST64 SCNxMAX SCNxPTR SCNx8 SCNx16 SCNx32 SCNx64 SCNxLEAST8 SCNxLEAST16 SCNxLEAST32 SCNxLEAST64 SCNxFAST8 SCNxFAST16 SCNxFAST32 SCNxFAST64
+ and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq
+ CHAR_MIN CHAR_MAX SCHAR_MIN SCHAR_MAX WCHAR_MIN WCHAR_MAX SHRT_MIN SHRT_MAX INT_MIN INT_MAX LONG_MIN LONG_MAX LLONG_MIN LLONG_MAX MB_LEN_MAX UCHAR_MAX USHRT_MAX UINT_MAX ULONG_MAX ULLONG_MAX CHAR_BIT
+ LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME
+ HUGE_VAL HUGE_VALF HUGE_VALL INFINITY NAN FP_INFINITE FP_NAN FP_NORMAL FP_SUBNORMAL FP_ZERO FP_FAST_FMA FP_FAST_FMAF FP_FAST_FMAL FP_ILOGB0 FP_ILOGBNAN MATH_ERRNO MATH_ERREXCEPT math_errhandling isgreater isgreaterequal isless islessequal islessgreater isunordered
+ setjmp
+ SIG_DFL SIG_ERR SIG_IGN SIGABRT SIGFPE SIGILL SIGINT SIGSEGV SIGTERM
+ va_start va_arg va_end va_copy
+ ATOMIC_BOOL_LOCK_FREE ATOMIC_CHAR_LOCK_FREE ATOMIC_CHAR16_T_LOCK_FREE ATOMIC_CHAR32_T_LOCK_FREE ATOMIC_WCHAR_T_LOCK_FREE ATOMIC_SHORT_LOCK_FREE ATOMIC_INT_LOCK_FREE ATOMIC_LONG_LOCK_FREE ATOMIC_LLONG_LOCK_FREE ATOMIC_POINTER_LOCK_FREE ATOMIC_FLAG_INIT ATOMIC_VAR_INIT memory_order_relaxed memory_order_consume memory_order_acquire memory_order_release memory_order_acq_rel memory_order_seq_cst kill_dependency
+ true false
+ NULL
+ _IOFBF _IOLBF _IONBF BUFSIZ EOF FOPEN_MAX FILENAME_MAX TMP_MAX L_tmpnam SEEK_CUR SEEK_END SEEK_SET stderr stdin stdout
+ EXIT_FAILURE EXIT_SUCCESS MB_CUR_MAX RAND_MAX
+ PTRDIFF_MIN PTRDIFF_MAX SIG_ATOMIC_MIN SIG_ATOMIC_MAX WINT_MIN WINT_MAX INTMAX_MIN INTMAX_MAX INTPTR_MIN INTPTR_MAX INT8_MIN INT8_MAX INT16_MIN INT16_MAX INT32_MIN INT32_MAX INT64_MIN INT64_MAX INT_LEAST8_MIN INT_LEAST8_MAX INT_LEAST16_MIN INT_LEAST16_MAX INT_LEAST32_MIN INT_LEAST32_MAX INT_LEAST64_MIN INT_LEAST64_MAX INT_FAST8_MIN INT_FAST8_MAX INT_FAST16_MIN INT_FAST16_MAX INT_FAST32_MIN INT_FAST32_MAX INT_FAST64_MIN INT_FAST64_MAX UINTMAX_MAX UINTPTR_MAX UINT8_MAX UINT16_MAX UINT32_MAX UINT64_MAX UINT_LEAST8_MAX UINT_LEAST16_MAX UINT_LEAST32_MAX UINT_LEAST64_MAX UINT_FAST8_MAX UINT_FAST16_MAX UINT_FAST32_MAX UINT_FAST64_MAX INTMAX_C INT8_C INT16_C INT32_C INT64_C UINTMAX_C UINT8_C UINT16_C UINT32_C UINT64_C
+ mtx_plain mtx_recursive mtx_timed thrd_timedout thrd_success thrd_busy thrd_error thrd_nomem ONCE_FLAG_INIT TSS_DTOR_ITERATION
+ CLOCKS_PER_SEC TIME_UTC
+ WEOF
+ noreturn
+ R_OK W_OK X_OK F_OK F_LOCK F_ULOCK F_TLOCK F_TEST'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list c_static_words $(join "${keywords} ${attributes} ${types} ${macros}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/c/code/keywords regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/c/code/attributes regex \b($(join "${attributes}" '|'))\b 0:attribute
+ add-highlighter shared/c/code/types regex \b($(join "${types}" '|'))\b 0:type
+ add-highlighter shared/c/code/values regex \b($(join "${macros}" '|'))\b 0:value
+ "
+}
+
+# c++ specific
+
+# raw strings
+add-highlighter shared/cpp/raw_string region -match-capture %{R"([^(]*)\(} %{\)([^")]*)"} fill string
+
+# integer literals
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b[1-9]('?\d+)*(ul?l?|ll?u?)?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0b[01]('?[01]+)*(ul?l?|ll?u?)?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0('?[0-7]+)*(ul?l?|ll?u?)?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0x[\da-f]('?[\da-f]+)*(ul?l?|ll?u?)?\b(?!\.)} 0:value
+
+# floating point literals
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b\d('?\d+)*\.([fl]\b|\B)(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b\d('?\d+)*\.?e[+-]?\d('?\d+)*[fl]?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)(\b(\d('?\d+)*)|\B)\.\d('?[\d]+)*(e[+-]?\d('?\d+)*)?[fl]?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0x[\da-f]('?[\da-f]+)*\.([fl]\b|\B)(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0x[\da-f]('?[\da-f]+)*\.?p[+-]?\d('?\d+)*)?[fl]?\b(?!\.)} 0:value
+add-highlighter shared/cpp/code/ regex %{(?i)(?<!\.)\b0x([\da-f]('?[\da-f]+)*)?\.\d('?[\d]+)*(p[+-]?\d('?\d+)*)?[fl]?\b(?!\.)} 0:value
+
+# character literals (no multi-character literals)
+add-highlighter shared/cpp/code/char regex %{(\b(u8|u|U|L)|\B)'((\\.)|[^'\\])'\B} 0:value
+
+evaluate-commands %sh{
+ # Grammar
+ keywords='alignas alignof and and_eq asm bitand bitor break case catch
+ compl const_cast continue decltype delete do dynamic_cast
+ else export for goto if new not not_eq operator or or_eq
+ reinterpret_cast return sizeof static_assert static_cast switch
+ throw try typedef typeid using while xor xor_eq'
+ attributes='audit auto axiom const consteval constexpr default explicit
+ extern final friend inline mutable noexcept override private
+ protected public register requires static thread_local typename
+ virtual volatile'
+ entities='class concept enum namespace struct template union'
+ types='bool byte char char8_t char16_t char32_t double float int long
+ max_align_t nullptr_t ptrdiff_t short signed size_t unsigned void
+ wchar_t'
+ values='NULL false nullptr this true'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list cpp_static_words $(join "${keywords} ${attributes} ${entities} ${types} ${values}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/cpp/code/keywords regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/cpp/code/attributes regex \b($(join "${attributes} ${entities}" '|'))\b 0:attribute
+ add-highlighter shared/cpp/code/types regex \b($(join "${types}" '|'))\b 0:type
+ add-highlighter shared/cpp/code/values regex \b($(join "${values}" '|'))\b 0:value
+ "
+}
+
+# c and c++ compiler macros
+evaluate-commands %sh{
+ builtin_macros="__cplusplus|__STDC_HOSTED__|__FILE__|__LINE__|__DATE__|__TIME__|__STDCPP_DEFAULT_NEW_ALIGNMENT__"
+
+ printf %s "
+ add-highlighter shared/c/code/macros regex \b(${builtin_macros})\b 0:builtin
+ add-highlighter shared/cpp/code/macros regex \b(${builtin_macros})\b 0:builtin
+ "
+}
+
+# objective-c specific
+add-highlighter shared/objc/code/number regex %{\b-?\d+[fdiu]?|'((\\.)?|[^'\\])'} 0:value
+
+evaluate-commands %sh{
+ # Grammar
+ keywords='break case continue default do else for goto if return switch
+ while'
+ attributes='IBAction IBOutlet __block assign auto const copy enum extern
+ inline nonatomic readonly retain static strong struct typedef
+ union volatile weak'
+ types='BOOL CGFloat NSInteger NSString NSUInteger bool char float
+ instancetype int long short signed size_t unsigned void'
+ values='FALSE NO NULL TRUE YES id nil self super'
+ decorators='autoreleasepool catch class end implementation interface
+ property protocol selector synchronized synthesize try'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list objc_static_words $(join "${keywords} ${attributes} ${types} ${values} ${decorators}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/objc/code/keywords regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/objc/code/attributes regex \b($(join "${attributes}" '|'))\b 0:attribute
+ add-highlighter shared/objc/code/types regex \b($(join "${types}" '|'))\b 0:type
+ add-highlighter shared/objc/code/values regex \b($(join "${values}" '|'))\b 0:value
+ add-highlighter shared/objc/code/decorators regex @($(join "${decorators}" '|'))\b 0:attribute
+ "
+}
+
+declare-option -docstring %{
+ control the type of include guard to be inserted in empty headers
+ Can be one of the following:
+ ifdef: old style ifndef/define guard
+ pragma: newer type of guard using "pragma once"
+} str c_include_guard_style "ifdef"
+
+define-command -hidden c-family-insert-include-guards %{
+ evaluate-commands %sh{
+ case "${kak_opt_c_include_guard_style}" in
+ ifdef)
+ echo 'execute-keys gg"%PI/<esc>xs^.*/<ret>dxs\.<ret>r_A_INCLUDED<esc>xyPPI#ifndef<space><esc>jI#define<space><esc>jI#endif<space>//<space><esc>O<esc>'
+ ;;
+ pragma)
+ echo 'execute-keys ggi#pragma<space>once<esc>'
+ ;;
+ *);;
+ esac
+ }
+}
+
+hook -group c-family-insert global BufNewFile .*\.(h|hh|hpp|hxx|H) c-family-insert-include-guards
+
+declare-option -docstring "colon separated list of path in which header files will be looked for" \
+ str-list alt_dirs '.' '..'
+
+define-command -hidden c-family-alternative-file %{
+ evaluate-commands %sh{
+ file="${kak_buffile##*/}"
+ file_noext="${file%.*}"
+ dir=$(dirname "${kak_buffile}")
+
+ # Set $@ to alt_dirs
+ eval "set -- ${kak_quoted_opt_alt_dirs}"
+
+ case ${file} in
+ *.c|*.cc|*.cpp|*.cxx|*.C|*.inl|*.m)
+ for alt_dir in "$@"; do
+ for ext in h hh hpp hxx H; do
+ case "$alt_dir" in
+ /*) altname="${alt_dir}/${file_noext}.${ext}" ;;
+ *) altname="${dir}/${alt_dir}/${file_noext}.${ext}" ;;
+ esac
+ if [ -f "${altname}" ]; then
+ printf 'edit %%{%s}\n' "${altname}"
+ exit
+ fi
+ done
+ done
+ ;;
+ *.h|*.hh|*.hpp|*.hxx|*.H)
+ for alt_dir in "$@"; do
+ for ext in c cc cpp cxx C m; do
+ case "$alt_dir" in
+ /*) altname="${alt_dir}/${file_noext}.${ext}" ;;
+ *) altname="${dir}/${alt_dir}/${file_noext}.${ext}" ;;
+ esac
+ if [ -f "${altname}" ]; then
+ printf 'edit %%{%s}\n' "${altname}"
+ exit
+ fi
+ done
+ done
+ ;;
+ *)
+ echo "fail 'extension not recognized'"
+ exit
+ ;;
+ esac
+ echo "fail 'alternative file not found'"
+ }
+}
+
+define-command c-alternative-file -docstring "Jump to the alternate c file (header/implementation)" %{
+ c-family-alternative-file
+}
+define-command cpp-alternative-file -docstring "Jump to the alternate cpp file (header/implementation)" %{
+ c-family-alternative-file
+}
+define-command objc-alternative-file -docstring "Jump to the alternate objc file (header/implementation)" %{
+ c-family-alternative-file
+}
+
+
+# Module aliases
+provide-module c %{ require-module c-family }
+provide-module cpp %{ require-module c-family }
+provide-module objc %{ require-module c-family }
diff --git a/autoload/filetype/cabal.kak b/autoload/filetype/cabal.kak
new file mode 100644
index 0000000..a1f4526
--- /dev/null
+++ b/autoload/filetype/cabal.kak
@@ -0,0 +1,88 @@
+# http://haskell.org/cabal
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](cabal) %{
+ set-option buffer filetype cabal
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=cabal %[
+ require-module cabal
+
+ hook window ModeChange pop:insert:.* -group cabal-trim-indent cabal-trim-indent
+ hook window InsertChar \n -group cabal-insert cabal-insert-on-new-line
+ hook window InsertChar \n -group cabal-indent cabal-indent-on-new-line
+ hook window InsertChar \{ -group cabal-indent cabal-indent-on-opening-curly-brace
+ hook window InsertChar \} -group cabal-indent cabal-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window cabal-.+ }
+]
+
+hook -group cabal-highlight global WinSetOption filetype=cabal %{
+ add-highlighter window/cabal ref cabal
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cabal }
+}
+
+
+provide-module cabal %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/cabal regions
+add-highlighter shared/cabal/code default-region group
+add-highlighter shared/cabal/line_comment region (--) $ fill comment
+add-highlighter shared/cabal/comment region -recurse \{- \{- -\} fill comment
+
+add-highlighter shared/cabal/code/ regex \b(true|false)\b|(([<>]?=?)?\d+(\.\d+)+) 0:value
+add-highlighter shared/cabal/code/ regex \b(if|else)\b 0:keyword
+add-highlighter shared/cabal/code/ regex ^\h*([A-Za-z][A-Za-z0-9_-]*)\h*: 1:variable
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden cabal-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden cabal-insert-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # copy '--' comment prefix and following white spaces
+ try %[ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P ]
+ ]
+]
+
+define-command -hidden cabal-indent-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # preserve previous line indent
+ try %[ execute-keys -draft <semicolon> K <a-&> ]
+ # filter previous line
+ try %[ execute-keys -draft k : cabal-trim-indent <ret> ]
+ # indent after lines ending with { or :
+ try %[ execute-keys -draft , k x <a-k> [:{]$ <ret> j <a-gt> ]
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> \h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ ]
+]
+
+define-command -hidden cabal-indent-on-opening-curly-brace %[
+ evaluate-commands -draft -itersel %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft h <a-F> ) M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+ ]
+]
+
+define-command -hidden cabal-indent-on-closing-curly-brace %[
+ evaluate-commands -draft -itersel %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -draft <a-h> <a-k> ^\h+\}$ <ret> h m s \A|.\z<ret> 1<a-&> ]
+ ]
+]
+
+]
diff --git a/autoload/filetype/clojure.kak b/autoload/filetype/clojure.kak
new file mode 100644
index 0000000..d876262
--- /dev/null
+++ b/autoload/filetype/clojure.kak
@@ -0,0 +1,237 @@
+# http://clojure.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](clj|cljc|cljs|cljx|edn) %{
+ set-option buffer filetype clojure
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+hook global WinSetOption filetype=clojure %{
+ require-module clojure
+ clojure-configure-window
+}
+
+hook -group clojure-highlight global WinSetOption filetype=clojure %{
+ add-highlighter window/clojure ref clojure
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/clojure }
+}
+
+hook -group clojure-insert global BufNewFile .*[.](clj|cljc|cljs|cljx) %{
+ require-module clojure
+ clojure-insert-ns
+}
+
+provide-module clojure %{
+
+require-module lisp
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/clojure regions
+add-highlighter shared/clojure/code default-region group
+add-highlighter shared/clojure/comment region '(?<!\\)(?:\\\\)*\K;' '$' fill comment
+add-highlighter shared/clojure/shebang region '(?<!\\)(?:\\\\)*\K#!' '$' fill comment
+add-highlighter shared/clojure/string region '(?<!\\)(?:\\\\)*\K"' '(?<!\\)(?:\\\\)*"' fill string
+
+add-highlighter shared/clojure/code/ regex \b(nil|true|false)\b 0:value
+add-highlighter shared/clojure/code/ regex \
+ \\(?:space|tab|newline|return|backspace|formfeed|u[0-9a-fA-F]{4}|o[0-3]?[0-7]{1,2}|.)\b 0:string
+
+evaluate-commands %sh{
+ exec awk -f - <<'EOF'
+ BEGIN{
+ symbol_char="[^\\s()\\[\\]{}\"\\;@^`~\\\\%/]";
+ in_core="(clojure\\.core/|(?<!/))";
+ split( \
+ "case cond condp cond-> cond->> def definline definterface defmacro defmethod "\
+ "defmulti defn defn- defonce defprotocol defrecord defstruct deftype fn if "\
+ "if-let if-not if-some let letfn new ns when when-first when-let when-not "\
+ "when-some . ..", keywords);
+
+ split( \
+ "* *' + +' - -' -> ->> ->ArrayChunk ->Eduction ->Vec ->VecNode ->VecSeq / < "\
+ "<= = == > >= StackTraceElement->vec Throwable->map accessor aclone "\
+ "add-classpath add-watch agent agent-error agent-errors aget alength alias "\
+ "all-ns alter alter-meta! alter-var-root amap ancestors and any? apply "\
+ "areduce array-map as-> aset aset-boolean aset-byte aset-char aset-double "\
+ "aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in "\
+ "associative? atom await await-for bases bean bigdec bigint biginteger "\
+ "binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set "\
+ "bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array "\
+ "boolean? booleans bound-fn bound-fn* bound? bounded-count butlast byte "\
+ "byte-array bytes bytes? cast cat catch char char-array char-escape-string "\
+ "char-name-string char? chars class class? clear-agent-errors "\
+ "clojure-version coll? comment commute comp comparator compare "\
+ "compare-and-set! compile complement completing concat conj conj! cons "\
+ "constantly construct-proxy contains? count counted? create-ns "\
+ "create-struct cycle dec dec' decimal? declare dedupe default-data-readers "\
+ "delay delay? deliver denominator deref derive descendants disj disj! "\
+ "dissoc dissoc! distinct distinct? do doall dorun doseq dosync dotimes doto "\
+ "double double-array double? doubles drop drop-last drop-while eduction "\
+ "empty empty? ensure ensure-reduced enumeration-seq error-handler "\
+ "error-mode eval even? every-pred every? ex-data ex-info extend "\
+ "extend-protocol extend-type extenders extends? false? ffirst file-seq "\
+ "filter filterv finally find find-keyword find-ns find-var first flatten "\
+ "float float-array float? floats flush fn? fnext fnil for force format "\
+ "frequencies future future-call future-cancel future-cancelled? "\
+ "future-done? future? gen-class gen-interface gensym get get-in get-method "\
+ "get-proxy-class get-thread-bindings get-validator group-by halt-when hash "\
+ "hash-map hash-ordered-coll hash-set hash-unordered-coll ident? identical? "\
+ "identity ifn? import in-ns inc inc' indexed? init-proxy inst-ms inst? "\
+ "instance? int int-array int? integer? interleave intern interpose into "\
+ "into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key "\
+ "keys keyword keyword? last lazy-cat lazy-seq line-seq list list* list? "\
+ "load load-file load-reader load-string loaded-libs locking long long-array "\
+ "longs loop macroexpand macroexpand-1 make-array make-hierarchy map "\
+ "map-entry? map-indexed map? mapcat mapv max max-key memfn memoize merge "\
+ "merge-with meta methods min min-key mix-collection-hash mod monitor-enter "\
+ "monitor-exit name namespace namespace-munge nat-int? neg-int? neg? newline "\
+ "next nfirst nil? nnext not not-any? not-empty not-every? not= ns-aliases "\
+ "ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve "\
+ "ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array "\
+ "odd? or parents partial partition partition-all partition-by pcalls peek "\
+ "persistent! pmap pop pop! pop-thread-bindings pos-int? pos? pr pr-str "\
+ "prefer-method prefers print print-str printf println println-str prn "\
+ "prn-str promise proxy proxy-mappings proxy-super push-thread-bindings "\
+ "pvalues qualified-ident? qualified-keyword? qualified-symbol? quot quote "\
+ "rand rand-int rand-nth random-sample range ratio? rational? rationalize "\
+ "re-find re-groups re-matcher re-matches re-pattern re-seq read read-line "\
+ "read-string reader-conditional reader-conditional? realized? record? recur "\
+ "reduce reduce-kv reduced reduced? reductions ref ref-history-count "\
+ "ref-max-history ref-min-history ref-set refer refer-clojure reify "\
+ "release-pending-sends rem remove remove-all-methods remove-method "\
+ "remove-ns remove-watch repeat repeatedly replace replicate require reset! "\
+ "reset-meta! reset-vals! resolve rest restart-agent resultset-seq reverse "\
+ "reversible? rseq rsubseq run! satisfies? second select-keys send send-off "\
+ "send-via seq seq? seqable? seque sequence sequential? set set! "\
+ "set-agent-send-executor! set-agent-send-off-executor! set-error-handler! "\
+ "set-error-mode! set-validator! set? short short-array shorts shuffle "\
+ "shutdown-agents simple-ident? simple-keyword? simple-symbol? slurp some "\
+ "some-> some->> some-fn some? sort sort-by sorted-map sorted-map-by "\
+ "sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with "\
+ "str string? struct struct-map subs subseq subvec supers swap! swap-vals! "\
+ "symbol symbol? sync tagged-literal tagged-literal? take take-last take-nth "\
+ "take-while test the-ns thread-bound? throw time to-array to-array-2d "\
+ "trampoline transduce transient tree-seq true? try type unchecked-add "\
+ "unchecked-add-int unchecked-byte unchecked-char unchecked-dec "\
+ "unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float "\
+ "unchecked-inc unchecked-inc-int unchecked-int unchecked-long "\
+ "unchecked-multiply unchecked-multiply-int unchecked-negate "\
+ "unchecked-negate-int unchecked-remainder-int unchecked-short "\
+ "unchecked-subtract unchecked-subtract-int underive unreduced "\
+ "unsigned-bit-shift-right update update-in update-proxy uri? use uuid? val "\
+ "vals var var-get var-set var? vary-meta vec vector vector-of vector? "\
+ "volatile! volatile? vreset! vswap! while with-bindings with-bindings* "\
+ "with-in-str with-local-vars with-meta with-open with-out-str "\
+ "with-precision with-redefs with-redefs-fn xml-seq zero? zipmap", core_fns);
+
+ split( \
+ "*1 *2 *3 *agent* *clojure-version* *command-line-args* *compile-files* "\
+ "*compile-path* *compiler-options* *data-readers* *default-data-reader-fn* "\
+ "*e *err* *file* *flush-on-newline* *in* *ns* *out* *print-dup* "\
+ "*print-length* *print-level* *print-meta* *print-namespace-maps* "\
+ "*print-readably* *read-eval* *unchecked-math* *warn-on-reflection*", core_vars);
+ }
+ function print_word_highlighter(words, face, first) {
+ printf("add-highlighter shared/clojure/code/ regex (?<!%s)%s(", \
+ symbol_char, in_core);
+ first = 1;
+ for (i in words) {
+ if (!first) { printf("|"); }
+ printf("\\Q%s\\E", words[i]);
+ first = 0;
+ }
+ printf(")(?!%s) 0:%s\n", symbol_char, face);
+ }
+ function print_static_words(words) {
+ for (i in words) {
+ printf("%s clojure.core/%s ", words[i], words[i]);
+ }
+ }
+ BEGIN{
+ # Keywords
+ printf("add-highlighter shared/clojure/code/ regex ::?(%s+/)?%s+ 0:value\n", symbol_char, symbol_char);
+
+ # Numbers
+ printf("add-highlighter shared/clojure/code/ regex (?<!%s)[-+]?(?:0(?:[xX][0-9a-fA-F]+|[0-7]*)|[1-9]\\d*)N? 0:value\n", symbol_char);
+ printf("add-highlighter shared/clojure/code/ regex (?<!%s)[-+]?(?:0|[1-9]\\d*)(?:\\.\\d*)(?:M|[eE][-+]?\\d+)? 0:value\n", symbol_char);
+ printf("add-highlighter shared/clojure/code/ regex (?<!%s)[-+]?(?:0|[1-9]\\d*)/(?:0|[1-9]\\d*) 0:value\n", symbol_char);
+
+ print_word_highlighter(keywords, "keyword");
+ print_word_highlighter(core_fns, "function");
+ print_word_highlighter(core_vars, "variable");
+
+ printf("declare-option str-list clojure_static_words ")
+ print_static_words(keywords);
+ print_static_words(core_fns);
+ print_static_words(core_vars);
+ printf("\n");
+ }
+EOF
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden clojure-configure-window %{
+ set-option window static_words %opt{clojure_static_words}
+
+ hook window ModeChange pop:insert:.* -group clojure-trim-indent clojure-trim-indent
+ hook window InsertChar \n -group clojure-indent clojure-indent-on-new-line
+
+ set-option buffer extra_word_chars '_' . / * ? + - < > ! : "'"
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window clojure-.+ }
+}
+
+define-command -hidden clojure-trim-indent lisp-trim-indent
+
+declare-option \
+ -docstring 'regex matching the head of forms which have options *and* indented bodies' \
+ regex clojure_special_indent_forms \
+ '(?:def.*|doseq|for|fn\*?|if(-.*|)|let.*|loop|ns|testing|with-.*|when(-.*|))'
+
+define-command -hidden clojure-indent-on-new-line %{
+ # registers: i = best align point so far; w = start of first word of form
+ evaluate-commands -draft -save-regs '/"|^@iw' -itersel %{
+ execute-keys -draft 'gk"iZ'
+ try %{
+ execute-keys -draft '[bl"i<a-Z><gt>"wZ'
+
+ try %{
+ # If a special form, indent another (indentwidth - 1) spaces
+ execute-keys -draft '"wze<a-K>[\s()\[\]\{\}]<ret><a-k>\A' %opt{clojure_special_indent_forms} '\z<ret>'
+ execute-keys -draft '"wze<a-L>s.{' %sh{printf $(( kak_opt_indentwidth - 1 ))} '}\K.*<ret><a-;>;"i<a-Z><gt>'
+ } catch %{
+ # If not special and parameter appears on line 1, indent to parameter
+ execute-keys -draft '"wz<a-K>[()[\]{}]<ret>e<a-K>[\s()\[\]\{\}]<ret><a-l>s\h\K[^\s].*<ret><a-;>;"i<a-Z><gt>'
+ }
+ }
+ try %{ execute-keys -draft '[rl"i<a-Z><gt>' }
+ try %{ execute-keys -draft '[Bl"i<a-Z><gt>' }
+ execute-keys -draft ';"i<a-z>a&,'
+ }
+}
+
+declare-option -docstring %{
+ top-level directories which can contain clojure files
+ e.g. '(src|test|dev)'
+} regex clojure_source_directories '(src|test|dev)'
+
+define-command -docstring %{clojure-insert-ns: Insert namespace directive at top of Clojure source file} \
+ clojure-insert-ns %{
+ evaluate-commands -draft %{
+ execute-keys -save-regs '' 'gk\O' "%val{bufname}" '<esc>giZ'
+ try %{ execute-keys 'z<a-l>s\.clj[csx]?$<ret><a-d>' }
+ try %{ execute-keys 'z<a-l>s^' "%opt{clojure_source_directories}" '/<ret><a-d>' }
+ try %{ execute-keys 'z<a-l>s/<ret>r.' }
+ try %{ execute-keys 'z<a-l>s_<ret>r-' }
+ execute-keys 'z<a-l>\c(ns <c-r>")<ret><esc>'
+ }
+}
+
+}
diff --git a/autoload/filetype/cmake.kak b/autoload/filetype/cmake.kak
new file mode 100644
index 0000000..86fda25
--- /dev/null
+++ b/autoload/filetype/cmake.kak
@@ -0,0 +1,36 @@
+hook global BufCreate .+\.cmake|.*/CMakeLists.txt %{
+ set-option buffer filetype cmake
+}
+
+hook global BufCreate .*/CMakeCache.txt %{
+ set-option buffer filetype ini
+}
+
+hook global WinSetOption filetype=cmake %{
+ require-module cmake
+}
+
+hook -group cmake-highlight global WinSetOption filetype=cmake %{
+ add-highlighter window/cmake ref cmake
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cmake }
+}
+
+provide-module cmake %{
+
+add-highlighter shared/cmake regions
+add-highlighter shared/cmake/code default-region group
+add-highlighter shared/cmake/comment region '#' '$' fill comment
+add-highlighter shared/cmake/argument region -recurse '\(' '\w+\h*\(\K' '(?=\))' regions
+
+add-highlighter shared/cmake/code/ regex '\w+\h*(?=\()' 0:meta
+
+add-highlighter shared/cmake/argument/args default-region regex '\$\{\w+\}' 0:variable
+add-highlighter shared/cmake/argument/comment region '#' '$' fill comment
+add-highlighter shared/cmake/argument/quoted region '"' '(?<!\\)(\\\\)*"' group
+add-highlighter shared/cmake/argument/raw-quoted region -match-capture '\[(=*)\[' '\](=*)\]' ref cmake/argument/quoted
+
+add-highlighter shared/cmake/argument/quoted/ fill string
+add-highlighter shared/cmake/argument/quoted/ regex '\$\{\w+\}' 0:variable
+add-highlighter shared/cmake/argument/quoted/ regex '\w+\h*(?=\()' 0:function
+
+}
diff --git a/autoload/filetype/coffee.kak b/autoload/filetype/coffee.kak
new file mode 100644
index 0000000..c9ee330
--- /dev/null
+++ b/autoload/filetype/coffee.kak
@@ -0,0 +1,92 @@
+# http://coffeescript.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](coffee) %{
+ set-option buffer filetype coffee
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=coffee %{
+ require-module coffee
+
+ hook window ModeChange pop:insert:.* -group coffee-trim-indent coffee-trim-indent
+ hook window InsertChar \n -group coffee-insert coffee-insert-on-new-line
+ hook window InsertChar \n -group coffee-indent coffee-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window coffee-.+ }
+}
+
+hook -group coffee-highlight global WinSetOption filetype=coffee %{
+ add-highlighter window/coffee ref coffee
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/coffee }
+}
+
+
+provide-module coffee %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/coffee regions
+add-highlighter shared/coffee/code default-region group
+add-highlighter shared/coffee/single_string region "'" "'" fill string
+add-highlighter shared/coffee/single_string_alt region "'''" "'''" fill string
+add-highlighter shared/coffee/double_string region '"' (?<!\\)(\\\\)*" regions
+add-highlighter shared/coffee/double_string_alt region '"""' '"""' ref shared/coffee/double_string
+add-highlighter shared/coffee/regex region '/' (?<!\\)(\\\\)*/[gimy]* regions
+add-highlighter shared/coffee/regex_alt region '///' ///[gimy]* ref shared/coffee/regex
+add-highlighter shared/coffee/comment1 region '#' '$' fill comment
+add-highlighter shared/coffee/comment2 region '###' '###' fill comment
+
+# Regular expression flags are: g → global match, i → ignore case, m → multi-lines, y → sticky
+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
+
+add-highlighter shared/coffee/double_string/base default-region fill string
+add-highlighter shared/coffee/double_string/interpolation region -recurse \{ \Q#{ \} fill meta
+add-highlighter shared/coffee/regex/base default-region fill meta
+add-highlighter shared/coffee/regex/interpolation region -recurse \{ \Q#{ \} fill meta
+
+# Keywords are collected at
+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
+# http://coffeescript.org/documentation/docs/lexer.html#section-63
+add-highlighter shared/coffee/code/ regex [$@]\w* 0:variable
+add-highlighter shared/coffee/code/ regex \b(Array|Boolean|Date|Function|Number|Object|RegExp|String)\b 0:type
+add-highlighter shared/coffee/code/ regex \b(document|false|no|null|off|on|parent|self|this|true|undefined|window|yes)\b 0:value
+add-highlighter shared/coffee/code/ regex \b(and|is|isnt|not|or)\b 0:operator
+add-highlighter shared/coffee/code/ regex \b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|function|if|implements|import|in|instanceof|interface|let|native|new|package|private|protected|public|return|static|super|switch|throw|try|typeof|var|void|while|with|yield)\b 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden coffee-trim-indent %{
+ evaluate-commands -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden coffee-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s '^\h*\K#\h*' <ret> y gh j P }
+ }
+}
+
+define-command -hidden coffee-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : coffee-trim-indent <ret> }
+ # indent after start structure
+ try %{ execute-keys -draft k x <a-k> ^ \h * (case|catch|class|else|finally|for|function|if|switch|try|while|with) \b | (=|->) $ <ret> j <a-gt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/conf.kak b/autoload/filetype/conf.kak
new file mode 100644
index 0000000..9b71589
--- /dev/null
+++ b/autoload/filetype/conf.kak
@@ -0,0 +1,30 @@
+hook global BufCreate .+\.(repo|cfg|properties|desktop) %{
+ set-option buffer filetype conf
+}
+
+hook global WinCreate .+\.ini %{
+ try %{
+ execute-keys /^\h*#<ret>
+ set-option buffer filetype conf
+ }
+}
+
+hook global WinSetOption filetype=conf %{
+ require-module conf
+}
+
+hook -group conf-highlight global WinSetOption filetype=conf %{
+ add-highlighter window/conf ref conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/conf }
+}
+
+provide-module conf %{
+
+add-highlighter shared/conf regions
+add-highlighter shared/conf/code default-region group
+add-highlighter shared/conf/comment region '(^|\h)\K#' $ fill comment
+
+add-highlighter shared/conf/code/ regex "(?S)^\h*(\[.+?\])\h*$" 1:title
+add-highlighter shared/conf/code/ regex "^\h*([^\[][^=\n]*)=([^\n]*)" 1:variable 2:value
+
+}
diff --git a/autoload/filetype/coq.kak b/autoload/filetype/coq.kak
new file mode 100644
index 0000000..cab1abf
--- /dev/null
+++ b/autoload/filetype/coq.kak
@@ -0,0 +1,127 @@
+
+# Detection
+# --------
+
+hook global BufCreate .*\.v %{
+ set-option buffer filetype coq
+}
+
+# Initialization
+# --------------
+
+hook global WinSetOption filetype=coq %{
+ require-module coq
+ hook window ModeChange pop:insert:.* -group coq-trim-indent coq-trim-indent
+ hook window InsertChar \n -group coq-indent coq-copy-indent-on-newline
+
+ set-option window static_words %opt{coq_static_words}
+ add-highlighter window/coq ref coq
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-highlighter window/coq
+ remove-hooks window coq-indent
+ }
+}
+
+provide-module coq %{
+
+# Syntax
+# ------
+
+# This is a `looks sensible' keyword syntax highlighting, far from being correct.
+# Note that only the core language and the proof language is supported,
+# the Ltac language is not (for now).
+
+add-highlighter shared/coq regions
+
+add-highlighter shared/coq/comment region -recurse \Q(* \Q(* \Q*) fill comment
+add-highlighter shared/coq/string region (?<!")" (?<!")("")*" fill string
+
+add-highlighter shared/coq/command default-region group
+
+# This is not any lexical convention of coq, simply highlighting used to make
+# proofs look better, based on how people usually use notations
+add-highlighter shared/coq/command/ regex [`!@#$%^&*-=+\\:\;|<>/]+ 0:operator
+add-highlighter shared/coq/command/ regex \(dfs\)|\(bfs\) 0:operator
+add-highlighter shared/coq/command/ regex [()\[\]{}] 0:operator
+
+# numeral literals
+add-highlighter shared/coq/command/ regex [-]?[0-9][0-9_]*(\.[0-9_]+)?([eE][+-][0-9_]+)? 0:value
+
+evaluate-commands %sh{
+ # These are builtin keywords of the Gallina language (without tactics)
+ keywords="_ IF Prop SProp Set Type as at by cofix discriminated else end exists exists2 fix for"
+ keywords="${keywords} forall fun if in lazymatch let match multimatch return then using where with"
+ keywords="${keywords} inside outside"
+
+ # These are (part of) coq top level commands
+ commands="Abort About Add Admitted All Arguments Axiom Back BackTo"
+ commands="${commands} Canonical Cd Check Coercion CoFixpoint Collection Compute Conjecture Context Contextual Corollary"
+ commands="${commands} Declare Defined Definition Delimit Drop End Eval Example Existential Export"
+ commands="${commands} Fact Fail File Fixpoint Focus From Function Generalizable Global Goal Grab"
+ commands="${commands} Hint Hypotheses Hypothesis Immediate Implicit Import Include Inductive"
+ commands="${commands} Lemma Let Library Load LoadPath Local Locate Module No Notation Opaque"
+ commands="${commands} Parameter Parameters Primitive Print Proof Property Proposition Pwd Qed Quit"
+ commands="${commands} Rec Record Redirect Register Remark Remove Require Reset"
+ commands="${commands} Section Search SearchAbout SearchHead SearchPattern SearchRewrite Show Strategy"
+ commands="${commands} Test Theorem Time Timeout Transparent Types Universes Undo Unfocus Unfocused Unset Variable Variables"
+
+ # These are (part of) coq's builtin tactics
+ tactics="abstract absurd admit all apply assert assert_fails"
+ tactics="${tactics} assert_succeeds assumption auto autoapply"
+ tactics="${tactics} autorewrite autounfold btauto by case cbn"
+ tactics="${tactics} cbv change clear clearbody cofix compare"
+ tactics="${tactics} compute congr congruence constructor contradict"
+ tactics="${tactics} cut cutrewrite cycle decide decompose dependent"
+ tactics="${tactics} destruct discriminate do done double"
+ tactics="${tactics} eapply eassert eauto eexact elim elimtype exact exfalso"
+ tactics="${tactics} fail field first firstorder fix fold functional"
+ tactics="${tactics} generalize guard have hnf idtac induction injection"
+ tactics="${tactics} instantiate intro intros intuition inversion"
+ tactics="${tactics} inversion_clear lapply lazy last move omega"
+ tactics="${tactics} pattern pose progress red refine reflexivity"
+ tactics="${tactics} remember rename repeat replace rewrite right ring"
+ tactics="${tactics} set setoid_reflexivity setoid_replace setoid_rewrite"
+ tactics="${tactics} setoid_symmetry setoid_transitivity simpl simple"
+ tactics="${tactics} simplify_eq solve specialize split start stop"
+ tactics="${tactics} subst symmetry tauto transitivity trivial try"
+ tactics="${tactics} under unfold unify unlock"
+
+ echo declare-option str-list coq_static_words ${keywords} ${commands} ${tactics}
+
+ keywords_regex=$(echo ${keywords} | tr ' ' '|')
+ printf %s "
+ add-highlighter shared/coq/command/ regex \b(${keywords_regex})\b 0:keyword
+ "
+ commands_regex=$(echo ${commands} | tr ' ' '|')
+ printf %s "
+ add-highlighter shared/coq/command/ regex ^[\h\n]*(${commands_regex})\b 0:variable
+ "
+
+ tactics_regex=$(echo ${tactics} | tr ' ' '|')
+ printf %s "
+ add-highlighter shared/coq/command/ regex \b(${tactics_regex})\b 0:keyword
+ "
+}
+
+# Indentation
+# -----------
+# Coq's syntax is based heavily on keywords and program structure,
+# not based on explicit, unique delimiters, like braces in C-family.
+# So it is difficult to properly indent using only regex...
+# Hence here only a simple mechanism of copying indent is done.
+define-command -hidden coq-copy-indent-on-newline %{
+ evaluate-commands -draft -itersel %{
+ try %{ execute-keys -draft k x s ^\h+ <ret> y gh j P }
+ }
+}
+
+define-command -hidden coq-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+}
diff --git a/autoload/filetype/crystal.kak b/autoload/filetype/crystal.kak
new file mode 100644
index 0000000..e354067
--- /dev/null
+++ b/autoload/filetype/crystal.kak
@@ -0,0 +1,258 @@
+# Crystal
+# https://crystal-lang.org
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate '.*\.cr' %{
+ set-option buffer filetype crystal
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=crystal %{
+ require-module crystal
+
+ add-highlighter window/crystal ref crystal
+ evaluate-commands set-option window static_words %opt{crystal_keywords} %opt{crystal_attributes} %opt{crystal_objects}
+
+ hook window ModeChange pop:insert:.* -group crystal-trim-indent crystal-trim-indent
+ hook window InsertChar .* -group crystal-indent crystal-indent-on-char
+ hook window InsertChar '\n' -group crystal-indent crystal-indent-on-new-line
+ hook window InsertChar '\n' -group crystal-insert crystal-insert-on-new-line
+
+ hook -always -once window WinSetOption filetype=.* %{
+ remove-highlighter window/crystal
+ remove-hooks window crystal-.+
+ }
+}
+
+provide-module crystal %§
+
+declare-option -hidden str-list crystal_keywords 'abstract' 'alias' 'annotation' 'as' 'asm' 'begin' 'break' 'case' 'class' 'def' 'do' 'else' 'elsif' 'end' 'ensure' 'enum' 'extend' 'false' 'for' 'fun' 'if' 'include' 'instance_sizeof' 'is_a?' 'lib' 'macro' 'module' 'next' 'nil' 'nil?' 'of' 'offsetof' 'out' 'pointerof' 'private' 'protected' 'require' 'rescue' 'responds_to?' 'return' 'select' 'self' 'sizeof' 'struct' 'super' 'then' 'true' 'type' 'typeof' 'uninitialized' 'union' 'unless' 'until' 'verbatim' 'when' 'while' 'with' 'yield'
+# https://crystal-lang.org/reference/syntax_and_semantics/methods_and_instance_variables.html#getters-and-setters
+declare-option -hidden str-list crystal_attributes 'getter' 'setter' 'property'
+declare-option -hidden str-list crystal_operators '+' '-' '*' '/' '//' '%' '|' '&' '^' '~' '**' '<<' '<' '<=' '==' '!=' '=~' '!~' '>>' '>' '>=' '<=>' '===' '[]' '[]=' '[]?' '[' '&+' '&-' '&*' '&**'
+declare-option -hidden str-list crystal_objects 'Adler32' 'ArgumentError' 'Array' 'Atomic' 'Base64' 'Benchmark' 'BigDecimal' 'BigFloat' 'BigInt' 'BigRational' 'BitArray' 'Bool' 'Box' 'Bytes' 'Channel' 'Char' 'Class' 'Colorize' 'Comparable' 'Complex' 'Concurrent' 'ConcurrentExecutionException' 'CRC32' 'Crypto' 'Crystal' 'CSV' 'Debug' 'Deprecated' 'Deque' 'Digest' 'Dir' 'DivisionByZeroError' 'DL' 'ECR' 'Enum' 'Enumerable' 'ENV' 'Errno' 'Exception' 'Fiber' 'File' 'FileUtils' 'Flags' 'Flate' 'Float' 'Float32' 'Float64' 'GC' 'Gzip' 'Hash' 'HTML' 'HTTP' 'Indexable' 'IndexError' 'INI' 'Int' 'Int128' 'Int16' 'Int32' 'Int64' 'Int8' 'InvalidBigDecimalException' 'InvalidByteSequenceError' 'IO' 'IPSocket' 'Iterable' 'Iterator' 'JSON' 'KeyError' 'Levenshtein' 'Link' 'LLVM' 'Logger' 'Markdown' 'Math' 'MIME' 'Mutex' 'NamedTuple' 'Nil' 'NilAssertionError' 'NotImplementedError' 'Number' 'OAuth' 'OAuth2' 'Object' 'OpenSSL' 'OptionParser' 'OverflowError' 'PartialComparable' 'Path' 'Pointer' 'PrettyPrint' 'Proc' 'Process' 'Random' 'Range' 'Readline' 'Reference' 'Reflect' 'Regex' 'SemanticVersion' 'Set' 'Signal' 'Slice' 'Socket' 'Spec' 'StaticArray' 'String' 'StringPool' 'StringScanner' 'Struct' 'Symbol' 'System' 'TCPServer' 'TCPSocket' 'Termios' 'Time' 'Tuple' 'TypeCastError' 'UDPSocket' 'UInt128' 'UInt16' 'UInt32' 'UInt64' 'UInt8' 'Unicode' 'Union' 'UNIXServer' 'UNIXSocket' 'URI' 'UUID' 'VaList' 'Value' 'WeakRef' 'XML' 'YAML' 'Zip' 'Zlib'
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/crystal regions
+add-highlighter shared/crystal/code default-region group
+
+# Comments
+# https://crystal-lang.org/reference/syntax_and_semantics/comments.html
+# Avoid string literals with interpolation
+add-highlighter shared/crystal/comment region '#(?!\{)' '$' fill comment
+
+# String
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html
+add-highlighter shared/crystal/string region '"' '(?<!\\)(\\\\)*"' regions
+
+# Percent string literals
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html#percent-string-literals
+add-highlighter shared/crystal/parenthesis-string region -recurse '\(' '%Q?\(' '\)' regions
+add-highlighter shared/crystal/bracket-string region -recurse '\[' '%Q?\[' '\]' regions
+add-highlighter shared/crystal/brace-string region -recurse '\{' '%Q?\{' '\}' regions
+add-highlighter shared/crystal/angle-string region -recurse '<' '%Q?<' '>' regions
+add-highlighter shared/crystal/pipe-string region '%Q?\|' '\|' regions
+
+# Raw
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html#percent-string-literals
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html#percent-string-array-literal
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html#percent-symbol-array-literal
+add-highlighter shared/crystal/raw-parenthesis-string region -recurse '\(' '%[qwi]\(' '\)' fill string
+add-highlighter shared/crystal/raw-bracket-string region -recurse '\[' '%[qwi]\[' '\]' fill string
+add-highlighter shared/crystal/raw-brace-string region -recurse '\{' '%[qwi]\{' '\}' fill string
+add-highlighter shared/crystal/raw-angle-string region -recurse '<' '%[qwi]<' '>' fill string
+add-highlighter shared/crystal/raw-pipe-string region '%[qwi]\|' '\|' fill string
+
+# Here document
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html#heredoc
+add-highlighter shared/crystal/heredoc region -match-capture '<<-(\w+)' '^\h*(\w+)$' regions
+# Raw
+add-highlighter shared/crystal/raw-heredoc region -match-capture "<<-'(\w+)'" '^\h*(\w+)$' regions
+add-highlighter shared/crystal/raw-heredoc/fill default-region fill string
+add-highlighter shared/crystal/raw-heredoc/interpolation region -recurse '\{' '#\{' '\}' fill meta
+
+# Symbol
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html
+add-highlighter shared/crystal/quoted-symbol region ':"' '(?<!\\)(\\\\)*"' fill value
+
+# Regular expressions
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/regex.html
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/regex.html#modifiers
+add-highlighter shared/crystal/regex region '/' '(?<!\\)(\\\\)*/[imx]*' regions
+# Avoid unterminated regular expression
+add-highlighter shared/crystal/division region ' / ' '.\K' group
+
+# Percent regex literals
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/regex.html#percent-regex-literals
+add-highlighter shared/crystal/parenthesis-regex region -recurse '\(' '%r?\(' '\)[imx]*' regions
+add-highlighter shared/crystal/bracket-regex region -recurse '\[' '%r?\[' '\][imx]*' regions
+add-highlighter shared/crystal/brace-regex region -recurse '\{' '%r?\{' '\}[imx]*' regions
+add-highlighter shared/crystal/angle-regex region -recurse '<' '%r?<' '>[imx]*' regions
+add-highlighter shared/crystal/pipe-regex region '%r?\|' '\|[imx]*' regions
+
+# Command
+# https://crystal-lang.org/reference/syntax_and_semantics/literals/command.html
+add-highlighter shared/crystal/command region '`' '(?<!\\)(\\\\)*`' regions
+
+# Percent command literals
+add-highlighter shared/crystal/parenthesis-command region -recurse '\(' '%x?\(' '\)' regions
+add-highlighter shared/crystal/bracket-command region -recurse '\[' '%x?\[' '\]' regions
+add-highlighter shared/crystal/brace-command region -recurse '\{' '%x?\{' '\}' regions
+add-highlighter shared/crystal/angle-command region -recurse '<' '%x?<' '>' regions
+add-highlighter shared/crystal/pipe-command region '%x?\|' '\|' regions
+
+evaluate-commands %sh[
+ # Keywords
+ eval "set -- $kak_quoted_opt_crystal_keywords"
+ regex="\\b(?:\\Q$1\\E"
+ shift
+ for keyword do
+ regex="$regex|\\Q$keyword\\E"
+ done
+ regex="$regex)\\b"
+ printf 'add-highlighter shared/crystal/code/keywords regex %s 0:keyword\n' "$regex"
+
+ # Attributes
+ eval "set -- $kak_quoted_opt_crystal_attributes"
+ regex="\\b(?:\\Q$1\\E"
+ shift
+ for attribute do
+ regex="$regex|\\Q$attribute\\E"
+ done
+ regex="$regex)\\b"
+ printf 'add-highlighter shared/crystal/code/attributes regex %s 0:attribute\n' "$regex"
+
+ # Symbols
+ eval "set -- $kak_quoted_opt_crystal_operators"
+ # Avoid to match modules
+ regex="(?<!:):(?:\\w+[?!]?"
+ for operator do
+ regex="$regex|\\Q$operator\\E"
+ done
+ regex="$regex)"
+ printf 'add-highlighter shared/crystal/code/symbols regex %%(%s) 0:value\n' "$regex"
+
+ # Objects
+ eval "set -- $kak_quoted_opt_crystal_objects"
+ regex="\\b(?:\\Q$1\\E"
+ shift
+ for object do
+ regex="$regex|\\Q$object\\E"
+ done
+ regex="$regex)\\b"
+ printf 'add-highlighter shared/crystal/code/objects regex %s 0:builtin\n' "$regex"
+
+ # Interpolation
+ # String
+ # https://crystal-lang.org/reference/syntax_and_semantics/literals/string.html#interpolation
+ for id in string parenthesis-string bracket-string brace-string angle-string pipe-string heredoc; do
+ printf "
+ add-highlighter shared/crystal/$id/fill default-region fill string
+ add-highlighter shared/crystal/$id/interpolation region -recurse '\\{' '#\\{' '\\}' ref crystal
+ "
+ done
+
+ # Regular expressions
+ # https://crystal-lang.org/reference/syntax_and_semantics/literals/regex.html#interpolation
+ for id in regex parenthesis-regex bracket-regex brace-regex angle-regex pipe-regex; do
+ printf "
+ add-highlighter shared/crystal/$id/fill default-region fill meta
+ add-highlighter shared/crystal/$id/interpolation region -recurse '\\{' '#\\{' '\\}' ref crystal
+ "
+ done
+
+ # Command
+ for id in command parenthesis-command bracket-command brace-command angle-command pipe-command; do
+ printf "
+ add-highlighter shared/crystal/$id/fill default-region fill meta
+ add-highlighter shared/crystal/$id/interpolation region -recurse '\\{' '#\\{' '\\}' ref crystal
+ "
+ done
+]
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden crystal-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h+$ <ret> d }
+ }
+}
+
+define-command -hidden crystal-indent-on-char %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # align 'else' to 'if/case'
+ try %{ execute-keys -draft x <a-k> ^\h*else$ <ret> <a-a>i <a-semicolon> <a-?> ^\h*(?:if|case) <ret> <a-S> 1<a-&> }
+ # align 'elsif' to 'if'
+ try %{ execute-keys -draft x <a-k> ^\h*elsif$ <ret> <a-a>i <a-semicolon> <a-?> ^\h*(?:if) <ret> <a-S> 1<a-&> }
+ # align 'when' to 'case'
+ try %{ execute-keys -draft x <a-k> ^\h*when$ <ret> <a-a>i <a-semicolon> <a-?> ^\h*(?:case) <ret> <a-S> 1<a-&> }
+ # align 'rescue' to 'begin/def'
+ try %{ execute-keys -draft x <a-k> ^\h*rescue$ <ret> <a-a>i <a-semicolon> <a-?> ^\h*(?:begin|def) <ret> <a-S> 1<a-&> }
+ # align 'end' to opening structure
+ try %{ execute-keys -draft x <a-k> ^\h*end$ <ret> <a-a>i <a-semicolon> <a-?> ^\h*(?:begin|case|class|def|for|if|module|unless|until|while) <ret> <a-S> 1<a-&> }
+ }
+}
+
+define-command -hidden crystal-indent-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # Copy previous line indent
+ try %{ execute-keys -draft K <a-&> }
+ # Remove previous line's trailing spaces
+ try %{ execute-keys -draft k :crystal-trim-indent <ret> }
+ # Indent after start structure/opening statement
+ try %{ execute-keys -draft k x <a-k> ^\h*(?:begin|case|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while|.+\bdo$|.+\bdo\h\|.+(?=\|))[^0-9A-Za-z_!?] <ret> j <a-gt> }
+ }
+}
+
+define-command -hidden crystal-insert-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # copy _#_ comment prefix and following white spaces
+ try %{ execute-keys -draft k x s '^\h*\K#\h*' <ret> y j x<semicolon> P }
+ # wisely add end structure
+ evaluate-commands -save-regs x %[
+ try %{ execute-keys -draft k x s ^ \h + <ret> \" x y } catch %{ reg x '' }
+ try %[
+ evaluate-commands -draft %[
+ # Check if previous line opens a block
+ execute-keys -draft kx <a-k>^<c-r>x(?:begin|case|class|def|for|if|module|unless|until|while|.+\bdo$|.+\bdo\h\|.+(?=\|))[^0-9A-Za-z_!?]<ret>
+ # Check that we do not already have an end for this indent level which is first set via `crystal-indent-on-new-line` hook
+ execute-keys -draft }i J x <a-K> ^<c-r>x(?:end|else|elsif|rescue|when)[^0-9A-Za-z_!?]<ret>
+ ]
+ execute-keys -draft o<c-r>xend<esc> # insert a new line with containing end
+ ]
+ ]
+ ]
+]
+
+define-command -hidden crystal-fetch-keywords %{
+ set-register dquote %sh{
+ curl --location https://github.com/crystal-lang/crystal/raw/master/src/compiler/crystal/syntax/lexer.cr |
+ kak -f '%1scheck_ident_or_keyword\(:(\w+\??), \w+\)<ret>y%<a-R>a<ret><esc><a-_>a<del><esc>|sort<ret>'
+ }
+}
+
+define-command -hidden crystal-fetch-operators %{
+ set-register dquote %sh{
+ curl --location https://github.com/crystal-lang/crystal/raw/master/src/compiler/crystal/syntax/parser.cr |
+ kak -f '/AtomicWithMethodCheck =<ret>x1s:"([^"]+)"<ret>y%<a-R>i''<esc>a''<ret><esc><a-_>a<del><esc>'
+ }
+}
+
+define-command -hidden crystal-fetch-objects %{
+ set-register dquote %sh{
+ curl --location https://crystal-lang.org/api/ |
+ # Remove Top Level Namespace
+ kak -f '%1sdata-id="github.com/crystal-lang/crystal/(\w+)"<ret>)<a-,>y%<a-R>a<ret><esc><a-_>a<del><esc>'
+ }
+}
+
diff --git a/autoload/filetype/css.kak b/autoload/filetype/css.kak
new file mode 100644
index 0000000..b498a43
--- /dev/null
+++ b/autoload/filetype/css.kak
@@ -0,0 +1,186 @@
+# http://w3.org/Style/CSS
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](css) %{
+ set-option buffer filetype css
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=css %[
+ require-module css
+
+ hook window ModeChange pop:insert:.* -group css-trim-indent css-trim-indent
+ hook window InsertChar \n -group css-insert css-insert-on-new-line
+ hook window InsertChar \n -group css-indent css-indent-on-new-line
+ hook window InsertChar \} -group css-indent css-indent-on-closing-curly-brace
+ set-option buffer extra_word_chars '_' '-'
+ set-face global cssLogicalOperator +i@keyword
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window css-.+ }
+]
+
+hook -group css-highlight global WinSetOption filetype=css %{
+ add-highlighter window/css ref css
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/css }
+}
+
+
+provide-module css %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+add-highlighter shared/css regions
+add-highlighter shared/css/code default-region group
+add-highlighter shared/css/attr_selector region \[ \] regions
+add-highlighter shared/css/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/css/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/css/comment region /\* \*/ fill comment
+
+evaluate-commands %sh{
+ # html tags
+ # generated from the URL & <code> below
+ # includes elements that cannot be styled, which is fine.
+ #
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element
+ html_tags='html body address article aside footer header h1 h2 h3 h4 h5 h6 main nav section blockquote dd div dl dt figcaption figure hr li ol p pre ul a abbr b bdi bdo br cite code data dfn em i kbd mark q rp rt ruby s samp small span strong sub sup time u var wbr area audio img map track video embed iframe object param picture portal source canvas noscript script del ins caption col colgroup table tbody td tfoot th thead tr button datalist fieldset form input label legend meter optgroup option output progress select textarea details dialog menu summary slot template acronym applet basefont bgsound big blink center content dir font frame frameset hgroup image keygen marquee menuitem nobr noembed noframes plaintext rb rtc shadow spacer strike tt xmp'
+
+ # Units
+ # ‾‾‾‾‾
+ # generated from the URL & <code> below
+ # includes #rgb, #rrggbb, #rrggbbaa as color values {3,8}
+ #
+ # https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units
+ units='% cap ch cm deg dpcm dpi dppx em ex grad Hz ic in kHz lh mm ms pc pt px Q rad rem rlh s turn vb vh vi vmax vmin vw x'
+
+ logical_ops='and not only from to'
+ keywords='!important auto inherit initial unset none'
+ media_types='all print screen speech'
+
+ # easing_re='linear|ease(-(in-out|in|out))?|step-(start|end)'
+
+ # combinators='+ > ~ ||'
+ # attribute_op='= ~= |= ^= $= *='
+
+ join() { eval set -- $1; IFS="|"; echo "$*"; }
+
+ # Selectors
+ # ‾‾‾‾‾‾‾‾‾
+ # universal: *, ns|*, *|*, |*
+ # class/id: .class, #id
+ # type: element
+ # attr: [attr=val]
+
+ # order below matters
+ printf %s "
+ add-highlighter shared/css/code/tag_selectors regex \b($(join "${html_tags}"))((:[a-z:])|[\h.#,]) 1:keyword
+
+ add-highlighter shared/css/code/functional_notation regex ([a-zA-Z0-9-_]+[a-zA-Z0-9])\( 1:keyword
+
+ add-highlighter shared/css/code/logical_operators regex (\b($(join "${logical_ops}"))\b|$(join "${keywords}")) 1:cssLogicalOperator
+
+ add-highlighter shared/css/code/media_types regex \b($(join "${media_types}"))\b 1:+i
+
+ # (after functional notation as they may contain paranthesis)
+ add-highlighter shared/css/code/pseudo regex (:{1,2})([a-z-]+) 0:attribute
+
+ add-highlighter shared/css/code/at_rules regex @[a-z-]+ 0:function
+
+ add-highlighter shared/css/code/css_property regex ([A-Za-z][A-Za-z0-9_-]*)\h*:\h 1:operator 1:+a
+
+ add-highlighter shared/css/code/selectors regex (\*|[*]?[.][A-Za-z][A-Za-z0-9_-]+) 0:type
+ add-highlighter shared/css/code/selectors_id regex (\*|[*]?[#][A-Za-z][A-Za-z0-9_-]+) 0:type 0:+i
+
+ add-highlighter shared/css/code/hex_values regex (#[0-9A-Fa-f]{3,8})\b 0:value 0:+a
+ add-highlighter shared/css/code/units regex \b(\d*\.)?\d+($(join "${units}"))?\b 0:value 0:+a
+ "
+}
+
+# Attribute Selectors
+add-highlighter shared/css/attr_selector/base default-region group
+add-highlighter shared/css/attr_selector/base/ regex ([a-zA-Z0-9-]+) 1:attribute
+add-highlighter shared/css/attr_selector/base/ regex \h(i|s) 1:type
+add-highlighter shared/css/attr_selector/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/css/attr_selector/single_string region "'" (?<!\\)(\\\\)*' fill string
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden css-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden css-indent-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ execute-keys <semicolon>
+ try %<
+ # if previous line is part of a comment, do nothing
+ execute-keys -draft <a-?>/\*<ret> <a-K>^\h*[^/*\h]<ret>
+ > catch %<
+ # else if previous line closed a paren (possibly followed by words and a comment),
+ # copy indent of the opening paren line
+ execute-keys -draft kx 1s(\))(\h+\w+)*\h*(\;\h*)?(?://[^\n]+)?\n\z<ret> m<a-semicolon>J <a-S> 1<a-&>
+ > catch %<
+ # else indent new lines with the same level as the previous one
+ execute-keys -draft K <a-&>
+ >
+ # filter previous line
+ try %< execute-keys -draft k x <a-k>^\h+$<ret> Hd >
+ # indent after lines ending with with {
+ try %[ execute-keys -draft k x <a-k> \{$ <ret> j <a-gt> ]
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ ]
+]
+
+define-command -hidden css-insert-on-new-line %[
+ evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %[
+ # if the previous line isn't within a comment scope, break
+ execute-keys -draft kx <a-k>^(\h*/\*|\h+\*(?!/))<ret>
+
+ # find comment opening, validate it was not closed, and check its using star prefixes
+ execute-keys -draft <a-?>/\*<ret><a-H> <a-K>\*/<ret> <a-k>\A\h*/\*([^\n]*\n\h*\*)*[^\n]*\n\h*.\z<ret>
+
+ try %[
+ # if the previous line is opening the comment, insert star preceeded by space
+ execute-keys -draft kx<a-k>^\h*/\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the next line is a comment line insert a star
+ execute-keys -draft jx<a-k>^\h+\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the previous line is an empty comment line, close the comment scope
+ execute-keys -draft kx<a-k>^\h+\*\h+$<ret> x1s\*(\h*)<ret>c/<esc>
+ ] catch %[
+ # if the previous line is a non-empty comment line, add a star
+ execute-keys -draft i*<space><esc>
+ ]
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft s\h+$<ret> d ]
+ # align the new star with the previous one
+ execute-keys Kx1s^[^*]*(\*)<ret>&
+ ]
+ >
+]
+
+define-command -hidden css-indent-on-closing-curly-brace %[
+ evaluate-commands -draft -itersel %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -draft <a-h> <a-k> ^\h+\}$ <ret> m s \A|.\z <ret> 1<a-&> ]
+ ]
+]
+
+]
diff --git a/autoload/filetype/cucumber.kak b/autoload/filetype/cucumber.kak
new file mode 100644
index 0000000..f8f5415
--- /dev/null
+++ b/autoload/filetype/cucumber.kak
@@ -0,0 +1,102 @@
+# http://cukes.info
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](feature|story) %{
+ set-option buffer filetype cucumber
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=cucumber %{
+ require-module cucumber
+
+ hook window ModeChange pop:insert:.* -group cucumber-trim-indent cucumber-trim-indent
+ hook window InsertChar \n -group cucumber-insert cucumber-insert-on-new-line
+ hook window InsertChar \n -group cucumber-indent cucumber-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window cucumber-.+ }
+}
+
+hook -group cucumber-highlight global WinSetOption filetype=cucumber %{
+ add-highlighter window/cucumber ref cucumber
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cucumber }
+}
+
+
+provide-module cucumber %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/cucumber regions
+add-highlighter shared/cucumber/code default-region group
+add-highlighter shared/cucumber/language region ^\h*#\h*language: $ group
+add-highlighter shared/cucumber/comment region ^\h*# $ fill comment
+
+add-highlighter shared/cucumber/language/ fill meta
+add-highlighter shared/cucumber/language/ regex \S+$ 0:value
+
+# Spoken languages
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+# https://github.com/cucumber/cucumber/wiki/Spoken-languages
+#
+# curl --location https://github.com/cucumber/gherkin/raw/master/lib/gherkin/i18n.json
+#
+# {
+# "en": {
+# "name": "English",
+# "native": "English",
+# "feature": "Feature|Business Need|Ability",
+# "background": "Background",
+# "scenario": "Scenario",
+# "scenario_outline": "Scenario Outline|Scenario Template",
+# "examples": "Examples|Scenarios",
+# "given": "*|Given",
+# "when": "*|When",
+# "then": "*|Then",
+# "and": "*|And",
+# "but": "*|But"
+# },
+# …
+# }
+#
+# jq 'with_entries({ key: .key, value: .value | del(.name) | del(.native) | join("|") })'
+#
+# {
+# "en": "Feature|Business Need|Ability|Background|Scenario|Scenario Outline|Scenario Template|Examples|Scenarios|*|Given|*|When|*|Then|*|And|*|But",
+# …
+# }
+
+add-highlighter shared/cucumber/code/ regex \b(Feature|Business\h+Need|Ability|Background|Scenario|Scenario\h+Outline|Scenario\h+Template|Examples|Scenarios|Given|When|Then|And|But)\b 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden cucumber-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden cucumber-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden cucumber-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : cucumber-trim-indent <ret> }
+ # indent after lines containing :
+ try %{ execute-keys -draft , k x <a-k> : <ret> j <a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/cue.kak b/autoload/filetype/cue.kak
new file mode 100644
index 0000000..f400837
--- /dev/null
+++ b/autoload/filetype/cue.kak
@@ -0,0 +1,168 @@
+# https://cuelang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](cue) %{
+ set-option buffer filetype cue
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=cue %[
+ require-module cue
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group cue-trim-indent cue-trim-indent
+ hook window InsertChar \n -group cue-insert cue-insert-on-new-line
+ hook window InsertChar \n -group cue-indent cue-indent-on-new-line
+ hook window InsertChar \{ -group cue-indent cue-indent-on-opening-curly-brace
+ hook window InsertChar [)}] -group cue-indent cue-indent-on-closing
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window cue-.+ }
+]
+
+hook -group cue-highlight global WinSetOption filetype=cue %{
+ add-highlighter window/cue ref cue
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/cue }
+}
+
+provide-module cue %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+# https://cuelang.org/docs/references/spec/
+
+add-highlighter shared/cue regions
+add-highlighter shared/cue/code default-region group
+add-highlighter shared/cue/simple_string region '"' (?<!\\)(\\\\)*" regions
+add-highlighter shared/cue/simple_bytes region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/cue/multiline_string region '"""' '"""' ref shared/cue/simple_string
+add-highlighter shared/cue/multiline_bytes region "'''" "'''" ref shared/cue/simple_bytes
+add-highlighter shared/cue/line_comment region "//" "$" fill comment
+
+add-highlighter shared/cue/simple_string/base default-region fill string
+add-highlighter shared/cue/simple_string/interpolation region -recurse \( \\\( \) fill meta
+
+evaluate-commands %sh{
+ # Grammar
+ binary_digit="[01]"
+ decimal_digit="[0-9]"
+ hex_digit="[0-9a-fA-F]"
+ octal_digit="[0-7]"
+
+ decimal_lit="[1-9](_?${decimal_digit})*"
+ binary_lit="0b${binary_digit}(_?${binary_digit})*"
+ hex_lit="0[xX]${hex_digit}(_?${hex_digit})*"
+ octal_lit="0o${octal_digit}(_?${octal_digit})*"
+
+ decimals="${decimal_digit}(_?${decimal_digit})*"
+ multiplier="([KMGTP]i?)?"
+ exponent="([eE][+-]?${decimals})"
+ si_lit="(${decimals}(\.${decimals})?${multiplier}|\.${decimals}${multiplier})"
+
+ int_lit="\b(${decimal_lit}|${si_lit}|${octal_lit}|${binary_lit}|${hex_lit})\b"
+ float_lit="\b${decimals}\.(${decimals})?${exponent}?|\b${decimals}${exponent}\b|\.${decimals}${exponent}?\b"
+
+ operator_chars="(\+|-|\*|/|&|&&|\||\|\||=|==|=~|!|!=|!~|<|>|<=|>=)"
+ punctuation="(_\|_|:|::|,|\.|\.\.\.|\(|\{|\[|\)|\}|\])"
+
+ function_calls="\w+(?=\()"
+ identifier="(?!\d)[\w_$]+"
+ reserved="\b__${identifier}"
+
+ preamble="^(package|import)\b"
+
+ functions="len close and or"
+ keywords="for in if let"
+ operators="div mod quo rem"
+ types="
+ bool string bytes rune number
+ uint uint8 uint16 uint32 uint64 uint128
+ int int8 int16 int32 int64 int128
+ float float32 float64
+ "
+ values="null true false"
+
+ join() { sep=$2; set -- $1; IFS="$sep"; echo "$*"; }
+
+ static_words="$(join "package import ${functions} ${keywords} ${operators} ${types} ${values}" ' ')"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list cue_static_words ${static_words}"
+
+ functions="$(join "${functions}" '|')"
+ keywords="$(join "${keywords}" '|')"
+ operators="$(join "${operators}" '|')"
+ types="$(join "${types}" '|')"
+ values="$(join "${values}" '|')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/cue/code/ regex ${float_lit} 0:value
+ add-highlighter shared/cue/code/ regex ${function_calls} 0:function
+ add-highlighter shared/cue/code/ regex ${int_lit} 0:value
+ add-highlighter shared/cue/code/ regex ${operator_chars} 0:operator
+ add-highlighter shared/cue/code/ regex ${preamble} 0:keyword
+ add-highlighter shared/cue/code/ regex ${punctuation} 0:operator
+ add-highlighter shared/cue/code/ regex ${reserved} 0:keyword
+ add-highlighter shared/cue/code/ regex \b(${functions})\b 0:builtin
+ add-highlighter shared/cue/code/ regex \b(${keywords})\b 0:keyword
+ add-highlighter shared/cue/code/ regex \b(${operators})\b 0:operator
+ add-highlighter shared/cue/code/ regex \b(${types})\b 0:type
+ add-highlighter shared/cue/code/ regex \b(${values})\b 0:value
+ "
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden cue-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden cue-insert-on-new-line %~
+ evaluate-commands -draft -itersel %<
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K//[!/]?\h* <ret> y<c-o>P<esc> }
+ >
+~
+
+define-command -hidden cue-indent-on-new-line %~
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ try %<
+ # only if we didn't copy a comment
+ execute-keys -draft x <a-K> ^\h*// <ret>
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft k x <a-k> [{(]\h*$ <ret> j <a-gt> ]
+ # indent after lines ending with [{(].+ and move first parameter to own line
+ try %< execute-keys -draft [c[({],[)}] <ret> <a-k> \A[({][^\n]+\n[^\n]*\n?\z <ret> L i<ret><esc> <gt> <a-S> <a-&> >
+ # deindent closing brace(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> >
+ >
+ # filter previous line
+ try %{ execute-keys -draft k : cue-trim-indent <ret> }
+ >
+~
+
+define-command -hidden cue-indent-on-opening-curly-brace %[
+ evaluate-commands -draft -itersel %_
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft h <a-F> ) M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+ _
+]
+
+define-command -hidden cue-indent-on-closing %[
+ evaluate-commands -draft -itersel %_
+ # align to opening curly brace or paren when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h*[)}]$ <ret> h m <a-S> 1<a-&> >
+ _
+]
+
diff --git a/autoload/filetype/d.kak b/autoload/filetype/d.kak
new file mode 100644
index 0000000..359c1ca
--- /dev/null
+++ b/autoload/filetype/d.kak
@@ -0,0 +1,145 @@
+# http://dlang.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.di? %{
+ set-option buffer filetype d
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=d %{
+ require-module d
+
+ set-option window static_words %opt{d_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group d-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group d-insert d-insert-on-new-line
+ hook window InsertChar \n -group d-indent d-indent-on-new-line
+ hook window InsertChar \{ -group d-indent d-indent-on-opening-curly-brace
+ hook window InsertChar \} -group d-indent d-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window d-.+ }
+}
+
+hook -group d-highlight global WinSetOption filetype=d %{
+ add-highlighter window/d ref d
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/d }
+}
+
+provide-module d %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/d regions
+add-highlighter shared/d/code default-region group
+add-highlighter shared/d/string region %{(?<!')(?<!'\\)"} %{(?<!\\)(?:\\\\)*"} group
+add-highlighter shared/d/verbatim_string region %{(?<!')(?<!'\\)`} %{(?<!\\)(?:\\\\)*`} fill meta
+add-highlighter shared/d/verbatim_string_prefixed region %{r`([^(]*)\(} %{\)([^)]*)`} fill meta
+add-highlighter shared/d/docstring1 region -recurse '/\+' '/\+\+' '\+/' fill documentation
+add-highlighter shared/d/docstring2 region '/\*\*' '\*/' fill documentation
+add-highlighter shared/d/docstring3 region /// $ fill documentation
+add-highlighter shared/d/disabled region -recurse '/\+' '/\+[^+]?' '\+/' fill comment
+add-highlighter shared/d/comment1 region '/\*[^*]?' '\*/' fill comment
+add-highlighter shared/d/comment2 region '//[^/]?' $ fill comment
+
+add-highlighter shared/d/string/ fill string
+add-highlighter shared/d/string/ regex %{\\(x[0-9a-fA-F]{2}|[0-7]{1,3}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})\b} 0:value
+add-highlighter shared/d/code/ regex %{'((\\.)?|[^'\\])'} 0:value
+add-highlighter shared/d/code/ regex "-?([0-9_]*\.(?!0[xXbB]))?\b([0-9_]+|0[xX][0-9a-fA-F_]*\.?[0-9a-fA-F_]+|0[bb][01_]+)([ep]-?[0-9_]+)?[fFlLuUi]*\b" 0:value
+add-highlighter shared/d/code/ regex "\b(this)\b\s*[^(]" 1:value
+add-highlighter shared/d/code/ regex "((?:~|\b)this)\b\s*\(" 1:function
+add-highlighter shared/d/code/ regex '(#line)\h+(\d+)(\h+"[^"\n]*")?' 1:meta 2:value 3:string
+
+evaluate-commands %sh{
+ # Grammar
+
+ keywords="abstract|alias|align|asm|assert|auto|body|break|case|cast"
+ keywords="${keywords}|catch|cent|class|const|continue|debug"
+ keywords="${keywords}|default|delegate|delete|deprecated|do|else|enum|export|extern"
+ keywords="${keywords}|final|finally|for|foreach|foreach_reverse|function|goto"
+ keywords="${keywords}|if|immutable|import|in|inout|interface|invariant"
+ keywords="${keywords}|is|lazy|macro|mixin|module|new|nothrow|out|override"
+ keywords="${keywords}|package|pragma|private|protected|public|pure|ref|return|scope"
+ keywords="${keywords}|shared|static|struct|super|switch|synchronized|template"
+ keywords="${keywords}|throw|try|typedef|typeid|typeof|union"
+ keywords="${keywords}|unittest|version|volatile|while|with"
+ attributes="abstract|align|auto|const|debug|deprecated|export|extern|final"
+ attributes="${attributes}|immutable|inout|nothrow|package|private|protected"
+ attributes="${attributes}|public|pure|ref|override|scope|shared|static|synchronized|version"
+ attributes="${attributes}|__gshared|__traits|__vector|__parameters"
+ types="bool|byte|cdouble|cent|cfloat|char|creal|dchar|double|dstring|float"
+ types="${types}|idouble|ifloat|int|ireal|long|ptrdiff_t|real|size_t|short"
+ types="${types}|string|ubyte|ucent|uint|ulong|ushort|void|wchar|wstring"
+ values="true|false|null"
+ tokens="__FILE__|__MODULE__|__LINE__|__FUNCTION__"
+ tokens="${tokens}|__PRETTY_FUNCTION__|__DATE__|__EOF__|__TIME__"
+ tokens="${tokens}|__TIMESTAMP__|__VENDOR__|__VERSION__|#line"
+ properties="this|init|sizeof|alignof|mangleof|stringof|infinity|nan|dig|epsilon|mant_dig"
+ properties="${properties}|max_10_exp|min_exp|max|min_normal|re|im|classinfo"
+ properties="${properties}|length|dup|keys|values|rehash|clear"
+ decorators="disable|property|nogc|safe|trusted|system"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list d_static_words ${keywords} ${attributes} ${types} ${values} ${decorators} ${properties}" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/d/code/ regex \b(${keywords})\b 0:keyword
+ add-highlighter shared/d/code/ regex \b(${attributes})\b 0:attribute
+ add-highlighter shared/d/code/ regex \b(${types})\b 0:type
+ add-highlighter shared/d/code/ regex \b(${values})\b 0:value
+ add-highlighter shared/d/code/ regex @(${decorators})\b 0:attribute
+ add-highlighter shared/d/code/ regex \b(${tokens})\b 0:builtin
+ add-highlighter shared/d/code/ regex \.(${properties})\b 1:builtin
+ "
+}
+
+add-highlighter shared/d/code/ regex "\bimport\s+([\w._-]+)(?:\s*=\s*([\w._-]+))?" 1:module 2:module
+add-highlighter shared/d/code/ regex "\bmodule\s+([\w_-]+)\b" 1:module
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden d-insert-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+ =
+~
+
+define-command -hidden d-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after if|else|while|for
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ =
+~
+
+define-command -hidden d-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden d-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
diff --git a/autoload/filetype/dart.kak b/autoload/filetype/dart.kak
new file mode 100644
index 0000000..d662359
--- /dev/null
+++ b/autoload/filetype/dart.kak
@@ -0,0 +1,126 @@
+# https://dartlang.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.dart %{
+ set-option buffer filetype dart
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=dart %{
+ require-module dart
+
+ set-option window static_words %opt{dart_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group dart-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group dart-insert dart-insert-on-new-line
+ hook window InsertChar \n -group dart-indent dart-indent-on-new-line
+ hook window InsertChar \{ -group dart-indent dart-indent-on-opening-curly-brace
+ hook window InsertChar \} -group dart-indent dart-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window dart-.+ }
+}
+
+hook -group dart-highlight global WinSetOption filetype=dart %{
+ add-highlighter window/dart ref dart
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/dart }
+}
+
+
+provide-module dart %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/dart regions
+add-highlighter shared/dart/code default-region group
+add-highlighter shared/dart/double_string region '"' (?<!\\)(\\\\)*" group
+add-highlighter shared/dart/single_string region "'" (?<!\\)(\\\\)*' group
+add-highlighter shared/dart/comment region /\* \*/ fill comment
+add-highlighter shared/dart/comment_line region '//' $ fill comment
+
+add-highlighter shared/dart/code/ regex %{-?([0-9]*\.(?!0[xX]))?\b([0-9]+|0[xX][0-9a-fA-F]+)\.?([eE][+-]?[0-9]+)?i?\b} 0:value
+
+# String interpolation
+add-highlighter shared/dart/double_string/ fill string
+add-highlighter shared/dart/double_string/ regex \$\{.*?\} 0:value
+add-highlighter shared/dart/single_string/ fill string
+add-highlighter shared/dart/single_string/ regex \$\{.*?\} 0:value
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="abstract|do|import|super|as|in|switch|assert|else|interface|async"
+ keywords="${keywords}|enum|is|this|export|library|throw|await|external|mixin|break|extends"
+ keywords="${keywords}|new|try|case|factory|typedef|catch|operator|class|final|part|extension"
+ keywords="${keywords}|const|finally|rethrow|while|continue|for|return|with|covariant"
+ keywords="${keywords}|get|set|yield|default|if|static|deferred|implements"
+ generator_keywords="async\*|sync\*|yield\*"
+
+ types="void|bool|num|int|double|dynamic|var"
+ values="false|true|null"
+
+ annotations="@[a-zA-Z]+"
+ functions="(_?[a-z][a-zA-Z0-9]*)(\(|\w+=>)"
+ classes="_?[A-Z][a-zA-Z0-9]*"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list dart_static_words ${keywords} ${attributes} ${types} ${values}" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/dart/code/ regex \b(${keywords})\b 0:keyword
+ add-highlighter shared/dart/code/ regex \b(${generator_keywords}) 0:keyword
+ add-highlighter shared/dart/code/ regex \b(${attributes})\b 0:attribute
+ add-highlighter shared/dart/code/ regex \b(${types})\b 0:type
+ add-highlighter shared/dart/code/ regex \b(${values})\b 0:value
+ add-highlighter shared/dart/code/ regex \b(${functions}) 2:function
+ add-highlighter shared/dart/code/ regex (${annotations})\b 0:meta
+ add-highlighter shared/dart/code/ regex \b(${classes})\b 0:module
+ "
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden dart-insert-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+ =
+~
+
+define-command -hidden dart-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after if|else|while|for
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ =
+~
+
+define-command -hidden dart-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden dart-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
diff --git a/autoload/filetype/dhall.kak b/autoload/filetype/dhall.kak
new file mode 100644
index 0000000..94b69d6
--- /dev/null
+++ b/autoload/filetype/dhall.kak
@@ -0,0 +1,101 @@
+# https://dhall-lang.org
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](dhall) %{
+ set-option buffer filetype dhall
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=dhall %{
+ require-module dhall
+
+ hook window ModeChange pop:insert:.* -group dhall-trim-indent dhall-trim-indent
+ hook window InsertChar \n -group dhall-insert dhall-insert-on-new-line
+ hook window InsertChar \n -group dhall-indent dhall-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window dhall-.+ }
+}
+
+hook -group dhall-highlight global WinSetOption filetype=dhall %{
+ add-highlighter window/dhall ref dhall
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/dhall }
+}
+
+
+provide-module dhall %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/dhall regions
+add-highlighter shared/dhall/code default-region group
+add-highlighter shared/dhall/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/dhall/comment region -recurse \{- \{- -\} fill comment
+add-highlighter shared/dhall/line_comment region -- $ fill comment
+
+# Matches multi-line string literals
+add-highlighter shared/dhall/multiline_string region \Q''\E$ [^']''[^'] fill string
+
+# Matches quoted labels
+add-highlighter shared/dhall/quoted_label region ` ` fill normal
+
+# Matches built-in types
+add-highlighter shared/dhall/code/ regex \b(Location|Sort|Kind|Type|Text|Bool|Natural|Integer|Double|List|Optional|\{\})\b 0:type
+
+# Matches built-in keywords
+add-highlighter shared/dhall/code/ regex \b(if|then|else|let|in|using|missing|as|merge|toMap)\b 0:keyword
+
+# Matches bulit-in values
+add-highlighter shared/dhall/code/ regex \b(True|False|Some|None|-?Infinity|\{=\}|NaN)\b 0:value
+
+# Matches built-in operators
+add-highlighter shared/dhall/code/ regex (,|:|\|\||&&|==|!=|=|\+|\*|\+\+|#|⩓|//\\\\|→|->|\?|λ|\\|\^|⫽|//|\[|\]|\{|\}) 0:operator
+
+# Matches built-in functions
+add-highlighter shared/dhall/code/ regex \b(Natural-fold|Natural-build|Natural-isZero|Natural-even|Natural-odd|Natural-toInteger|Natural-show|Integer-toDouble|Integer-show|Natural-subtract|Double-show|List-build|List-fold|List-length|List-head|List-last|List-indexed|List-reverse|Optional-fold|Optional-build|Text-show)\b 0:keyword
+
+# Matches http[s] imports
+add-highlighter shared/dhall/code/ regex \b(http[s]://\S+)\b(\s+sha256:[a-f0-9]{64}\b)? 0:meta
+
+# Matches local imports
+add-highlighter shared/dhall/code/ regex (~|\.|\.\.|/)\S+ 0:meta
+
+# Matches number (natural, integer, double) literals
+add-highlighter shared/dhall/code/ regex \b(\+|-)?\d+(\.\d+)?(e(\+|-)?\d+)?\b 0:value
+
+# Matches union syntax
+add-highlighter shared/dhall/union region -recurse < < > group
+add-highlighter shared/dhall/union/sep regex (<|\|)\s*((?:_|[A-Z])(?:[a-zA-Z0-9-/_]*))\s*(?:(:)([^|>]*))? 1:operator 2:attribute 3:operator 4:type
+add-highlighter shared/dhall/union/end regex > 0:operator
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden dhall-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden dhall-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+define-command -hidden dhall-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft \; K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : dhall-trim-indent <ret> }
+ # indent after lines ending with let, : or =
+ try %{ execute-keys -draft \; k x <a-k> (\blet|:|=)$ <ret> j <a-gt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/diff-parse.pl b/autoload/filetype/diff-parse.pl
new file mode 100644
index 0000000..203e9b1
--- /dev/null
+++ b/autoload/filetype/diff-parse.pl
@@ -0,0 +1,143 @@
+#!/usr/bin/env perl
+
+use warnings;
+
+sub quote {
+ my $token = shift;
+ $token =~ s/'/''/g;
+ return "'$token'";
+}
+sub fail {
+ my $reason = shift;
+ print "set-register e fail " . quote("diff-parse.pl: $reason");
+ exit;
+}
+
+my $begin;
+my $end;
+
+while (defined $ARGV[0]) {
+ if ($ARGV[0] eq "--") {
+ shift;
+ last;
+ }
+ if ($ARGV[0] =~ m{^(BEGIN|END)$}) {
+ if (not defined $ARGV[1]) {
+ fail "missing argument to $ARGV[0]";
+ }
+ if ($ARGV[0] eq "BEGIN") {
+ $begin = $ARGV[1];
+ } else {
+ $end = $ARGV[1];
+ }
+ shift, shift;
+ next;
+ }
+ fail "unknown argument: $ARGV[0]";
+}
+
+# Inputs
+our $directory = $ENV{PWD};
+our $strip;
+our $in_file;
+our $in_file_line;
+our $version = "+";
+
+eval $begin if defined $begin;
+
+$in_file = "$directory/$in_file" if defined $in_file;
+
+# Outputs
+our $diff_line = 0;
+our $commit;
+our $file;
+our $file_line;
+our $diff_line_text;
+
+my $other_version;
+if ($version eq "+") {
+ $other_version = "-";
+} else {
+ $other_version = "+";
+}
+my $is_recursive_diff = 0;
+my $state = "header";
+my $fallback_file;
+my $other_file;
+my $other_file_line;
+
+sub strip {
+ my $is_recursive_diff = shift;
+ my $f = shift;
+
+ my $effective_strip;
+ if (defined $strip) {
+ $effective_strip = $strip;
+ } else {
+ # A "diff -r" or "git diff" adds "diff" lines to
+ # the output. If no such line is present, we have
+ # a plain diff between files (not directories), so
+ # there should be no need to strip the directory.
+ $effective_strip = $is_recursive_diff ? 1 : 0;
+ }
+
+ if ($f !~ m{^/}) {
+ $f =~ s,^([^/]+/+){$effective_strip},, or fail "directory prefix underflow";
+ $f = "$directory/$f";
+ }
+ return $f;
+}
+
+while (<STDIN>) {
+ $diff_line++;
+ s/^(> )*//g;
+ $diff_line_text = $_;
+ if (m{^commit (\w+)}) {
+ $commit = $1;
+ next;
+ }
+ if (m{^diff\b}) {
+ $state = "header";
+ $is_recursive_diff = 1;
+ if (m{^diff -\S* (\S+) (\S+)$}) {
+ $fallback_file = strip $is_recursive_diff, ($version eq "+" ? $2 : $1);
+ }
+ next;
+ }
+ if ($state eq "header") {
+ if (m{^[$version]{3} ([^\t\n]+)}) {
+ $file = strip $is_recursive_diff, $1;
+ next;
+ }
+ if (m{^[$other_version]{3} ([^\t\n]+)}) {
+ $other_file = strip $is_recursive_diff, $1;
+ next;
+ }
+ }
+ if (m{^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@}) {
+ $state = "contents";
+ $file_line = ($version eq "+" ? $2 : $1) - 1;
+ $other_file_line = ($version eq "+" ? $1 : $2) - 1;
+ } else {
+ my $iscontext = m{^[ ]};
+ if (m{^[ $version]}) {
+ $file_line++ if defined $file_line;
+ }
+ if (m{^[ $other_version]}) {
+ $other_file_line++ if defined $other_file_line;
+ }
+ }
+ if (defined $in_file and defined $file and $file eq $in_file) {
+ if (defined $in_file_line and defined $file_line and $file_line >= $in_file_line) {
+ last;
+ }
+ }
+}
+if (not defined $file) {
+ $file = ($fallback_file or $other_file);
+}
+if (not defined $file) {
+ fail "missing diff header";
+}
+
+eval $end if defined $end;
diff --git a/autoload/filetype/diff.kak b/autoload/filetype/diff.kak
new file mode 100644
index 0000000..baa6599
--- /dev/null
+++ b/autoload/filetype/diff.kak
@@ -0,0 +1,141 @@
+hook global BufCreate .*\.(diff|patch) %{
+ set-option buffer filetype diff
+}
+
+hook global WinSetOption filetype=diff %{
+ require-module diff
+ map buffer normal <ret> :diff-jump<ret>
+}
+
+hook -group diff-highlight global WinSetOption filetype=diff %{
+ add-highlighter window/diff ref diff
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/diff }
+}
+
+provide-module diff %§
+
+add-highlighter shared/diff group
+add-highlighter shared/diff/ regex "^\+[^\n]*\n" 0:green,default
+add-highlighter shared/diff/ regex "^-[^\n]*\n" 0:red,default
+add-highlighter shared/diff/ regex "^@@[^\n]*@@" 0:cyan,default
+# If any trailing whitespace was introduced in diff, show it with red background
+add-highlighter shared/diff/ regex "^\+[^\n]*?(\h+)\n" 1:default,red
+
+define-command diff-jump -params .. -docstring %{
+ diff-jump [<switches>] [<directory>]: edit the diff's source file at the cursor position.
+ Paths are resolved relative to <directory>, or the current working directory if unspecified.
+
+ Switches:
+ - jump to the old file instead of the new file
+ -<num> strip <num> leading directory components, like -p<num> in patch(1). Defaults to 1 if there is a 'diff' line (as printed by 'diff -r'), or 0 otherwise.
+ } %{
+ evaluate-commands -draft -save-regs c %{
+ # Save the column because we will move the cursor.
+ set-register c %val{cursor_column}
+ # If there is a "diff" line, we don't need to look further back.
+ try %{
+ execute-keys %{<a-l><semicolon><a-?>^(?:> )*diff\b<ret>x}
+ } catch %{
+ # A single file diff won't have a diff line. Start parsing from
+ # the buffer start, so we can tell if +++/--- lines are headers
+ # or content.
+ execute-keys Gk
+ }
+ diff-parse BEGIN %{
+ my $seen_ddash = 0;
+ foreach (@ARGV) {
+ if ($seen_ddash or !m{^-}) {
+ $directory = $_;
+ } elsif ($_ eq "-") {
+ $version = "-", $other_version = "+";
+ } elsif (m{^-(\d+)$}) {
+ $strip = $1;
+ } elsif ($_ eq "--") {
+ $seen_ddash = 1;
+ } else {
+ fail "unknown option: $_";
+ }
+ }
+ } END %exp{
+ my $file_column;
+ if (not defined $file_line) {
+ $file_line = "";
+ $file_column = "";
+ } else {
+ my $diff_column = %reg{c};
+ $file_column = $diff_column - 1; # Account for [ +-] diff prefix.
+ # If the cursor was on a hunk header, go to the section header if possible.
+ if ($diff_line_text =~ m{^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+) @@ )([^\n]*)}) {
+ my $hunk_header_prefix = $1;
+ my $hunk_header_from_userdiff = $2;
+ open FILE, "<", $file or fail "failed to open file: $!: $file";
+ my @lines = <FILE>;
+ for (my $i = $file_line - 1; $i >= 0 and $i < scalar @lines; $i--) {
+ if ($lines[$i] !~ m{\Q$hunk_header_from_userdiff}) {
+ next;
+ }
+ $file_line = $i + 1;
+ # Re-add 1 because the @@ line does not have a [ +-] diff prefix.
+ $file_column = $diff_column + 1 - length $hunk_header_prefix;
+ last;
+ }
+ }
+ }
+ printf "set-register c %%s $file_line $file_column\n", quote($file);
+ } -- %arg{@}
+ evaluate-commands -client %val{client} %{
+ evaluate-commands -try-client %opt{jumpclient} %{
+ edit -existing -- %reg{c}
+ }
+ }
+ }
+}
+complete-command diff-jump file
+
+define-command -hidden diff-parse -params 2.. %{
+ evaluate-commands -save-regs ae| %{
+ set-register a %arg{@}
+ set-register e nop
+ set-register | %{
+ eval set -- "$kak_quoted_reg_a"
+ perl "${kak_runtime}/rc/filetype/diff-parse.pl" "$@" >"$kak_command_fifo"
+ }
+ execute-keys <a-|><ret>
+ %reg{e}
+ }
+}
+
+
+define-command \
+ -docstring %{diff-select-file: Select surrounding patch file} \
+ -params 0 \
+ diff-select-file %{
+ evaluate-commands -itersel -save-regs 'ose/' %{
+ try %{
+ execute-keys '"oZgl<a-?>^diff <ret>;"sZ' 'Ge"eZ'
+ try %{ execute-keys '"sz?\n(?=diff )<ret>"e<a-Z><lt>' }
+ execute-keys '"ez'
+ } catch %{
+ execute-keys '"oz'
+ fail 'Not in a diff file'
+ }
+ }
+}
+
+define-command \
+ -docstring %{diff-select-hunk: Select surrounding patch hunk} \
+ -params 0 \
+ diff-select-hunk %{
+ evaluate-commands -itersel -save-regs 'ose/' %{
+ try %{
+ execute-keys '"oZgl<a-?>^@@ <ret>;"sZ' 'Ge"eZ'
+ try %{ execute-keys '"sz?\n(?=diff )<ret>"e<a-Z><lt>' }
+ try %{ execute-keys '"sz?\n(?=@@ )<ret>"e<a-Z><lt>' }
+ execute-keys '"ez'
+ } catch %{
+ execute-keys '"oz'
+ fail 'Not in a diff hunk'
+ }
+ }
+}
diff --git a/autoload/filetype/dockerfile.kak b/autoload/filetype/dockerfile.kak
new file mode 100644
index 0000000..a9000d8
--- /dev/null
+++ b/autoload/filetype/dockerfile.kak
@@ -0,0 +1,55 @@
+# http://docker.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# See https://docs.docker.com/reference/builder
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*/?Dockerfile(\..+)?$ %{
+ set-option buffer filetype dockerfile
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=dockerfile %{
+ require-module dockerfile
+ set-option window static_words %opt{dockerfile_static_words}
+}
+
+hook -group dockerfile-highlight global WinSetOption filetype=dockerfile %{
+ add-highlighter window/dockerfile ref dockerfile
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/dockerfile }
+}
+
+provide-module dockerfile %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/dockerfile regions
+add-highlighter shared/dockerfile/code default-region group
+add-highlighter shared/dockerfile/double_string region '"' '(?<!\\)(\\\\)*"' fill string
+add-highlighter shared/dockerfile/single_string region "'" "'" fill string
+add-highlighter shared/dockerfile/comment region '#' $ fill comment
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL"
+ keywords="${keywords}|MAINTAINER|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list dockerfile_static_words ONBUILD|${keywords}" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/dockerfile/code/ regex '^(?i)(ONBUILD\h+)?(${keywords})\b' 2:keyword
+ add-highlighter shared/dockerfile/code/ regex '^(?i)(ONBUILD)\h+' 1:keyword
+ "
+}
+
+add-highlighter shared/dockerfile/code/ regex (?<!\\)(?:\\\\)*\K\$\{[\w_]+\} 0:value
+add-highlighter shared/dockerfile/code/ regex (?<!\\)(?:\\\\)*\K\$[\w_]+ 0:value
+
+}
diff --git a/autoload/filetype/elixir.kak b/autoload/filetype/elixir.kak
new file mode 100644
index 0000000..9c06052
--- /dev/null
+++ b/autoload/filetype/elixir.kak
@@ -0,0 +1,128 @@
+# http://elixir-lang.org
+# ----------------------
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](ex|exs) %{
+ set-option buffer filetype elixir
+}
+
+hook global BufCreate .*[.]html[.]l?eex %{
+ set-option buffer filetype eex
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=elixir %{
+ require-module elixir
+
+ hook window ModeChange pop:insert:.* -group elixir-trim-indent elixir-trim-indent
+ hook window InsertChar \n -group elixir-indent elixir-indent-on-new-line
+ hook window InsertChar \n -group elixir-insert elixir-insert-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window elixir-.+ }
+}
+
+hook -group elixir-highlight global WinSetOption filetype=elixir %{
+ add-highlighter window/elixir ref elixir
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/elixir }
+}
+
+hook global WinSetOption filetype=eex %{
+ require-module eex
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window eex-.+ }
+}
+
+hook -group eex-highlight global WinSetOption filetype=eex %{
+ add-highlighter window/eex ref eex
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/eex }
+}
+
+provide-module eex %{
+require-module html
+require-module elixir
+
+add-highlighter shared/eex regions
+add-highlighter shared/eex/html default-region ref html
+add-highlighter shared/eex/comment region '<%#' '%>' fill comment
+add-highlighter shared/eex/quote region '<%%' '%>' ref html
+add-highlighter shared/eex/code region '<%=?' '%>' ref elixir
+}
+
+provide-module elixir %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/elixir regions
+add-highlighter shared/elixir/code default-region group
+add-highlighter shared/elixir/double_string region -match-capture ("""|") (?<!\\)(?:\\\\)*("""|") regions
+add-highlighter shared/elixir/single_string region "'" "(?<!\\)(?:\\\\)*'" fill string
+add-highlighter shared/elixir/comment region '#' '$' fill comment
+
+add-highlighter shared/elixir/leex region -match-capture '~L("""|")' '(?<!\\)(?:\\\\)*("""|")' ref eex
+
+add-highlighter shared/elixir/double_string/base default-region fill string
+add-highlighter shared/elixir/double_string/interpolation region -recurse \{ \Q#{ \} fill builtin
+
+add-highlighter shared/elixir/code/ regex ':[\w_]+\b' 0:type
+add-highlighter shared/elixir/code/ regex '[\w_]+:' 0:type
+add-highlighter shared/elixir/code/ regex '[A-Z][\w_]+\b' 0:module
+add-highlighter shared/elixir/code/ regex '(:[\w_]+)(\.)' 1:module
+add-highlighter shared/elixir/code/ regex '\b_\b' 0:default
+add-highlighter shared/elixir/code/ regex '\b_[\w_]+\b' 0:default
+add-highlighter shared/elixir/code/ regex '~[a-zA-Z]\(.*?[^\\]\)' 0:string
+add-highlighter shared/elixir/code/ regex \b(true|false|nil)\b 0:value
+add-highlighter shared/elixir/code/ regex (->|<-|<<|>>|=>) 0:builtin
+add-highlighter shared/elixir/code/ regex \b(require|alias|use|import)\b 0:keyword
+add-highlighter shared/elixir/code/ regex \b(__MODULE__|__DIR__|__ENV__|__CALLER__)\b 0:value
+add-highlighter shared/elixir/code/ regex \b(def|defp|defmacro|defmacrop|defstruct|defmodule|defimpl|defprotocol|defoverridable)\b 0:keyword
+add-highlighter shared/elixir/code/ regex \b(fn|do|end|when|case|if|else|unless|var!|for|cond|quote|unquote|receive|with|raise|reraise|try|catch)\b 0:keyword
+add-highlighter shared/elixir/code/ regex '@[\w_]+\b' 0:attribute
+add-highlighter shared/elixir/code/ regex '\b\d+[\d_]*\b' 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden elixir-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden elixir-insert-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y jgi P }
+ # wisely add end structure
+ evaluate-commands -save-regs x %[
+ try %{ execute-keys -draft k x s ^ \h + <ret> \" x y } catch %{ reg x '' }
+ try %[
+ evaluate-commands -draft %[
+ # Check if previous line opens a block
+ execute-keys -draft kx <a-k>^<c-r>x(.+\bdo$)<ret>
+ # Check that we do not already have an end for this indent level which is first set via `elixir-indent-on-new-line` hook
+ execute-keys -draft }i J x <a-K> ^<c-r>x(end|else)[^0-9A-Za-z_!?]<ret>
+ ]
+ execute-keys -draft o<c-r>xend<esc> # insert a new line with containing end
+ ]
+ ]
+ ]
+]
+
+define-command -hidden elixir-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # indent after line ending with:
+ # try %{ execute-keys -draft k x <a-k> (\bdo|\belse|->)$ <ret> & }
+ # filter previous line
+ try %{ execute-keys -draft k : elixir-trim-indent <ret> }
+ # indent after lines ending with do or ->
+ try %{ execute-keys -draft <semicolon> k x <a-k> ^.+(\bdo|->)$ <ret> j <a-gt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/elm.kak b/autoload/filetype/elm.kak
new file mode 100644
index 0000000..0bd5196
--- /dev/null
+++ b/autoload/filetype/elm.kak
@@ -0,0 +1,85 @@
+# http://elm-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](elm) %{
+ set-option buffer filetype elm
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=elm %{
+ require-module elm
+
+ hook window ModeChange pop:insert:.* -group elm-trim-indent elm-trim-indent
+ hook window InsertChar \n -group elm-insert elm-insert-on-new-line
+ hook window InsertChar \n -group elm-indent elm-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window elm-.+ }
+}
+
+hook -group elm-highlight global WinSetOption filetype=elm %{
+ add-highlighter window/elm ref elm
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/elm }
+}
+
+
+provide-module elm %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/elm regions
+add-highlighter shared/elm/code default-region group
+add-highlighter shared/elm/multiline_string region '"""' '"""' fill string
+add-highlighter shared/elm/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/elm/line_comment region (--) $ fill comment
+add-highlighter shared/elm/comment region -recurse \{- \{- -\} fill comment
+
+add-highlighter shared/elm/code/ regex \b[A-Z]\w*\b 0:type
+add-highlighter shared/elm/code/ regex \b[a-z]\w*\b 0:variable
+add-highlighter shared/elm/code/ regex ^[a-z]\w*\b 0:function
+add-highlighter shared/elm/code/ regex "-?\b[0-9]*\.?[0-9]+" 0:value
+add-highlighter shared/elm/code/ regex \B[-+<>!@#$%^&*=:/\\|]+\B 0:operator
+add-highlighter shared/elm/code/ regex \b(import|exposing|as|module|port)\b 0:meta
+add-highlighter shared/elm/code/ regex \b(type|alias|if|then|else|case|of|let|in|infix|_)\b) 0:keyword
+add-highlighter shared/elm/code/ regex (?<![-+<>!@#$%^&*=:/\\|])(->|:|=|\|)(?![-+<>!@#$%^&*=:/\\|]) 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+# http://elm-lang.org/docs/style-guide
+
+define-command -hidden elm-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden elm-indent-after "
+ execute-keys -draft <semicolon> k x <a-k> ^\\h*if|[=(]$|\\b(case\\h+[\\w']+\\h+of|let|in)$|(\\{\\h+\\w+|\\w+\\h+->)$ <ret> j <a-gt>
+"
+
+define-command -hidden elm-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden elm-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # align to first clause
+ try %{ execute-keys -draft <semicolon> k x X s ^\h*(if|then|else)?\h*(([\w']+\h+)+=)?\h*(case\h+[\w']+\h+of|let)\h+\K.* <ret> s \A|.\z <ret> & }
+ # filter previous line
+ try %{ execute-keys -draft k : elm-trim-indent <ret> }
+ # indent after lines beginning with condition or ending with expression or =(
+ try %{ elm-indent-after }
+ }
+}
+
+]
diff --git a/autoload/filetype/elvish.kak b/autoload/filetype/elvish.kak
new file mode 100644
index 0000000..5377862
--- /dev/null
+++ b/autoload/filetype/elvish.kak
@@ -0,0 +1,67 @@
+# Syntax highlighting and indentation for Elvish (https://elv.sh)
+
+hook global BufCreate .*\.elv %{
+ set-option buffer filetype elvish
+}
+
+hook global WinSetOption filetype=elvish %<
+ require-module elvish
+
+ hook window ModeChange pop:insert:.* -group elvish-trim-indent elvish-trim-indent
+ hook window InsertChar \n -group elvish-insert elvish-indent
+ hook window InsertChar [\]})] -group elvish-insert elvish-deindent
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window elvish-.+ }
+>
+
+hook -group elvish-highlight global WinSetOption filetype=elvish %{
+ add-highlighter window/elvish ref elvish
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/elvish }
+}
+
+provide-module elvish %§
+
+add-highlighter shared/elvish regions
+add-highlighter shared/elvish/code default-region group
+add-highlighter shared/elvish/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/elvish/single_string region "'" ('')*' fill string
+add-highlighter shared/elvish/comment region '#' $ fill comment
+
+add-highlighter shared/elvish/code/variable regex \$[\w\d-_:~]+ 0:variable
+add-highlighter shared/elvish/code/variable_in_assignment regex (?:^|\{\s|\(|\||\;)\h*(?:var|set|tmp|del)((?:\h+[\w\d-_:~]+)*) 1:variable
+add-highlighter shared/elvish/code/variable_in_for regex (?:^|\{\s|\(|\||\;)\h*for\h+([\w\d-_:~]*) 1:variable
+add-highlighter shared/elvish/code/variable_in_catch regex \}\h+(?:catch|except)\h+([\w\d-_:~]*) 1:variable
+
+add-highlighter shared/elvish/code/builtin regex (?:^|\{\s|\(|\||\;)\h*(!=|!=s|%|\*|\+|-gc|-ifaddrs|-log|-override-wcwidth|-stack|-|/|<|<=|<=s|<s|==|==s|>|>=|>=s|>s|all|assoc|base|bool|break|call|cd|compare|constantly|continue|count|defer|deprecate|dissoc|drop|each|eawk|echo|eq|eval|exact-num|exec|exit|external|fail|fg|float64|from-json|from-lines|from-terminated|get-env|has-env|has-external|has-key|has-value|is|keys|kind-of|make-map|multi-error|nop|not-eq|not|ns|num|one|only-bytes|only-values|order|peach|pprint|print|printf|put|rand|randint|range|read-line|read-upto|repeat|repr|resolve|return|run-parallel|search-external|set-env|show|sleep|slurp|src|styled|styled-segment|take|tilde-abbr|time|to-json|to-lines|to-string|to-terminated|unset-env|use-mod|wcswidth)(?![-:])\b 1:builtin
+add-highlighter shared/elvish/code/keyword regex (?:^|\{\s|\(|\||\;)\h*(use|var|set|tmp|del|and|or|coalesce|pragma|while|for|try|fn|if)(?![-:])\b 1:keyword
+add-highlighter shared/elvish/code/keyword_block regex \}\h+(catch|elif|else|except|finally)(?![-:])\b 1:keyword
+
+add-highlighter shared/elvish/code/metachar regex [*?|&\;<>()[\]{}] 0:operator
+
+define-command -hidden elvish-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden elvish-indent %< evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %<
+ # if the previous line is a comment, copy indent, # and whitespace
+ execute-keys -draft k x s^\h*#\h*<ret> yjP
+ > catch %<
+ # copy indent
+ execute-keys -draft K <a-&>
+ # indent after { [ ( |
+ try %< execute-keys -draft k x <a-k>[[{(|]\h*$<ret> j <a-gt> >
+ >
+>>
+
+define-command -hidden elvish-deindent %< evaluate-commands -draft -itersel %<
+ try %<
+ # Deindent only when there is a lone closing character
+ execute-keys -draft x <a-k>^\h*[^\h]$<ret> <a-lt>
+ >
+>>
diff --git a/autoload/filetype/erlang.kak b/autoload/filetype/erlang.kak
new file mode 100644
index 0000000..35b60cf
--- /dev/null
+++ b/autoload/filetype/erlang.kak
@@ -0,0 +1,68 @@
+# Erlang/OTP
+# https://erlang.org
+# ----------------------
+
+# Detection and Initialization sections were adapted from rc/filetype/elixir.kak
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+hook global BufCreate .*[.](erl|hrl) %{
+ set-option buffer filetype erlang
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+hook global WinSetOption filetype=erlang %{
+ require-module erlang
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window erlang-.+ }
+}
+
+hook -group erlang-highlight global WinSetOption filetype=erlang %{
+ add-highlighter window/erlang ref erlang
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/erlang }
+}
+
+provide-module erlang %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/erlang regions
+add-highlighter shared/erlang/default default-region group
+
+add-highlighter shared/erlang/comment region '(?<!\$)%' '$' fill comment
+add-highlighter shared/erlang/attribute_atom_single_quoted region %{-'} %{(?<!\\)(?:\\\\)*'(?=[\( \.])} fill builtin
+add-highlighter shared/erlang/attribute region '\b-[a-z][\w@]*(?=[\( \.])' '\K' fill builtin
+add-highlighter shared/erlang/atom_single_quoted region %{'} %{(?<!\\)(?:\\\\)*'} fill type
+add-highlighter shared/erlang/char_list region %{"} %{(?<!\\)(?:\\\\)*"} fill string
+add-highlighter shared/erlang/dollar_double_quote region %{\$\\?"} '\K' fill value
+add-highlighter shared/erlang/dollar_single_quote region %{\$\\?'} '\K' fill value
+
+# default-region regex highlighters
+add-highlighter shared/erlang/default/atom regex '\b[a-z][\w@]*\b' 0:type
+add-highlighter shared/erlang/default/funtion_call regex '\b[a-z][\w@]*(?=\()' 0:function
+add-highlighter shared/erlang/default/keywords regex '\b(after|begin|case|try|catch|end|fun|if|of|receive|when|andalso|orelse|bnot|not|div|rem|band|and|bor|bxor|bsl|bsr|or|xor)\b' 0:keyword
+add-highlighter shared/erlang/default/variable_name regex '\b(?<!\?)[A-Z_][\w@]*\b' 0:variable
+add-highlighter shared/erlang/default/module_prefix regex '\b([a-z][\w_@]+)(?=:)' 1:value
+# e.g. #Ref<0.1380825548.292038421.197518>
+add-highlighter shared/erlang/default/ref regex '#Ref<\d+\.\d+\.\d+\.\d+>' 0:value
+# e.g. #Port<0.1>
+add-highlighter shared/erlang/default/port regex '#Port<\d+\.\d+>' 0:value
+# e.g. <0.401.0>
+add-highlighter shared/erlang/default/pid regex '<\d+\.\d+\.\d+>' 0:value
+add-highlighter shared/erlang/default/base_number regex '\b(\d[_\d]*(?<!_)#[a-zA-Z0-9][a-z_A-Z0-9]*(?<!_)(?!\{))\b' 1:value
+add-highlighter shared/erlang/default/float regex '\b(?<![\.])(\d[\d_]*(?<!_)\.\d[\d_]*(?<!_)(?:e[+-]?\d[\d_]*(?<!_))?)\b' 1:value
+add-highlighter shared/erlang/default/integer regex '\b(?<!/)(\d[\d_]*)(?<!_)\b' 1:value
+# e.g $\xff
+add-highlighter shared/erlang/default/dollar_hex regex '\$\\x[0-9a-f][0-9a-f]' 0:value
+# e.g. $\^a $\^C
+add-highlighter shared/erlang/default/dollar_ctrl_char regex '\$\\\^[a-zA-Z]' 0:value
+# e.g. $\101, $\70
+add-highlighter shared/erlang/default/dollar_octal regex '\$\\[0-7][0-7][0-7]?' 0:value
+# e.g. $\↕ $\7
+add-highlighter shared/erlang/default/dollar_esc_char regex '\$\\[^x]' 0:value
+# e.g. $↕ $a
+add-highlighter shared/erlang/default/dollar_char regex '\$.' 0:value
+
+]
diff --git a/autoload/filetype/eruby.kak b/autoload/filetype/eruby.kak
new file mode 100644
index 0000000..273fa07
--- /dev/null
+++ b/autoload/filetype/eruby.kak
@@ -0,0 +1,26 @@
+# eRuby
+# http://www2a.biglobe.ne.jp/~seki/ruby/erb.html
+
+hook global BufCreate '.*\.erb' %{
+ set-option buffer filetype eruby
+}
+
+hook global WinSetOption filetype=eruby %{
+ require-module eruby
+ add-highlighter window/eruby ref eruby
+ hook -group eruby-indent window InsertChar '\n' html-indent-on-new-line
+ hook -always -once window WinSetOption filetype=.* %{
+ remove-highlighter window/eruby
+ remove-hooks window eruby-.+
+ }
+}
+
+provide-module eruby %{
+ require-module ruby
+ require-module html
+ add-highlighter shared/eruby regions
+ add-highlighter shared/eruby/html default-region ref html
+ add-highlighter shared/eruby/simple-expression-tag region '<%=' '%>' ref ruby
+ add-highlighter shared/eruby/simple-execution-tag region '<%' '%>' ref ruby
+ add-highlighter shared/eruby/simple-comment-tag region '<%#' '%>' fill comment
+}
diff --git a/autoload/filetype/etc.kak b/autoload/filetype/etc.kak
new file mode 100644
index 0000000..b04b7b4
--- /dev/null
+++ b/autoload/filetype/etc.kak
@@ -0,0 +1,95 @@
+# Highlighting for common files in /etc
+hook global BufCreate .*/etc/(hosts|networks|services) %{ set-option buffer filetype etc-hosts }
+hook global BufCreate .*/etc/resolv.conf %{ set-option buffer filetype etc-resolv-conf }
+hook global BufCreate .*/etc/shadow %{ set-option buffer filetype etc-shadow }
+hook global BufCreate .*/etc/passwd %{ set-option buffer filetype etc-passwd }
+hook global BufCreate .*/etc/gshadow %{ set-option buffer filetype etc-gshadow }
+hook global BufCreate .*/etc/group %{ set-option buffer filetype etc-group }
+hook global BufCreate .*/etc/(fs|m)tab %{ set-option buffer filetype etc-fstab }
+hook global BufCreate .*/etc/environment %{ set-option buffer filetype sh }
+hook global BufCreate .*/etc/env.d/.* %{ set-option buffer filetype sh }
+hook global BufCreate .*/etc/profile(\.(csh|env))? %{ set-option buffer filetype sh }
+hook global BufCreate .*/etc/profile\.d/.* %{ set-option buffer filetype sh }
+
+
+hook global WinSetOption filetype=etc-(hosts|resolv-conf|shadow|passwd|gshadow|group|fstab) %{
+ require-module "etc-%val{hook_param_capture_1}"
+}
+
+
+hook -group etc-resolv-conf-highlight global WinSetOption filetype=etc-resolv-conf %{
+ add-highlighter window/etc-resolv-conf ref etc-resolv-conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-resolv-conf }
+}
+hook -group etc-hosts-highlight global WinSetOption filetype=etc-hosts %{
+ add-highlighter window/etc-hosts ref etc-hosts
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-hosts }
+}
+hook -group etc-fstab-highlight global WinSetOption filetype=etc-fstab %{
+ add-highlighter window/etc-fstab ref etc-fstab
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-fstab }
+}
+hook -group etc-group-highlight global WinSetOption filetype=etc-group %{
+ add-highlighter window/etc-group ref etc-group
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-group }
+}
+hook -group etc-gshadow-highlight global WinSetOption filetype=etc-gshadow %{
+ add-highlighter window/etc-gshadow ref etc-gshadow
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-gshadow }
+}
+hook -group etc-shadow-highlight global WinSetOption filetype=etc-shadow %{
+ add-highlighter window/etc-shadow ref etc-shadow
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-shadow }
+}
+hook -group etc-passwd-highlight global WinSetOption filetype=etc-passwd %{
+ add-highlighter window/etc-passwd ref etc-passwd
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/etc-passwd }
+}
+
+
+# Highlighters
+
+provide-module etc-resolv-conf %{
+## /etc/resolv.conf
+add-highlighter shared/etc-resolv-conf group
+add-highlighter shared/etc-resolv-conf/ regex ^#.*?$ 0:comment
+add-highlighter shared/etc-resolv-conf/ regex ^(nameserver|server|domain|sortlist|options)[\s\t]+(.*?)$ 1:type 2:attribute
+}
+
+provide-module etc-hosts %{
+## /etc/hosts
+add-highlighter shared/etc-hosts group
+add-highlighter shared/etc-hosts/ regex ^(.+?)[\s\t]+?(.*?)$ 1:type 2:attribute
+add-highlighter shared/etc-hosts/ regex '#.*?$' 0:comment
+}
+
+provide-module etc-fstab %{
+## /etc/fstab
+add-highlighter shared/etc-fstab group
+add-highlighter shared/etc-fstab/ regex ^(\S{1,})\s+?(\S{1,})\s+?(\S{1,})\s+?(\S{1,})\s+?(\S{1,})\s+?(\S{1,})(?:\s+)?$ 1:keyword 2:value 3:type 4:string 5:attribute 6:attribute
+add-highlighter shared/etc-fstab/ regex '#.*?$' 0:comment
+}
+
+provide-module etc-group %{
+## /etc/group
+add-highlighter shared/etc-group group
+add-highlighter shared/etc-group/ regex ^(\S+?):(\S+?)?:(\S+?)?:(\S+?)?$ 1:keyword 2:type 3:value 4:string
+}
+
+provide-module etc-gshadow %{
+## /etc/gshadow
+add-highlighter shared/etc-gshadow group
+add-highlighter shared/etc-gshadow/ regex ^(\S+?):(\S+?)?:(\S+?)?:(\S+?)?$ 1:keyword 2:type 3:value 4:string
+}
+
+provide-module etc-shadow %{
+## /etc/shadow
+add-highlighter shared/etc-shadow group
+add-highlighter shared/etc-shadow/ regex ^(\S+?):(\S+?):([0-9]+?):([0-9]+?)?:([0-9]+?)?:([0-9]+?)?:([0-9]+?)?:([0-9]+?)?:(.*?)?$ 1:keyword 2:type 3:value 4:value 5:value 6:value 7:value 8:value
+}
+
+provide-module etc-passwd %{
+## /etc/passwd
+add-highlighter shared/etc-passwd group
+add-highlighter shared/etc-passwd/ regex ^(\S+?):(\S+?):([0-9]+?):([0-9]+?):(.*?)?:(.+?):(.+?)$ 1:keyword 2:type 3:value 4:value 5:string 6:attribute 7:attribute
+}
diff --git a/autoload/filetype/exherbo.kak b/autoload/filetype/exherbo.kak
new file mode 100644
index 0000000..7271425
--- /dev/null
+++ b/autoload/filetype/exherbo.kak
@@ -0,0 +1,138 @@
+## Repository metadata files
+hook global BufCreate .*/metadata/mirrors\.conf %{ set-option buffer filetype paludis-mirrors-conf }
+hook global BufCreate .*/metadata/licence_groups.conf %{ set-option buffer filetype exheres-0-licence-groups }
+hook global BufCreate .*/metadata/options/descriptions/.*\.conf %{ set-option buffer filetype exheres-0-licence-groups }
+hook global BufCreate .*/metadata/.*\.conf %{ set-option buffer filetype exheres-0-metadata }
+
+## News items
+hook global BufCreate .*/metadata/news/.*/.*\.txt %{ set-option buffer filetype glep42 }
+
+## exheres-0, exlib
+hook global BufCreate .*\.(exheres-0|exlib) %{ set-option buffer filetype sh }
+
+# Paludis configurations
+hook global BufCreate .*/etc/paludis(-.*)?/bashrc %{ set-option buffer filetype sh }
+hook global BufCreate .*/etc/paludis(-.*)?/general(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/licences(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-options-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/mirrors(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-mirrors-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/options(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-options-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/output(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/package_(unmask|mask)(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-specs-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/platforms(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-specs-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/repositories/.*\.conf %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/repository\.template %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/repository_defaults\.conf %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/specpath\.conf %{ set-option buffer filetype paludis-key-value-conf }
+hook global BufCreate .*/etc/paludis(-.*)?/suggestions(\.conf.d/.*.conf|\.conf) %{ set-option buffer filetype paludis-specs-conf }
+
+hook global WinSetOption filetype=exheres-0-(metadata|options-descriptions|licence-groups) %{
+ require-module exheres
+}
+
+hook -group exheres-0-metadata-highlight global WinSetOption filetype=exheres-0-metadata %{
+ add-highlighter window/exheres-0-metadata ref exheres-0-metadata
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/exheres-0-metadata }
+}
+
+hook -group exheres-0-options-descriptions-highlight global WinSetOption filetype=exheres-0-options-descriptions %{
+ add-highlighter window/exheres-0-options-descriptions ref exheres-0-options-descriptions
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/exheres-0-options-descriptions }
+}
+
+hook -group exheres-0-licence-groups-highlight global WinSetOption filetype=exheres-0-licence-groups %{
+ add-highlighter window/exheres-0-licence-groups ref exheres-0-licence-groups
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/exheres-0-licence-groups }
+}
+
+provide-module exheres %{
+# Highlighters
+## exheres-0 Repository metadata files
+add-highlighter shared/exheres-0-metadata group
+add-highlighter shared/exheres-0-metadata/ regex ^#.*?$ 0:comment
+add-highlighter shared/exheres-0-metadata/ regex ^(?:[\s\t]+)?(\*)?(\S+)(?:[\s\t]+)?=(?:[\s\t]+)?(.+?)?$ 1:type 2:attribute 3:string
+add-highlighter shared/exheres-0-metadata/ regex ^(?:[\s\t]+)?[\S]+[\s\t]+=[\s\t]+\[.+?[\s\t]+\] 0:string
+add-highlighter shared/exheres-0-metadata/ regex ^(?:[\s\t]+)?(\S+)\s\[\[$ 0:type
+add-highlighter shared/exheres-0-metadata/ regex ^(?:[\s\t]+)?\]\]$ 0:type
+
+## exheres-0 options descriptions
+add-highlighter shared/exheres-0-options-descriptions group
+add-highlighter shared/exheres-0-options-descriptions/ regex ^#.*?$ 0:comment
+add-highlighter shared/exheres-0-options-descriptions/ regex ^(?:[\s\t]+)?[\S]+[\s\t]+-[\s\t]+\[.+?[\s\t]+\] 0:string
+add-highlighter shared/exheres-0-options-descriptions/ regex ^(?:[\s\t]+)?(\S+)\s\[\[$ 0:type
+add-highlighter shared/exheres-0-options-descriptions/ regex ^(?:[\s\t]+)?\]\]$ 0:type
+
+## metadata/licence_groups.conf
+add-highlighter shared/exheres-0-licence-groups group
+add-highlighter shared/exheres-0-licence-groups/ regex [\s\t]+(\S+(?:[\s\t]+))*$ 0:attribute
+add-highlighter shared/exheres-0-licence-groups/ regex ^(\S+) 0:type
+add-highlighter shared/exheres-0-licence-groups/ regex ^#.*?$ 0:comment
+}
+
+hook global WinSetOption filetype=paludis-(key-value|options|mirrors|specs)-conf %{
+ require-module paludis
+}
+
+hook -group paludis-options-conf-highlight global WinSetOption filetype=paludis-options-conf %{
+ add-highlighter window/paludis-options-conf ref paludis-options-conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/paludis-options-conf }
+}
+
+hook -group paludis-key-value-conf-highlight global WinSetOption filetype=paludis-key-value-conf %{
+ add-highlighter window/paludis-key-value-conf ref paludis-key-value-conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/paludis-key-value-conf }
+}
+
+hook -group paludis-mirrors-conf-highlight global WinSetOption filetype=paludis-mirrors-conf %{
+ add-highlighter window/paludis-mirrors-conf ref paludis-mirrors-conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/paludis-mirrors-conf }
+}
+
+hook -group paludis-specs-conf-highlight global WinSetOption filetype=paludis-specs-conf %{
+ add-highlighter window/paludis-specs-conf ref paludis-specs-conf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/paludis-specs-conf }
+}
+provide-module paludis %{
+## Paludis configurations
+### options.conf
+add-highlighter shared/paludis-options-conf group
+add-highlighter shared/paludis-options-conf/ regex [\s\t]+(\S+(?:[\s\t]+))*$ 0:attribute
+add-highlighter shared/paludis-options-conf/ regex (?::)(?:[\s\t]+)(.*?$) 1:attribute
+add-highlighter shared/paludis-options-conf/ regex [\s\t]+(\S+=)(\S+) 1:attribute 2:value
+add-highlighter shared/paludis-options-conf/ regex [\s\t](\S+:) 0:keyword
+add-highlighter shared/paludis-options-conf/ regex [\s\t](-\S+)(.*?) 1:red
+add-highlighter shared/paludis-options-conf/ regex ^(\S+/\S+) 0:type
+add-highlighter shared/paludis-options-conf/ regex ^#.*?$ 0:comment
+
+## general.conf, repository.template
+add-highlighter shared/paludis-key-value-conf group
+add-highlighter shared/paludis-key-value-conf/ regex ^[\s\t]?(\S+)[\s\t+]=[\s\t+](.*?)$ 1:attribute 2:value
+add-highlighter shared/paludis-key-value-conf/ regex ^#.*?$ 0:comment
+
+## mirrors.conf
+add-highlighter shared/paludis-mirrors-conf group
+add-highlighter shared/paludis-mirrors-conf/ regex ^[\s\t+]?(\S+)[\s\t+](.*?)$ 1:type 2:value
+add-highlighter shared/paludis-mirrors-conf/ regex ^#.*?$ 0:comment
+
+## package_(unmask|mask).conf, platforms.conf
+add-highlighter shared/paludis-specs-conf group
+add-highlighter shared/paludis-specs-conf/ regex [\s\t]+(\S+(?:[\s\t]+))*$ 0:attribute
+add-highlighter shared/paludis-specs-conf/ regex ^(\S+/\S+) 0:type
+add-highlighter shared/paludis-specs-conf/ regex ^#.*?$ 0:comment
+}
+
+hook global WinSetOption filetype=glep42 %{
+ require-module glep42
+}
+
+hook -group glep42-highlight global WinSetOption filetype=glep42 %{
+ add-highlighter window/glep42 ref glep42
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/glep42 }
+}
+
+provide-module glep42 %{
+## News items (GLEP42)
+add-highlighter shared/glep42 group
+add-highlighter shared/glep42/ regex ^(Title|Author|Translator|Content-Type|Posted|Revision|News-Item-Format|Display-If-Installed|Display-If-Keyword|Display-If-Profile):([^\n]*(?:\n\h+[^\n]+)*)$ 1:keyword 2:attribute
+add-highlighter shared/glep42/ regex <[^@>]+@.*?> 0:string
+add-highlighter shared/glep42/ regex ^>.*?$ 0:comment
+}
diff --git a/autoload/filetype/fennel.kak b/autoload/filetype/fennel.kak
new file mode 100644
index 0000000..7b82a8e
--- /dev/null
+++ b/autoload/filetype/fennel.kak
@@ -0,0 +1,109 @@
+# http://fennel-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.]fnl %{
+ set-option buffer filetype fennel
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+hook global WinSetOption filetype=fennel %{
+ require-module fennel
+ fennel-configure-window
+}
+
+hook -group fennel-highlight global WinSetOption filetype=fennel %{
+ add-highlighter window/fennel ref fennel
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/fennel }
+}
+
+provide-module fennel %{
+
+require-module lisp
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/fennel regions
+add-highlighter shared/fennel/code default-region group
+add-highlighter shared/fennel/comment region '(?<!\\)(?:\\\\)*\K;' '$' fill comment
+add-highlighter shared/fennel/shebang region '(?<!\\)(?:\\\\)*\K#!' '$' fill comment
+add-highlighter shared/fennel/string region '(?<!\\)(?:\\\\)*\K"' '(?<!\\)(?:\\\\)*"' fill string
+add-highlighter shared/fennel/colon-string region '\W\K:[-\w?\\^_!$%&*+./@|<=>]' '(?![-\w?\\^_!$%&*+./@|<=>])' fill keyword
+add-highlighter shared/fennel/code/ regex \\(?:space|tab|newline|return|backspace|formfeed|u[0-9a-fA-F]{4}|o[0-3]?[0-7]{1,2}|.)\b 0:string
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="require-macros eval-compiler doc lua hashfn macro macros import-macros pick-args pick-values macroexpand macrodebug
+ do values if when each for fn lambda λ partial while set global var local let tset set-forcibly! doto match or and
+ not not= collect icollect accumulate rshift lshift bor band bnot bxor with-open"
+ re_keywords='\\$ \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 \\$\\.\\.\\.'
+ builtins="_G _VERSION arg assert bit32 collectgarbage coroutine debug
+ dofile error getfenv getmetatable io ipairs length load
+ loadfile loadstring math next os package pairs pcall
+ print rawequal rawget rawlen rawset require select setfenv
+ setmetatable string table tonumber tostring type unpack xpcall"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+# Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list fennel_static_words $(join "${keywords} ${builtins} false nil true" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/fennel/code/keywords regex \b($(join "${keywords} ${re_keywords}" '|'))\b 0:keyword
+ add-highlighter shared/fennel/code/builtins regex \b($(join "${builtins}" '|'))\b 0:builtin
+ "
+}
+
+add-highlighter shared/fennel/code/operator regex (\.|\?\.|\+|\.\.|\^|-|\*|%|/|>|<|>=|<=|=|\.\.\.|:|->|->>|-\?>|-\?>>) 0:operator
+add-highlighter shared/fennel/code/value regex \b(false|nil|true|[0-9]+(:?\.[0-9])?(:?[eE]-?[0-9]+)?|0x[0-9a-fA-F])\b 0:value
+add-highlighter shared/fennel/code/function_declaration regex \((?:fn|lambda|λ)\s+([\S]+) 1:function
+add-highlighter shared/fennel/code/method_call regex (?:\w+|\$[0-9]{0,1}):(\S+)\b 1:function
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden fennel-configure-window %{
+ set-option window static_words %opt{fennel_static_words}
+
+ hook window ModeChange pop:insert:.* -group fennel-trim-indent fennel-trim-indent
+ hook window InsertChar \n -group fennel-indent fennel-indent-on-new-line
+
+ set-option buffer extra_word_chars '_' . / * ? + - < > ! : "'"
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window fennel-.+ }
+}
+
+define-command -hidden fennel-trim-indent lisp-trim-indent
+
+declare-option \
+ -docstring 'regex matching the head of forms which have options *and* indented bodies' \
+ regex fennel_special_indent_forms \
+ '(?:if|when|each|for|fn|lambda|λ|partial|while|local|var|doto|let)'
+
+define-command -hidden fennel-indent-on-new-line %{
+ # registers: i = best align point so far; w = start of first word of form
+ evaluate-commands -draft -save-regs '/"|^@iw' -itersel %{
+ execute-keys -draft 'gk"iZ'
+ try %{
+ execute-keys -draft '[bl"i<a-Z><gt>"wZ'
+
+ try %{
+ # If a special form, indent another (indentwidth - 1) spaces
+ execute-keys -draft '"wze<a-K>[\s()\[\]\{\}]<ret><a-k>\A' %opt{fennel_special_indent_forms} '\z<ret>'
+ execute-keys -draft '"wze<a-L>s.{' %sh{printf $(( kak_opt_indentwidth - 1 ))} '}\K.*<ret><a-;>;"i<a-Z><gt>'
+ } catch %{
+ # If not special and parameter appears on line 1, indent to parameter
+ execute-keys -draft '"wz<a-K>[()[\]{}]<ret>e<a-K>[\s()\[\]\{\}]<ret><a-l>s\h\K[^\s].*<ret><a-;>;"i<a-Z><gt>'
+ }
+ }
+ try %{ execute-keys -draft '[rl"i<a-Z><gt>' }
+ try %{ execute-keys -draft '[Bl"i<a-Z><gt>' }
+ execute-keys -draft ';"i<a-z>a&,'
+ }
+}
+
+}
diff --git a/autoload/filetype/fidl.kak b/autoload/filetype/fidl.kak
new file mode 100644
index 0000000..351eeb4
--- /dev/null
+++ b/autoload/filetype/fidl.kak
@@ -0,0 +1,79 @@
+# Detection
+hook global BufCreate .*\.fidl %{
+ set-option buffer filetype fidl
+}
+
+hook global WinSetOption filetype=fidl %<
+ require-module fidl
+ hook window ModeChange pop:insert:.* -group fidl-trim-indent fidl-trim-indent
+ hook window InsertChar \n -group fidl-indent fidl-indent-on-new-line
+ hook window InsertChar [)}] -group fidl-indent fidl-indent-on-closing
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window fidl-.+ }
+>
+
+hook -group fidl-highlight global WinSetOption filetype=fidl %{
+ add-highlighter window/fidl ref fidl
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/fidl }
+}
+
+provide-module fidl %§
+
+# Highlighters
+
+add-highlighter shared/fidl regions
+add-highlighter shared/fidl/code default-region group
+
+add-highlighter shared/fidl/string region \" (?<!\\)(\\\\)*" fill string
+
+add-highlighter shared/fidl/line_doc region ///(?!/) $ fill documentation
+add-highlighter shared/fidl/line_comment region // $ group
+add-highlighter shared/fidl/line_comment/comment fill comment
+add-highlighter shared/fidl/line_comment/todo regex TODO|FIXME: 0:meta
+
+add-highlighter shared/fidl/attributes region @[a-zA-Z] \b fill meta
+
+add-highlighter shared/fidl/code/keywords regex \b(as|bits|compose|const|enum|error|flexible|optional|library|protocol|resource|service|strict|struct|table|type|union|using)\b 0:keyword
+add-highlighter shared/fidl/code/types regex \b(array|bool|bytes?|client_end|float(32|64)|server_end|string|u?int(8|16|32|64)|vector)\b 0:type
+
+add-highlighter shared/fidl/code/literals group
+add-highlighter shared/fidl/code/literals/bool regex \b(true|false)\b 0:value
+add-highlighter shared/fidl/code/literals/decimal regex \b([0-9]+(\.[0-9]+)?)\b 0:value
+add-highlighter shared/fidl/code/literals/hexadecimal regex \b(0x[0-9a-fA-F])\b 0:value
+add-highlighter shared/fidl/code/literals/binary regex \b(0b[01])\b 0:value
+
+# Commands
+
+define-command -hidden fidl-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden fidl-indent-on-new-line %~
+ evaluate-commands -draft -itersel %@
+ try %{ # line comment
+ # copy the commenting prefix
+ execute-keys -draft k x s ^\h*/{2,}\h* <ret> yjghP
+ } catch %`
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # align to opening ( or { if possible
+ try %+
+ execute-keys -draft <a-k> [)}] <ret> m <a-S> 1<a-&>
+ + catch %+
+ # indent after lines ending with ( or {
+ try %! execute-keys -draft k x <a-k> [({]$ <ret> j <a-gt> !
+ +
+ `
+ # remove trailing white spaces
+ try %{ execute-keys -draft k : fidl-trim-indent <ret> }
+ @
+~
+
+define-command -hidden fidl-indent-on-closing %~
+ evaluate-commands -draft -itersel %@
+ # align to opening ( or { when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h*[)}]$ <ret> m <a-S> 1<a-&> >
+ @
+~
+
diff --git a/autoload/filetype/fish.kak b/autoload/filetype/fish.kak
new file mode 100644
index 0000000..0bfcc4f
--- /dev/null
+++ b/autoload/filetype/fish.kak
@@ -0,0 +1,87 @@
+# http://fishshell.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](fish) %{
+ set-option buffer filetype fish
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=fish %{
+ require-module fish
+
+ hook window ModeChange pop:insert:.* -group fish-trim-indent fish-trim-indent
+ hook window InsertChar .* -group fish-indent fish-indent-on-char
+ hook window InsertChar \n -group fish-insert fish-insert-on-new-line
+ hook window InsertChar \n -group fish-indent fish-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window fish-.+ }
+}
+
+hook -group fish-highlight global WinSetOption filetype=fish %{
+ add-highlighter window/fish ref fish
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/fish }
+}
+
+
+provide-module fish %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/fish regions
+add-highlighter shared/fish/code default-region group
+add-highlighter shared/fish/double_string region (?<!\\)(?:\\\\)*\K" (?<!\\)(\\\\)*" group
+add-highlighter shared/fish/single_string region (?<!\\)(?:\\\\)*\K' (?<!\\)(\\\\)*' fill string
+add-highlighter shared/fish/comment region (?<!\\)(?:\\\\)*(?:^|\h)\K# '$' fill comment
+
+add-highlighter shared/fish/double_string/ fill string
+add-highlighter shared/fish/double_string/ regex ((?<!\\)(?:\\\\)*\K\$\w+)|(\{\$\w+\}) 0:variable
+
+add-highlighter shared/fish/code/ regex (?<!\\)(?:\\\\)*\K(\$\w+)|(\{\$\w+\}) 0:variable
+
+# Command names are collected using `builtin --names`.
+add-highlighter shared/fish/code/ regex \b(?<![$-])(and|argparse|begin|bg|bind|block|break|breakpoint|builtin|case|cd|command|commandline|complete|contains|continue|count|disown|echo|else|emit|end|eval|exec|exit|false|fg|for|function|functions|history|if|jobs|math|not|or|printf|pwd|random|read|realpath|return|set|set_color|source|status|string|switch|test|time|true|ulimit|wait|while)\b(?!-) 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden fish-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft x 1s^(\h+)$<ret> d }
+ }
+}
+
+define-command -hidden fish-indent-on-char %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # align middle and end structures to start and indent when necessary
+ try %{ execute-keys -draft x<a-k>^\h*(else)$<ret><a-semicolon><a-?>^\h*(if)<ret>s\A|.\z<ret>1<a-&> }
+ try %{ execute-keys -draft x<a-k>^\h*(end)$<ret><a-semicolon><a-?>^\h*(begin|for|function|if|switch|while)<ret>s\A|.\z<ret>1<a-&> }
+ try %{ execute-keys -draft x<a-k>^\h*(case)$<ret><a-semicolon><a-?>^\h*(switch)<ret>s\A|.\z<ret>1<a-&> }
+ }
+}
+
+define-command -hidden fish-insert-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*#\h* <ret> y jgh P }
+ }
+}
+
+define-command -hidden fish-indent-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after start structure
+ try %{ execute-keys -draft kx<a-k>^\h*(begin|case|else|for|function|if|while)\b<ret>j<a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/fsharp.kak b/autoload/filetype/fsharp.kak
new file mode 100644
index 0000000..64d6b2c
--- /dev/null
+++ b/autoload/filetype/fsharp.kak
@@ -0,0 +1,157 @@
+# https://fsharp.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](fs|fsx|fsi) %{
+ set-option buffer filetype fsharp
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=fsharp %{
+ require-module fsharp
+
+ # indent on newline
+ hook window ModeChange pop:insert:.* -group fsharp-trim-indent fsharp-trim-indent
+ hook window InsertChar \n -group fsharp-insert fsharp-insert-on-new-line
+ hook window InsertChar \n -group fsharp-indent fsharp-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window fsharp-.+ }
+}
+
+hook -group fsharp-highlight global WinSetOption filetype=fsharp %{
+ add-highlighter window/fsharp ref fsharp
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/fsharp }
+}
+
+provide-module fsharp %§
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/fsharp regions
+add-highlighter shared/fsharp/code default-region group
+add-highlighter shared/fsharp/docstring region \(\*(?!\)) (\*\)) regions
+add-highlighter shared/fsharp/double_string region @?(?<!')" (?<!\\)(\\\\)*"B? fill string
+add-highlighter shared/fsharp/comment region '//' '$' fill comment
+# https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/attributes
+add-highlighter shared/fsharp/attributes region "\[<" ">\]" fill meta
+
+add-highlighter shared/fsharp/docstring/ default-region fill comment
+# ability to write highlighted code inside docstring:
+add-highlighter shared/fsharp/docstring/ region '>>> \K' '\z' ref fsharp
+add-highlighter shared/fsharp/docstring/ region '\.\.\. \K' '\z' ref fsharp
+
+evaluate-commands %sh{
+ # Grammar
+ meta="open"
+
+ # exceptions taken from fsharp.vim colors (https://github.com/fsharp/vim-fsharp)
+ exceptions="try|failwith|failwithf|finally|invalid_arg|raise|rethrow"
+
+ # keywords taken from fsharp.vim colors (https://github.com/fsharp/vim-fsharp)
+ keywords="abstract|as|assert|base|begin|class|default|delegate"
+ keywords="${keywords}|do|done|downcast|downto|elif|else|end|exception"
+ keywords="${keywords}|extern|for|fun|function|global|if|in|inherit|inline"
+ keywords="${keywords}|interface|lazy|let|match|member|module|mutable"
+ keywords="${keywords}|namespace|new|of|override|rec|static|struct|then"
+ keywords="${keywords}|to|type|upcast|use|val|void|when|while|with"
+ keywords="${keywords}|async|atomic|break|checked|component|const|constraint"
+ keywords="${keywords}|constructor|continue|decimal|eager|event|external"
+ keywords="${keywords}|fixed|functor|include|method|mixin|object|parallel"
+ keywords="${keywords}|process|pure|return|seq|tailcall|trait|yield"
+ # additional operator keywords (Microsoft.FSharp.Core.Operators)
+ keywords="${keywords}|box|hash|sizeof|nameof|typeof|typedefof|unbox|ref|fst|snd"
+ keywords="${keywords}|stdin|stdout|stderr"
+ # math operators (Microsoft.FSharp.Core.Operators)
+ keywords="${keywords}|abs|acos|asin|atan|atan2|ceil|cos|cosh|exp|floor|log"
+ keywords="${keywords}|log10|pown|round|sign|sin|sinh|sqrt|tan|tanh"
+
+
+ types="array|bool|byte|char|decimal|double|enum|exn|float"
+ types="${types}|float32|int|int16|int32|int64|lazy_t|list|nativeint"
+ types="${types}|obj|option|sbyte|single|string|uint|uint32|uint64"
+ types="${types}|uint16|unativeint|unit"
+
+ fsharpCoreMethod="printf|printfn|sprintf|eprintf|eprintfn|fprintf|fprintfn"
+
+ # Add the language's grammar to the static completion list
+ printf '%s\n' "hook global WinSetOption filetype=fsharp %{
+ set-option window static_words ${values} ${meta} ${exceptions} ${keywords} ${types}
+ }" | tr '|' ' '
+
+ # Highlight keywords
+ printf '%s\n' "
+ add-highlighter shared/fsharp/code/ regex '\b(${meta})\b' 0:meta
+ add-highlighter shared/fsharp/code/ regex '\b(${exceptions})\b' 0:function
+ add-highlighter shared/fsharp/code/ regex '\b(${fsharpCoreMethod})\b' 0:function
+ add-highlighter shared/fsharp/code/ regex '\b(${keywords})\b' 0:keyword
+ "
+}
+
+# computation expression keywords prefixed with !
+add-highlighter shared/fsharp/code/ regex "\w+!" 0:keyword
+# brackets
+add-highlighter shared/fsharp/code/ regex "[\[\]\(\){}]" 0:bracket
+# accomodate typically overloaded operators
+add-highlighter shared/fsharp/code/ regex "\B(<<>>|<\|\|>)\B" 0:operator
+# fsharp operators
+add-highlighter shared/fsharp/code/ regex "\B(->|<-|<=|>=)\B" 0:operator
+add-highlighter shared/fsharp/code/ regex "(\b(not)\b|\b(and)\b)" 0:operator
+add-highlighter shared/fsharp/code/ regex (?<=[\w\s\d'"_])((\?)([><+-/*%=]{1,2})(\??)|(\??)([><+-/*%=]{1,2})(\?)) 0:operator
+add-highlighter shared/fsharp/code/ regex (?<=[\w\s\d'"_])((\|)+>|<(\|)+|<@|@>|<@@|@@>|:>|:\?|:=|(!|#)(?=\w)|:\?>|\?|~([-+]|(~){0,2})) 0:operator
+add-highlighter shared/fsharp/code/ regex (?<=[\w\s\d'"_])(<>|::|\h\|\h|(\|\|)+|@|\.\.|<=|>=|(<)+|(>)+|!=|==|\|\|\||(\^)+|(&)+|\+|-|(\*)+|//|/|%+|=) 0:operator
+add-highlighter shared/fsharp/code/ regex (?<=[\w\s\d'"_])((?<![=<>!])=(?![=])|[+*-]=) 0:builtin
+# integer literals
+add-highlighter shared/fsharp/code/ regex %{(?<!\.)\b[1-9]('?\d+)*(u?(l|L|y|s|n)?|[IMm])\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?<!\.)\b0b[01]('?[01]+)*(u?(l|L|y|s|n)?|[IMm])\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?<!\.)\b0('?[0-7]+)*(u?(l|L|y|s|n)?|[IMm])\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?<!\.)\b0x[\da-fA-F]('?[\da-fA-F]+)*(u?(l|L|y|s|n)?)?\b(?!\.)} 0:value
+# floating point literals
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)\b\d('?\d+)*\.((lf|[fm])\b|\B)(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)\b\d('?\d+)*\.?e[+-]?\d('?\d+)*(lf|[fm])?\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)(\b(\d('?\d+)*)|\B)\.\d('?[\d]+)*(e[+-]?\d('?\d+)*)?(lf|[fm])?\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)\b0x[\da-f]('?[\da-f]+)*((lf|[fm])\b|\B)(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)\b0x[\da-f]('?[\da-f]+)*\.?p[+-]?\d('?\d+)*)?(lf|[fm])?\b(?!\.)} 0:value
+add-highlighter shared/fsharp/code/ regex %{(?i)(?<!\.)\b0x([\da-f]('?[\da-f]+)*)?\.\d('?[\d]+)*(p[+-]?\d('?\d+)*)?(lf|[fm])?\b(?!\.)} 0:value
+# character literals
+add-highlighter shared/fsharp/code/char regex %{\B'((\\.)|[^'\\])'(\b(B)|\B)} 0:value
+# other literals
+add-highlighter shared/fsharp/code/ regex "\b(true|false)\b" 0:value
+add-highlighter shared/fsharp/code/ regex "\B(\(\))\B" 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden fsharp-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden fsharp-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*//\h* <ret> y jgh P }
+ }
+}
+
+define-command -hidden fsharp-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft \; K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with =
+ try %{ execute-keys -draft , k x <a-k> =$ <ret> j <a-gt> }
+ # indent after line ending with "do"
+ try %{ execute-keys -draft , k x <a-k> \bdo$ <ret> j <a-gt> }
+ }
+}
+
diff --git a/autoload/filetype/gas.kak b/autoload/filetype/gas.kak
new file mode 100644
index 0000000..73f8db7
--- /dev/null
+++ b/autoload/filetype/gas.kak
@@ -0,0 +1,99 @@
+# Detection
+# ---------
+hook global BufCreate .*\.(s|S|asm)$ %{
+ set-option buffer filetype gas
+}
+
+hook global WinSetOption filetype=gas %{
+ require-module gas
+
+ hook window ModeChange pop:insert:.* -group gas-trim-indent gas-trim-indent
+ hook window InsertChar \n -group gas-indent gas-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window gas-.+ }
+}
+
+hook -group gas-highlight global WinSetOption filetype=gas %{
+ add-highlighter window/gas ref gas
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/gas }
+}
+
+
+provide-module gas %{
+
+add-highlighter shared/gas regions
+add-highlighter shared/gas/code default-region group
+add-highlighter shared/gas/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/gas/commentMulti region /\* \*/ fill comment
+add-highlighter shared/gas/commentSingle1 region '#' '$' fill comment
+add-highlighter shared/gas/commentSingle2 region ';' '$' fill comment
+
+# Constant
+add-highlighter shared/gas/code/ regex (0[xX][0-9a-fA-F]+|\b[0-9]+)\b 0:value
+
+# Labels
+add-highlighter shared/gas/code/ regex ^\h*([A-Za-z0-9_.-]+): 0:operator
+
+# ARM Directives
+add-highlighter shared/gas/code/ regex ((^|\s+)\.([248]byte|align|arch(_extension)?|arm|bsscantunwind|code|[cf]pu|[dq]n|eabi_attribute|even|extend|ldouble|fnend|fnstart|force_thumb|handlerdata|inst(\.[nw])?|ltorg|movsp|object_arch|packed|pad|personality(index)?|pool|req|save|setfp|screl32|syntax|thumb(_func|_set)?|tlsdescseq|unreq|unwind_raw|vsave)(\h+|$)) 0:type
+
+# Assembler Directives
+add-highlighter shared/gas/code/ regex ((^|\s+)\.(abort|ABORT|align|app-file|ascii|asciz|balign[wl]|byte|comm|data|def|desc|dim|double|eject|else|endif|equ|extern|file|fill|float|global|globl|hword|ident|if|include|int|irp|irpc|lcomm|iflags|line|linkonce|ln|mri|list|loc|local|long|macro|nolist|octa|org|print|purgem|p2align[wl]?|psize|quad|rept|sbttl|section|set|short|single|size|skip|space|stab[dns]|string|struct|tag|text|title|type|title|uleb128|val|vtable_entry|weak|word|rodata|zero)(\h+|$)) 0:type
+
+# Registers
+add-highlighter shared/gas/code/ regex \%(([re](ax|bx|cx|dx|si|di|bp|sp))|(al|bl|cl|dl|sil|dil|bpl|spl)|(r[8-9][dwb])|(r1[0-5][dwb])|(cs|ds|es|fs|gs|ss|ip|eflags)|([xy]mm[0-9]|[xy]mm1[0-5]))\b 0:variable
+
+# General Instructions
+add-highlighter shared/gas/code/ regex \
+^\h*(mov|lea|call|test|cmp)([bwlq])?\b|\
+^\h*(bswap[lq]|cmpxchg[bwlq]|cmpxchg8b|cwt[ld]|movabs([bwlq])?|popa([lw])?|pusha([wl])?)\b|\
+^\h*(and|or|not|xor|sar|sal|shr|shl|sub|add|(i)?mul|(i)?div|inc|dec|adc|sbb)([bwlq])?\b|\
+^\h*(rcl|rcr|rol|ror|shld|shrd)([bwlq])?\b|\
+^\h*(bsf|bsr|bt|btc|btr|bts)([wlq])?\b|\
+^\h*(cmps|lods|movs)([sxbwdq])?\b|\
+^\h*(ret([bwlq])?|[il]ret([dq])?|leave|movzb[wlq]|movzw[lq]|movsb[wlq]|movsw[lq]|movslq|cwt[dl]|clt[sdq]|cqt[od])\b|\
+^\h*set(([bagl])?e|(n)?[zlesgabop]|(n)?(ae|le|ge|be))\b|\
+^\h*(cmovn[eszlgba]|cmov[glab]e|cmov[esglabz]|cmovn[lgba]e)\b|\
+^\h*(jmp|j[esglabzcop]|jn[esglabzcop]|j[glasbp]e|jn[glab]e|j(e)?cxz|jpo)\b|\
+^\h*(aa[adms]|da[as]|xadd[bwlq]|xchg[lwq])\b|\
+^\h*(rep|repnz|repz|scas([qlwb])?|stos([qlwb])?)\b|\
+^\h*(cl[cdi]|cmc|lahf|popf([lwq])?|pushf([lwq])?|sahf|st[cdi])\b|\
+^\h*(l[defgs]s([wl])?|cpuid|nop|ud2|xlat(b)?)\b|\
+^\h*(lea|call|push|pop)([wlq])?\b|\
+^\h*(in|ins([lwb])?|out|outs([lwb])?)\b|\
+^\h*(cb(t)?w|cwde|cdqe|cwd|cdq|cqo|sahf|lahf|por|pxor|movap[ds])\b|\
+^\h*(bound([wl])?|enter|int(o)?|lcall|loop(n)?[ez]|pause)\b 0:keyword
+
+#Floating Point Instructions
+add-highlighter shared/gas/code/ regex \
+^\h*f(add|sub|mul|com|comp|sub|subr|div|divr|ld|xch|st|nop|stp|ldenv|chs|abs)\b|\
+^\h*f(tst|xam|ldcw|ld1|ld2[te]|ldpi|ld[gn]2|ldz|(n)?stenv|2xm1|yl2x|p(a)?tan)\b|\
+^\h*f(xtract|prem(1)?|(dec|inc)stp|(n)?stcw|yl2xp1|sqrt|sincos|rndint|scale|sin|cos|iadd)\b|\
+^\h*f(cmov[bn]e|cmove|cmovn[beu]|cmovnbe|cmovu|imul|icom|icomp|isub|isubr|icomp)\b|\
+^\h*(div|add|sub|mul|div)[ps]s\b|\
+^\h*(div|add|sub|mul|div)[ps]d\b|\
+^\h*(vmovs[ds]|vmovap[sd])\b|\
+^\h*(vcvtts[ds]2si(q)?|vcvtsi2s[d](q)?|vunpcklps|vcvtps2pd|vmovddup|vcvtpd2psx)\b|\
+^\h*(cvtss2s[di]|cvtsi2s[ds]|cvtsd2s[is]|cvtdq2p[ds]|cvtpd2(dq|pi|ps)|cvtpi2p[ds]|cvtps2p[id])\b|\
+^\h*(cvttp[ds]2dq|cvttp[ds]2pi|cvtts[ds]2si)\b|\
+^\h*(vxorp[sd]|vandp[sd]|ucomis[sd])\b 0:keyword
+
+define-command -hidden gas-trim-indent %{
+ evaluate-commands -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h+$ <ret> d }
+ }
+}
+
+define-command -hidden gas-indent-on-new-line %~
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : gas-trim-indent <ret> }
+ # indent after label
+ try %[ execute-keys -draft k x <a-k> :$ <ret> j <a-gt> ]
+ >
+~
+
+}
diff --git a/autoload/filetype/gdscript.kak b/autoload/filetype/gdscript.kak
new file mode 100644
index 0000000..842a51e
--- /dev/null
+++ b/autoload/filetype/gdscript.kak
@@ -0,0 +1,126 @@
+# http://godotengine.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](gd) %{
+ set-option buffer filetype gdscript
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=gdscript %{
+ require-module gdscript
+
+ set-option window static_words %opt{gdscript_static_words}
+
+ hook window InsertChar \n -group gdscript-insert gdscript-insert-on-new-line
+ hook window InsertChar \n -group gdscript-indent gdscript-indent-on-new-line
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group gdscript-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window gdscript-.+ }
+}
+
+hook -group gdscript-highlight global WinSetOption filetype=gdscript %{
+ add-highlighter window/gdscript ref gdscript
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/gdscript }
+}
+
+provide-module gdscript %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/gdscript regions
+add-highlighter shared/gdscript/code default-region group
+
+add-highlighter shared/gdscript/string region -match-capture ("|'|"""|''') (?<!\\)(?:\\\\)*("|'|"""|''') group
+add-highlighter shared/gdscript/string/ fill string
+add-highlighter shared/gdscript/string/ regex \\[abfnrtv\n\\] 0:meta
+add-highlighter shared/gdscript/string/ regex '%%' 0:meta
+add-highlighter shared/gdscript/string/ regex '%[cs]' 0:value
+add-highlighter shared/gdscript/string/ regex '%0?[+-]?([\d]*|\*?)\.?([\d]*|\*?)[dfoxX]' 0:value
+add-highlighter shared/gdscript/string/ regex '%0?([\d]*|\*?)\.?([\d]*|\*?)[+-]?[dfoxX]' 0:value
+
+add-highlighter shared/gdscript/comment region '#' $ fill comment
+
+# integers
+add-highlighter shared/gdscript/code/ regex (?i)\b0b[01]+l?\b 0:value
+add-highlighter shared/gdscript/code/ regex (?i)\b0x[\da-f]+l?\b 0:value
+add-highlighter shared/gdscript/code/ regex (?i)\b0o?[0-7]+l?\b 0:value
+add-highlighter shared/gdscript/code/ regex (?i)\b([1-9]\d*|0)l?\b 0:value
+# floats
+add-highlighter shared/gdscript/code/ regex \b\d+[eE][+-]?\d+\b 0:value
+add-highlighter shared/gdscript/code/ regex (\b\d+)?\.\d+\b 0:value
+add-highlighter shared/gdscript/code/ regex \b\d+\. 0:value
+# functions
+add-highlighter shared/gdscript/code/ regex _?[a-zA-Z]\w*\s*(?=\() 0:function
+add-highlighter shared/gdscript/code/ regex (?:func\h+)(_?\w+)(?:<[^>]+?>)?\( 1:function
+# operators
+add-highlighter shared/gdscript/code/ regex \+|-|\*|/|%|=|<|>|&|\||\^|~|:= 0:operator
+# constants & enums
+add-highlighter shared/gdscript/code/ regex \b[A-Z_][A-Z0-9_]*\b 0:variable
+# annotations
+add-highlighter shared/gdscript/code/ regex @\w+ 0:attribute
+# special case of get =, set =
+add-highlighter shared/gdscript/code/ regex \b(get)\h*=\h*(\w+) 1:keyword 2:function
+add-highlighter shared/gdscript/code/ regex \b(set)\h*=\h*(\w+) 1:keyword 2:function
+# keywords and built-ins
+evaluate-commands %sh{
+ keywords="as await break breakpoint class class_name const continue elif else enum extends for func if in is match pass return self signal static super var void while"
+
+ values="false true null"
+
+ # Built in `nushell` with `ls doc/classes | each {open $in.name | get attributes.name} | str join " "`, the rest are built similarly
+ builtin_classes="AABB AESContext AStar2D AStar3D AStarGrid2D AcceptDialog AnimatableBody2D AnimatableBody3D AnimatedSprite2D AnimatedSprite3D AnimatedTexture Animation AnimationLibrary AnimationNode AnimationNodeAdd2 AnimationNodeAdd3 AnimationNodeAnimation AnimationNodeBlend2 AnimationNodeBlend3 AnimationNodeBlendSpace1D AnimationNodeBlendSpace2D AnimationNodeBlendTree AnimationNodeOneShot AnimationNodeOutput AnimationNodeStateMachine AnimationNodeStateMachinePlayback AnimationNodeStateMachineTransition AnimationNodeSync AnimationNodeTimeScale AnimationNodeTimeSeek AnimationNodeTransition AnimationPlayer AnimationRootNode AnimationTrackEditPlugin AnimationTree Area2D Area3D Array ArrayMesh ArrayOccluder3D AspectRatioContainer AtlasTexture AudioBusLayout AudioEffect AudioEffectAmplify AudioEffectBandLimitFilter AudioEffectBandPassFilter AudioEffectCapture AudioEffectChorus AudioEffectCompressor AudioEffectDelay AudioEffectDistortion AudioEffectEQ AudioEffectEQ10 AudioEffectEQ21 AudioEffectEQ6 AudioEffectFilter AudioEffectHighPassFilter AudioEffectHighShelfFilter AudioEffectInstance AudioEffectLimiter AudioEffectLowPassFilter AudioEffectLowShelfFilter AudioEffectNotchFilter AudioEffectPanner AudioEffectPhaser AudioEffectPitchShift AudioEffectRecord AudioEffectReverb AudioEffectSpectrumAnalyzer AudioEffectSpectrumAnalyzerInstance AudioEffectStereoEnhance AudioListener2D AudioListener3D AudioServer AudioStream AudioStreamGenerator AudioStreamGeneratorPlayback AudioStreamMicrophone AudioStreamPlayback AudioStreamPlaybackPolyphonic AudioStreamPlaybackResampled AudioStreamPlayer AudioStreamPlayer2D AudioStreamPlayer3D AudioStreamPolyphonic AudioStreamRandomizer AudioStreamWAV BackBufferCopy BaseButton BaseMaterial3D Basis BitMap Bone2D BoneAttachment3D BoneMap BoxContainer BoxMesh BoxOccluder3D BoxShape3D Button ButtonGroup CPUParticles2D CPUParticles3D Callable CallbackTweener Camera2D Camera3D CameraAttributes CameraAttributesPhysical CameraAttributesPractical CameraFeed CameraServer CameraTexture CanvasGroup CanvasItem CanvasItemMaterial CanvasLayer CanvasModulate CanvasTexture CapsuleMesh CapsuleShape2D CapsuleShape3D CenterContainer CharFXTransform CharacterBody2D CharacterBody3D CheckBox CheckButton CircleShape2D ClassDB CodeEdit CodeHighlighter CollisionObject2D CollisionObject3D CollisionPolygon2D CollisionPolygon3D CollisionShape2D CollisionShape3D Color ColorPicker ColorPickerButton ColorRect CompressedCubemap CompressedCubemapArray CompressedTexture2D CompressedTexture2DArray CompressedTexture3D CompressedTextureLayered ConcavePolygonShape2D ConcavePolygonShape3D ConeTwistJoint3D ConfigFile ConfirmationDialog Container Control ConvexPolygonShape2D ConvexPolygonShape3D Crypto CryptoKey Cubemap CubemapArray Curve Curve2D Curve3D CurveTexture CurveXYZTexture CylinderMesh CylinderShape3D DTLSServer DampedSpringJoint2D Decal Dictionary DirAccess DirectionalLight2D DirectionalLight3D DisplayServer EditorCommandPalette EditorDebuggerPlugin EditorDebuggerSession EditorExportPlatform EditorExportPlugin EditorFeatureProfile EditorFileDialog EditorFileSystem EditorFileSystemDirectory EditorFileSystemImportFormatSupportQuery EditorImportPlugin EditorInspector EditorInspectorPlugin EditorInterface EditorNode3DGizmo EditorNode3DGizmoPlugin EditorPaths EditorPlugin EditorProperty EditorResourceConversionPlugin EditorResourcePicker EditorResourcePreview EditorResourcePreviewGenerator EditorSceneFormatImporter EditorScenePostImport EditorScenePostImportPlugin EditorScript EditorScriptPicker EditorSelection EditorSettings EditorSpinSlider EditorSyntaxHighlighter EditorTranslationParserPlugin EditorUndoRedoManager EditorVCSInterface EncodedObjectAsID Engine EngineDebugger EngineProfiler Environment Expression FileAccess FileDialog FileSystemDock FlowContainer FogMaterial FogVolume Font FontFile FontVariation GDExtension GDExtensionManager GPUParticles2D GPUParticles3D GPUParticlesAttractor3D GPUParticlesAttractorBox3D GPUParticlesAttractorSphere3D GPUParticlesAttractorVectorField3D GPUParticlesCollision3D GPUParticlesCollisionBox3D GPUParticlesCollisionHeightField3D GPUParticlesCollisionSDF3D GPUParticlesCollisionSphere3D Generic6DOFJoint3D Geometry2D Geometry3D GeometryInstance3D Gradient GradientTexture1D GradientTexture2D GraphEdit GraphNode GridContainer GrooveJoint2D HBoxContainer HFlowContainer HMACContext HScrollBar HSeparator HSlider HSplitContainer HTTPClient HTTPRequest HashingContext HeightMapShape3D HingeJoint3D IP Image ImageFormatLoader ImageFormatLoaderExtension ImageTexture ImageTexture3D ImageTextureLayered ImmediateMesh ImporterMesh ImporterMeshInstance3D Input InputEvent InputEventAction InputEventFromWindow InputEventGesture InputEventJoypadButton InputEventJoypadMotion InputEventKey InputEventMIDI InputEventMagnifyGesture InputEventMouse InputEventMouseButton InputEventMouseMotion InputEventPanGesture InputEventScreenDrag InputEventScreenTouch InputEventShortcut InputEventWithModifiers InputMap InstancePlaceholder IntervalTweener ItemList JNISingleton JSON JSONRPC JavaClass JavaClassWrapper JavaScriptBridge JavaScriptObject Joint2D Joint3D KinematicCollision2D KinematicCollision3D Label Label3D LabelSettings Light2D Light3D LightOccluder2D LightmapGI LightmapGIData LightmapProbe Lightmapper LightmapperRD Line2D LineEdit LinkButton MainLoop MarginContainer Marker2D Marker3D Marshalls Material MenuBar MenuButton Mesh MeshDataTool MeshInstance2D MeshInstance3D MeshLibrary MeshTexture MethodTweener MissingNode MissingResource MovieWriter MultiMesh MultiMeshInstance2D MultiMeshInstance3D MultiplayerAPI MultiplayerAPIExtension MultiplayerPeer MultiplayerPeerExtension Mutex NavigationAgent2D NavigationAgent3D NavigationLink2D NavigationLink3D NavigationMesh NavigationMeshGenerator NavigationObstacle2D NavigationObstacle3D NavigationPathQueryParameters2D NavigationPathQueryParameters3D NavigationPathQueryResult2D NavigationPathQueryResult3D NavigationPolygon NavigationRegion2D NavigationRegion3D NavigationServer2D NavigationServer3D NinePatchRect Node Node2D Node3D Node3DGizmo NodePath ORMMaterial3D OS Object Occluder3D OccluderInstance3D OccluderPolygon2D OfflineMultiplayerPeer OmniLight3D OptimizedTranslation OptionButton PCKPacker PackedByteArray PackedColorArray PackedDataContainer PackedDataContainerRef PackedFloat32Array PackedFloat64Array PackedInt32Array PackedInt64Array PackedScene PackedStringArray PackedVector2Array PackedVector3Array PacketPeer PacketPeerDTLS PacketPeerExtension PacketPeerStream PacketPeerUDP Panel PanelContainer PanoramaSkyMaterial ParallaxBackground ParallaxLayer ParticleProcessMaterial Path2D Path3D PathFollow2D PathFollow3D Performance PhysicalBone2D PhysicalBone3D PhysicalSkyMaterial PhysicsBody2D PhysicsBody3D PhysicsDirectBodyState2D PhysicsDirectBodyState2DExtension PhysicsDirectBodyState3D PhysicsDirectBodyState3DExtension PhysicsDirectSpaceState2D PhysicsDirectSpaceState2DExtension PhysicsDirectSpaceState3D PhysicsDirectSpaceState3DExtension PhysicsMaterial PhysicsPointQueryParameters2D PhysicsPointQueryParameters3D PhysicsRayQueryParameters2D PhysicsRayQueryParameters3D PhysicsServer2D PhysicsServer2DExtension PhysicsServer2DManager PhysicsServer3D PhysicsServer3DExtension PhysicsServer3DManager PhysicsServer3DRenderingServerHandler PhysicsShapeQueryParameters2D PhysicsShapeQueryParameters3D PhysicsTestMotionParameters2D PhysicsTestMotionParameters3D PhysicsTestMotionResult2D PhysicsTestMotionResult3D PinJoint2D PinJoint3D PlaceholderCubemap PlaceholderCubemapArray PlaceholderMaterial PlaceholderMesh PlaceholderTexture2D PlaceholderTexture2DArray PlaceholderTexture3D PlaceholderTextureLayered Plane PlaneMesh PointLight2D PointMesh Polygon2D PolygonOccluder3D PolygonPathFinder Popup PopupMenu PopupPanel PortableCompressedTexture2D PrimitiveMesh PrismMesh ProceduralSkyMaterial ProgressBar ProjectSettings Projection PropertyTweener QuadMesh QuadOccluder3D Quaternion RDAttachmentFormat RDFramebufferPass RDPipelineColorBlendState RDPipelineColorBlendStateAttachment RDPipelineDepthStencilState RDPipelineMultisampleState RDPipelineRasterizationState RDPipelineSpecializationConstant RDSamplerState RDShaderFile RDShaderSPIRV RDShaderSource RDTextureFormat RDTextureView RDUniform RDVertexAttribute RID RandomNumberGenerator Range RayCast2D RayCast3D Rect2 Rect2i RectangleShape2D RefCounted ReferenceRect ReflectionProbe RemoteTransform2D RemoteTransform3D RenderingDevice RenderingServer Resource ResourceFormatLoader ResourceFormatSaver ResourceImporter ResourceLoader ResourcePreloader ResourceSaver ResourceUID RibbonTrailMesh RichTextEffect RichTextLabel RigidBody2D RigidBody3D RootMotionView SceneState SceneTree SceneTreeTimer Script ScriptCreateDialog ScriptEditor ScriptEditorBase ScriptExtension ScriptLanguage ScriptLanguageExtension ScrollBar ScrollContainer SegmentShape2D Semaphore SeparationRayShape2D SeparationRayShape3D Separator Shader ShaderGlobalsOverride ShaderInclude ShaderMaterial Shape2D Shape3D ShapeCast2D ShapeCast3D Shortcut Signal Skeleton2D Skeleton3D SkeletonIK3D SkeletonModification2D SkeletonModification2DCCDIK SkeletonModification2DFABRIK SkeletonModification2DJiggle SkeletonModification2DLookAt SkeletonModification2DPhysicalBones SkeletonModification2DStackHolder SkeletonModification2DTwoBoneIK SkeletonModificationStack2D SkeletonProfile SkeletonProfileHumanoid Skin SkinReference Sky Slider SliderJoint3D SoftBody3D SphereMesh SphereOccluder3D SphereShape3D SpinBox SplitContainer SpotLight3D SpringArm3D Sprite2D Sprite3D SpriteBase3D SpriteFrames StandardMaterial3D StaticBody2D StaticBody3D StreamPeer StreamPeerBuffer StreamPeerExtension StreamPeerGZIP StreamPeerTCP StreamPeerTLS String StringName StyleBox StyleBoxEmpty StyleBoxFlat StyleBoxLine StyleBoxTexture SubViewport SubViewportContainer SurfaceTool SyntaxHighlighter SystemFont TCPServer TLSOptions TabBar TabContainer TextEdit TextLine TextMesh TextParagraph TextServer TextServerDummy TextServerExtension TextServerManager Texture Texture2D Texture2DArray Texture3D TextureButton TextureLayered TextureProgressBar TextureRect Theme ThemeDB Thread TileData TileMap TileMapPattern TileSet TileSetAtlasSource TileSetScenesCollectionSource TileSetSource Time Timer TorusMesh TouchScreenButton Transform2D Transform3D Translation TranslationServer Tree TreeItem TriangleMesh TubeTrailMesh Tween Tweener UDPServer UndoRedo VBoxContainer VFlowContainer VScrollBar VSeparator VSlider VSplitContainer Variant Vector2 Vector2i Vector3 Vector3i Vector4 Vector4i VehicleBody3D VehicleWheel3D VideoStream VideoStreamPlayback VideoStreamPlayer Viewport ViewportTexture VisibleOnScreenEnabler2D VisibleOnScreenEnabler3D VisibleOnScreenNotifier2D VisibleOnScreenNotifier3D VisualInstance3D VisualShader VisualShaderNode VisualShaderNodeBillboard VisualShaderNodeBooleanConstant VisualShaderNodeBooleanParameter VisualShaderNodeClamp VisualShaderNodeColorConstant VisualShaderNodeColorFunc VisualShaderNodeColorOp VisualShaderNodeColorParameter VisualShaderNodeComment VisualShaderNodeCompare VisualShaderNodeConstant VisualShaderNodeCubemap VisualShaderNodeCubemapParameter VisualShaderNodeCurveTexture VisualShaderNodeCurveXYZTexture VisualShaderNodeCustom VisualShaderNodeDerivativeFunc VisualShaderNodeDeterminant VisualShaderNodeDistanceFade VisualShaderNodeDotProduct VisualShaderNodeExpression VisualShaderNodeFaceForward VisualShaderNodeFloatConstant VisualShaderNodeFloatFunc VisualShaderNodeFloatOp VisualShaderNodeFloatParameter VisualShaderNodeFresnel VisualShaderNodeGlobalExpression VisualShaderNodeGroupBase VisualShaderNodeIf VisualShaderNodeInput VisualShaderNodeIntConstant VisualShaderNodeIntFunc VisualShaderNodeIntOp VisualShaderNodeIntParameter VisualShaderNodeIs VisualShaderNodeLinearSceneDepth VisualShaderNodeMix VisualShaderNodeMultiplyAdd VisualShaderNodeOuterProduct VisualShaderNodeOutput VisualShaderNodeParameter VisualShaderNodeParameterRef VisualShaderNodeParticleAccelerator VisualShaderNodeParticleBoxEmitter VisualShaderNodeParticleConeVelocity VisualShaderNodeParticleEmit VisualShaderNodeParticleEmitter VisualShaderNodeParticleMeshEmitter VisualShaderNodeParticleMultiplyByAxisAngle VisualShaderNodeParticleOutput VisualShaderNodeParticleRandomness VisualShaderNodeParticleRingEmitter VisualShaderNodeParticleSphereEmitter VisualShaderNodeProximityFade VisualShaderNodeRandomRange VisualShaderNodeRemap VisualShaderNodeResizableBase VisualShaderNodeSDFRaymarch VisualShaderNodeSDFToScreenUV VisualShaderNodeSample3D VisualShaderNodeScreenUVToSDF VisualShaderNodeSmoothStep VisualShaderNodeStep VisualShaderNodeSwitch VisualShaderNodeTexture VisualShaderNodeTexture2DArray VisualShaderNodeTexture2DArrayParameter VisualShaderNodeTexture2DParameter VisualShaderNodeTexture3D VisualShaderNodeTexture3DParameter VisualShaderNodeTextureParameter VisualShaderNodeTextureParameterTriplanar VisualShaderNodeTextureSDF VisualShaderNodeTextureSDFNormal VisualShaderNodeTransformCompose VisualShaderNodeTransformConstant VisualShaderNodeTransformDecompose VisualShaderNodeTransformFunc VisualShaderNodeTransformOp VisualShaderNodeTransformParameter VisualShaderNodeTransformVecMult VisualShaderNodeUIntConstant VisualShaderNodeUIntFunc VisualShaderNodeUIntOp VisualShaderNodeUIntParameter VisualShaderNodeUVFunc VisualShaderNodeUVPolarCoord VisualShaderNodeVarying VisualShaderNodeVaryingGetter VisualShaderNodeVaryingSetter VisualShaderNodeVec2Constant VisualShaderNodeVec2Parameter VisualShaderNodeVec3Constant VisualShaderNodeVec3Parameter VisualShaderNodeVec4Constant VisualShaderNodeVec4Parameter VisualShaderNodeVectorBase VisualShaderNodeVectorCompose VisualShaderNodeVectorDecompose VisualShaderNodeVectorDistance VisualShaderNodeVectorFunc VisualShaderNodeVectorLen VisualShaderNodeVectorOp VisualShaderNodeVectorRefract VoxelGI VoxelGIData WeakRef Window WorkerThreadPool World2D World3D WorldBoundaryShape2D WorldBoundaryShape3D WorldEnvironment X509Certificate XMLParser XRAnchor3D XRCamera3D XRController3D XRInterface XRInterfaceExtension XRNode3D XROrigin3D XRPose XRPositionalTracker XRServer bool float int"
+
+ builtin_methods="abs absf absi acos asin atan atan2 bezier_derivative bezier_interpolate bytes_to_var bytes_to_var_with_objects ceil ceilf ceili clamp clampf clampi cos cosh cubic_interpolate cubic_interpolate_angle cubic_interpolate_angle_in_time cubic_interpolate_in_time db_to_linear deg_to_rad ease error_string exp floor floorf floori fmod fposmod hash instance_from_id inverse_lerp is_equal_approx is_finite is_inf is_instance_id_valid is_instance_valid is_nan is_same is_zero_approx lerp lerp_angle lerpf linear_to_db log max maxf maxi min minf mini move_toward nearest_po2 pingpong posmod pow print print_rich print_verbose printerr printraw prints printt push_error push_warning rad_to_deg rand_from_seed randf randf_range randfn randi randi_range randomize remap rid_allocate_id rid_from_int64 round roundf roundi seed sign signf signi sin sinh smoothstep snapped snappedf snappedi sqrt step_decimals str str_to_var tan tanh typeof var_to_bytes var_to_bytes_with_objects var_to_str weakref wrap wrapf wrapi"
+
+ gdscript_methods="Color8 assert char convert dict_to_inst get_stack inst_to_dict is_instance_of len load preload print_debug print_stack range type_exists"
+
+ gdscript_constants="PI TAU INF NAN"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list gdscript_static_words $(join "${keywords} ${values} ${builtin_classes} ${builtin_methods} ${gdscript_methods} ${gdscript_constants}" ' ')"
+
+ printf %s "
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${keywords}" '|'))\b' 0:keyword
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${values}" '|'))\b' 0:value
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${builtin_classes}" '|'))\b' 0:type
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${builtin_methods}" '|'))\b\(' 1:builtin
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${gdscript_methods}" '|'))\b\(' 1:builtin
+ add-highlighter shared/gdscript/code/ regex '\b($(join "${gdscript_constants}" '|'))\b' 0:keyword
+ "
+}
+# nodes
+add-highlighter shared/gdscript/code/ regex \$[\w/]+\b 0:module
+add-highlighter shared/gdscript/code/ regex \%\w+(?!/)\b 0:string
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden gdscript-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*#\h* <ret> y jgh P }
+ }
+}
+
+define-command -hidden gdscript-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with :
+ try %{ execute-keys -draft , k x <a-k> :$ <ret> <a-K> ^\h*# <ret> j <a-gt> }
+ # deindent closing brace/bracket when after cursor (for arrays and dictionaries)
+ try %< execute-keys -draft x <a-k> ^\h*[}\]] <ret> gh / [}\]] <ret> m <a-S> 1<a-&> >
+ >
+>
+
diff --git a/autoload/filetype/gentoo-linux.kak b/autoload/filetype/gentoo-linux.kak
new file mode 100644
index 0000000..251dad1
--- /dev/null
+++ b/autoload/filetype/gentoo-linux.kak
@@ -0,0 +1,4 @@
+# portage ebuild file
+hook global BufCreate .*\.ebuild %{
+ set-option buffer filetype sh
+}
diff --git a/autoload/filetype/git.kak b/autoload/filetype/git.kak
new file mode 100644
index 0000000..4501162
--- /dev/null
+++ b/autoload/filetype/git.kak
@@ -0,0 +1,69 @@
+hook global BufCreate .*(COMMIT_EDITMSG|MERGE_MSG) %{
+ set-option buffer filetype git-commit
+}
+
+hook global BufCreate .*/NOTES_EDITMSG %{
+ set-option buffer filetype git-notes
+}
+
+hook global BufCreate .*(\.git(config|modules)|git/config) %{
+ set-option buffer filetype ini
+}
+
+hook global BufCreate .*\.gitignore %{
+ set-option buffer filetype git-ignore
+}
+
+hook global BufCreate .*git-rebase-todo %{
+ set-option buffer filetype git-rebase
+}
+
+hook global WinSetOption filetype=git-(commit|ignore|notes|rebase) %{
+ require-module "git-%val{hook_param_capture_1}"
+}
+
+hook -group git-commit-highlight global WinSetOption filetype=git-commit %{
+ add-highlighter window/git-commit ref git-commit
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-commit }
+}
+
+hook -group git-ignore-highlight global WinSetOption filetype=git-ignore %{
+ add-highlighter window/git-ignore ref git-ignore
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-ignore }
+}
+
+hook -group git-notes-highlight global WinSetOption filetype=git-notes %{
+ add-highlighter window/git-notes ref git-notes
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-notes }
+}
+
+hook -group git-rebase-highlight global WinSetOption filetype=git-rebase %{
+ add-highlighter window/git-rebase ref git-rebase
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-rebase }
+}
+
+provide-module git-commit %{
+require-module diff
+add-highlighter shared/git-commit regions
+add-highlighter shared/git-commit/diff region '^diff --git' '^(?=diff --git)' ref diff # highlight potential diffs from the -v option
+add-highlighter shared/git-commit/comments region ^# $ group
+add-highlighter shared/git-commit/comments/ fill comment
+add-highlighter shared/git-commit/comments/ regex "\b(?:(modified)|(deleted)|(new file)|(renamed|copied)):([^\n]*)$" 1:yellow 2:red 3:green 4:blue 5:magenta
+}
+
+provide-module git-ignore %{
+add-highlighter shared/git-ignore group
+add-highlighter shared/git-ignore/glob regex '(?<!\\)(?:\\\\)*\K(\*\*?|\?|\[.*?(?<!\\)(?:\\\\)*\])' 0:operator
+add-highlighter shared/git-ignore/negate regex '^!' 0:operator
+add-highlighter shared/git-ignore/comments regex '^#.*?$' 0:comment
+}
+
+provide-module git-notes %{
+add-highlighter shared/git-notes regex ^#[^\n]*$ 0:comment
+}
+
+provide-module git-rebase %{
+add-highlighter shared/git-rebase group
+add-highlighter shared/git-rebase/ regex "^\h*#[^\n]*\n" 0:comment
+add-highlighter shared/git-rebase/ regex "^(?:(pick|p)|(edit|reword|squash|fixup|exec|break|drop|label|reset|merge|[ersfxbdltm])) (\w+)" 1:keyword 2:value 3:meta
+}
diff --git a/autoload/filetype/github.kak b/autoload/filetype/github.kak
new file mode 100644
index 0000000..17e593a
--- /dev/null
+++ b/autoload/filetype/github.kak
@@ -0,0 +1,18 @@
+hook global BufCreate .*/CODEOWNERS %{
+ set-option buffer filetype codeowners
+}
+
+hook global WinSetOption filetype=codeowners %{
+ require-module codeowners
+}
+
+hook -group codeowners-hightlight global WinSetOption filetype=codeowners %{
+ add-highlighter window/codeowners ref codeowners
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/codeowners }
+}
+
+provide-module codeowners %{
+add-highlighter shared/codeowners regions
+add-highlighter shared/codeowners/comments region ^# $ group
+add-highlighter shared/codeowners/comments/ fill comment
+}
diff --git a/autoload/filetype/gluon.kak b/autoload/filetype/gluon.kak
new file mode 100644
index 0000000..2c4b96b
--- /dev/null
+++ b/autoload/filetype/gluon.kak
@@ -0,0 +1,103 @@
+# http://gluon-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](glu) %{
+ set-option buffer filetype gluon
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=gluon %{
+ require-module gluon
+
+ set-option window extra_word_chars '_' "'"
+ hook window ModeChange pop:insert:.* -group gluon-trim-indent gluon-trim-indent
+ hook window InsertChar \n -group gluon-insert gluon-insert-on-new-line
+ hook window InsertChar \n -group gluon-indent gluon-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window gluon-.+
+ }
+}
+
+hook -group gluon-highlight global WinSetOption filetype=gluon %{
+ add-highlighter window/gluon ref gluon
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-highlighter window/gluon
+ }
+}
+
+
+provide-module gluon %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/gluon regions
+add-highlighter shared/gluon/code default-region group
+add-highlighter shared/gluon/string region (?<!')" (?<!\\)(\\\\)*" fill string
+add-highlighter shared/gluon/raw_string region -match-capture \br(#+)" \"(#+) fill string
+add-highlighter shared/gluon/comment region /\* \*/ fill comment
+add-highlighter shared/gluon/line_comment region // $ fill comment
+add-highlighter shared/gluon/attribute region -recurse \[ '#\[' \] fill meta
+# balance out bracket ]
+
+# matches hexadecimal literals
+add-highlighter shared/gluon/code/ regex \b0x+[A-Fa-f0-9]+ 0:value
+# matches decimal and floating-point literals
+add-highlighter shared/gluon/code/ regex \b\d+([.]\d+)? 0:value
+
+# matches keywords
+add-highlighter shared/gluon/code/ regex \
+ (?<!')\b(type|if|then|else|match|with|let|rec|do|seq|in)\b(?!') 0:keyword
+
+# matches macros
+add-highlighter shared/gluon/code/ regex \b\w+! 0:meta
+
+# matches uppercase identifiers: Monad Some
+add-highlighter shared/gluon/code/ regex \b[A-Z][\w']* 0:variable
+
+# matches operators: ... > < <= ^ <*> <$> etc
+# matches dot: .
+# matches keywords: @ : ->
+add-highlighter shared/gluon/code/ regex (?<![~<=>|:!?/.@$*&#%+\^\-\\])[~<=>|:!?/.@$*&#%+\^\-\\]+ 0:operator
+
+# matches 'x' '\\' '\'' '\n' '\0'
+# not incomplete literals: '\'
+add-highlighter shared/gluon/code/ regex \B'([^\\]|[\\]['"\w\d\\])' 0:string
+# this has to come after operators so '-' etc is correct
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden gluon-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden gluon-insert-on-new-line %~
+ evaluate-commands -draft -itersel %_
+ # copy // and /// comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K///?\h* <ret> y gh j P }
+ _
+~
+
+define-command -hidden gluon-indent-on-new-line %~
+ evaluate-commands -draft -itersel %_
+ # preserve previous line indent
+ try %{ execute-keys -draft \; K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : gluon-trim-indent <ret> }
+ # indent after lines ending with (open) braces, =, ->, condition, rec,
+ # or in
+ try %{ execute-keys -draft \; k x <a-k> (\(|\{|\[|=|->|\b(?:then|else|rec|in))$ <ret> j <a-gt> }
+ # deindent closing brace(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[})\]] <ret> gh / \})\]] <ret> m <a-S> 1<a-&> >
+ _
+~
+
diff --git a/autoload/filetype/go.kak b/autoload/filetype/go.kak
new file mode 100644
index 0000000..4f8407c
--- /dev/null
+++ b/autoload/filetype/go.kak
@@ -0,0 +1,174 @@
+# https://golang.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.go %{
+ set-option buffer filetype go
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=go %{
+ require-module go
+
+ set-option window static_words %opt{go_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group go-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group go-indent go-indent-on-new-line
+ hook window InsertChar \{ -group go-indent go-indent-on-opening-curly-brace
+ hook window InsertChar \} -group go-indent go-indent-on-closing-curly-brace
+ hook window InsertChar \n -group go-comment-insert go-insert-comment-on-new-line
+ hook window InsertChar \n -group go-closing-delimiter-insert go-insert-closing-delimiter-on-new-line
+
+ alias window alt go-alternative-file
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window go-.+
+ unalias window alt go-alternative-file
+ }
+}
+
+hook -group go-highlight global WinSetOption filetype=go %{
+ add-highlighter window/go ref go
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/go }
+}
+
+provide-module go %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/go regions
+add-highlighter shared/go/code default-region group
+add-highlighter shared/go/back_string region '`' '`' fill string
+add-highlighter shared/go/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/go/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/go/comment region /\* \*/ fill comment
+add-highlighter shared/go/comment_line region '//' $ fill comment
+
+add-highlighter shared/go/code/numeric regex %{-?([0-9]*\.(?!0[xX]))?\b([0-9]+|0[xX][0-9a-fA-F]+)\.?([eE][+-]?[0-9]+)?i?\b} 0:value
+add-highlighter shared/go/code/function regex "\b(\w*)\b\h*(?:\[[\w\s\.,]*\])?\h*\(" 1:function
+add-highlighter shared/go/code/operator regex "(\+|-|\*|/|%|\+\+|--|\+=|-=|\*=|/=|%=|==|!=|>|<|>=|<=|&|&&|\|\||!|<-|:=|\.\.\.)" 1:operator
+
+evaluate-commands %sh{
+ # Grammar
+ keywords='break default func interface select case defer go map struct
+ chan else goto package switch const fallthrough if range type
+ continue for import return var'
+ types='any bool byte chan comparable complex128 complex64 error float32 float64 int int16 int32
+ int64 int8 interface intptr map rune string struct uint uint16 uint32 uint64 uint8 uintptr'
+ values='false true nil iota'
+ functions='append cap close complex copy delete imag len make new panic print println real recover'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list go_static_words $(join "${keywords} ${attributes} ${types} ${values} ${functions}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/go/code/keyword regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/go/code/attribute regex \b($(join "${attributes}" '|'))\b 0:attribute
+ add-highlighter shared/go/code/type regex \b($(join "${types}" '|'))\b 0:type
+ add-highlighter shared/go/code/value regex \b($(join "${values}" '|'))\b 0:value
+ add-highlighter shared/go/code/builtin regex \b($(join "${functions}" '|'))\b 0:builtin
+ "
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command go-alternative-file -docstring 'Jump to the alternate file (implementation ↔ test)' %{ evaluate-commands %sh{
+ case $kak_buffile in
+ *_test.go)
+ altfile=${kak_buffile%_test.go}.go
+ test ! -f "$altfile" && echo "fail 'implementation file not found'" && exit
+ ;;
+ *.go)
+ altfile=${kak_buffile%.go}_test.go
+ test ! -f "$altfile" && echo "fail 'test file not found'" && exit
+ ;;
+ *)
+ echo "fail 'alternative file not found'" && exit
+ ;;
+ esac
+ printf "edit -- '%s'" "$(printf %s "$altfile" | sed "s/'/''/g")"
+}}
+
+define-command -hidden go-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ try %<
+ try %{ # line comment
+ execute-keys -draft kx s ^\h*// <ret>
+ } catch %{ # block comment
+ execute-keys -draft <a-?> /\* <ret> <a-K>\*/<ret>
+ }
+ > catch %<
+ # indent after lines with an unclosed { or (
+ try %< execute-keys -draft [c[({],[)}] <ret> <a-k> \A[({][^\n]*\n[^\n]*\n?\z <ret> j<a-gt> >
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ >
+ =
+~
+
+define-command -hidden go-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden go-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+define-command -hidden go-insert-comment-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+ ]
+]
+
+define-command -hidden go-insert-closing-delimiter-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # Wisely add '}'.
+ evaluate-commands -save-regs x %[
+ # Save previous line indent in register x.
+ try %[ execute-keys -draft kxs^\h+<ret>"xy ] catch %[ reg x '' ]
+ try %[
+ # Validate previous line and that it is not closed yet.
+ execute-keys -draft kx <a-k>^<c-r>x.*\{\h*\(?\h*$<ret> j}iJx <a-K>^<c-r>x\)?\h*\}<ret>
+ # Insert closing '}'.
+ execute-keys -draft o<c-r>x}<esc>
+ # Delete trailing '}' on the line below the '{'.
+ execute-keys -draft xs\}$<ret>d
+ ]
+ ]
+
+ # Wisely add ')'.
+ evaluate-commands -save-regs x %[
+ # Save previous line indent in register x.
+ try %[ execute-keys -draft kxs^\h+<ret>"xy ] catch %[ reg x '' ]
+ try %[
+ # Validate previous line and that it is not closed yet.
+ execute-keys -draft kx <a-k>^<c-r>x.*\(\h*$<ret> J}iJx <a-K>^<c-r>x\)<ret>
+ # Insert closing ')'.
+ execute-keys -draft o<c-r>x)<esc>
+ # Delete trailing ')' on the line below the '('.
+ execute-keys -draft xs\)\h*\}?\h*$<ret>d
+ ]
+ ]
+ ]
+]
+
diff --git a/autoload/filetype/graphql.kak b/autoload/filetype/graphql.kak
new file mode 100644
index 0000000..85bf71d
--- /dev/null
+++ b/autoload/filetype/graphql.kak
@@ -0,0 +1,100 @@
+# http://graphql.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](graphqls?) %{
+ set-option buffer filetype graphql
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=graphql %{
+ require-module graphql
+
+ hook window ModeChange pop:insert:.* -group graphql-trim-indent graphql-trim-indent
+ hook window InsertChar .* -group graphql-indent graphql-indent-on-char
+ hook window InsertChar \n -group graphql-indent graphql-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window graphql-.+ }
+}
+
+hook -group graphql-highlight global WinSetOption filetype=graphql %{
+ add-highlighter window/graphql ref graphql
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/graphql }
+}
+
+
+provide-module graphql %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/graphql regions
+add-highlighter shared/graphql/code default-region group
+add-highlighter shared/graphql/line-description region '#' '\n' fill comment
+add-highlighter shared/graphql/block-description region '"""' '"""' fill comment
+add-highlighter shared/graphql/description region '"' '"\s*\n' fill comment
+add-highlighter shared/graphql/object region -recurse \{ [{] [}] regions
+
+# Objects
+add-highlighter shared/graphql/object/line-description region '#' '\n' fill comment
+add-highlighter shared/graphql/object/block-description region '"""' '"""' fill comment
+add-highlighter shared/graphql/object/field default-region group
+add-highlighter shared/graphql/object/field/ regex ([A-Za-z][A-Za-z0-9_-]*)(?:\([^)]*\))?\h*[:{] 1:attribute
+add-highlighter shared/graphql/object/field/ regex ^\h*([A-Za-z][A-Za-z0-9_-]*)\h*$ 1:attribute
+
+# Values
+add-highlighter shared/graphql/object/field/values regex \b(true|false|null|\d+(?:\.\d+)?(?:[eE][+-]?\d*)?)\b 0:value
+add-highlighter shared/graphql/object/field/variables regex \$[a-zA-Z0-9]+\b 0:variable
+# add-highlighter shared/graphql/object/field/string regex '"([^"]|\\")*"' 0:string
+add-highlighter shared/graphql/object/field/string regex '"(?:[^"\\]|\\.)*"' 0:string
+
+# Meta
+add-highlighter shared/graphql/object/field/directives regex @(?:include|skip) 0:meta
+
+# Attributes
+add-highlighter shared/graphql/object/field/required regex '(?<=[\w\]])(?<bang>!)' bang:operator
+add-highlighter shared/graphql/object/field/assignment regex '=' 0:operator
+
+# Keywords
+add-highlighter shared/graphql/code/top-level regex '\bschema\b' 0:keyword
+add-highlighter shared/graphql/code/keywords regex '\b(?<name>enum|fragment|input|implements|interface|mutation|on|query|scalar|subscription|type|union)\h+(?:[A-Za-z]\w*)' name:keyword
+
+# Types
+add-highlighter shared/graphql/object/field/scalars regex \b(Boolean|Float|ID|Int|String)\b 0:type
+
+# Operators
+add-highlighter shared/graphql/object/field/expand-fragment regex '\.\.\.(?=\w)' 0:operator
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden graphql-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden graphql-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h+[\]}]$ <ret> m <a-S> 1<a-&> >
+ >
+>
+
+define-command -hidden graphql-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : graphql-trim-indent <ret> }
+ # indent after lines ending with opener token
+ try %< execute-keys -draft k x <a-k> [[{]\h*$ <ret> j <a-gt> >
+ # deindent closer token(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[}\]] <ret> gh / [}\]] <ret> m <a-S> 1<a-&> >
+ >
+>
+
diff --git a/autoload/filetype/gren.kak b/autoload/filetype/gren.kak
new file mode 100644
index 0000000..d61e59d
--- /dev/null
+++ b/autoload/filetype/gren.kak
@@ -0,0 +1,7 @@
+# http://gren-lang.org/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](gren) %{
+ set-option buffer filetype elm
+}
+
diff --git a/autoload/filetype/groovy.kak b/autoload/filetype/groovy.kak
new file mode 100644
index 0000000..b6b7305
--- /dev/null
+++ b/autoload/filetype/groovy.kak
@@ -0,0 +1,92 @@
+# Adapted from the file created by Daniel Lewan TeddyDD
+
+hook global BufCreate "(.+\.(groovy|gvy|gy|gsh|gradle))|.+[Jj]enkinsfile.*" %{
+ set-option buffer filetype groovy
+}
+
+hook global WinSetOption filetype=groovy %{
+ require-module groovy
+
+ set-option window static_words %opt{groovy_static_words}
+
+ hook window ModeChange pop:insert:.* -group groovy-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group groovy-insert groovy-insert-on-new-line
+ hook window InsertChar \n -group groovy-indent groovy-indent-on-new-line
+ hook window InsertChar \{ -group groovy-indent groovy-indent-on-opening-curly-brace
+ hook window InsertChar \} -group groovy-indent groovy-indent-on-closing-curly-brace
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window groovy-.+ }
+}
+
+hook -group groovy-highlight global WinSetOption filetype=groovy %{
+ add-highlighter window/groovy ref groovy
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/groovy }
+}
+
+provide-module groovy %§
+
+add-highlighter shared/groovy regions
+
+add-highlighter shared/groovy/code default-region group
+add-highlighter shared/groovy/triple_quote region '"{3}' (?<!\\)(\\\\)*"{3} fill string
+add-highlighter shared/groovy/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/groovy/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/groovy/comment1 region '/\*[^*]?' '\*/' fill comment
+add-highlighter shared/groovy/comment2 region '//[^/]?' $ fill comment
+add-highlighter shared/groovy/shellbang region '#!.+' $ fill comment
+add-highlighter shared/groovy/dollar_string region "\$/" "(?<!\$)/\$" fill string
+# add-highlighter shared/groovy/code/identifiers regex '\b[$_]?[a-zA-Z0-9_]+\b' 0:variable
+add-highlighter shared/groovy/code/declaration regex "(?<typ>\w+)(?:\[.*?\])?\s+(\$?\w+)\s=" typ:type
+add-highlighter shared/groovy/code/numbers regex '\b[-+]?0x[A-Fa-f0-9_]+[.A-Fa-f0-9_p]*[lLiDgGIF]?|\b[-+]?[\d]+b?[.p_\dEe]*[lLiDgGIF]?' 0:value
+add-highlighter shared/groovy/slashy_string region "\b/\w" "(?<!\\)\w/\b" fill string
+
+evaluate-commands %sh{
+ keywords="as|assert|break|case|catch|class|const|continue|def|default"
+ keywords="${keywords}|do|else|enum|extends|finally|for|goto|if|implements|import|in"
+ keywords="${keywords}|instanceof|interface|new|package|return|super|switch|this|throw"
+ keywords="${keywords}|throws|trait|try|while"
+ builtins="true|false|null"
+ types="byte|char|short|int|long|BigInteger|float|double|BigDecimal|boolean"
+
+ printf %s "
+ add-highlighter shared/groovy/code/keyword regex \b(${keywords})\b 0:keyword
+ add-highlighter shared/groovy/code/builtin regex \b(${builtins})\b 0:builtin
+ add-highlighter shared/groovy/code/types regex \b(${types})\b 0:type
+
+ declare-option str-list groovy_static_words \"${keywords}|${types}|${builtins}\"
+ "
+}
+
+define-command -hidden groovy-insert-on-new-line %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+]
+
+define-command -hidden groovy-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after keywords
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for|try|catch)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-space><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ =
+~
+
+define-command -hidden groovy-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden groovy-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
diff --git a/autoload/filetype/haml.kak b/autoload/filetype/haml.kak
new file mode 100644
index 0000000..492e78a
--- /dev/null
+++ b/autoload/filetype/haml.kak
@@ -0,0 +1,78 @@
+# http://haml.info
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](haml) %{
+ set-option buffer filetype haml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=haml %{
+ require-module haml
+
+ hook window ModeChange pop:insert:.* -group haml-trim-indent haml-trim-indent
+ hook window InsertChar \n -group haml-insert haml-insert-on-new-line
+ hook window InsertChar \n -group haml-indent haml-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window haml-.+ }
+}
+
+hook -group haml-highlight global WinSetOption filetype=haml %{
+ add-highlighter window/haml ref haml
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/haml }
+}
+
+
+provide-module haml %[
+require-module ruby
+require-module coffee
+require-module sass
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/haml regions
+add-highlighter shared/haml/code default-region group
+add-highlighter shared/haml/comment region ^\h*/ $ fill comment
+
+# Filters
+# http://haml.info/docs/yardoc/file.REFERENCE.html#filters
+add-highlighter shared/haml/eval1 region -recurse \{ ^\h*%([A-Za-z][A-Za-z0-9_-]*)([#.][A-Za-z][A-Za-z0-9_-]*)?\{\K|#\{\K (?=\}) ref ruby
+add-highlighter shared/haml/eval2 region ^\h*[=-]\K (?<!\|)(?=\n) ref ruby
+add-highlighter shared/haml/coffee region ^\h*:coffee\K ^\h*[%=-]\K ref coffee
+add-highlighter shared/haml/sass region ^\h*:sass\K ^\h*[%=-]\K ref sass
+
+add-highlighter shared/haml/code/ regex ^\h*(:[a-z]+|-|=)|^(!!!)$ 0:meta
+add-highlighter shared/haml/code/ regex ^\h*%([A-Za-z][A-Za-z0-9_-]*)([#.][A-Za-z][A-Za-z0-9_-]*)? 1:keyword 2:variable
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden haml-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden haml-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '/' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K/\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden haml-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : haml-trim-indent <ret> }
+ # indent after lines beginning with : or -
+ try %{ execute-keys -draft k x <a-k> ^\h*[:-] <ret> j <a-gt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/hare.kak b/autoload/filetype/hare.kak
new file mode 100644
index 0000000..19c2296
--- /dev/null
+++ b/autoload/filetype/hare.kak
@@ -0,0 +1,144 @@
+# detection
+hook global BufCreate .*[.]ha %{
+ set-option buffer filetype hare
+}
+
+# initialisation
+hook global WinSetOption filetype=hare %{
+ require-module hare
+ hook window ModeChange pop:insert:.* -group hare-trim-indent hare-trim-indent
+ hook window InsertChar \n -group hare-insert hare-insert-on-new-line
+ hook window InsertChar \n -group hare-indent hare-indent-on-new-line
+ hook window InsertChar \{ -group hare-indent hare-indent-on-opening-curly-brace
+ hook window InsertChar \} -group hare-indent hare-indent-on-closing-curly-brace
+}
+
+hook -group hare-highlight global WinSetOption filetype=hare %{
+ add-highlighter window/hare ref hare
+ hook -once -always window WinSetOption filetype=*. %{ remove-highlighter window/hare }
+}
+
+# highlighters
+provide-module hare %§
+ add-highlighter shared/hare regions
+ add-highlighter shared/hare/code default-region group
+ add-highlighter shared/hare/comment region // $ fill comment
+
+ add-highlighter shared/hare/rawstring region ` ` group
+ add-highlighter shared/hare/rawstring/ fill string
+
+ add-highlighter shared/hare/string region '"' (?<!\\)(\\\\)*" group
+ add-highlighter shared/hare/string/ fill string
+ add-highlighter shared/hare/string/ regex '(\\0|\\a|\\b|\\f|\\n|\\r|\\t|\\v|\\\\|\\")' 0:meta
+ add-highlighter shared/hare/string/ regex "\\'" 0:meta
+ add-highlighter shared/hare/string/ regex "(\\x[0-9a-fA-F]{2})" 0:meta
+ add-highlighter shared/hare/string/ regex "(\\u[0-9a-fA-F]{4})" 0:meta
+ add-highlighter shared/hare/string/ regex "(\\U[0-9a-fA-F]{8})" 0:meta
+
+ add-highlighter shared/hare/rune region "'" (?<!\\)(\\\\)*' group
+ add-highlighter shared/hare/rune/ fill string
+ add-highlighter shared/hare/rune/ regex "(\\0|\\a|\\b|\\f|\\n|\\r|\\t|\\v|\\\\|\\')" 0:meta
+ add-highlighter shared/hare/rune/ regex '\\"' 0:meta
+ add-highlighter shared/hare/rune/ regex "(\\x[0-9a-fA-F]{2})" 0:meta
+ add-highlighter shared/hare/rune/ regex "(\\u[0-9a-fA-F]{4})" 0:meta
+ add-highlighter shared/hare/rune/ regex "(\\U[0-9a-fA-F]{8})" 0:meta
+
+ # imports
+ add-highlighter shared/hare/code/ regex "\buse\s.*?(?=;)" 0:module
+ add-highlighter shared/hare/code/ regex "\buse\b" 0:meta
+
+ # functions
+ add-highlighter shared/hare/code/ regex "\b([0-9a-zA-Z_]*)\h*\(" 1:function
+
+ # attributes
+ add-highlighter shared/hare/code/ regex "@(packed|offset|init|fini|test|noreturn|symbol)\b" 0:attribute
+
+ # declarations
+ add-highlighter shared/hare/code/ regex "\b(let|export|const)\b" 0:meta
+ add-highlighter shared/hare/code/ regex "\b(fn|type|def)\b" 0:keyword
+
+ # builtins
+ add-highlighter shared/hare/code/ regex "\b(len|offset|free|alloc|assert|append|abort|delete|insert|vastart|vaarg|vaend)\b" 0:builtin
+ add-highlighter shared/hare/code/ regex "\b(as|is)\b" 0:builtin
+
+ # types
+ add-highlighter shared/hare/code/ regex "\b(struct|union|enum)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(nullable|null|void)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(u8|u16|u32|u64|uint)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(i8|i16|i32|i64|int)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(size|uintptr|char)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(f32|f64)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(str|rune)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(bool)\b" 0:type
+ add-highlighter shared/hare/code/ regex "\b(valist)\b" 0:type
+
+ # literals
+ add-highlighter shared/hare/code/ regex "\b(true|false)\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(z|(i|u)(8|16|32|64)?)?\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?((?=e)|(?=u)|(?=i))" 0:value
+ add-highlighter shared/hare/code/ regex "\b0b[0-1]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b0b[0-1]+((?=u)|(?=i))" 0:value
+ add-highlighter shared/hare/code/ regex "\b0o[0-7]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b0o[0-7]+((?=u)|(?=i))" 0:value
+ add-highlighter shared/hare/code/ regex "\b0x[0-9a-fA-F]+(z|(i|u)(8|16|32|64)?)?\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b0x[0-9a-fA-F]+((?=u)|(?=i))" 0:value
+
+ # floats
+ add-highlighter shared/hare/code/ regex "\b[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?(f32|f64)?\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?((?=e)|(?=f))" 0:value
+ add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(f32|f64)\b" 0:value
+ add-highlighter shared/hare/code/ regex "\b[0-9]+([eE][-+]?[0-9]+)?(?=f)" 0:value
+
+ # constants
+ add-highlighter shared/hare/code/ regex "\b[0-9A-Z_]*\b" 0:value
+
+ # control flow
+ add-highlighter shared/hare/code/ regex "\b(for|if|else|switch|match|return|break|continue|defer|yield|case|static)\b" 0:keyword
+
+ # operators
+ add-highlighter shared/hare/code/ regex "(=|\+|-|\*|/|<|>|!|\?|&|\||\.\.(\.)?)" 0:operator
+
+ # commands
+ define-command -hidden hare-indent-on-new-line %[ evaluate-commands -draft -itersel %[
+ # preserve indentation on new lines
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j i<tab> ]
+ # cleanup trailing white spaces on the previous line
+ execute-keys -draft k :hare-trim-indent <ret>
+ # indent after match/switch's case statements
+ try %[ execute-keys -draft kx <a-k> case\h.*=>\h*$ <ret> j<a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ ] ]
+
+ define-command -hidden hare-insert-on-new-line %{ evaluate-commands -draft -itersel %{
+ try %{ evaluate-commands -draft -save-regs '/"' %{
+ # copy the comment prefix
+ execute-keys -save-regs '' k x s ^\h*\K//\h* <ret> y
+ try %{
+ # paste the comment prefix
+ execute-keys x j x s ^\h* <ret>P
+ }
+ } }
+ try %{
+ # remove trailing whitespace on the above line
+ execute-keys -draft k :hare-trim-indent <ret>
+ }
+ } }
+
+ define-command -hidden hare-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+ ]
+
+ define-command -hidden hare-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+ ]
+
+ define-command -hidden hare-trim-indent %{ evaluate-commands -draft -itersel %{
+ # remove trailing whitespace
+ try %{ execute-keys -draft x s \h+$ <ret> d }
+ } }
diff --git a/autoload/filetype/haskell.kak b/autoload/filetype/haskell.kak
new file mode 100644
index 0000000..9beed99
--- /dev/null
+++ b/autoload/filetype/haskell.kak
@@ -0,0 +1,127 @@
+# http://haskell.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](hs) %{
+ set-option buffer filetype haskell
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=haskell %{
+ require-module haskell
+
+ set-option buffer extra_word_chars '_' "'"
+ hook window ModeChange pop:insert:.* -group haskell-trim-indent haskell-trim-indent
+ hook window InsertChar \n -group haskell-insert haskell-insert-on-new-line
+ hook window InsertChar \n -group haskell-indent haskell-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window haskell-.+ }
+}
+
+hook -group haskell-highlight global WinSetOption filetype=haskell %{
+ add-highlighter window/haskell ref haskell
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/haskell }
+}
+
+
+provide-module haskell %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/haskell regions
+add-highlighter shared/haskell/code default-region group
+add-highlighter shared/haskell/string region (?<!'\\)(?<!')" (?<!\\)(\\\\)*" fill string
+add-highlighter shared/haskell/macro region ^\K# (?<!\\)\n fill meta
+add-highlighter shared/haskell/pragma region -recurse \{- \{-# '#-\}' fill meta
+add-highlighter shared/haskell/comment region -recurse \{- \{- -\} fill comment
+add-highlighter shared/haskell/line_comment region --(?![!#$%&*+./<>?@\\\^|~=]) $ fill comment
+add-highlighter shared/haskell/quasiquote region \[\b[_a-z]['\w]*#?\| \|\] regex \[\b[_a-z]['\w]*#?\|(.*?)\|\] 1:string
+
+add-highlighter shared/haskell/code/ regex (?<!')\b0x+[A-Fa-f0-9]+ 0:value
+add-highlighter shared/haskell/code/ regex (?<!')\b\d+([.]\d+)? 0:value
+add-highlighter shared/haskell/code/ regex (?<!')\b(import|hiding|qualified|module)(?!')\b 0:keyword
+add-highlighter shared/haskell/code/ regex (?<!')\b(import)(?!')\b[^\n]+(?<!')\b(as)(?!')\b 2:keyword
+add-highlighter shared/haskell/code/ regex (?<!')\b(class|data|default|deriving|infix|infixl|infixr|instance|module|newtype|pattern|type|where)(?!')\b 0:keyword
+add-highlighter shared/haskell/code/ regex (?<!')\b(case|do|else|if|in|let|mdo|of|proc|rec|then)(?!')\b 0:attribute
+add-highlighter shared/haskell/code/ regex (?<!')\b(type|data)\b\s+(\bfamily\b)?(?!') 0:keyword
+
+# The complications below is because period has many uses:
+# As function composition operator (possibly without spaces) like "." and "f.g"
+# Hierarchical modules like "Data.Maybe"
+# Qualified imports like "Data.Maybe.Just", "Data.Maybe.maybe", "Control.Applicative.<$>"
+# Quantifier separator in "forall a . [a] -> [a]"
+# Enum comprehensions like "[1..]" and "[a..b]" (making ".." and "Module..." illegal)
+
+# matches uppercase identifiers: Monad Control.Monad
+# not non-space separated dot: Just.const
+add-highlighter shared/haskell/code/ regex \b([A-Z]['\w]*\.)*[A-Z]['\w]*(?!['\w])(?![.a-z]) 0:variable
+
+# matches infix identifier: `mod` `Apa._T'M`
+add-highlighter shared/haskell/code/ regex `\b([A-Z]['\w]*\.)*[\w]['\w]*` 0:operator
+# matches imported operators: M.! M.. Control.Monad.>>
+# not operator keywords: M... M.->
+add-highlighter shared/haskell/code/ regex \b[A-Z]['\w]*\.[~<=>|:!?/.@$*&#%+\^\-\\]+ 0:operator
+# matches dot: .
+# not possibly incomplete import: a.
+# not other operators: !. .!
+add-highlighter shared/haskell/code/ regex (?<![\w~<=>|:!?/.@$*&#%+\^\-\\])\.(?![~<=>|:!?/.@$*&#%+\^\-\\]) 0:operator
+# matches other operators: ... > < <= ^ <*> <$> etc
+# not dot: .
+# not operator keywords: @ .. -> :: ~
+add-highlighter shared/haskell/code/ regex (?<![~<=>|:!?/.@$*&#%+\^\-\\])[~<=>|:!?/.@$*&#%+\^\-\\]+ 0:operator
+
+# matches operator keywords: @ ->
+add-highlighter shared/haskell/code/ regex (?<![~<=>|:!?/.@$*&#%+\^\-\\])(@|~|<-|->|=>|::|=|:|[|])(?![~<=>|:!?/.@$*&#%+\^\-\\]) 1:keyword
+# matches: forall [..variables..] .
+# not the variables
+add-highlighter shared/haskell/code/ regex \b(forall|∀)\b[^.\n]*?(\.) 1:keyword 2:keyword
+
+# matches 'x' '\\' '\'' '\n' '\0'
+# not incomplete literals: '\'
+# not valid identifiers: w' _'
+add-highlighter shared/haskell/code/ regex \B'([^\\]|[\\]['"\w\d\\])' 0:string
+# this has to come after operators so '-' etc is correct
+
+# matches function names in type signatures
+add-highlighter shared/haskell/code/ regex ^\s*(?:where\s+|let\s+|default\s+)?([_a-z]['\w]*#?(?:,\s*[_a-z]['\w]*#?)*)\s+::\s 1:meta
+
+# matches deriving strategies
+add-highlighter shared/haskell/code/ regex \bderiving\s+\b(stock|newtype|anyclass|via)\b 1:keyword
+add-highlighter shared/haskell/code/ regex \bderiving\b\s+(?:[A-Z]['\w]+|\([',\w\s]+?\))\s+\b(via)\b 1:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+# http://en.wikibooks.org/wiki/Haskell/Indentation
+
+define-command -hidden haskell-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden haskell-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden haskell-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # align to first clause
+ try %{ execute-keys -draft <semicolon> k x X s ^\h*(if|then|else)?\h*(([\w']+\h+)+=)?\h*(case\h+[\w']+\h+of|do|let|where)\h+\K.* <ret> s \A|.\z <ret> & }
+ # filter previous line
+ try %{ execute-keys -draft k : haskell-trim-indent <ret> }
+ # indent after lines beginning with condition or ending with expression or =(
+ try %{ execute-keys -draft <semicolon> k x <a-k> ^\h*if|[=(]$|\b(case\h+[\w']+\h+of|do|let|where)$ <ret> j <a-gt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/hbs.kak b/autoload/filetype/hbs.kak
new file mode 100644
index 0000000..5ea99bc
--- /dev/null
+++ b/autoload/filetype/hbs.kak
@@ -0,0 +1,112 @@
+# http://handlebarsjs.com/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](hbs) %{
+ set-option buffer filetype hbs
+}
+
+hook global WinSetOption filetype=hbs %{
+ require-module hbs
+
+ hook window ModeChange pop:insert:.* -group hbs-trim-indent hbs-trim-indent
+ hook window InsertChar \n -group hbs-insert hbs-insert-on-new-line
+ hook window InsertChar \n -group hbs-indent hbs-indent-on-new-line
+ hook window InsertChar .* -group hbs-indent hbs-indent-on-char
+ hook window InsertChar '>' -group hbs-indent html-indent-on-greater-than
+ hook window InsertChar \n -group hbs-indent html-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window hbs-.+ }
+}
+
+hook -group hbs-highlight global WinSetOption filetype=hbs %{
+ maybe-add-hbs-to-html
+ add-highlighter window/hbs-file ref hbs-file
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/hbs-file }
+}
+
+
+provide-module hbs %[
+
+require-module html
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/hbs regions
+add-highlighter shared/hbs/comment region \{\{!-- --\}\} fill comment
+add-highlighter shared/hbs/comment_alt region \{\{! \}\} fill comment
+add-highlighter shared/hbs/block-expression region \{\{[#/] \}\} regions
+add-highlighter shared/hbs/expression region \{\{ \}\} regions
+
+define-command -hidden add-mutual-highlighters -params 1 %~
+ add-highlighter "shared/hbs/%arg{1}/code" default-region group
+ add-highlighter "shared/hbs/%arg{1}/single-quote" region '"' (?<!\\)(\\\\)*" fill string
+ add-highlighter "shared/hbs/%arg{1}/double-quote" region "'" (?<!\\)(\\\\)*' fill string
+ add-highlighter "shared/hbs/%arg{1}/code/variable" regex \b([\w-]+)\b 1:variable
+ add-highlighter "shared/hbs/%arg{1}/code/attribute" regex \b([\w-]+)= 1:attribute
+ add-highlighter "shared/hbs/%arg{1}/code/helper" regex (?:(?:\{\{)|\()([#/]?[\w-]+(?:/[\w-]+)*) 1:keyword
+~
+
+add-mutual-highlighters expression
+add-mutual-highlighters block-expression
+
+add-highlighter shared/hbs/block-expression/code/yield regex \b(as)\s|[\w-]+|\}\} 1:keyword
+
+
+# wrapper around shared/html highlighter. The shared/hbs highlighter will be
+# added into shared/html when a buffer of filetype 'hbs' is actively displayed in the window.
+add-highlighter shared/hbs-file regions
+add-highlighter shared/hbs-file/html default-region ref html
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden hbs-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden hbs-indent-on-char %[
+ evaluate-commands -draft -itersel %[
+ # de-indent after closing a yielded block tag
+ try %[ execute-keys -draft , <a-h> s ^\h+\{\{/([\w-.]+(?:/[\w-.]+)*)\}\}$ <ret> {c\{\{#<c-r>1,\{\{/<c-r>1\}\} <ret> s \A|.\z <ret> 1<a-&> ]
+ ]
+]
+
+define-command -hidden hbs-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '/' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K/\h* <ret> y j p }
+ }
+}
+
+define-command -hidden hbs-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : hbs-trim-indent <ret> }
+ # indent after lines beginning with : or -
+ try %{ execute-keys -draft k x <a-k> ^\h*[:-] <ret> j <a-gt> }
+ }
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+declare-option bool hbs_highlighters_enabled false
+
+define-command -hidden maybe-add-hbs-to-html %{ evaluate-commands %sh{
+ if [ "$kak_opt_hbs_highlighters_enabled" == "false" ]; then
+ printf %s "
+ add-highlighter shared/html/hbs region '\{\{' '\}\}' ref hbs
+ add-highlighter shared/html/tag/hbs region '\{\{' '\}\}' ref hbs
+ set-option global hbs_highlighters_enabled true
+ "
+ fi
+} }
+
+]
diff --git a/autoload/filetype/html.kak b/autoload/filetype/html.kak
new file mode 100644
index 0000000..0b09ef6
--- /dev/null
+++ b/autoload/filetype/html.kak
@@ -0,0 +1,87 @@
+# http://w3.org/html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.html %{
+ set-option buffer filetype html
+}
+
+hook global BufCreate .*\.xml %{
+ set-option buffer filetype xml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=(html|xml) %{
+ require-module html
+
+ hook window ModeChange pop:insert:.* -group "%val{hook_param_capture_1}-trim-indent" html-trim-indent
+ hook window InsertChar '>' -group "%val{hook_param_capture_1}-indent" html-indent-on-greater-than
+ hook window InsertChar \n -group "%val{hook_param_capture_1}-indent" html-indent-on-new-line
+
+ hook -once -always window WinSetOption "filetype=.*" "
+ remove-hooks window ""%val{hook_param_capture_1}-.+""
+ "
+}
+
+hook -group html-highlight global WinSetOption filetype=(html|xml) %{
+ add-highlighter "window/%val{hook_param_capture_1}" ref html
+ hook -once -always window WinSetOption "filetype=.*" "
+ remove-highlighter ""window/%val{hook_param_capture_1}""
+ "
+}
+
+
+provide-module html %[
+
+try %{
+ require-module css
+ require-module javascript
+}
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/html regions
+add-highlighter shared/html/comment region <!-- --> fill comment
+add-highlighter shared/html/tag region < > regions
+add-highlighter shared/html/style region <style\b.*?>\K (?=</style>) ref css
+add-highlighter shared/html/script region <script\b.*?>\K (?=</script>) ref javascript
+
+add-highlighter shared/html/tag/base default-region group
+add-highlighter shared/html/tag/ region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/html/tag/ region "'" "'" fill string
+
+add-highlighter shared/html/tag/base/ regex \b([a-zA-Z0-9_-]+)=? 1:attribute
+add-highlighter shared/html/tag/base/ regex </?(\w+) 1:keyword
+add-highlighter shared/html/tag/base/ regex <(!DOCTYPE(\h+\w+)+) 1:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden html-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden html-indent-on-greater-than %[
+ evaluate-commands -draft -itersel %[
+ # align closing tag to opening when alone on a line
+ try %[ execute-keys -draft , <a-h> s ^\h+<lt>/(\w+)<gt>$ <ret> {c<lt><c-r>1,<lt>/<c-r>1<gt> <ret> s \A|.\z <ret> 1<a-&> ]
+ ]
+]
+
+define-command -hidden html-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : html-trim-indent <ret> }
+ # indent after lines ending with opening tag except when it starts with a closing tag
+ try %{ execute-keys -draft k x <a-k> <lt>(?!area)(?!base)(?!br)(?!col)(?!command)(?!embed)(?!hr)(?!img)(?!input)(?!keygen)(?!link)(?!menuitem)(?!meta)(?!param)(?!source)(?!track)(?!wbr)(?!/)(?!>)[a-zA-Z0-9_-]+[^>]*?>$ <ret>jx<a-K>^\s*<lt>/<ret><a-gt> } }
+}
+
+]
diff --git a/autoload/filetype/i3.kak b/autoload/filetype/i3.kak
new file mode 100644
index 0000000..deafb6c
--- /dev/null
+++ b/autoload/filetype/i3.kak
@@ -0,0 +1,94 @@
+hook global BufCreate .*(sway|i3)/config %{
+ set buffer filetype i3
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=i3 %[
+ require-module i3
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group i3-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group i3-insert i3-insert-on-new-line
+ hook window InsertChar \n -group i3-indent i3-indent-on-new-line
+ hook window InsertChar \} -group i3-indent i3-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window i3-.+ }
+]
+
+hook -group i3-highlight global WinSetOption filetype=i3 %{
+ add-highlighter window/i3 ref i3
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/i3 }
+}
+
+
+provide-module i3 %[
+
+add-highlighter shared/i3 regions
+add-highlighter shared/i3/code default-region group
+add-highlighter shared/i3/double_string region %{"} %{"} group
+add-highlighter shared/i3/single_string region %{'} %{'} group
+add-highlighter shared/i3/exec region %{((?<=exec )|(?<=--no-startup-id ))(?!--no-startup-id)} "$" fill string
+add-highlighter shared/i3/comment region "#" "$" fill comment
+
+add-highlighter shared/i3/double_string/ fill string
+add-highlighter shared/i3/single_string/ fill string
+
+# Symbols
+add-highlighter shared/i3/code/ regex "[+|→]" 0:operator
+add-highlighter shared/i3/code/ regex "\$\w+" 0:variable
+
+# keys
+add-highlighter shared/i3/code/ regex "\b(Shift|Control|Ctrl|Mod1|Mod2|Mod3|Mod4|Mod5|Mode_switch|Return|Escape|Print)\b" 0:value
+
+# keywords
+add-highlighter shared/i3/code/ regex "\b(bind|bindcode|bindsym|assign|new_window|default_(floating_)?border|popup_during_fullscreen|font|floating_modifier|default_orientation|workspace_layout|for_window|focus_follows_mouse|bar|position|colors|output|tray_output|workspace_buttons|workspace_auto_back_and_forth|binding_mode_indicator|debuglog|floating_minimum_size|floating_maximum_size|force_focus_wrapping|force_xinerama|force_display_urgency_hint|hidden_state|modifier|new_float|shmlog|socket_path|verbose|mouse_warping|strip_workspace_numbers|focus_on_window_activation|no_focus|set|mode|set_from_resource)\b" 0:keyword
+# function keywords
+add-highlighter shared/i3/code/ regex "\b(exit|reload|restart|kill|fullscreen|global|layout|border|focus|move|open|split|append_layout|mark|unmark|resize|grow|shrink|show|nop|rename|title_format|sticky)\b" 0:function
+add-highlighter shared/i3/code/ regex "\b(exec|exec_always|i3bar_command|status_command)\b" 0:function
+# " these are not keywords but we add them for consistency
+add-highlighter shared/i3/code/ regex "\b(no|false|inactive)\b" 0:value
+
+# values
+add-highlighter shared/i3/code/ regex "\b(1pixel|default|stacked|tabbed|normal|none|tiling|stacking|floating|enable|disable|up|down|horizontal|vertical|auto|up|down|left|right|parent|child|px|or|ppt|leave_fullscreen|toggle|mode_toggle|scratchpad|width|height|top|bottom|client|hide|primary|yes|all|active|window|container|to|absolute|center|on|off|ms|smart|ignore|pixel|splith|splitv|output|true)\b" 0:value
+add-highlighter shared/i3/code/ regex "\b(next|prev|next_on_output|prev_on_output|back_and_forth|current|number|none|vertical|horizontal|both|dock|hide|invisible|gaps|smart_gaps|smart_borders|inner|outer|current|all|plus|minus|no_gaps)\b" 0:value
+
+# double-dash arguments
+add-highlighter shared/i3/code/ regex "--(release|border|whole-window|toggle|no-startup-id)" 0:attribute
+
+# color
+add-highlighter shared/i3/double_string/ regex "#[0-9a-fA-F]{6}" 0:value
+add-highlighter shared/i3/single_string/ regex "#[0-9a-fA-F]{6}" 0:value
+
+# attributes
+add-highlighter shared/i3/code/ regex "client\.(background|statusline|background|separator|statusline)" 1:attribute
+add-highlighter shared/i3/code/ regex "client\.(focused_inactive|focused_tab_title|focused|unfocused|urgent|inactive_workspace|urgent_workspace|focused_workspace|active_workspace|placeholder)" 1:attribute
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden i3-insert-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # copy # comments prefix
+ try %{ execute-keys -draft kx s ^\h*#\h* <ret> y jgh P }
+ =
+~
+
+define-command -hidden i3-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with {
+ try %[ execute-keys -draft kx <a-k> \{\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ =
+~
+
+define-command -hidden i3-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+]
diff --git a/autoload/filetype/ini.kak b/autoload/filetype/ini.kak
new file mode 100644
index 0000000..75762f6
--- /dev/null
+++ b/autoload/filetype/ini.kak
@@ -0,0 +1,24 @@
+hook global BufCreate .+\.ini %{
+ set-option buffer filetype ini
+}
+
+hook global WinSetOption filetype=ini %{
+ require-module ini
+}
+
+hook -group ini-highlight global WinSetOption filetype=ini %{
+ add-highlighter window/ini ref ini
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/ini }
+}
+
+
+provide-module ini %{
+
+add-highlighter shared/ini regions
+add-highlighter shared/ini/code default-region group
+add-highlighter shared/ini/comment region '(^|\h)\K[#;]' $ fill comment
+
+add-highlighter shared/ini/code/ regex "(?S)^\h*(\[.+?\])\h*$" 1:title
+add-highlighter shared/ini/code/ regex "^\h*([^\[][^=\n]*)=([^\n]*)" 1:variable 2:value
+
+}
diff --git a/autoload/filetype/janet.kak b/autoload/filetype/janet.kak
new file mode 100644
index 0000000..00f39c2
--- /dev/null
+++ b/autoload/filetype/janet.kak
@@ -0,0 +1,81 @@
+# http://janet-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](janet|jdn) %{
+ set-option buffer filetype janet
+}
+
+
+hook global WinSetOption filetype=janet %{
+ require-module janet
+
+ hook window ModeChange pop:insert:.* -group janet-trim-indent janet-trim-indent
+ hook window InsertChar \n -group janet-indent janet-indent-on-new-line
+ set-option buffer extra_word_chars ! @ $ '%' ^ & * - _ + = : < > . ?
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window janet-.+ }
+}
+
+hook -group janet-highlight global WinSetOption filetype=janet %{
+ add-highlighter window/janet ref janet
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/janet }
+}
+
+provide-module janet %{
+
+require-module lisp
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/janet regions
+add-highlighter shared/janet/code default-region group
+add-highlighter shared/janet/comment region '(?<!\\)(?:\\\\)*\K#' '$' fill comment
+add-highlighter shared/janet/comment-form region -recurse \( '(?<!\\)(?:\\\\)*\K\(comment ' '\)' fill comment
+add-highlighter shared/janet/string region '(?<!\\)(?:\\\\)*\K"' '(?<!\\)(?:\\\\)*"' fill string
+add-highlighter shared/janet/raw-string region -match-capture '(`+)' '(`+)' fill string
+add-highlighter shared/janet/code/ regex \b(nil|true|false)\b 0:value
+add-highlighter shared/janet/code/number regex \W\K(?:[\-+]?\dx?[\der._+a-f]*)\b 0:value
+add-highlighter shared/janet/code/function-definition regex \((?:defn-?|fn)\s([!@$%\^&*\-_+=:<>.?\w/]+) 1:function
+add-highlighter shared/janet/code/function-call regex \(([!@$%\^&*\-_+=:<>.?\w/]+) 1:function
+add-highlighter shared/janet/code/keyword regex \W\K:[!@$%\^&*\-_+=:<>.?\w/]+ 0:value
+add-highlighter shared/janet/code/special regex \((def-?|defn-?|var-?|fn|do|quote|if|splice|while|break|set|quasiquote|unquote|upscope)\s 1:keyword
+add-highlighter shared/janet/code/ regex \W\K(&|&opt|&keys|&named)\W 1:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden janet-trim-indent lisp-trim-indent
+
+declare-option \
+ -docstring 'regex matching the head of forms which have options *and* indented bodies' \
+ regex janet_special_indent_forms \
+ '(?:def.*|while|for|fn\*?|if(-.*|)|let.*|loop|seq|with(-.*|)|when(-.*|))|defer|do|match|var'
+
+define-command -hidden janet-indent-on-new-line %{
+ # registers: i = best align point so far; w = start of first word of form
+ evaluate-commands -draft -save-regs '/"|^@iw' -itersel %{
+ execute-keys -draft 'gk"iZ'
+ try %{
+ execute-keys -draft '[bl"i<a-Z><gt>"wZ'
+
+ try %{
+ # If a special form, indent another j
+ execute-keys -draft '"wze<a-k>\A' %opt{janet_special_indent_forms} '\z<ret><a-L>s.\K.*<ret><a-;>;"i<a-Z><gt>'
+ } catch %{
+ # If not special and parameter appears on line 1, indent to parameter
+ execute-keys -draft '"wze<a-l>s\h\K[^\s].*<ret><a-;>;"i<a-Z><gt>'
+ }
+ }
+ try %{ execute-keys -draft '[rl"i<a-Z><gt>' }
+ try %{ execute-keys -draft '[Bl"i<a-Z><gt>' }
+ execute-keys -draft '"i<a-z>a&,'
+ # trim trailing whitespace on the previous line
+ try %{ execute-keys -draft k : janet-trim-indent <ret> }
+ }
+}
+
+}
diff --git a/autoload/filetype/java.kak b/autoload/filetype/java.kak
new file mode 100644
index 0000000..69b0d74
--- /dev/null
+++ b/autoload/filetype/java.kak
@@ -0,0 +1,129 @@
+hook global BufCreate .*\.java %{
+ set-option buffer filetype java
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=java %{
+ require-module java
+
+ set-option window static_words %opt{java_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group java-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group java-insert java-insert-on-new-line
+ hook window InsertChar \n -group java-indent java-indent-on-new-line
+ hook window InsertChar \{ -group java-indent java-indent-on-opening-curly-brace
+ hook window InsertChar \} -group java-indent java-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window java-.+ }
+}
+
+hook -group java-highlight global WinSetOption filetype=java %{
+ add-highlighter window/java ref java
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/java }
+}
+
+provide-module java %§
+
+add-highlighter shared/java regions
+add-highlighter shared/java/code default-region group
+add-highlighter shared/java/string region %{(?<!')"} %{(?<!\\)(\\\\)*"} fill string
+add-highlighter shared/java/character region %{'} %{(?<!\\)'} fill value
+add-highlighter shared/java/comment region /\* \*/ fill comment
+add-highlighter shared/java/inline_documentation region /// $ fill documentation
+add-highlighter shared/java/line_comment region // $ fill comment
+
+add-highlighter shared/java/code/ regex "(?<!\w)@\w+\b" 0:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden java-insert-on-new-line %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+]
+
+define-command -hidden java-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after keywords
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for|try|catch)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ =
+~
+
+define-command -hidden java-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden java-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+# Shell
+# ‾‾‾‾‾
+# Oracle 2021, 3.9 Keywords, Chapter 3. Lexical Structure, Java Language Specification, Java SE 17, viewed 25 September 2021, <https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.9>
+#
+evaluate-commands %sh{
+ values='false null this true'
+
+ types='boolean byte char double float int long short unsigned void'
+
+ keywords='assert break case catch class continue default do else enum extends
+ finally for if implements import instanceof interface new package return
+ static strictfp super switch throw throws try var while yield'
+
+ attributes='abstract final native non-sealed permits private protected public
+ record sealed synchronized transient volatile'
+
+ modules='exports module open opens provides requires to transitive uses with'
+
+ # ---------------------------------------------------------------------------------------------- #
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+ # ---------------------------------------------------------------------------------------------- #
+ add_highlighter() { printf "add-highlighter shared/java/code/ regex %s %s\n" "$1" "$2"; }
+ # ---------------------------------------------------------------------------------------------- #
+ add_word_highlighter() {
+
+ while [ $# -gt 0 ]; do
+ words=$1 face=$2; shift 2
+ regex="\\b($(join "${words}" '|'))\\b"
+ add_highlighter "$regex" "1:$face"
+ done
+
+ }
+
+ # highlight: open<space> not open()
+ add_module_highlighter() {
+
+ while [ $# -gt 0 ]; do
+ words=$1 face=$2; shift 2
+ regex="\\b($(join "${words}" '|'))\\b(?=\\s)"
+ add_highlighter "$regex" "1:$face"
+ done
+
+ }
+ # ---------------------------------------------------------------------------------------------- #
+ printf %s\\n "declare-option str-list java_static_words $(join "${values} ${types} ${keywords} ${attributes} ${modules}" ' ')"
+ # ---------------------------------------------------------------------------------------------- #
+ add_word_highlighter "$values" "value" "$types" "type" "$keywords" "keyword" "$attributes" "attribute"
+ # ---------------------------------------------------------------------------------------------- #
+ add_module_highlighter "$modules" "module"
+ # ---------------------------------------------------------------------------------------------- #
+}
+
diff --git a/autoload/filetype/javascript.kak b/autoload/filetype/javascript.kak
new file mode 100644
index 0000000..05e0bc6
--- /dev/null
+++ b/autoload/filetype/javascript.kak
@@ -0,0 +1,224 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.][cm]?(js)x? %{
+ set-option buffer filetype javascript
+}
+
+hook global BufCreate .*[.][cm]?(ts)x? %{
+ set-option buffer filetype typescript
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=(javascript|typescript) %{
+ require-module javascript
+
+ hook window ModeChange pop:insert:.* -group "%val{hook_param_capture_1}-trim-indent" javascript-trim-indent
+ hook window InsertChar .* -group "%val{hook_param_capture_1}-indent" javascript-indent-on-char
+ hook window InsertChar \n -group "%val{hook_param_capture_1}-insert" javascript-insert-on-new-line
+ hook window InsertChar \n -group "%val{hook_param_capture_1}-indent" javascript-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* "
+ remove-hooks window %val{hook_param_capture_1}-.+
+ "
+}
+
+hook -group javascript-highlight global WinSetOption filetype=javascript %{
+ add-highlighter window/javascript ref javascript
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/javascript }
+}
+
+hook -group typescript-highlight global WinSetOption filetype=typescript %{
+ add-highlighter window/typescript ref typescript
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/typescript }
+}
+
+
+provide-module javascript %§
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden javascript-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft x 1s^(\h+)$<ret> d }
+}
+
+define-command -hidden javascript-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %/ execute-keys -draft <a-h> <a-k> ^\h+[\]}]$ <ret> m s \A|.\z <ret> 1<a-&> /
+ >
+>
+
+define-command -hidden javascript-insert-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %[
+ evaluate-commands -draft -save-regs '/"' %[
+ # copy the commenting prefix
+ execute-keys -save-regs '' k x1s^\h*(//+\h*)<ret> y
+ try %[
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*//+\h*$<ret> jxs^\h*<ret>P
+ ] catch %[
+ # if there is no text in the previous comment, remove it completely
+ execute-keys d
+ ]
+ ]
+ ]
+ try %[
+ # if the previous line isn't within a comment scope, break
+ execute-keys -draft kx <a-k>^(\h*/\*|\h+\*(?!/))<ret>
+
+ # find comment opening, validate it was not closed, and check its using star prefixes
+ execute-keys -draft <a-?>/\*<ret><a-H> <a-K>\*/<ret> <a-k>\A\h*/\*([^\n]*\n\h*\*)*[^\n]*\n\h*.\z<ret>
+
+ try %[
+ # if the previous line is opening the comment, insert star preceeded by space
+ execute-keys -draft kx<a-k>^\h*/\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the next line is a comment line insert a star
+ execute-keys -draft jx<a-k>^\h+\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the previous line is an empty comment line, close the comment scope
+ execute-keys -draft kx<a-k>^\h+\*\h+$<ret> x1s\*(\h*)<ret>c/<esc>
+ ] catch %[
+ # if the previous line is a non-empty comment line, add a star
+ execute-keys -draft i*<space><esc>
+ ]
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft s\h+$<ret> d ]
+ # align the new star with the previous one
+ execute-keys Kx1s^[^*]*(\*)<ret><a-(><a-&>
+ ]
+ >
+>
+
+define-command -hidden javascript-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %<
+ # if previous line is part of a comment, do nothing
+ execute-keys -draft <a-?>/\*<ret> <a-K>^\h*[^/*\h]<ret>
+ > catch %<
+ # else if previous line closed a paren (possibly followed by words and a comment),
+ # copy indent of the opening paren line
+ execute-keys -draft kx 1s(\))(\h+\w+)*\h*(\;\h*)?(?://[^\n]+)?\n\z<ret> m<a-semicolon>J <a-S> 1<a-&>
+ > catch %<
+ # else indent new lines with the same level as the previous one
+ execute-keys -draft K <a-&>
+ >
+ # remove previous empty lines resulting from the automatic indent
+ try %< execute-keys -draft k x <a-k>^\h+$<ret> Hd >
+ # indent after an opening brace or parenthesis at end of line
+ try %< execute-keys -draft k x <a-k>[{(]\h*$<ret> j <a-gt> >
+ # indent after a label (works for case statements)
+ try %< execute-keys -draft k x s[a-zA-Z0-9_-]+:\h*$<ret> j <a-gt> >
+ # indent after a statement not followed by an opening brace
+ try %< execute-keys -draft k x s\)\h*(?://[^\n]+)?\n\z<ret> \
+ <a-semicolon>mB <a-k>\A\b(if|for|while)\b<ret> <a-semicolon>j <a-gt> >
+ try %< execute-keys -draft k x s \belse\b\h*(?://[^\n]+)?\n\z<ret> \
+ j <a-gt> >
+ # deindent after a single line statement end
+ try %< execute-keys -draft K x <a-k>\;\h*(//[^\n]+)?$<ret> \
+ K x s\)(\h+\w+)*\h*(//[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ MB <a-k>\A\b(if|for|while)\b<ret> <a-S>1<a-&> >
+ try %< execute-keys -draft K x <a-k>\;\h*(//[^\n]+)?$<ret> \
+ K x s \belse\b\h*(?://[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ <a-S>1<a-&> >
+ # deindent closing brace(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <esc> m <a-S> 1<a-&> >
+ # align to the opening parenthesis or opening brace (whichever is first)
+ # on a previous line if its followed by text on the same line
+ try %< evaluate-commands -draft %<
+ # Go to opening parenthesis and opening brace, then select the most nested one
+ try %< execute-keys [c [({],[)}] <ret> >
+ # Validate selection and get first and last char
+ execute-keys <a-k>\A[{(](\h*\S+)+\n<ret> <a-K>"(([^"]*"){2})*<ret> <a-K>'(([^']*'){2})*<ret> <a-:><a-semicolon>L <a-S>
+ # Remove possibly incorrect indent from new line which was copied from previous line
+ try %< execute-keys -draft , <a-h> s\h+<ret> d >
+ # Now indent and align that new line with the opening parenthesis/brace
+ execute-keys 1<a-&> &
+ > >
+ >
+>
+
+# Highlighting and hooks bulder for JavaScript and TypeScript
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+define-command -hidden init-javascript-filetype -params 1 %~
+ # Highlighters
+ # ‾‾‾‾‾‾‾‾‾‾‾‾
+
+ add-highlighter "shared/%arg{1}" regions
+ add-highlighter "shared/%arg{1}/code" default-region group
+ add-highlighter "shared/%arg{1}/double_string" region '"' (?<!\\)(\\\\)*" fill string
+ add-highlighter "shared/%arg{1}/single_string" region "'" (?<!\\)(\\\\)*' fill string
+ add-highlighter "shared/%arg{1}/literal" region "`" (?<!\\)(\\\\)*` group
+ add-highlighter "shared/%arg{1}/comment_line" region // '$' fill comment
+ add-highlighter "shared/%arg{1}/comment" region /\* \*/ fill comment
+ add-highlighter "shared/%arg{1}/shebang" region ^#! $ fill meta
+ add-highlighter "shared/%arg{1}/division" region '[\w\)\]]\K(/|(\h+/\s+))' '(?=\w)' group # Help Kakoune to better detect /…/ literals
+ add-highlighter "shared/%arg{1}/regex" region / (?<!\\)(\\\\)*/[gimuy]* fill meta
+ add-highlighter "shared/%arg{1}/jsx" region -recurse (?<![\w<])<[a-zA-Z>][\w:.-]* (?<![\w<])<[a-zA-Z>][\w:.-]*(?!\hextends)(?=[\s/>])(?!>\()) (</.*?>|/>) regions
+
+ # Regular expression flags are: g → global match, i → ignore case, m → multi-lines, u → unicode, y → sticky
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
+
+ add-highlighter "shared/%arg{1}/literal/" fill string
+ add-highlighter "shared/%arg{1}/literal/" regex \$\{.*?\} 0:value
+
+ add-highlighter "shared/%arg{1}/code/" regex (?:^|[^$_])\b(document|false|null|parent|self|this|true|undefined|window)\b 1:value
+ add-highlighter "shared/%arg{1}/code/" regex "-?\b[0-9]*\.?[0-9]+" 0:value
+ add-highlighter "shared/%arg{1}/code/" regex \b(Array|Boolean|Date|Function|Number|Object|RegExp|String|Symbol)\b 0:type
+
+ # jsx: In well-formed xml the number of opening and closing tags match up regardless of tag name.
+ #
+ # We inline a small XML highlighter here since it anyway need to recurse back up to the starting highlighter.
+ # To make things simple we assume that jsx is always enabled.
+
+ add-highlighter "shared/%arg{1}/jsx/tag" region -recurse < <(?=[/a-zA-Z>]) (?<!=)> regions
+ add-highlighter "shared/%arg{1}/jsx/expr" region -recurse \{ \{ \} ref %arg{1}
+
+ add-highlighter "shared/%arg{1}/jsx/tag/base" default-region group
+ add-highlighter "shared/%arg{1}/jsx/tag/double_string" region =\K" (?<!\\)(\\\\)*" fill string
+ add-highlighter "shared/%arg{1}/jsx/tag/single_string" region =\K' (?<!\\)(\\\\)*' fill string
+ add-highlighter "shared/%arg{1}/jsx/tag/expr" region -recurse \{ \{ \} group
+
+ add-highlighter "shared/%arg{1}/jsx/tag/base/" regex (\w+) 1:attribute
+ add-highlighter "shared/%arg{1}/jsx/tag/base/" regex </?([\w-$]+) 1:keyword
+ add-highlighter "shared/%arg{1}/jsx/tag/base/" regex (</?|/?>) 0:meta
+
+ add-highlighter "shared/%arg{1}/jsx/tag/expr/" ref %arg{1}
+
+ # Keywords are collected at
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
+ add-highlighter "shared/%arg{1}/code/" regex \b(async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|finally|for|function|get|if|import|in|instanceof|let|new|of|return|set|static|super|switch|throw|try|typeof|var|void|while|with|yield)\b 0:keyword
+~
+
+init-javascript-filetype javascript
+init-javascript-filetype typescript
+
+# Highlighting specific to TypeScript
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+add-highlighter shared/typescript/code/ regex \b(array|boolean|date|number|object|regexp|string|symbol)\b 0:type
+
+# Keywords grabbed from https://github.com/Microsoft/TypeScript/issues/2536
+add-highlighter shared/typescript/code/ regex \b(as|constructor|declare|enum|from|implements|interface|module|namespace|package|private|protected|public|readonly|static|type)\b 0:keyword
+
+
+# Aliases
+# ‾‾‾‾‾‾‾
+provide-module typescript %{ require-module javascript }
diff --git a/autoload/filetype/jinja.kak b/autoload/filetype/jinja.kak
new file mode 100644
index 0000000..890a7ac
--- /dev/null
+++ b/autoload/filetype/jinja.kak
@@ -0,0 +1,35 @@
+# https://palletsprojects.com/p/jinja/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module jinja %[
+
+require-module python
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/jinja regions
+add-highlighter shared/jinja/comment region '\{#' '#\}' fill comment
+
+# TODO: line statements # …
+
+add-highlighter shared/jinja/statement region '\{%' '%\}' group
+add-highlighter shared/jinja/statement/ ref python
+add-highlighter shared/jinja/statement/ regex \{%[+-]?|[+-]?%\} 0:value
+add-highlighter shared/jinja/statement/tests regex \b(callable|even|le|none|string|defined|ge|lower|number|undefined|divisibleby|gt|lt|odd|upper|eq|in|mapping|sameas|escaped|iterable|ne|sequence)\b 0:builtin
+add-highlighter shared/jinja/statement/functions regex \b(range|lipsum)\b 0:function
+add-highlighter shared/jinja/statement/macro regex \b(((end)?(call|macro)))\b 0:keyword
+add-highlighter shared/jinja/statement/extensions regex \b(((end)?(block|trans))|(pluralize))\b 0:keyword
+add-highlighter shared/jinja/statement/control regex \b(((end)?(if|for|with))|(break|continue))\b 0:keyword
+add-highlighter shared/jinja/statement/filters regex \b(?:(?:(filter)\s+|\|\s*)(abs|attr|batch|capitalize|center|default|dictsort|e|escape|filesizeformat|first|float|forceescape|format|groupby|indent|int|join|last|length|list|lower|map|max|min|pprint|random|reject|rejectattr|replace|reverse|round|safe|select|selectattr|slice|sort|string|striptags|sum|title|tojson|trim|truncate|unique|upper|urlencode|urlize|wordcount|wordwrap|xmlattr)|(endfilter))\b 1:keyword 3:keyword 2:builtin
+add-highlighter shared/jinja/statement/ regex \b((end)?(autoescape|raw|set))\b 0:keyword
+add-highlighter shared/jinja/statement/ regex \b(do|extends|include)\b 0:keyword
+add-highlighter shared/jinja/statement/ regex \bignore\s+missing\b 0:meta
+add-highlighter shared/jinja/statement/ regex \bwith(out)?\s+context\b 0:meta
+
+add-highlighter shared/jinja/expression region '\{\{' '\}\}' group
+add-highlighter shared/jinja/expression/ ref python
+add-highlighter shared/jinja/expression/ regex \{\{|\}\} 0:value
+add-highlighter shared/jinja/expression/filters regex \|\s*(abs|attr|batch|capitalize|center|default|dictsort|e|escape|filesizeformat|first|float|forceescape|format|groupby|indent|int|join|last|length|list|lower|map|max|min|pprint|random|reject|rejectattr|replace|reverse|round|safe|select|selectattr|slice|sort|string|striptags|sum|title|tojson|trim|truncate|unique|upper|urlencode|urlize|wordcount|wordwrap|xmlattr)\b 1:builtin
+
+]
diff --git a/autoload/filetype/json.kak b/autoload/filetype/json.kak
new file mode 100644
index 0000000..8ca7c2a
--- /dev/null
+++ b/autoload/filetype/json.kak
@@ -0,0 +1,69 @@
+# http://json.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](json) %{
+ set-option buffer filetype json
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=json %{
+ require-module json
+
+ hook window ModeChange pop:insert:.* -group json-trim-indent json-trim-indent
+ hook window InsertChar .* -group json-indent json-indent-on-char
+ hook window InsertChar \n -group json-indent json-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window json-.+ }
+}
+
+hook -group json-highlight global WinSetOption filetype=json %{
+ add-highlighter window/json ref json
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/json }
+}
+
+
+provide-module json %(
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/json regions
+add-highlighter shared/json/code default-region group
+add-highlighter shared/json/string region '"' (?<!\\)(\\\\)*" fill string
+
+add-highlighter shared/json/code/ regex \b(true|false|null|\d+(?:\.\d+)?(?:[eE][+-]?\d*)?)\b 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden json-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden json-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h+[\]}]$ <ret> m <a-S> 1<a-&> >
+ >
+>
+
+define-command -hidden json-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : json-trim-indent <ret> }
+ # indent after lines ending with opener token
+ try %< execute-keys -draft k x <a-k> [[{]\h*$ <ret> j <a-gt> >
+ # deindent closer token(s) when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[}\]] <ret> gh / [}\]] <ret> m <a-S> 1<a-&> >
+ >
+>
+
+)
diff --git a/autoload/filetype/julia.kak b/autoload/filetype/julia.kak
new file mode 100644
index 0000000..5f6bbaa
--- /dev/null
+++ b/autoload/filetype/julia.kak
@@ -0,0 +1,42 @@
+# http://julialang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(jl) %{
+ set-option buffer filetype julia
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=julia %{
+ require-module julia
+}
+
+hook -group julia-highlight global WinSetOption filetype=julia %{
+ add-highlighter window/julia ref julia
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/julia }
+}
+
+
+provide-module julia %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/julia regions
+add-highlighter shared/julia/code default-region group
+add-highlighter shared/julia/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/julia/comment region -recurse '#=' '#=' '=#' fill comment
+add-highlighter shared/julia/line_comment region '#' '$' fill comment
+
+# taken from https://github.com/JuliaEditorSupport/julia-emacs/blob/master/julia-mode.el
+add-highlighter shared/julia/code/ regex %{\b(true|false|C_NULL|Inf|NaN|Inf32|NaN32|nothing|\b-?\d+[fdiu]?)\b} 0:value
+add-highlighter shared/julia/code/ regex \b(if|else|elseif|while|for|begin|end|quote|try|catch|return|local|abstract|function|macro|ccall|finally|typealias|break|continue|type|global|module|using|import|export|const|let|bitstype|do|in|baremodule|importall|immutable|mutable|struct|where)\b 0:keyword
+add-highlighter shared/julia/code/ regex \b(Number|Real|BigInt|Integer|UInt|UInt8|UInt16|UInt32|UInt64|UInt128|Int|Int8|Int16|Int32|Int64|Int128|BigFloat|FloatingPoint|Float16|Float32|Float64|Complex128|Complex64|Bool|Cuchar|Cshort|Cushort|Cint|Cuint|Clonglong|Culonglong|Cintmax_t|Cuintmax_t|Cfloat|Cdouble|Cptrdiff_t|Cssize_t|Csize_t|Cchar|Clong|Culong|Cwchar_t|Cvoid|Char|ASCIIString|UTF8String|ByteString|SubString|AbstractString|Array|DArray|AbstractArray|AbstractVector|AbstractMatrix|AbstractSparseMatrix|SubArray|StridedArray|StridedVector|StridedMatrix|VecOrMat|StridedVecOrMat|DenseArray|SparseMatrixCSC|BitArray|Range|OrdinalRange|StepRange|UnitRange|FloatRange|Tuple|NTuple|Vararg|DataType|Symbol|Function|Vector|Matrix|Union|Type|Any|Complex|String|Ptr|Void|Exception|Task|Signed|Unsigned|Associative|Dict|IO|IOStream|Rational|Regex|RegexMatch|Set|IntSet|Expr|WeakRef|ObjectIdDict|AbstractRNG|MersenneTwister)\b 0:type
+add-highlighter shared/julia/code/ regex \w+!*(?=\() 0:function
+add-highlighter shared/julia/code/ regex @\w+!*\b 0:meta
+add-highlighter shared/julia/code/ regex '(?:\?|=|:=|\+=|-=|\*=|/=|//=|\.//=|\.\*=|\./=|\\=|\.\\=|\^=|\.\^=|÷=|\.÷=|%=|\.%=|\|=|&=|\$=|=>|<<=|>>=|>>>=|~|\.\+=|\.-=|--|-->|←|→|↔|↚|↛|↠|↣|↦|↮|⇎|⇏|⇒|⇔|⇴|⇶|⇷|⇸|⇹|⇺|⇻|⇼|⇽|⇾|⇿|⟵|⟶|⟷|⟷|⟹|⟺|⟻|⟼|⟽|⟾|⟿|⤀|⤁|⤂|⤃|⤄|⤅|⤆|⤇|⤌|⤍|⤎|⤏|⤐|⤑|⤔|⤕|⤖|⤗|⤘|⤝|⤞|⤟|⤠|⥄|⥅|⥆|⥇|⥈|⥊|⥋|⥎|⥐|⥒|⥓|⥖|⥗|⥚|⥛|⥞|⥟|⥢|⥤|⥦|⥧|⥨|⥩|⥪|⥫|⥬|⥭|⥰|⧴|⬱|⬰|⬲|⬳|⬴|⬵|⬶|⬷|⬸|⬹|⬺|⬻|⬼|⬽|⬾|⬿|⭀|⭁|⭂|⭃|⭄|⭇|⭈|⭉|⭊|⭋|⭌|←|→|&&|\|\||>|<|>=|≥|<=|≤|==|===|≡|!=|≠|!==|≢|\.>|\.<|\.>=|\.≥|\.<=|\.≤|\.==|\.!=|\.≠|\.=|\.!|<:|>:|∈|∉|∋|∌|⊆|⊈|⊂|⊄|⊊|∝|∊|∍|∥|∦|∷|∺|∻|∽|∾|≁|≃|≄|≅|≆|≇|≈|≉|≊|≋|≌|≍|≎|≐|≑|≒|≓|≔|≕|≖|≗|≘|≙|≚|≛|≜|≝|≞|≟|≣|≦|≧|≨|≩|≪|≫|≬|≭|≮|≯|≰|≱|≲|≳|≴|≵|≶|≷|≸|≹|≺|≻|≼|≽|≾|≿|⊀|⊁|⊃|⊅|⊇|⊉|⊋|⊏|⊐|⊑|⊒|⊜|⊩|⊬|⊮|⊰|⊱|⊲|⊳|⊴|⊵|⊶|⊷|⋍|⋐|⋑|⋕|⋖|⋗|⋘|⋙|⋚|⋛|⋜|⋝|⋞|⋟|⋠|⋡|⋢|⋣|⋤|⋥|⋦|⋧|⋨|⋩|⋪|⋫|⋬|⋭|⋲|⋳|⋴|⋵|⋶|⋷|⋸|⋹|⋺|⋻|⋼|⋽|⋾|⋿|⟈|⟉|⟒|⦷|⧀|⧁|⧡|⧣|⧤|⧥|⩦|⩧|⩪|⩫|⩬|⩭|⩮|⩯|⩰|⩱|⩲|⩳|⩴|⩵|⩶|⩷|⩸|⩹|⩺|⩻|⩼|⩽|⩾|⩿|⪀|⪁|⪂|⪃|⪄|⪅|⪆|⪇|⪈|⪉|⪊|⪋|⪌|⪍|⪎|⪏|⪐|⪑|⪒|⪓|⪔|⪕|⪖|⪗|⪘|⪙|⪚|⪛|⪜|⪝|⪞|⪟|⪠|⪡|⪢|⪣|⪤|⪥|⪦|⪧|⪨|⪩|⪪|⪫|⪬|⪭|⪮|⪯|⪰|⪱|⪲|⪳|⪴|⪵|⪶|⪷|⪸|⪹|⪺|⪻|⪼|⪽|⪾|⪿|⫀|⫁|⫂|⫃|⫄|⫅|⫆|⫇|⫈|⫉|⫊|⫋|⫌|⫍|⫎|⫏|⫐|⫑|⫒|⫓|⫔|⫕|⫖|⫗|⫘|⫙|⫷|⫸|⫹|⫺|⊢|⊣|\|>|<\||:|\.\.|\+|-|⊕|⊖|⊞|⊟|\.\+|\.-|\+\+|\||∪|∨|\$|⊔|±|∓|∔|∸|≂|≏|⊎|⊻|⊽|⋎|⋓|⧺|⧻|⨈|⨢|⨣|⨤|⨥|⨦|⨧|⨨|⨩|⨪|⨫|⨬|⨭|⨮|⨹|⨺|⩁|⩂|⩅|⩊|⩌|⩏|⩐|⩒|⩔|⩖|⩗|⩛|⩝|⩡|⩢|⩣|<<|>>|>>>|\.<<|\.>>|\.>>>|\*|/|\./|÷|\.÷|%|⋅|∘|×|\.%|\.\*|\\|\.\\|&|∩|∧|⊗|⊘|⊙|⊚|⊛|⊠|⊡|⊓|∗|∙|∤|⅋|≀|⊼|⋄|⋆|⋇|⋉|⋊|⋋|⋌|⋏|⋒|⟑|⦸|⦼|⦾|⦿|⧶|⧷|⨇|⨰|⨱|⨲|⨳|⨴|⨵|⨶|⨷|⨸|⨻|⨼|⨽|⩀|⩃|⩄|⩋|⩍|⩎|⩑|⩓|⩕|⩘|⩚|⩜|⩞|⩟|⩠|⫛|⊍|▷|⨝|⟕|⟖|⟗|//|\.//|\^|\.\^|↑|↓|⇵|⟰|⟱|⤈|⤉|⤊|⤋|⤒|⤓|⥉|⥌|⥍|⥏|⥑|⥔|⥕|⥘|⥙|⥜|⥝|⥠|⥡|⥣|⥥|⥮|⥯|↑|↓|::|\.)' 0:operator
+}
diff --git a/autoload/filetype/just.kak b/autoload/filetype/just.kak
new file mode 100644
index 0000000..8c43fcc
--- /dev/null
+++ b/autoload/filetype/just.kak
@@ -0,0 +1,79 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*/?[jJ]ustfile %{
+ set-option buffer filetype justfile
+}
+
+hook global WinSetOption filetype=justfile %{
+ require-module justfile
+
+ hook window ModeChange pop:insert:.* -group justfile-trim-indent justfile-trim-indent
+ hook window InsertChar \n -group justfile-insert just-insert-on-new-line
+ hook window InsertChar \n -group justfile-indent just-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window justfile-.+ }
+}
+
+hook -group justfile-highlight global WinSetOption filetype=justfile %{
+ add-highlighter window/justfile ref justfile
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/justfile }
+}
+
+
+provide-module justfile %{
+
+# Indentation
+# ‾‾‾‾‾‾‾‾‾‾‾
+
+define-command -hidden justfile-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden just-insert-on-new-line %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*//\h* <ret> y jgh P }
+}
+
+define-command -hidden just-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # cleanup trailing white spaces on previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>"_d }
+ }
+}
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/justfile regions
+
+add-highlighter shared/justfile/content default-region group
+add-highlighter shared/justfile/content/recipe regex '^@?([\w-]+)([^\n]*):(?!=)([^\n]*)' 1:function 2:meta 3:keyword
+add-highlighter shared/justfile/content/assignments regex ^([\w-]+\h*:=\h*[^\n]*) 1:meta
+add-highlighter shared/justfile/content/operator regex '((^@|:=|=|\+|\(|\)))' 1:operator
+add-highlighter shared/justfile/content/strings regions
+add-highlighter shared/justfile/content/strings/double region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/justfile/content/strings/single region "'" (?<!\\)(\\\\)*' fill string
+
+add-highlighter shared/justfile/comment region '#' '$' fill comment
+
+add-highlighter shared/justfile/inline region '`' '`' ref sh
+
+add-highlighter shared/justfile/body region '^\h+' '^[^\h]' group
+add-highlighter shared/justfile/body/interpreters regions
+add-highlighter shared/justfile/body/interpreters/defaultshell default-region group
+add-highlighter shared/justfile/body/interpreters/defaultshell/ ref sh
+add-highlighter shared/justfile/body/interpreters/defaultshell/ regex '^\h+(@)' 1:operator
+
+add-highlighter shared/justfile/body/interpreters/bash region '^\h+#!\h?/usr/bin/env bash' '^[^\h]' ref sh
+add-highlighter shared/justfile/body/interpreters/sh region '^\h+#!\h?/usr/bin/env sh' '^[^\h]' ref sh
+
+add-highlighter shared/justfile/body/ regex '(\{{2})([\w-]+(?:\(\))?)(\}{2})' 1:operator 2:variable 3:operator
+
+
+}
diff --git a/autoload/filetype/kakrc.kak b/autoload/filetype/kakrc.kak
new file mode 100644
index 0000000..1914e46
--- /dev/null
+++ b/autoload/filetype/kakrc.kak
@@ -0,0 +1,130 @@
+# http://kakoune.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate (.*/)?(kakrc|.*\.kak) %{
+ set-option buffer filetype kak
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=kak %~
+ require-module kak
+
+ set-option window static_words %opt{kak_static_words}
+
+ hook window InsertChar \n -group kak-insert kak-insert-on-new-line
+ hook window InsertChar \n -group kak-indent kak-indent-on-new-line
+ hook window InsertChar [>)}\]] -group kak-indent kak-indent-on-closing-matching
+ hook window InsertChar (?![[{(<>)}\]])[^\s\w] -group kak-indent kak-indent-on-closing-char
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group kak-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+ set-option buffer extra_word_chars '_' '-'
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window kak-.+ }
+~
+
+hook -group kak-highlight global WinSetOption filetype=kak %{
+ add-highlighter window/kakrc ref kakrc
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/kakrc }
+}
+
+provide-module kak %§
+
+require-module sh
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/kakrc regions
+add-highlighter shared/kakrc/code default-region group
+add-highlighter shared/kakrc/comment region (^|\h)\K# $ fill comment
+add-highlighter shared/kakrc/double_string region -recurse %{(?<!")("")+(?!")} %{(^|\h)\K"} %{"(?!")} group
+add-highlighter shared/kakrc/single_string region -recurse %{(?<!')('')+(?!')} %{(^|\h)\K'} %{'(?!')} group
+add-highlighter shared/kakrc/shell1 region -recurse '\{' '(^|\h)\K%?%sh\{' '\}' ref sh
+add-highlighter shared/kakrc/shell2 region -recurse '\(' '(^|\h)\K%?%sh\(' '\)' ref sh
+add-highlighter shared/kakrc/shell3 region -recurse '\[' '(^|\h)\K%?%sh\[' '\]' ref sh
+add-highlighter shared/kakrc/shell4 region -recurse '<' '(^|\h)\K%?%sh<' '>' ref sh
+add-highlighter shared/kakrc/shell5 region -recurse '\{' '(^|\h)\K-?shell-script(-completion|-candidates)?\h+%\{' '\}' ref sh
+add-highlighter shared/kakrc/shell6 region -recurse '\(' '(^|\h)\K-?shell-script(-completion|-candidates)?\h+%\(' '\)' ref sh
+add-highlighter shared/kakrc/shell7 region -recurse '\[' '(^|\h)\K-?shell-script(-completion|-candidates)?\h+%\[' '\]' ref sh
+add-highlighter shared/kakrc/shell8 region -recurse '<' '(^|\h)\K-?shell-script(-completion|-candidates)?\h+%<' '>' ref sh
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="add-highlighter alias arrange-buffers buffer buffer-next buffer-previous catch
+ change-directory colorscheme debug declare-option declare-user-mode define-command complete-command
+ delete-buffer delete-buffer! echo edit edit! enter-user-mode evaluate-commands execute-keys
+ fail hook info kill kill! map nop on-key prompt provide-module quit quit!
+ remove-highlighter remove-hooks rename-buffer rename-client rename-session require-module
+ select set-face set-option set-register source trigger-user-hook try
+ unalias unmap unset-face unset-option update-option
+ write write! write-all write-all-quit write-quit write-quit!"
+ attributes="global buffer window current
+ normal insert prompt goto view user object
+ number-lines show-matching show-whitespaces fill regex dynregex group flag-lines
+ ranges line column wrap ref regions region default-region replace-ranges"
+ types="int bool str regex int-list str-list completions line-specs range-specs str-to-str-map"
+ values="default black red green yellow blue magenta cyan white yes no false true"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list kak_static_words $(join "${keywords} ${attributes} ${types} ${values}" ' ')'"
+
+ # Highlight keywords (which are always surrounded by whitespace)
+ printf '%s\n' "add-highlighter shared/kakrc/code/keywords regex (?:\s|\A)\K($(join "${keywords}" '|'))(?:(?=\s)|\z) 0:keyword
+ add-highlighter shared/kakrc/code/attributes regex (?:\s|\A)\K($(join "${attributes}" '|'))(?:(?=\s)|\z) 0:attribute
+ add-highlighter shared/kakrc/code/types regex (?:\s|\A)\K($(join "${types}" '|'))(?:(?=\s)|\z) 0:type
+ add-highlighter shared/kakrc/code/values regex (?:\s|\A)\K($(join "${values}" '|'))(?:(?=\s)|\z) 0:value"
+}
+
+add-highlighter shared/kakrc/code/colors regex \b(rgb:[0-9a-fA-F]{6}|rgba:[0-9a-fA-F]{8})\b 0:value
+add-highlighter shared/kakrc/code/numbers regex \b\d+\b 0:value
+
+add-highlighter shared/kakrc/double_string/fill fill string
+add-highlighter shared/kakrc/double_string/escape regex '""' 0:default+b
+add-highlighter shared/kakrc/single_string/fill fill string
+add-highlighter shared/kakrc/single_string/escape regex "''" 0:default+b
+
+add-highlighter shared/kak ref kakrc
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden kak-insert-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*#\h* <ret> y jgh P }
+ =
+~
+
+define-command -hidden kak-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with %\w*[^\s\w]
+ try %{ execute-keys -draft k x <a-k> \%\w*[^\s\w]$ <ret> j <a-gt> }
+ # deindent closing brace when after cursor
+ try %_ execute-keys -draft -itersel x <a-k> ^\h*([>)}\]]) <ret> gh / <c-r>1 <ret> m <a-S> 1<a-&> _
+ # deindent closing char(s)
+ try %{ execute-keys -draft -itersel x <a-k> ^\h*([^\s\w]) <ret> gh / <c-r>1 <ret> <a-?> <c-r>1 <ret> <a-T>% <a-k> \w*<c-r>1$ <ret> <a-S> 1<a-&> }
+ =
+~
+
+define-command -hidden kak-indent-on-closing-matching %~
+ # align to opening matching brace when alone on a line
+ try %= execute-keys -draft -itersel <a-h><a-k>^\h*\Q %val{hook_param} \E$<ret> mGi s \A|.\z<ret> 1<a-&> =
+~
+
+define-command -hidden kak-indent-on-closing-char %{
+ # align to opening matching character when alone on a line
+ try %{ execute-keys -draft -itersel <a-h><a-k>^\h*\Q %val{hook_param} \E$<ret>gi<a-f> %val{hook_param} <a-T>%<a-k>\w*\Q %val{hook_param} \E$<ret> s \A|.\z<ret> gi 1<a-&> }
+}
+
diff --git a/autoload/filetype/kickstart.kak b/autoload/filetype/kickstart.kak
new file mode 100644
index 0000000..0e7441b
--- /dev/null
+++ b/autoload/filetype/kickstart.kak
@@ -0,0 +1,38 @@
+hook global BufCreate .*\.ks %{
+ set-option buffer filetype kickstart
+}
+
+hook global WinSetOption filetype=kickstart %{
+ require-module kickstart
+}
+
+hook -group kickstart-highlight global WinSetOption filetype=kickstart %{
+ add-highlighter window/kickstart ref kickstart
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/kickstart }
+}
+
+
+provide-module kickstart %{
+
+add-highlighter shared/kickstart regions
+add-highlighter shared/kickstart/code default-region group
+add-highlighter shared/kickstart/comment region '(^|\h)\K#' $ fill comment
+add-highlighter shared/kickstart/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/kickstart/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/kickstart/packages region '^\h*\K%packages\b' '^\h*\K%end\b' group
+add-highlighter shared/kickstart/shell region '^\h*\K%(pre|pre-install|post)\b' '^\h*\K%end\b' group
+
+add-highlighter shared/kickstart/code/ regex "^\h*\b(auth|authconfig|autopart|autostep|bootloader|btrfs|clearpart|cmdline|device|dmraid|driverdisk|fcoe|firewall|firstboot|group|graphical|halt|ignoredisk|install|cdrom|harddrive|liveimg|nfs|url|iscsi|iscsiname|keyboard|lang|logvol|logging|mediacheck|monitor|multipath|network|part|partition|poweroff|raid|realm|reboot|repo|rescue|rootpw|selinux|services|shutdown|sshkey|sshpw|skipx|text|timezone|updates|upgrade|user|vnc|volgroup|xconfig|zerombr|zfcp)\b" 1:keyword
+add-highlighter shared/kickstart/code/ regex '(--[\w-]+=? ?)([^-"\n][^\h\n]*)?' 1:attribute 2:string
+add-highlighter shared/kickstart/code/ regex '%(include|ksappend)\b' 0:keyword
+
+add-highlighter shared/kickstart/packages/ regex "^\h*[\w-]*" 0:value
+add-highlighter shared/kickstart/packages/ regex "#[^\n]*" 0:comment
+add-highlighter shared/kickstart/packages/ regex "^\h*@\^?[\h\w-]*" 0:attribute
+add-highlighter shared/kickstart/packages/ regex '\A\h*\K%packages\b' 0:type
+add-highlighter shared/kickstart/packages/ regex '^\h*%end\b' 0:type
+add-highlighter shared/kickstart/shell/ regex '\A\h*\K%(pre-install|pre|post)\b' 0:type
+add-highlighter shared/kickstart/shell/ regex '^\h*%end\b' 0:type
+add-highlighter shared/kickstart/shell/ ref sh
+
+}
diff --git a/autoload/filetype/kotlin.kak b/autoload/filetype/kotlin.kak
new file mode 100644
index 0000000..d1707a8
--- /dev/null
+++ b/autoload/filetype/kotlin.kak
@@ -0,0 +1,187 @@
+# References --------------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾‾‾
+# Team: Yerlan & Kirk Duncan
+#
+# Kotlin 2020, Keywords and Operators, v1.4.0, viewed 9 September 2020, https://kotlinlang.org/docs/reference/keyword-reference.html
+# Kdoc 2020, Documenting Kotlin Code, Block Tags, v1.4.0, viewed 9 September 2020, https://kotlinlang.org/docs/reference/kotlin-doc.html
+# Oracle 2020, Java Platform, Standard Edition & Java Development Kit, Version 14 API Specification, viewed 8 September 2020, https://docs.oracle.com/en/java/javase/14/docs/api/index.html
+#
+# File types --------------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾‾‾
+hook global BufCreate .*[.](kt|kts) %{
+ set-option buffer filetype kotlin
+}
+
+# Initialization ----------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+hook global WinSetOption filetype=kotlin %{
+ require-module kotlin
+
+ set-option window static_words %opt{kotlin_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group kotlin-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group kotlin-indent kotlin-insert-on-new-line
+ hook window InsertChar \n -group kotlin-indent kotlin-indent-on-new-line
+ hook window InsertChar \{ -group kotlin-indent kotlin-indent-on-opening-curly-brace
+ hook window InsertChar \} -group kotlin-indent kotlin-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window kotlin-.+ }
+}
+
+hook -group kotlin-highlighter global WinSetOption filetype=kotlin %{
+ add-highlighter window/kotlin ref kotlin
+ add-highlighter window/kdoc ref kdoc
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-highlighter window/kotlin
+ remove-highlighter window/kdoc
+ }
+}
+
+hook global BufSetOption filetype=kotlin %{
+ require-module kotlin
+
+ set-option buffer comment_line '//'
+ set-option buffer comment_block_begin '/*'
+ set-option buffer comment_block_end '*/'
+
+ hook -once -always buffer BufSetOption filetype=.* %{ remove-hooks buffer kotlin-.+ }
+}
+
+# Module ------------------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾
+provide-module kotlin %§
+
+add-highlighter shared/kotlin regions
+add-highlighter shared/kotlin/code default-region group
+add-highlighter shared/kotlin/string region %{(?<!')"} %{(?<!\\)(\\\\)*"} group
+add-highlighter shared/kotlin/character region %{'} %{(?<!\\)'} group
+add-highlighter shared/kotlin/comment region /\* \*/ fill comment
+add-highlighter shared/kotlin/inline_documentation region /// $ fill documentation
+add-highlighter shared/kotlin/line_comment region // $ fill comment
+
+add-highlighter shared/kotlin/code/annotations regex @\w+\b|\b\w+@(?=\{) 0:meta
+add-highlighter shared/kotlin/code/identifiers regex \b(field|it)\b 1:variable
+add-highlighter shared/kotlin/code/fields regex \.([A-Za-z_][\w]*)\s*?\. 1:type
+
+# String interpolation
+add-highlighter shared/kotlin/string/ fill string
+add-highlighter shared/kotlin/string/ regex \$\{.*?\} 0:value
+
+# Character
+add-highlighter shared/kotlin/character/ fill value
+add-highlighter shared/kotlin/character/ regex ('.{1})(.+)(') 2:meta
+
+# As at 15 March 2021, method see: https://regex101.com/r/Mhy4HG/1
+add-highlighter shared/kotlin/code/methods regex ::([A-Za-z_][\w]*)|\.([A-Za-z_][\w]*)\s*?[\(\{]|\.([A-Za-z_][\w]*)[\s\)\}>](?=[^\(\{]) 1:function 2:function 3:function
+
+# Test suite functions: fun `this is a valid character function test`()
+add-highlighter shared/kotlin/code/fun_tests regex ^\h*?fun\s*?`(.[^<>:/\[\]\\\.]+?)`\h*?(?=\() 1:default+iuf
+add-highlighter shared/kotlin/code/delimiters regex (\(|\)|\[|\]|\{|\}|\;|') 1:operator
+add-highlighter shared/kotlin/code/operators regex (\+|-|\*|&|=|\\|\?|%|\|-|!|\||->|\.|,|<|>|:|\^|/) 1:operator
+add-highlighter shared/kotlin/code/numbers regex \b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFf])?\b 0:value
+
+# Generics need improvement, as after a colon will match as a constant only.
+# val program: IOU = XXXX; val cat: DOG = XXXX. matches IOU or DOG as a
+# CONSTANT when it could be generics. See: https://regex101.com/r/VPO5LE/10
+add-highlighter shared/kotlin/code/constants_and_generics regex \b((?<==\h)\([A-Z][A-Z0-9_]+(?=[<:\;])|(?<!<)[A-Z][A-Z0-9_]+\b(?!<[>\)]))|\b((?<!=\s)(?<!\.)[A-Z]+\d*?(?![\(\;:])(?=[,\)>\s]))\b 1:meta 2:type
+
+add-highlighter shared/kotlin/code/target regex @(delegate|field|file|get|param|property|receiver|set|setparam)(?=:) 0:meta
+add-highlighter shared/kotlin/code/soft regex \b(by|catch|constructor|dynamic|finally|get|import|init|set|where)\b 1:keyword
+add-highlighter shared/kotlin/code/hard regex \b(as|as\?|break|class|continue|do|else|false|for|fun|if|in|!in|interface|is|!is|null|object|package|return|super|this|throw|true|try|typealias|val|var|when|while)\b 1:keyword
+
+add-highlighter shared/kotlin/code/modifier regex \b(actual|abstract|annotation|companion|const|crossinline|data|enum|expect|external|final|infix|inline|inner|internal|lateinit|noinline|open|operator|out|override|private|protected|public|reified|sealed|suspend|tailrec|vararg)\b(?=[\s\n]) 1:attribute
+
+add-highlighter shared/kotlin/code/type regex \b(Annotation|Any|Boolean|BooleanArray|Byte|ByteArray|Char|Character|CharArray|CharSequence|Class|ClassLoader|Cloneable|Comparable|Compiler|DeprecationLevel|Double|DoubleArray|Enum|Float|FloatArray|Function|Int|IntArray|Integer|Lazy|LazyThreadSafetyMode|Long|LongArray|Math|Nothing|Number|Object|Package|Pair|Process|Runnable|Runtime|SecurityManager|Short|ShortArray|StackTraceElement|StrictMath|String|StringBuffer|System|Thread|ThreadGroup|ThreadLocal|Triple|Unit|Void)\b(?=[^<]) 1:type
+
+# Kdoc --------------------------------------------------------------------------------------------- #
+# ‾‾‾‾
+add-highlighter shared/kdoc group
+add-highlighter shared/kdoc/tag regex \*(?:\s+)?(@(author|constructor|exception|param|property|receiver|return|sample|see|since|suppress|throws))\b 1:default+ui
+
+# Discolour ---------------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾‾
+add-highlighter shared/kotlin/code/discolour regex ^(package|import)(?S)(.+) 2:default+fa
+
+# Commands ----------------------------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾
+define-command -hidden kotlin-insert-on-new-line %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+]
+
+define-command -hidden kotlin-indent-on-new-line %~
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a pattern match on when/where statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(when|where).*$ <ret> j<a-gt> ]
+ # indent after term on an expression
+ try %[ execute-keys -draft kx <a-k> =\h*?$ <ret> j<a-gt> ]
+ # indent after keywords
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(catch|do|else|for|if|try|while)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ >
+~
+
+define-command -hidden kotlin-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden kotlin-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+# Exceptions, Errors, and Types -------------------------------------------------------------------- #
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+# macro: 93le<a-;>i<ret><esc><esc>
+evaluate-commands %sh{
+
+ kotlin_keywords='abstract actual annotation as break by catch class companion const
+ constructor continue crossinline data delegate do dynamic else enum expect external
+ false field file final finally for fun get if import in infix init inline inner
+ interface internal is lateinit noinline null object open operator out override
+ package param private property protected public receiver reified return sealed set
+ setparam super suspend tailrec this throw true try typealias val var vararg when where while'
+
+ kotlin_types='Annotation Any Boolean BooleanArray Byte ByteArray Char Character CharArray
+ CharSequence Class ClassLoader Cloneable Comparable Compiler DeprecationLevel Double
+ DoubleArray Enum Float FloatArray Function Int IntArray Integer Lazy LazyThreadSafetyMode
+ Long LongArray Math Nothing Number Object Package Pair Process Runnable Runtime
+ SecurityManager Short ShortArray StackTraceElement StrictMath String StringBuffer System
+ Thread ThreadGroup ThreadLocal Triple Unit Void'
+
+ # ------------------------------------------------------------------------------------------------ #
+
+ kotlin_kdocs='author constructor exception param property receiver return sample see since suppress throws'
+
+ # ------------------------------------------------------------------------------------------------ #
+
+ kotlin_errors_exceptions='CharacterCodingException Error AssertionError NotImplementedError
+ OutOfMemoryErrorIllegalCallableAccessException IllegalPropertyDelegateAccessException
+ NoSuchPropertyException RuntimeException Throwable'
+
+ # ------------------------------------------------------------------------------------------------ #
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # ------------------------------------------------------------------------------------------------ #
+
+ printf %s\\n "declare-option str-list kotlin_static_words $(join "${kotlin_keywords} ${kotlin_types} ${kotlin_kdocs} ${kotlin_errors_exceptions}" ' ')"
+
+ # ------------------------------------------------------------------------------------------------ #
+
+ printf %s\\n "add-highlighter shared/kotlin/code/errors_exceptions regex \b($(join "${kotlin_errors_exceptions}" '|'))\b 0:type"
+}
+# ------------------------------------------------------------------------------------------------- #
diff --git a/autoload/filetype/latex.kak b/autoload/filetype/latex.kak
new file mode 100644
index 0000000..70c3767
--- /dev/null
+++ b/autoload/filetype/latex.kak
@@ -0,0 +1,144 @@
+# https://www.latex-project.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(tex|cls|sty|dtx) %{
+ set-option buffer filetype latex
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=latex %(
+ require-module latex
+
+ hook window InsertChar \n -group latex-insert %{ latex-insert-on-newline }
+ hook window InsertChar \n -group latex-indent %{ latex-indent-newline }
+ hook window InsertChar \} -group latex-indent %{ latex-indent-closing-brace }
+ hook window ModeChange pop:insert:.* -group latex-indent %{ latex-trim-indent }
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window latex-indent }
+ hook window InsertChar \n -group latex-insert latex-insert-on-new-line
+)
+
+hook -group latex-highlight global WinSetOption filetype=latex %{
+ add-highlighter window/latex ref latex
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/latex }
+}
+
+provide-module latex %~
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/latex regions
+add-highlighter shared/latex/content default-region group
+# Region for control sequence (includes latex2e arguments and options)
+# starting with unescaped \ and ending :
+# - at eol, or
+# - at word boundaries not preceded nor followed by @ : \ { } [ ] *, or
+# - after an unescaped }
+add-highlighter shared/latex/cs region '(?<!\\)(?:\\\\)*\K\\[@\w]' '/\n|(?<![@:\\{}\[\]*])(?![@:\\{}\[\]*])\b|(?<!\\)(?:\\\\)*\K\}\K' group
+add-highlighter shared/latex/comment region '(?<!\\)(?:\\\\)*\K%' '\n' fill comment
+
+# Document and LaTeX2e control sequence
+add-highlighter shared/latex/cs/ regex '(?:\\[a-zA-Z@]+)' 0:keyword
+## Options passed to LaTeX2e control sequences, between brackets
+add-highlighter shared/latex/cs/ regex '\\[a-zA-Z@]+\b\[([^\]]+)\]' 1:value
+## Emphasized text
+add-highlighter shared/latex/cs/ regex '\\(?:emph|textit|textsl)\{([^}]+)\}' 1:default+i
+## Underlined text
+add-highlighter shared/latex/cs/ regex '\\underline\{([^}]+)\}' 1:default+u
+## Bold text
+add-highlighter shared/latex/cs/ regex '\\textbf\{([^}]+)\}' 1:default+b
+## Section headings
+add-highlighter shared/latex/cs/ regex '\\(part|section)\*?\{([^}]+)\}' 2:title
+add-highlighter shared/latex/cs/ regex '\\(chapter|(sub)+section|(sub)*paragraph)\*?\{([^}]+)\}' 4:header
+
+# LaTeX3 control sequence
+## Functions (expl3 doc) module_name:arguments_types.
+add-highlighter shared/latex/cs/ regex '\\(?:__|@@_)?[a-zA-Z@]+_\w+(:[nNpTFDwcVvxefo]+)?' 0:function 1:+db@type
+## Variables (expl3 doc): scope_name_type
+add-highlighter shared/latex/cs/ regex '\\([lgc]_)[a-zA-Z@]+_\w+' 0:variable 1:+db
+## l3kernel modules (l3kernel/doc/l3prefixes.csv)
+add-highlighter shared/latex/cs/ regex '\\(alignment|alloc|ampersand|atsign|backslash|bitset|bool|box|catcode|cctab|char|chk|circumflex|clist|code|codedoc|coffin|colon|color|cs|debug|dim|document|dollar|driver|e|else|empty|etex|exp|expl|false|fi|file|flag|fp|group|hash|hbox|hcoffin|if|inf|initex|insert|int|intarray|ior|iow|job|kernel|keys|keyval|left|log|lua|luatex|mark|marks|math|max|minus|mode|msg|muskip|nan|nil|no|novalue|one|or|other|parameter|pdf|pdftex|peek|percent|pi|prg|prop|ptex|quark|recursion|ref|regex|reverse|right|scan|seq|skip|sort|space|stop|str|sys|tag|term|tex|text|tilde|tl|tmpa|tmpb|token|true|underscore|uptex|use|utex|vbox|vcoffin|xetex|zero)_' 0:+db
+# LaTeX3 types (expl3 doc)
+add-highlighter shared/latex/cs/ regex '_(bool|box|cctab|clist|coffin|dim|fp|ior|iow|int|muskip|prop|seq|skip|str|tl)\b' 0:+db
+
+# This belongs to content group as the LaTeX3 convention is separating macros names, args and options
+# with spaces and thus should not be catched by the cs region
+## macros arguments
+add-highlighter shared/latex/content/ regex '(?<!\\)(?:\\\\)*\K#+[1-9]' 0:string
+## group containing words and numbers (list separated by ; , / or spaces)
+add-highlighter shared/latex/content/ regex '(?<!\\)(?:\\\\)*\K\{([\s/;,.\w\d]+)\}' 1:string
+
+# Math mode between dollar signs/pairs
+add-highlighter shared/latex/content/ regex '((?<!\\)(?:\\\\)*\K\$(\\\$|[^$])+\$)|((?<!\\)(?:\\\\)*\K\$\$(\\\$|[^$])+\$\$)|((?<!\\)(?:\\\\)*\K\\\[.*?\\\])|(\\\(.*?\\\))' 0:meta
+
+# Indent
+# ------
+
+define-command -hidden latex-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ try %{ execute-keys x 1s^(\h+)$<ret> d }
+ }
+}
+
+define-command -hidden latex-insert-on-newline %{
+ # copy '%' comment prefix and following white spaces
+ try %{ execute-keys -draft kx s^\h*%\h*<ret> y jgh P }
+}
+
+define-command -hidden latex-indent-newline %(
+ evaluate-commands -no-hooks -draft -itersel %(
+ # preserve previous line indent
+ try %{ execute-keys -draft K<a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft kx s\h+$<ret> d }
+ # indent after line ending with {
+ try %( execute-keys -draft kx <a-k>\{$<ret> j<a-gt> )
+ # deindent closing brace(s) when after cursor
+ try %( execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> )
+ # indent after line ending with \begin{...}[...]{...}, with multiple
+ # sets of arguments possible
+ try %(
+ execute-keys -draft \
+ kx \
+ <a-k>\\begin\h*\{[^\}]+\}(\h|\[.*\]|\{.*\})*$<ret> \
+ j<a-gt>
+ )
+ )
+)
+
+define-command -hidden latex-indent-closing-brace %(
+ evaluate-commands -no-hooks -draft -itersel %(
+ # Align lone } with matching bracket
+ try %( execute-keys -draft x_ <a-k>\A\}\z<ret> m<a-S>1<a-&> )
+ # Align \end{...} with corresponding \begin{...}
+ try %(
+ execute-keys -draft h<a-h> 1s\\end\h*\{([^\}]+)\}\z<ret> \
+ <a-?>\\begin\s*\{<c-r>.\}<ret> <a-S>1<a-&>
+ )
+ )
+)
+
+define-command -hidden latex-insert-on-new-line %(
+ evaluate-commands -no-hooks -draft -itersel %(
+ # Wisely add "\end{...}".
+ evaluate-commands -save-regs xz %(
+ # Save previous line indent in register x.
+ try %( execute-keys -draft kxs^\h+<ret>"xy ) catch %( reg x '' )
+ # Save item of begin in register z.
+ try %( execute-keys -draft kxs\{.*\}<ret>"zy ) catch %( reg z '' )
+ try %(
+ # Validate previous line and that it is not closed yet.
+ execute-keys -draft kx <a-k>^<c-r>x\h*\\begin\{.*\}<ret> J}iJx <a-K>^<c-r>x(\\end\<c-r>z<backspace>\})<ret>
+ # Auto insert "\end{...}".
+ execute-keys -draft o<c-r>x\end<c-r>z<esc>
+ )
+ )
+ )
+)
+
+~
diff --git a/autoload/filetype/ledger.kak b/autoload/filetype/ledger.kak
new file mode 100644
index 0000000..31417b7
--- /dev/null
+++ b/autoload/filetype/ledger.kak
@@ -0,0 +1,144 @@
+# Detection
+# ---------
+
+# The .ledger suffix is not required by ledger, but the best I can do.
+hook global BufCreate .*\.ledger %{
+ set-option buffer filetype ledger
+}
+
+# Initialization
+# --------------
+
+hook global WinSetOption filetype=ledger %{
+ require-module ledger
+
+ hook window InsertChar \n -group ledger-indent ledger-indent-on-new-line
+ hook window ModeChange pop:insert:.* -group ledger-trim-indent ledger-trim-indent
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window ledger-.+
+ unset-option window static_words # Remove static completion
+ }
+}
+
+hook -group ledger-highlight global WinSetOption filetype=ledger %{
+ add-highlighter window/ledger ref ledger
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-highlighter window/ledger
+ }
+}
+
+# Completion
+# ----------
+
+hook -group ledger-complete global WinSetOption filetype=ledger %{
+ set-option window static_words account note alias payee check assert eval \
+ default apply fixed bucket capture comment commodity format nomarket \
+ define end include tag test year
+}
+
+provide-module ledger %[
+
+# Highlighters
+# ------------
+#
+# TODO: highlight tag comments
+
+add-highlighter shared/ledger regions
+
+# The following highlighters implement
+# https://www.ledger-cli.org/3.0/doc/ledger3.html#Transactions-and-Comments
+
+add-highlighter shared/ledger/transaction region '^[0-9]' '^(?=\H)' group
+add-highlighter shared/ledger/transaction/first_line regex \
+ '^([0-9].*?)\h.*?(( +|\t+);.*?)?$' 1:function 2:string
+add-highlighter shared/ledger/transaction/posting regex \
+ '^\h+([^\h;].*?)(( +|\t+).*?)?(( +|\t+);.*?)?$' 1:type 2:value 4:string
+add-highlighter shared/ledger/transaction/note regex '^\h+;[^$]*?$' 0:string
+
+add-highlighter shared/ledger/comment region '^(;|#|%|\||\*)' '$' fill comment
+
+# TODO: Improve
+add-highlighter shared/ledger/other region '^(P|=|~)' '$' fill meta
+
+# The following highlighters implement
+# https://www.ledger-cli.org/3.0/doc/ledger3.html#Command-Directives
+
+add-highlighter shared/ledger/default default-region group
+
+# Add highlighters for simple one-line command directives
+evaluate-commands %sh{
+ # TODO: Is `expr` also a command directive? The documentation confuses me.
+ for cmd in 'apply account' 'apply fixed' 'assert' 'bucket' 'check' 'end' \
+ 'include' 'apply tag' 'test' 'year'; do
+ echo "add-highlighter shared/ledger/default/ regex '^${cmd}\b' 0:function"
+ done
+}
+
+add-highlighter shared/ledger/account region '^account' '^(?=\H)' group
+add-highlighter shared/ledger/account/first_line regex '^account' 0:function
+add-highlighter shared/ledger/account/note regex '^\h*note' 0:function
+add-highlighter shared/ledger/account/alias regex '^\h*alias' 0:function
+add-highlighter shared/ledger/account/payee regex '^\h*payee' 0:function
+add-highlighter shared/ledger/account/check regex '^\h*check' 0:function
+add-highlighter shared/ledger/account/assert regex '^\h*assert' 0:function
+add-highlighter shared/ledger/account/eval regex '^\h*eval' 0:function
+add-highlighter shared/ledger/account/default regex '^\h*default' 0:function
+
+add-highlighter shared/ledger/alias region '^alias' '$' group
+add-highlighter shared/ledger/alias/keyword regex '^alias' 0:function
+add-highlighter shared/ledger/alias/key regex '^alias\h([^$=]*)=?' 1:variable
+add-highlighter shared/ledger/alias/value regex '^alias\h.*?=(.*?)$' 1:value
+
+add-highlighter shared/ledger/capture region '^capture' '$' group
+add-highlighter shared/ledger/capture/keyword regex '^capture' 0:function
+add-highlighter shared/ledger/capture/account regex \
+ '^capture\h+(.*?)( +|\t+|$)' 1:type
+add-highlighter shared/ledger/capture/regex regex \
+ '^capture\h+.*?( +|\t+)(.*?)$' 2:value
+
+add-highlighter shared/ledger/comment_block region '^comment' '^end comment' \
+ fill comment
+
+add-highlighter shared/ledger/commodity region '^commodity' '^(?=\H)' group
+add-highlighter shared/ledger/commodity/first_line regex '^commodity' 0:function
+add-highlighter shared/ledger/commodity/note regex '^\h*note' 0:function
+add-highlighter shared/ledger/commodity/format regex '^\h*format' 0:function
+add-highlighter shared/ledger/commodity/nomarket regex '^\h*nomarket' 0:function
+add-highlighter shared/ledger/commodity/alias regex '^\h*alias' 0:function
+add-highlighter shared/ledger/commodity/default regex '^\h*default' 0:function
+
+add-highlighter shared/ledger/define region '^define' '$' group
+add-highlighter shared/ledger/define/keyword regex '^define' 0:function
+add-highlighter shared/ledger/define/key regex '^define\h([^$=]*)=?' 1:variable
+add-highlighter shared/ledger/define/value regex '^define\h.*?=(.*?)$' 1:value
+
+add-highlighter shared/ledger/payee region '^payee' '^(?=\H)' group
+add-highlighter shared/ledger/payee/first_line regex '^payee' 0:function
+add-highlighter shared/ledger/payee/alias regex '^\h*alias' 0:function
+add-highlighter shared/ledger/payee/uuid regex '^\h*uuid' 0:function
+
+add-highlighter shared/ledger/tag region '^tag' '^(?=\H)' group
+add-highlighter shared/ledger/tag/first_line regex '^tag' 0:function
+add-highlighter shared/ledger/tag/check regex '^\h*check' 0:function
+add-highlighter shared/ledger/tag/assert regex '^\h*assert' 0:function
+
+# Commands
+# --------
+
+define-command -hidden ledger-indent-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # preserve previous line indent
+ try %[ execute-keys -draft <semicolon> K <a-&> ]
+ # cleanup trailing whitespaces from previous line
+ try %[ execute-keys -draft k x s \h+$ <ret> d ]
+ # indent after the first line of a transaction
+ try %[ execute-keys -draft kx <a-k>^[0-9]<ret> j<a-gt> ]
+ ]
+]
+
+define-command -hidden ledger-trim-indent %{
+ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d }
+}
+
+]
diff --git a/autoload/filetype/lisp.kak b/autoload/filetype/lisp.kak
new file mode 100644
index 0000000..d018e78
--- /dev/null
+++ b/autoload/filetype/lisp.kak
@@ -0,0 +1,81 @@
+# http://common-lisp.net
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](lisp) %{
+ set-option buffer filetype lisp
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=lisp %{
+ require-module lisp
+
+ hook window ModeChange pop:insert:.* -group lisp-trim-indent lisp-trim-indent
+ hook window InsertChar \n -group lisp-indent lisp-indent-on-new-line
+ set-option buffer extra_word_chars '_' '+' '-' '*' '/' '@' '$' '%' '^' '&' '_' '=' '<' '>' '~' '.'
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window lisp-.+ }
+}
+
+hook -group lisp-highlight global WinSetOption filetype=lisp %{
+ add-highlighter window/lisp ref lisp
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/lisp }
+}
+
+provide-module lisp %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/lisp regions
+add-highlighter shared/lisp/code default-region group
+add-highlighter shared/lisp/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/lisp/comment region ';' '$' fill comment
+
+add-highlighter shared/lisp/code/ regex (#?(['`:]|,@?))?\b[a-zA-Z][\w!$%&*+./:<=>?@^_~-]* 0:variable
+add-highlighter shared/lisp/code/ regex \b(nil|true|false)\b 0:value
+add-highlighter shared/lisp/code/ regex (((\Q***\E)|(///)|(\Q+++\E)){1,3})|(1[+-])|(<|>|<=|=|>=) 0:operator
+add-highlighter shared/lisp/code/ regex \b(def[a-z]+|if|do|let|lambda|catch|and|assert|while|def|do|fn|finally|let|loop|new|quote|recur|set!|throw|try|var|case|if-let|if-not|when|when-first|when-let|when-not|(cond(->|->>)?))\b 0:keyword
+add-highlighter shared/lisp/code/ regex \*[a-zA-Z][\w!$%&*+./:<=>?@^_~-]*\* 0:variable
+add-highlighter shared/lisp/code/ regex (\b\d+)?\.\d+([eEsSfFdDlL]\d+)?\b 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden lisp-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+declare-option \
+ -docstring 'regex matching the head of forms which have options *and* indented bodies' \
+ regex lisp_special_indent_forms \
+ '(?:def.*|if(-.*|)|let.*|lambda|with-.*|when(-.*|))'
+
+define-command -hidden lisp-indent-on-new-line %{
+ # registers: i = best align point so far; w = start of first word of form
+ evaluate-commands -draft -save-regs '/"|^@iw' -itersel %{
+ execute-keys -draft 'gk"iZ'
+ try %{
+ execute-keys -draft '[bl"i<a-Z><gt>"wZ'
+
+ try %{
+ # If a special form, indent another (indentwidth - 1) spaces
+ execute-keys -draft '"wze<a-k>\A' %opt{lisp_special_indent_forms} '\z<ret>'
+ execute-keys -draft '"wze<a-L>s.{' %sh{printf $(( kak_opt_indentwidth - 1 ))} '}\K.*<ret><a-;>;"i<a-Z><gt>'
+ } catch %{
+ # If not "special" form and parameter appears on line 1, indent to parameter
+ execute-keys -draft '"wz<a-K>[()\[\]{}]<ret>e<a-l>s\h\K[^\s].*<ret><a-;>;"i<a-Z><gt>'
+ }
+ }
+ try %{ execute-keys -draft '[rl"i<a-Z><gt>' }
+ try %{ execute-keys -draft '[Bl"i<a-Z><gt>' }
+ execute-keys -draft ';"i<a-z>a&,'
+ }
+}
+
+}
diff --git a/autoload/filetype/lua.kak b/autoload/filetype/lua.kak
new file mode 100644
index 0000000..d08edd1
--- /dev/null
+++ b/autoload/filetype/lua.kak
@@ -0,0 +1,152 @@
+# http://lua.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](lua|rockspec) %{
+ set-option buffer filetype lua
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=lua %{
+ require-module lua
+
+ hook window ModeChange pop:insert:.* -group lua-trim-indent lua-trim-indent
+ hook window InsertChar .* -group lua-indent lua-indent-on-char
+ hook window InsertChar \n -group lua-indent lua-indent-on-new-line
+ hook window InsertChar \n -group lua-insert lua-insert-on-new-line
+
+ alias window alt lua-alternative-file
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window lua-.+
+ unalias window alt lua-alternative-file
+ }
+}
+
+hook -group lua-highlight global WinSetOption filetype=lua %{
+ add-highlighter window/lua ref lua
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/lua }
+}
+
+
+provide-module lua %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/lua regions
+add-highlighter shared/lua/code default-region group
+add-highlighter shared/lua/raw_string region -match-capture '\[(=*)\[' '\](=*)\]' fill string
+add-highlighter shared/lua/raw_comment region -match-capture '--\[(=*)\[' '\](=*)\]' fill comment
+add-highlighter shared/lua/double_string region '"' (?<!\\)(?:\\\\)*" fill string
+add-highlighter shared/lua/single_string region "'" (?<!\\)(?:\\\\)*' fill string
+add-highlighter shared/lua/comment region '--' $ fill comment
+
+add-highlighter shared/lua/code/variable regex \b\w*\b 0:variable # Everything in Lua is a variable!
+add-highlighter shared/lua/code/function_declaration regex \b(?:function\h+)(?:\w+\h*\.\h*)*([a-zA-Z_]\w*)\( 1:function
+add-highlighter shared/lua/code/function_call regex \b([a-zA-Z_]\w*)\h*(?=[\(\{]) 1:function
+add-highlighter shared/lua/code/keyword regex \b(break|do|else|elseif|end|for|function|goto|if|in|local|repeat|return|then|until|while)\b 0:keyword
+add-highlighter shared/lua/code/value regex \b(false|nil|true|self|[0-9]+(:?\.[0-9])?(:?[eE]-?[0-9]+)?|0x[0-9a-fA-F])\b 0:value
+add-highlighter shared/lua/code/symbolic_operator regex (\+|-|\*|/|%|\^|==?|~=|<=?|>=?|\.\.|\.\.\.|#) 0:operator
+add-highlighter shared/lua/code/keyword_operator regex \b(and|or|not)\b 0:operator
+add-highlighter shared/lua/code/module regex \b(_G|_ENV)\b 0:module
+add-highlighter shared/lua/code/attribute regex \B(<[a-zA-Z_]\w*>)\B 0:attribute
+add-highlighter shared/lua/code/label regex \s(::\w*::) 1:meta
+add-highlighter shared/lua/code/goto_label regex "\bgoto (\w*)\b" 1:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command lua-alternative-file -docstring 'Jump to the alternate file (implementation ↔ test)' %{ evaluate-commands %sh{
+ case $kak_buffile in
+ *spec/*_spec.lua)
+ altfile=$(eval printf %s\\n $(printf %s\\n $kak_buffile | sed s+spec/+'*'/+';'s/_spec//))
+ [ ! -f $altfile ] && echo "fail 'implementation file not found'" && exit
+ ;;
+ *.lua)
+ altfile=""
+ altdir=""
+ path=$kak_buffile
+ dirs=$(while [ $path ]; do printf %s\\n $path; path=${path%/*}; done | tail -n +2)
+ for dir in $dirs; do
+ altdir=$dir/spec
+ if [ -d $altdir ]; then
+ altfile=$altdir/$(realpath $kak_buffile --relative-to $dir | sed s+[^/]'*'/++';'s/.lua$/_spec.lua/)
+ break
+ fi
+ done
+ [ ! -d "$altdir" ] && echo "fail 'spec/ not found'" && exit
+ ;;
+ *)
+ echo "fail 'alternative file not found'" && exit
+ ;;
+ esac
+ printf %s\\n "edit $altfile"
+}}
+
+define-command -hidden lua-trim-indent %[
+ # remove trailing whitespaces
+ try %[ execute-keys -draft -itersel x s \h+$ <ret> d ]
+]
+
+define-command -hidden lua-indent-on-char %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # unindent middle and end structures
+ try %[ execute-keys -draft \
+ <a-h><a-k>^\h*(\b(end|else|elseif|until)\b|[)}])$<ret> \
+ :lua-indent-on-new-line<ret> \
+ <a-lt>
+ ]
+ ]
+]
+
+define-command -hidden lua-indent-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # remove trailing white spaces from previous line
+ try %[ execute-keys -draft k : lua-trim-indent <ret> ]
+ # preserve previous non-empty line indent
+ try %[ execute-keys -draft ,gh<a-?>^[^\n]+$<ret>s\A|.\z<ret>)<a-&> ]
+ # add one indentation level if the previous line is not a comment and:
+ # - starts with a block keyword that is not closed on the same line,
+ # - or contains an unclosed function expression,
+ # - or ends with an enclosed '(' or '{'
+ try %[ execute-keys -draft \
+ , Kx \
+ <a-K>\A\h*--<ret> \
+ <a-K>\A[^\n]*\b(end|until)\b<ret> \
+ <a-k>\A(\h*\b(do|else|elseif|for|function|if|repeat|while)\b|[^\n]*[({]$|[^\n]*\bfunction\b\h*[(])<ret> \
+ <a-:><semicolon><a-gt>
+ ]
+ ]
+]
+
+define-command -hidden lua-insert-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # copy -- comment prefix and following white spaces
+ try %[ execute-keys -draft kxs^\h*\K--\h*<ret> y gh j x<semicolon> P ]
+ # wisely add end structure
+ evaluate-commands -save-regs x %[
+ # save previous line indent in register x
+ try %[ execute-keys -draft kxs^\h+<ret>"xy ] catch %[ reg x '' ]
+ try %[
+ # check that starts with a block keyword that is not closed on the same line
+ execute-keys -draft \
+ kx \
+ <a-k>^\h*\b(else|elseif|do|for|function|if|while)\b|[^\n]\bfunction\b\h*[(]<ret> \
+ <a-K>\bend\b<ret>
+ # check that the block is empty and is not closed on a different line
+ execute-keys -draft <a-a>i <a-K>^[^\n]+\n[^\n]+\n<ret> jx <a-K>^<c-r>x\b(else|elseif|end)\b<ret>
+ # auto insert end
+ execute-keys -draft o<c-r>xend<esc>
+ # auto insert ) for anonymous function
+ execute-keys -draft kx<a-k>\([^)\n]*function\b<ret>jjA)<esc>
+ ]
+ ]
+ ]
+]
+
diff --git a/autoload/filetype/mail.kak b/autoload/filetype/mail.kak
new file mode 100644
index 0000000..b6ef616
--- /dev/null
+++ b/autoload/filetype/mail.kak
@@ -0,0 +1,30 @@
+hook global BufCreate .+\.eml %{
+ set-option buffer filetype mail
+}
+
+hook global WinSetOption filetype=mail %{
+ require-module mail
+ map buffer normal <ret> :diff-jump<ret>
+ hook -once -always window WinSetOption filetype=.* %{
+ unmap buffer normal <ret> :diff-jump<ret>
+ }
+}
+
+hook -group mail-highlight global WinSetOption filetype=mail %{
+ add-highlighter window/mail ref mail
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/mail }
+}
+
+
+provide-module mail %{
+
+require-module diff
+
+add-highlighter shared/mail group
+add-highlighter shared/mail/ ref diff
+add-highlighter shared/mail/ regex ^(From|To|Cc|Bcc|Subject|Reply-To|In-Reply-To|References|Date|Message-Id|User-Agent):([^\n]*(?:\n\h+[^\n]+)*)$ 1:keyword 2:attribute
+add-highlighter shared/mail/ regex <[a-zA-Z0-9!#$%&'*+/=?^`{|}~.-]+@[a-zA-Z0-9!#$%&'*+/=?^`{|}~.-]+> 0:string
+add-highlighter shared/mail/ regex ^>.*?$ 0:comment
+add-highlighter shared/mail/ regex ^--\ \n.* 0:comment
+
+}
diff --git a/autoload/filetype/makefile.kak b/autoload/filetype/makefile.kak
new file mode 100644
index 0000000..c668d69
--- /dev/null
+++ b/autoload/filetype/makefile.kak
@@ -0,0 +1,75 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*(/?[mM]akefile|\.mk|\.make) %{
+ set-option buffer filetype makefile
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=makefile %{
+ require-module makefile
+
+ set-option window static_words %opt{makefile_static_words}
+
+ hook window ModeChange pop:insert:.* -group makefile-trim-indent makefile-trim-indent
+ hook window InsertChar \n -group makefile-indent makefile-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window makefile-.+ }
+}
+
+hook -group makefile-highlight global WinSetOption filetype=makefile %{
+ add-highlighter window/makefile ref makefile
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/makefile }
+}
+
+provide-module makefile %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/makefile regions
+
+add-highlighter shared/makefile/content default-region group
+add-highlighter shared/makefile/comment region (?<!\\)(?:\\\\)*(?:^|\h)\K# '$' fill comment
+add-highlighter shared/makefile/evaluate-commands region -recurse \( (?<!\$)(?:\$\$)*\K\$\( \) fill value
+
+add-highlighter shared/makefile/content/ regex ^\S.*?(::|:|!)\s 0:variable
+add-highlighter shared/makefile/content/ regex [+?:]= 0:operator
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="ifeq|ifneq|ifdef|ifndef|else|endif|define|endef"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list makefile_static_words ${keywords}" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "add-highlighter shared/makefile/content/ regex \b(${keywords})\b 0:keyword"
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden makefile-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden makefile-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ ## If the line above is a target indent with a tab
+ try %{ execute-keys -draft Z kx <a-k>^\S.*?(::|:|!)\s<ret> z i<tab> }
+ # cleanup trailing white space son previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # indent after some keywords
+ try %{ execute-keys -draft Z kx <a-k> ^\h*(ifeq|ifneq|ifdef|ifndef|else|define)\b<ret> z <a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/markdown.kak b/autoload/filetype/markdown.kak
new file mode 100644
index 0000000..b515909
--- /dev/null
+++ b/autoload/filetype/markdown.kak
@@ -0,0 +1,130 @@
+# http://daringfireball.net/projects/markdown
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](markdown|md|mkd) %{
+ set-option buffer filetype markdown
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=markdown %{
+ require-module markdown
+
+ hook window ModeChange pop:insert:.* -group markdown-trim-indent markdown-trim-indent
+ hook window InsertChar \n -group markdown-insert markdown-insert-on-new-line
+ hook window InsertChar \n -group markdown-indent markdown-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window markdown-.+ }
+}
+
+hook -group markdown-load-languages global WinSetOption filetype=markdown %{
+ markdown-load-languages '%'
+}
+
+hook -group markdown-load-languages global WinSetOption filetype=markdown %{
+ hook -group markdown-load-languages window NormalIdle .* %{markdown-load-languages gtGbGl}
+ hook -group markdown-load-languages window InsertIdle .* %{markdown-load-languages gtGbGl}
+}
+
+
+hook -group markdown-highlight global WinSetOption filetype=markdown %{
+ add-highlighter window/markdown ref markdown
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/markdown }
+}
+
+
+provide-module markdown %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/markdown regions
+add-highlighter shared/markdown/inline default-region regions
+add-highlighter shared/markdown/inline/text default-region group
+
+add-highlighter shared/markdown/listblock region ^\h*[-*]\s ^(?=\S) regions
+add-highlighter shared/markdown/listblock/g default-region group
+add-highlighter shared/markdown/listblock/g/ ref markdown/inline
+add-highlighter shared/markdown/listblock/g/marker regex ^\h*([-*])\s 1:bullet
+
+add-highlighter shared/markdown/codeblock region -match-capture \
+ ^(\h*)```\h* \
+ ^(\h*)```\h*$ \
+ regions
+add-highlighter shared/markdown/codeblock/ default-region fill meta
+add-highlighter shared/markdown/listblock/codeblock region -match-capture \
+ ^(\h*)```\h* \
+ ^(\h*)```\h*$ \
+ regions
+add-highlighter shared/markdown/listblock/codeblock/ default-region fill meta
+add-highlighter shared/markdown/codeline region "^( {4}|\t)" "$" fill meta
+
+# https://spec.commonmark.org/0.29/#link-destination
+add-highlighter shared/markdown/angle_bracket_url region (?<=<)([a-z]+://|(mailto|magnet|xmpp):) (?!\\).(?=>)|\n fill link
+add-highlighter shared/markdown/inline/url region -recurse \( (\b[a-z]+://|(mailto|magnet|xmpp):) (?!\\).(?=\))|\s fill link
+add-highlighter shared/markdown/listblock/angle_bracket_url region (?<=<)(\b[a-z]+://|(mailto|magnet|xmpp):) (?!\\).(?=>)|\n fill link
+
+try %{
+ require-module html
+ add-highlighter shared/markdown/inline/tag region (?i)</?[a-z][a-z0-9-]*\s*([a-z_:]|(?=>)) > ref html/tag
+}
+
+add-highlighter shared/markdown/inline/code region -match-capture (`+) (`+) fill mono
+
+# Setext-style header
+add-highlighter shared/markdown/inline/text/ regex (\A|^\n)[^\n]+\n={2,}\h*\n\h*$ 0:title
+add-highlighter shared/markdown/inline/text/ regex (\A|^\n)[^\n]+\n-{2,}\h*\n\h*$ 0:header
+
+# Atx-style header
+add-highlighter shared/markdown/inline/text/ regex ^#[^\n]* 0:header
+
+add-highlighter shared/markdown/inline/text/ regex (?<!\*)(\*([^\s*]|([^\s*](\n?[^\n*])*[^\s*]))\*)(?!\*) 1:+i
+add-highlighter shared/markdown/inline/text/ regex (?<!_)(_([^\s_]|([^\s_](\n?[^\n_])*[^\s_]))_)(?!_) 1:+i
+add-highlighter shared/markdown/inline/text/ regex (?<!\*)(\*\*([^\s*]|([^\s*](\n?[^\n*])*[^\s*]))\*\*)(?!\*) 1:+b
+add-highlighter shared/markdown/inline/text/ regex (?<!_)(__([^\s_]|([^\s_](\n?[^\n_])*[^\s_]))__)(?!_) 1:+b
+add-highlighter shared/markdown/inline/text/ regex ^\h*(>\h*)+ 0:comment
+add-highlighter shared/markdown/inline/text/ regex "\H( {2,})$" 1:+r@meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command markdown-load-languages -params 1 %{
+ evaluate-commands -draft %{ try %{
+ execute-keys "%arg{1}1s```\h*\{?[.=]?(\w+)\}?<ret>"
+ evaluate-commands -itersel %{ try %{
+ require-module %val{selection}
+ add-highlighter "shared/markdown/codeblock/%val{selection}" region -match-capture "^(\h*)```\h*(%val{selection}\b|\{[.=]?%val{selection}\})" ^(\h*)``` regions
+ add-highlighter "shared/markdown/codeblock/%val{selection}/" default-region fill meta
+ add-highlighter "shared/markdown/codeblock/%val{selection}/inner" region \A\h*```[^\n]*\K (?=```) ref %val{selection}
+ add-highlighter "shared/markdown/listblock/codeblock/%val{selection}" region -match-capture "^(\h*)```\h*(%val{selection}\b|\{[.=]?%val{selection}\})" ^(\h*)``` regions
+ add-highlighter "shared/markdown/listblock/codeblock/%val{selection}/" default-region fill meta
+ add-highlighter "shared/markdown/listblock/codeblock/%val{selection}/inner" region \A\h*```[^\n]*\K (?=```) ref %val{selection}
+ }}
+ }}
+}
+
+define-command -hidden markdown-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden markdown-insert-on-new-line %{
+ try %{ execute-keys -draft -itersel k x s ^\h*\K((>\h*)+([*+-]\h)?|(>\h*)*[*+-]\h)\h* <ret> y gh j P }
+}
+
+define-command -hidden markdown-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # remove trailing white spaces
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ }
+}
+
+}
diff --git a/autoload/filetype/mercurial.kak b/autoload/filetype/mercurial.kak
new file mode 100644
index 0000000..255b02b
--- /dev/null
+++ b/autoload/filetype/mercurial.kak
@@ -0,0 +1,37 @@
+# https://www.mercurial-scm.org/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*hg-editor-.*\.txt$ %{
+ set-option buffer filetype hg-commit
+}
+
+hook global WinSetOption filetype=hg-commit %{
+ require-module hg-commit
+}
+
+hook -group hg-commit-highlight global WinSetOption filetype=hg-commit %{
+ add-highlighter window/hg-commit ref hg-commit
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/hg-commit-highlight }
+}
+
+provide-module hg-commit %{
+
+# Faces
+# ‾‾‾‾‾
+
+set-face global MercurialCommitComment cyan
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/hg-commit regions
+add-highlighter shared/hg-commit/comments region ^HG:\ $ group
+add-highlighter shared/hg-commit/comments/ fill comment
+add-highlighter shared/hg-commit/comments/ regex \
+ "\b(?:(changed)|(removed)|(added)|(bookmark)|(branch)|(user:)) ([^\n]*)$" \
+ 1:yellow 2:red 3:green 4:blue 5:magenta 6:white
+
+}
diff --git a/autoload/filetype/mercury.kak b/autoload/filetype/mercury.kak
new file mode 100644
index 0000000..813b4ca
--- /dev/null
+++ b/autoload/filetype/mercury.kak
@@ -0,0 +1,124 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](m) %{
+ set-option buffer filetype mercury
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=mercury %{
+ require-module mercury
+
+ set-option window static_words %opt{mercury_static_words}
+
+ hook window InsertChar \n -group mercury-insert mercury-insert-on-new-line
+ hook window InsertChar \n -group mercury-indent mercury-indent-on-new-line
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group mercury-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window mercury-.+ }
+}
+
+hook -group mercury-highlight global WinSetOption filetype=mercury %{
+ add-highlighter window/mercury ref mercury
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/mercury }
+}
+
+
+provide-module mercury %§
+
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/mercury regions
+add-highlighter shared/mercury/code default-region group
+add-highlighter shared/mercury/comment region '%' '$' fill comment
+add-highlighter shared/mercury/line_comment region '%' '$' fill comment
+
+add-highlighter shared/mercury/string region '"' (?<!\\)(\\\\)*" fill string
+# Integer formats
+add-highlighter shared/mercury/code/ regex '\b0b[01]+\b' 0:value
+add-highlighter shared/mercury/code/ regex '\b0x[\da-f]+\b' 0:value
+add-highlighter shared/mercury/code/ regex '\b0o?[0-7]+\b' 0:value
+add-highlighter shared/mercury/code/ regex '\b([1-9]\d*|0)(i8|i16|i32|i64|u|u8|u16|u32|u64)?\b' 0:value
+# Float formats
+add-highlighter shared/mercury/code/ regex '\b(\d+_\d+)*\d+[eE][+-]?\d+(\d+_\d+)*\b' 0:value
+add-highlighter shared/mercury/code/ regex '(\b\d+)\.\d+\b' 0:value
+add-highlighter shared/mercury/code/ regex '`[A-Za-z][A-Za-z_0-9]+`' 0:operator
+
+add-highlighter shared/mercury/code/ regex \b[a-z][A-Za-z_0-9]*(?=\.\w) 0:module
+add-highlighter shared/mercury/code/ regex \b[a-z][A-Za-z_0-9]*(?=__\w) 0:module
+# func() and pred()
+add-highlighter shared/mercury/code/ regex \b(pred|func)(?=\() 0:builtin
+add-highlighter shared/mercury/code/ regex \b[A-Z][A-Za-z_0-9]*\b 0:variable
+
+# operator symbols
+add-highlighter shared/mercury/code/ regex (\.|!|!\.|!:|@|\^|:|\*\*|\\|\*|/|//|<<|>>|\+|\+\+|-|--|/\\|\\/|\.\.|:=|=\^|<|=|=\.\.|=:=|=<|==|=\\=|>|>=|@<|@=<|@>|@>=|\\=|\\==|~=|\\\+|~|<=|<=>|=>|,|&|->|\;|::|==>|--->|-->|:-|\?-) 0:operator
+
+evaluate-commands %sh{
+ # Grammar
+ values="true fail"
+
+ # There is overlap between all of these. Not sure what to do about that.
+ operators="event div mod rem for is and or impure semipure not when all arbitrary atomic disable_warning disable_warnings promise_equivalent_solutions promise_equivalent_solution_setspromise_exclusive promise_exclusive_exhaustive promise_exhaustive promise_impure promise_pure promise_semipure require_complete_switch require_switch_arms_det require_switch_arms_semidet require_switch_arms_multi require_switch_arms_nondet require_switch_arms_cc_multi require_switch_arms_cc_nondet require_switch_arms_erroneous require_switch_arms_failure require_det require_semidet require_multi require_nondet require_cc_multi require_cc_nondet require_erroneous require_failure trace try some or_else then if else where catch catch_any initialize finalize rule solver type pred func inst mode typeclass instance pragma promise initialise finalise mutable module import_module use_module include_module"
+ modes="free bound in out di mdi uo muo"
+ determinisms="erroneous failure det semidet multi cc_multi nondet cc_nondet"
+ reserved_insts="any bound bound_unique clobbered clobbered_any free ground is mostly_clobbered mostly_unique mostly_unique_any not_reached unique unique_any"
+ reserved_modes="any_func any_pred func is pred"
+ reserved_types="int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float character string pred func pure semipure impure"
+
+ declarations="type solver type pred func inst mode typeclass instance pragma promise initialise finalise mutable module interface implementation import_module use_module include_module end_module"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list mercury_static_words $(join "${values} ${declarations} ${operators} ${determinisms} ${modes} ${reserved_insts} ${reserved_modes}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/mercury/code/ regex '\b($(join "${values}" '|'))\b' 0:value
+ add-highlighter shared/mercury/code/ regex '^:-(\s+($(join "${declarations}" '|')))+\b' 1:keyword
+ add-highlighter shared/mercury/code/ regex '\b($(join "${operators}" '|'))\b' 0:operator
+ add-highlighter shared/mercury/code/ regex '\b($(join "${determinisms}" '|'))\b' 0:keyword
+ add-highlighter shared/mercury/code/ regex '\b($(join "${modes}" '|'))\b' 0:keyword
+
+ # These overlap with previous. Not sure what the solution is.
+ # add-highlighter shared/mercury/code/ regex '\b($(join "${reserved_types}" '|'))\b' 0:type
+ # add-highlighter shared/mercury/code/ regex '\b($(join "${reserved_insts}" '|'))\b' 0:builtin
+ # add-highlighter shared/mercury/code/ regex '\b($(join "${reserved_modes}" '|'))\b' 0:builtin
+
+ # Implementation-defined literals - don't know how to get these to work
+ # add-highlighter shared/mercury/code/ regex '\$(file|line|module|pred)' 0:keyword
+ "
+}
+
+define-command -hidden mercury-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '%' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\%\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden mercury-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %/ execute-keys -draft <a-h> <a-k> ^\h+[]}]$ <ret> m s \A|.\z <ret> 1<a-&> /
+ >
+>
+
+define-command -hidden mercury-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with :-
+ try %{ execute-keys -draft , k x <a-k> :-$ <ret> j <a-gt> }
+ # deindent closing brace/bracket when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[}\])] <ret> gh / [}\])] <ret> m <a-S> 1<a-&> >
+ >
+>
+
diff --git a/autoload/filetype/meson.kak b/autoload/filetype/meson.kak
new file mode 100644
index 0000000..362af32
--- /dev/null
+++ b/autoload/filetype/meson.kak
@@ -0,0 +1,77 @@
+# meson syntax highlighting for kakoune (https://mesonbuild.com)
+#
+# For reference see:
+# https://mesonbuild.com/Syntax.html
+# https://github.com/mesonbuild/meson/blob/master/data/syntax-highlighting/vim/syntax/meson.vim
+
+## Detection
+
+hook global BufCreate (.*/|^)(meson\.build|meson_options\.txt) %{
+ set-option buffer filetype meson
+}
+
+## Initialization
+
+hook -group meson-highlight global WinSetOption filetype=meson %{
+ require-module meson
+
+ set-option window static_words %opt{meson_static_words}
+
+ add-highlighter window/meson ref meson
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/meson }
+}
+
+provide-module meson %§
+
+## Highlighters
+
+add-highlighter shared/meson regions
+add-highlighter shared/meson/code default-region group
+
+add-highlighter shared/meson/comment region '(\h|^)#' '$' fill comment
+
+# TODO: highlight escape sequences within strings
+add-highlighter shared/meson/string_multiline region "'''" "'''" fill string
+add-highlighter shared/meson/string_single region "'" (?<!\\)(\\\\)*' fill string
+
+# integer literals
+add-highlighter shared/meson/code/ regex '\b\d+\b' 0:value
+
+# operators
+add-highlighter shared/meson/code/ regex '(?:\+|-|\*|/|%|!=|=|<|>|\?|:)' 0:operator
+add-highlighter shared/meson/code/ regex '\b(?:and|not|or|in)\b' 0:operator
+
+# functions
+add-highlighter shared/meson/code/ regex "\b(\w+)\(" 1:function
+
+evaluate-commands %sh{
+ values="true false"
+
+ keywords="if else endif elif foreach endforeach break continue"
+
+ builtins="add_global_arguments add_global_link_arguments add_languages
+ add_project_arguments add_project_link_arguments add_test_setup
+ alias_target assert benchmark both_libraries build_machine build_target
+ configuration_data configure_file custom_target declare_dependency
+ dependency disabler environment error executable files find_library
+ find_program generator get_option get_variable gettext host_machine
+ import include_directories install_data install_headers install_man
+ install_subdir is_disabler is_variable jar join_paths library meson
+ message option project run_command run_target set_variable shared_library
+ shared_module static_library subdir subdir_done subproject summary
+ target_machine test vcs_tag warning"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list meson_static_words $(join "${values} ${keywords} ${builtins}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/meson/code/ regex '\b($(join "${values}" '|'))\b' 0:value
+ add-highlighter shared/meson/code/ regex '\b($(join "${keywords}" '|'))\b' 0:keyword
+ add-highlighter shared/meson/code/ regex '\b($(join "${builtins}" '|'))\b\(' 1:builtin
+ "
+}
+
diff --git a/autoload/filetype/mlb.kak b/autoload/filetype/mlb.kak
new file mode 100644
index 0000000..aa17423
--- /dev/null
+++ b/autoload/filetype/mlb.kak
@@ -0,0 +1,50 @@
+# http://mlton.org/MLBasis
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.mlb %{
+ set-option buffer filetype mlb
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=mlb %{
+ require-module mlb
+ set-option buffer extra_word_chars '_' '-' '.'
+ set-option window static_words %opt{mlb_static_words}
+}
+
+hook -group mlb-highlight global WinSetOption filetype=mlb %{
+ add-highlighter window/mlb ref mlb
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/mlb }
+}
+
+provide-module mlb %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/mlb regions
+add-highlighter shared/mlb/code default-region group
+add-highlighter shared/mlb/string region '"' '(?<!\\)(\\\\)*"' group
+add-highlighter shared/mlb/string/fill fill string
+add-highlighter shared/mlb/comment region -recurse '\(\*' '\(\*' '\*\)' fill comment
+
+evaluate-commands %sh{
+ keywords='basis bas and open local let in end structure signature functor ann'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ printf %s\\n "declare-option str-list mlb_static_words $(join "${keywords}" ' ')"
+ printf %s\\n "add-highlighter shared/mlb/code/ regex (?<![\w'-/.])($(join "${keywords}" '|'))(?![\w'-/.]) 0:keyword"
+}
+add-highlighter shared/mlb/code/ regex "=" 0:operator
+add-highlighter shared/mlb/code/ regex "\b([A-Z][\w']*)\b" 0:type
+add-highlighter shared/mlb/code/ regex "\b[A-Z]{2}[A-Z0-9_']+\b" 0:attribute
+add-highlighter shared/mlb/code/ regex "\$\(\w+\)" 0:variable
+add-highlighter shared/mlb/string/ regex "\$\(\w*\)" 0:variable
+
+]
diff --git a/autoload/filetype/moon.kak b/autoload/filetype/moon.kak
new file mode 100644
index 0000000..43d8dd4
--- /dev/null
+++ b/autoload/filetype/moon.kak
@@ -0,0 +1,120 @@
+# http://moonscript.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](moon) %{
+ set-option buffer filetype moon
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=moon %{
+ require-module moon
+
+ hook window ModeChange pop:insert:.* -group moon-trim-indent moon-trim-indent
+ hook window InsertChar .* -group moon-indent moon-indent-on-char
+ hook window InsertChar \n -group moon-insert moon-insert-on-new-line
+ hook window InsertChar \n -group moon-indent moon-indent-on-new-line
+
+ alias window alt moon-alternative-file
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window moon-.+
+ unalias window alt moon-alternative-file
+ }
+}
+
+hook -group moon-highlight global WinSetOption filetype=moon %{
+ add-highlighter window/moon ref moon
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/moon }
+}
+
+
+provide-module moon %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/moon regions
+add-highlighter shared/moon/code default-region group
+add-highlighter shared/moon/double_string region '"' (?<!\\)(\\\\)*" regions
+add-highlighter shared/moon/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/moon/comment region '--' '$' fill comment
+
+add-highlighter shared/moon/double_string/base default-region fill string
+add-highlighter shared/moon/double_string/interpolation region -recurse \{ \Q#{ \} fill meta
+
+add-highlighter shared/moon/code/ regex ([.\\](?=[A-Za-z]))|(\b[A-Za-z]\w*:)|(\b[A-Za-z]\w*\K!+)|(\W\K[@:][A-Za-z]\w*) 0:variable
+add-highlighter shared/moon/code/ regex \b(and|break|catch|class|continue|do|else(if)?|export|extends|false|finally|for|from|if|import|in|local|nil|not|or|return|super|switch|then|true|try|unless|using|when|while|with)\b 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command moon-alternative-file -docstring 'Jump to the alternate file (implementation ↔ test)' %{ evaluate-commands %sh{
+ case $kak_buffile in
+ *spec/*_spec.moon)
+ altfile=$(eval printf %s\\n $(printf %s\\n $kak_buffile | sed s+spec/+'*'/+';'s/_spec//))
+ [ ! -f $altfile ] && echo "fail 'implementation file not found'" && exit
+ ;;
+ *.moon)
+ path=$kak_buffile
+ dirs=$(while [ $path ]; do printf %s\\n $path; path=${path%/*}; done | tail -n +2)
+ for dir in $dirs; do
+ altdir=$dir/spec
+ if [ -d $altdir ]; then
+ altfile=$altdir/$(realpath $kak_buffile --relative-to $dir | sed s+[^/]'*'/++';'s/.moon$/_spec.moon/)
+ break
+ fi
+ done
+ [ ! -d $altdir ] && echo "fail 'spec/ not found'" && exit
+ ;;
+ *)
+ echo "fail 'alternative file not found'" && exit
+ ;;
+ esac
+ printf %s\\n "edit $altfile"
+}}
+
+define-command -hidden moon-trim-indent %{
+ evaluate-commands -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden moon-indent-on-char %{
+ evaluate-commands -draft -itersel %{
+ # align _else_ statements to start
+ try %{ execute-keys -draft x <a-k> ^ \h * (else(if)?) $ <ret> <a-semicolon> <a-?> ^ \h * (if|unless|when) <ret> s \A | \z <ret> ) <a-&> }
+ # align _when_ to _switch_ then indent
+ try %{ execute-keys -draft x <a-k> ^ \h * (when) $ <ret> <a-semicolon> <a-?> ^ \h * (switch) <ret> s \A | \z <ret> ) <a-&> ) , <gt> }
+ # align _catch_ and _finally_ to _try_
+ try %{ execute-keys -draft x <a-k> ^ \h * (catch|finally) $ <ret> <a-semicolon> <a-?> ^ \h * (try) <ret> s \A | \z <ret> ) <a-&> }
+ }
+}
+
+define-command -hidden moon-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden moon-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : moon-trim-indent <ret> }
+ # indent after start structure
+ try %{ execute-keys -draft k x <a-k> ^ \h * (class|else(if)?|for|if|switch|unless|when|while|with) \b | ([:=]|[-=]>) $ <ret> j <a-gt> }
+ # deindent after return statements
+ try %{ execute-keys -draft k x <a-k> ^ \h * (break|return) \b <ret> j <a-lt> }
+ }
+}
+
+]
diff --git a/autoload/filetype/nim.kak b/autoload/filetype/nim.kak
new file mode 100644
index 0000000..2c00ac3
--- /dev/null
+++ b/autoload/filetype/nim.kak
@@ -0,0 +1,132 @@
+# https://nim-lang.org/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.nim(s|ble)? %{
+ set-option buffer filetype nim
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=nim %{
+ require-module nim
+
+ set-option window static_words %opt{nim_static_words}
+
+ hook window InsertChar \n -group nim-insert nim-insert-on-new-line
+ hook window InsertChar \n -group nim-indent nim-indent-on-new-line
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group nim-trim-indent %{ try %{ exec -draft <semicolon> x s ^\h+$ <ret> d } }
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window nim-.+ }
+}
+
+hook -group nim-highlight global WinSetOption filetype=nim %{
+ add-highlighter window/nim ref nim
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/nim }
+}
+
+provide-module nim %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/nim regions
+add-highlighter shared/nim/code default-region group
+add-highlighter shared/nim/triple_string region '([A-Za-z](_?\w)*)?"""' '"""(?!")' fill string
+add-highlighter shared/nim/raw_string region [A-Za-z](_?[A-Za-z])*" (?<!")"(?!") fill string
+add-highlighter shared/nim/string region (?<!'\\)" ((?<!\\)(\\\\)*"|$) group
+add-highlighter shared/nim/inline_documentation region '##' $ fill documentation
+add-highlighter shared/nim/documentation region '##\[' '\]##' fill documentation
+add-highlighter shared/nim/comment region '#\[' '\]#' group
+add-highlighter shared/nim/comment_line region (?<![^'].')#(?!'\[) $ group
+
+add-highlighter shared/nim/string/fill fill string
+add-highlighter shared/nim/comment/fill fill comment
+add-highlighter shared/nim/comment_line/fill fill comment
+
+evaluate-commands %sh{
+ # Grammar
+ opchars='[=+-/<>@$~&%|!?^.:\\*]'
+ opnocol='[=+-/<>@$~&%|!?^.\\*]'
+ letter='A-Za-z\u000080-\u10FFFF'
+ customsuffix="'[${letter}](_?[${letter}0-9])*"
+ suffix="(${customsuffix}|[iIuU](8|16|32|64)|[fF](32|64)?|[dDuU])?"
+ floatsuffix="(${customsuffix}|[fF](32|64)?|[dD])?"
+ hexdigit='[0-9a-fA-F]'
+ octdigit='[0-7]'
+ bindigit='[01]'
+ hexlit="0[xX]${hexdigit}(_?${hexdigit})*"
+ declit="\d(_?\d)*"
+ octlit="0o${octdigit}(_?${octdigit})*"
+ binlit="0[bB]${bindigit}(_?${bindigit})*"
+ intlit="\b(${declit}|${hexlit}|${octlit}|${binlit})${suffix}\b"
+ exponent="([eE][+-]?${declit})"
+ floatlit="\b${declit}(\.${declit}${exponent}?|${exponent})${floatsuffix}\b"
+
+ keywords="addr asm bind block break case cast concept const continue
+ converter defer discard distinct do elif else end enum except export
+ finally for func if import include interface iterator let macro
+ method mixin nil out proc ptr raise ref return static template try type
+ unsafeAddr using var when while yield with without atomic generic"
+ operators="or xor and is isnot in notin of div mod shl shr not as from"
+ types="int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float
+ float32 float64 bool char object seq array cstring string tuple varargs
+ typedesc pointer byte set typed untyped void auto"
+ values="false true on off"
+
+ join() { sep=$2; set -- $1; IFS="$sep"; echo "$*"; }
+
+ static_words="$(join "${keywords} ${types} ${operator} ${values}" ' ')"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list nim_static_words ${static_words}"
+
+ keywords="$(join "${keywords}" '|')"
+ operators="$(join "${operators}" '|')"
+ types="$(join "${types}" '|')"
+ values="$(join "${values}" '|')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/nim/code/ regex ${opchars}+ 0:operator
+ add-highlighter shared/nim/code/ regex (?<!${opchars}):{1,2}(?!${opchars}) 0:meta
+ add-highlighter shared/nim/code/ regex :${opnocol}${opchars}* 0:operator
+ add-highlighter shared/nim/code/ regex (?<!${opchars})(\*)(:)(?!${opchars}) 1:operator 2:meta
+ add-highlighter shared/nim/code/ regex \b(${keywords})\b 0:keyword
+ add-highlighter shared/nim/code/ regex \b(${operators})\b 0:operator
+ add-highlighter shared/nim/code/ regex \b(${types})\b 0:type
+ add-highlighter shared/nim/code/ regex \b(${values})\b 0:value
+ add-highlighter shared/nim/code/ regex ${intlit} 0:value
+ add-highlighter shared/nim/code/ regex ${floatlit} 0:value
+ "
+}
+
+add-highlighter shared/nim/code/ regex '(,|;|`|\(\.?|\.?\)|\[[.:]?|\.?\]|\{\.?|\.?\})' 0:meta
+add-highlighter shared/nim/code/ regex %{'(\\([rcnlftvabe\\"']|0*[12]?\d?\d|x[0-9a-fA-F]{2})|[^'\n])'} 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden nim-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ exec -draft k x s ^\h*#\h* <ret> y jgh P }
+ }
+}
+
+define-command -hidden nim-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ exec -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ exec -draft k x s \h+$ <ret> d }
+ # indent after line ending with enum, tuple, object, type, import, export, const, let, var, ':' or '='
+ try %{ exec -draft , k x <a-k> (:|=|\b(?:enum|tuple|object|const|let|var|import|export|type))$ <ret> j <a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/ninja.kak b/autoload/filetype/ninja.kak
new file mode 100644
index 0000000..757b7ad
--- /dev/null
+++ b/autoload/filetype/ninja.kak
@@ -0,0 +1,106 @@
+# ref: https://ninja-build.org/manual.html#ref_ninja_file
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .+\.ninja %{
+ set-option buffer filetype ninja
+}
+
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=ninja %{
+ require-module ninja
+
+ set-option window static_words %opt{ninja_static_words}
+
+ hook window ModeChange pop:insert:.* -group ninja-trim-indent ninja-trim-indent
+ hook window InsertChar \n -group ninja-insert ninja-insert-on-new-line
+ hook window InsertChar \n -group ninja-indent ninja-indent-on-new-line
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group ninja-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window ninja-.+ }
+}
+
+hook -group ninja-highlight global WinSetOption filetype=ninja %{
+ add-highlighter window/ninja ref ninja
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/ninja }
+}
+
+
+provide-module ninja %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/ninja regions
+
+# `#`
+add-highlighter shared/ninja/comment region '#' '\n' fill comment
+
+# `subninja`, `include`, `rule`, `pool` and `default` declarations
+add-highlighter shared/ninja/sirpd region '^(subninja|include|rule|pool|default)\b' '(?<!\$)\n' group
+add-highlighter shared/ninja/sirpd/default regex '^(subninja|include)\b' 0:module
+add-highlighter shared/ninja/sirpd/rulepool regex '^(rule|pool|default)\b' 0:keyword
+add-highlighter shared/ninja/sirpd/linebreak regex '\$$' 0:operator
+
+# `build`
+add-highlighter shared/ninja/build region '^build\b' '(?<!\$)\n' group
+add-highlighter shared/ninja/build/build regex '^build\b' 0:keyword
+add-highlighter shared/ninja/build/rule regex ':\h+((\w|-)+)' 0:function
+add-highlighter shared/ninja/build/colonpipe regex ':|\||\|\|' 0:operator
+add-highlighter shared/ninja/build/linebreak regex '\$$' 0:operator
+add-highlighter shared/ninja/build/variables regex '\$(\w|-)+|\$\{(\w|-)+\}' 0:value
+
+# variables declarations
+add-highlighter shared/ninja/variable region '^\h*(\w|-)+\h*=' '(?<!\$)\n' group
+add-highlighter shared/ninja/variable/declaredname regex '^\h*((\w|-)+)\h*(=)' 1:variable 0:operator
+add-highlighter shared/ninja/variable/linebreak regex '\$$' 0:operator
+add-highlighter shared/ninja/variable/variables regex '\$(\w|-)+|\$\{(\w|-)+\}' 0:value
+
+# keywords/builtin variable names
+evaluate-commands %sh{
+ keywords="rule build command default subninja include"
+ reserved_names="builddir ninja_required_version pool depfile deps depfile msvc_deps_prefix description dyndep generator restat rspfile rspfile_content"
+
+ printf %s "
+ declare-option str-list ninja_static_words ${keywords} ${reserved_names}
+ "
+
+ reserved_names_regex="$(echo ${reserved_names} | tr ' ' '|')"
+ printf %s "
+ add-highlighter shared/ninja/variable/reserved_names regex '^\h*(${reserved_names_regex})\b' 0:meta
+ "
+}
+
+
+# Indent
+# ‾‾‾‾‾‾
+
+define-command -hidden ninja-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden ninja-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden ninja-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft \; K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : ninja-trim-indent <ret> }
+ # indent after lines begining with rule and pool
+ try %{ execute-keys -draft \; k x <a-k> ^(rule|pool|build)\b <ret> j <a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/nix.kak b/autoload/filetype/nix.kak
new file mode 100644
index 0000000..35e5e4e
--- /dev/null
+++ b/autoload/filetype/nix.kak
@@ -0,0 +1,124 @@
+# Nix package manager language
+# https://nixos.org/nix/manual/
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](nix) %{
+ set-option buffer filetype nix
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=nix %{
+ require-module nix
+
+ hook window ModeChange pop:insert:.* -group nix-trim-indent nix-trim-indent
+ hook window InsertChar .* -group nix-indent nix-indent-on-char
+ hook window InsertChar \n -group nix-insert nix-insert-on-new-line
+ hook window InsertChar \n -group nix-indent nix-indent-on-new-line
+
+ set-option buffer extra_word_chars _ -
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window nix-.+ }
+}
+
+hook -group nix-highlight global WinSetOption filetype=nix %{
+ add-highlighter window/nix ref nix
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/nix }
+}
+
+provide-module nix %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/nix regions
+add-highlighter shared/nix/code default-region group
+# Define strings. They can contain interpolated nix code,
+# which itself can contain strings.
+# Note that we currently cannot properly support nesting of the same-delimiter strings
+# because of https://github.com/mawww/kakoune/issues/1670
+add-highlighter shared/nix/double_string region '"' (?<!\\)(?:\\\\)*" regions
+# this is hard one: it is terminated by '', but ''$, ''\* and ''' are escapes.
+add-highlighter shared/nix/indent_string region "''" (?<!')(?:''')*''(?![\\'$]) regions
+add-highlighter shared/nix/comment1 region '#' '$' fill comment
+add-highlighter shared/nix/comment2 region /\* \*/ fill comment
+
+
+#add-highlighter shared/nix/code/ regex "([a-zA-Z_][a-zA-Z0-9-_.]*)\s*([=])" 1:variable
+add-highlighter shared/nix/code/ regex "(\b|-)[0-9]*\.?[0-9eE]+\b" 0:value
+
+add-highlighter shared/nix/double_string/str default-region fill string
+add-highlighter shared/nix/double_string/variable region -recurse \{ (?<!\\)(\\\\)*\$\{ \} ref nix
+add-highlighter shared/nix/indent_string/str default-region fill string
+# FIXME: the opening regex is not ideal. See https://nixos.org/nix/manual/#idm140737317975776
+# It should usually match "${", should match "'''${" (as "'''" is escaped itself),
+# but should not match "''${" and "''\${".
+# Seems that negative lookbehind semantics is not enough for some complex cases.
+add-highlighter shared/nix/indent_string/variable region -recurse \{ (?<![^']'')\$\{ \} ref nix
+
+add-highlighter shared/nix/code/ regex \b(true|false|null|let|in|with|if|then|else)\b 0:keyword
+add-highlighter shared/nix/code/ regex \b(rec)\b\s*\{ 1:keyword
+# Those are builtin functions available in global scope.
+# They should not be assigned to.
+add-highlighter shared/nix/code/ regex '[^.]\s*\b(builtins|inherit|baseNameOf|derivation|dirOf|fetchTarball|import|isNull|map|removeAttrs|throw|toString)\b\s*[^=]' 1:builtin
+
+add-highlighter shared/nix/code/ regex '\b\s*(\.)\s*\b' 1:operator
+add-highlighter shared/nix/code/ regex '[^-a-zA-Z0-9_](-+)' 1:operator
+add-highlighter shared/nix/code/ regex '\?' 0:operator
+add-highlighter shared/nix/code/ regex '\+\+=?' 0:operator
+add-highlighter shared/nix/code/ regex '(\*|/|\+)' 0:operator
+add-highlighter shared/nix/code/ regex '!' 0:operator
+add-highlighter shared/nix/code/ regex '//=?' 0:operator
+add-highlighter shared/nix/code/ regex '[<>]=?\??' 0:operator
+add-highlighter shared/nix/code/ regex '(==|!=)' 0:operator
+add-highlighter shared/nix/code/ regex '(&&|\|\|)' 0:operator
+add-highlighter shared/nix/code/ regex '->' 0:operator
+add-highlighter shared/nix/code/ regex \bor\b 0:operator
+
+# override any operators matched before
+# path:
+add-highlighter shared/nix/code/ regex '\s\(*(\.?\.?[-A-Za-z0-9/_+.]*/[-A-Za-z0-9/_+.]*)[;?]?' 1:meta
+# imported path:
+add-highlighter shared/nix/code/ regex <[-A-Za-z0-9/_+.]+> 0:meta
+# RFC 2396 URIs can be used without quoting. Strangely, "string" ends URL but ''indented'' one doesn't
+# List of prohibited characters was tested manually in nix-repl as it is not properly documented.
+add-highlighter shared/nix/code/ regex '([^:/?#\s]+):([^#(){}\[\]";`|\s\\]+)' 0:string
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden nix-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden nix-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %/ execute-keys -draft <a-h> <a-k> ^\h+[\]}]$ <ret> m s \A|.\z <ret> 1<a-&> /
+ >
+>
+
+define-command -hidden nix-insert-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ >
+>
+
+define-command -hidden nix-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : nix-trim-indent <ret> }
+ # indent after lines beginning / ending with opener token
+ try %_ execute-keys -draft k x <a-k> ^\h*[[{]|[[{]$ <ret> j <a-gt> _
+ # deindent closer token(s) when after cursor
+ try %_ execute-keys -draft x <a-k> ^\h*[}\]] <ret> gh / [}\]] <ret> m <a-S> 1<a-&> _
+ >
+>
+
diff --git a/autoload/filetype/ocaml.kak b/autoload/filetype/ocaml.kak
new file mode 100644
index 0000000..06a1bf5
--- /dev/null
+++ b/autoload/filetype/ocaml.kak
@@ -0,0 +1,130 @@
+# http://ocaml.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(ml|mli|mll|mly)$ %{
+ set-option buffer filetype ocaml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=ocaml %{
+ require-module ocaml
+ set-option window static_words %opt{ocaml_static_words}
+ hook window InsertChar -group ocaml-insert '\*' ocaml-insert-closing-comment-bracket
+ hook window InsertChar \n -group ocaml-insert ocaml-insert-on-new-line
+ hook window ModeChange pop:insert:.* -group ocaml-trim-indent ocaml-trim-indent
+}
+
+hook -group ocaml-highlight global WinSetOption filetype=ocaml %{
+ add-highlighter window/ocaml ref ocaml
+ alias window alt ocaml-alternative-file
+ hook -once -always window WinSetOption filetype=.* %{
+ unalias window alt ocaml-alternative-file
+ remove-highlighter window/ocaml
+ }
+}
+
+provide-module ocaml %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/ocaml regions
+add-highlighter shared/ocaml/code default-region group
+add-highlighter shared/ocaml/string region (?<!['\\])" (?<!\\)(\\\\)*" fill string
+add-highlighter shared/ocaml/quotedstring region -match-capture %"\{(\w*)\|" %"\|(\w*)\}" fill string
+add-highlighter shared/ocaml/comment region -recurse \Q(* \Q(* \Q*) fill comment
+
+add-highlighter shared/ocaml/code/char regex %{\B'([^'\\]|(\\[\\"'nrtb])|(\\\d{3})|(\\x[a-fA-F0-9]{2})|(\\o[0-7]{3}))'\B} 0:value
+
+# integer literals
+add-highlighter shared/ocaml/code/ regex \b[0-9][0-9_]*([lLn]?)\b 0:value
+add-highlighter shared/ocaml/code/ regex \b0[xX][A-Fa-f0-9][A-Fa-f0-9_]*([lLn]?)\b 0:value
+add-highlighter shared/ocaml/code/ regex \b0[oO][0-7][0-7_]*([lLn]?)\b 0:value
+add-highlighter shared/ocaml/code/ regex \b0[bB][01][01_]*([lLn]?)\b 0:value
+# float literals
+add-highlighter shared/ocaml/code/ regex \b[0-9][0-9_]*(\.[0-9_]*)?([eE][+-]?[0-9][0-9_]*)? 0:value
+add-highlighter shared/ocaml/code/ regex \b0[xX][A-Fa-f0-9][A-Fa-f0-9]*(\.[a-fA-F0-9_]*)?([pP][+-]?[0-9][0-9_]*)? 0:value
+# constructors. must be put before any module name highlighter, as a fallback for capitalized identifiers
+add-highlighter shared/ocaml/code/ regex \b[A-Z][a-zA-Z0-9_]*\b 0:value
+# the module name in a module path, e.g. 'M' in 'M.x'
+add-highlighter shared/ocaml/code/ regex (\b[A-Z][a-zA-Z0-9_]*[\h\n]*)(?=\.) 0:module
+# (simple) module declarations
+add-highlighter shared/ocaml/code/ regex (?<=module)([\h\n]+[A-Z][a-zA-Z0-9_]*) 0:module
+# (simple) signature declarations. 'type' is also highlighted, due to the lack of quantifiers in lookarounds.
+# Hence we must put keyword highlighters after this to recover proper highlight for 'type'
+add-highlighter shared/ocaml/code/ regex (?<=module)([\h\n]+type[\h\n]+[A-Z][a-zA-Z0-9_]*) 0:module
+# (simple) open statements
+add-highlighter shared/ocaml/code/ regex (?<=open)([\h\n]+[A-Z][a-zA-Z0-9_]*) 0:module
+# operators
+add-highlighter shared/ocaml/code/ regex [@!$%%^&*\-+=|<>/?]+ 0:operator
+
+
+# Macro
+# ‾‾‾‾‾
+
+evaluate-commands %sh{
+ keywords="and|as|asr|assert|begin|class|constraint|do|done|downto|else|end|exception|external|false"
+ keywords="${keywords}|for|fun|function|functor|if|in|include|inherit|initializer|land|lazy|let|lor"
+ keywords="${keywords}|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|object|of|open|or"
+ keywords="${keywords}|private|rec|sig|struct|then|to|true|try|type|val|virtual|when|while|with"
+
+ printf %s\\n "declare-option str-list ocaml_static_words ${keywords}" | tr '|' ' '
+
+# must be put at last, since we have highlighted some keywords ('type' in 'module type') to other things
+ printf %s "
+ add-highlighter shared/ocaml/code/ regex \b(${keywords})\b 0:keyword
+ "
+}
+
+# Conveniences
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+# C has header and source files and you need to often switch between them.
+# Similarly OCaml has .ml (implementation) and .mli (interface files) and
+# one often needs to switch between them.
+#
+# This command provides a simple functionality that allows you to accomplish this.
+define-command ocaml-alternative-file -docstring 'Switch between .ml and .mli file or vice versa' %{
+ evaluate-commands %sh{
+ if [ "${kak_buffile##*.}" = 'ml' ]; then
+ printf "edit -- '%s'" "$(printf %s "${kak_buffile}i" | sed "s/'/''/g")"
+ elif [ "${kak_buffile##*.}" = 'mli' ]; then
+ printf "edit -- '%s'" "$(printf %s "${kak_buffile%i}" | sed "s/'/''/g")"
+ fi
+ }
+}
+
+}
+
+# Remove trailing whitespaces
+define-command -hidden ocaml-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ try %{ execute-keys -draft x s \h+$ <ret> d }
+ }
+}
+
+# Preserve indentation when creating new line
+define-command -hidden ocaml-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy white spaces at the beginnig of the previous line
+ try %{ execute-keys -draft k x s ^\h+ <ret> y jgh P x s \h+$ <a-d> }
+ # increase indentation if the previous line ended with either '=' sign or do keyword
+ try %{ execute-keys -draft k x s (=|\bdo)$ <ret> j <a-gt> }
+ }
+}
+
+# The OCaml comment is `(* Some comment *)`. Like the C-family this can be a multiline comment.
+#
+# Recognize when the user is trying to commence a comment when they type `(*` and
+# then automatically insert `*)` on behalf of the user. A small convenience.
+define-command -hidden ocaml-insert-closing-comment-bracket %{
+ try %{
+ execute-keys -draft 'HH<a-k>\(\*<ret>'
+ execute-keys ' *)<left><left><left>'
+ }
+}
diff --git a/autoload/filetype/odin.kak b/autoload/filetype/odin.kak
new file mode 100644
index 0000000..4f43dea
--- /dev/null
+++ b/autoload/filetype/odin.kak
@@ -0,0 +1,119 @@
+hook global BufCreate .*\.odin %{
+ set-option buffer filetype odin
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=odin %{
+ require-module odin
+
+ set-option window static_words %opt{odin_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group odin-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group odin-insert odin-insert-on-new-line
+ hook window InsertChar \n -group odin-indent odin-indent-on-new-line
+ hook window InsertChar \{ -group odin-indent odin-indent-on-opening-curly-brace
+ hook window InsertChar \} -group odin-indent odin-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window odin-.+ }
+}
+
+hook -group odin-highlight global WinSetOption filetype=odin %{
+ add-highlighter window/odin ref odin
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/odin }
+}
+
+provide-module odin %§
+
+add-highlighter shared/odin regions
+add-highlighter shared/odin/code default-region group
+add-highlighter shared/odin/string region %{(?<!')"} %{(?<!\\)(\\\\)*"} fill string
+add-highlighter shared/odin/rawstring region ` ` fill string
+add-highlighter shared/odin/character region %{'} %{(?<!\\)'} fill value
+
+add-highlighter shared/odin/comment region -recurse /\* /\* \*/ fill comment
+add-highlighter shared/odin/inline_documentation region /// $ fill documentation
+add-highlighter shared/odin/line_comment region // $ fill comment
+
+add-highlighter shared/odin/code/ regex "(?<!\w)@\w+\b" 0:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden odin-insert-on-new-line %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}\h* <ret> y<c-o>P<esc> }
+]
+
+define-command -hidden odin-indent-on-new-line %<
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after keywords
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for|try|catch)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ =
+>
+
+define-command -hidden odin-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden odin-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+evaluate-commands %sh{
+ values='false true nil ---'
+ types='bool b8 b16 b32 b64
+ int i8 i16 i32 i64 i128
+ uint u8 u16 u32 u64 u128 uintptr
+ i16le i32le i64le i128le u16le u32le u64le u128le
+ i16be i32be i64be i128be u16be u32be u64be u128be
+ f16 f32 f64
+ f16le f32le f64le
+ f16be f32be f64be
+ complex32 complex64 complex128
+ quaternion64 quaternion128 quaternion256
+ rune
+ string cstring
+ rawptr
+ typeid
+ any'
+ keywords='asm auto_cast bit_set break case cast context continue defer distinct do dynamic else enum
+ fallthrough for foreign if import in map not_in or_else or_return package proc return struct
+ switch transmute typeid union using when where'
+ attributes=''
+ # ---------------------------------------------------------------------------------------------- #
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+ # ---------------------------------------------------------------------------------------------- #
+ add_highlighter() { printf "add-highlighter shared/odin/code/ regex %s %s\n" "$1" "$2"; }
+ # ---------------------------------------------------------------------------------------------- #
+ add_word_highlighter() {
+ while [ $# -gt 0 ]; do
+ words=$1 face=$2; shift 2
+ regex="\\b($(join "${words}" '|'))\\b"
+ add_highlighter "$regex" "1:$face"
+ done
+ }
+ # ---------------------------------------------------------------------------------------------- #
+ printf %s\\n "declare-option str-list odin_static_words $(join "${values} ${types} ${keywords} ${attributes} ${modules}" ' ')"
+ # ---------------------------------------------------------------------------------------------- #
+ add_word_highlighter "$values" "value" "$types" "type" "$keywords" "keyword" "$attributes" "attribute"
+ # ---------------------------------------------------------------------------------------------- #
+}
+
diff --git a/autoload/filetype/pascal.kak b/autoload/filetype/pascal.kak
new file mode 100644
index 0000000..eae4577
--- /dev/null
+++ b/autoload/filetype/pascal.kak
@@ -0,0 +1,217 @@
+# https://www.freepascal.org/docs-html/ref/ref.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection, see https://wiki.freepascal.org/file_types
+hook global BufCreate .*\.(p|pp|pas|pascal)$ %{
+ set-option buffer filetype pascal
+}
+hook global BufCreate .*\.(dpr|dpk|dfm)$ %{
+ set-option buffer filetype delphi
+}
+hook global BufCreate .*\.(lpr|lfm)$ %{
+ set-option buffer filetype freepascal
+}
+
+hook global WinSetOption filetype=((free|object)?pascal|delphi) %[
+ require-module pascal
+ hook window ModeChange pop:insert:.* -group pascal-trim-indent pascal-trim-indent
+ hook window InsertChar \n -group pascal-indent pascal-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window pascal-.+ }
+ set-option window static_words %opt{static_words}
+]
+
+hook -group pascal-highlight global WinSetOption filetype=((free|object)?pascal|delphi) %[
+ add-highlighter window/pascal ref pascal
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/pascal }
+]
+
+provide-module pascal %§
+
+add-highlighter shared/pascal regions
+add-highlighter shared/pascal/code default-region group
+
+evaluate-commands %sh¶
+ # This might seem like a lot of effort to highlight routines and
+ # properties correctly but it is worth it
+
+ id='([_a-zA-Z][\w]{,126})\s*' # identifier for variables etc.
+ id2="(?:$id\.)?$id(?:<.*?>)?" # (module|type).id
+ id4="(?:$id\.)?(?:$id\.)?(?:$id\.)?$id" # type.type.type.id
+ type=":\s*(specialize\s+)?(?:(array\s+of\s+)?$id2)" # 1:attribute 2:keyword 3:module 4:type
+
+ cat <<EOF
+ # routine without parameters
+ add-highlighter shared/pascal/code/simple_routine regex \
+ "\b(?i)(function|procedure|constructor|destructor|operator)\s+$id4(?:$type)?" \
+ 2:type 3:type 4:type 5:function 6:attribute 7:keyword 8:module 9:type
+ add-highlighter shared/pascal/simple_property region \
+ "\b(?i)property\s+$id2:" ";" group
+
+ # routine with parameters
+ add-highlighter shared/pascal/routine region \
+ "\b(?i)(function|procedure|constructor|destructor|operator)(\s+$id4)?\s*(<.*?>)?\s*\(" "\).*?;" regions
+ add-highlighter shared/pascal/property region \
+ "\b(?i)property\s+$id4\[" "\].*?;" regions
+
+ add-highlighter shared/pascal/routine/parameters region -recurse \( \( \) regions
+ add-highlighter shared/pascal/property/parameters region -recurse \[ \[ \] regions
+EOF
+
+ # Used to highlight "var1, var2, var3, var4 : type" declarations
+ x="(?:$id,\s*)?"
+
+ for r in property routine; do
+ cat <<EOF
+ add-highlighter shared/pascal/$r/parameters/default default-region group
+ add-highlighter shared/pascal/$r/parameters/default/ regex \
+ "(?i)(?:(constref|const|var|out|univ)\s+)?$x$x$x$x$x$id(?:$type)?" \
+ 1:attribute 2:variable 3:variable 4:variable 5:variable 6:variable 7:variable 8:attribute 9:keyword 10:module 11:type
+ add-highlighter shared/pascal/$r/default default-region group
+EOF
+ done
+
+ cat <<EOF
+ add-highlighter shared/pascal/routine/default/ regex \
+ "\b(?i)(function|procedure|constructor|destructor|operator)(?:\s+$id4)?" \
+ 1:keyword 2:type 3:type 4:type 5:function
+ add-highlighter shared/pascal/routine/default/return_type regex \
+ "(?i)$type" 1:attribute 2:keyword 3:module 4:type
+ add-highlighter shared/pascal/routine/default/ regex \
+ "(?i)(of\s+object|is\s+nested)" 1:keyword
+EOF
+
+ for r in property/default simple_property; do
+ cat <<EOF
+ add-highlighter shared/pascal/$r/ regex "\b(?i)(property)" 1:keyword
+ add-highlighter shared/pascal/$r/type regex ":\s*$id" 1:type
+
+ # https://www.freepascal.org/docs-html/ref/refsu33.html
+ add-highlighter shared/pascal/$r/specifier regex \
+ "\b(?i)(index|read|write|implements|(no)?default|stored)\b" 0:attribute
+EOF
+ done
+
+ for r in pascal pascal/routine pascal/routine/parameters pascal/property; do
+ cat <<EOF
+ # Example string: 'You''re welcome!'
+ add-highlighter shared/$r/string region \
+ -recurse %{(?<!')('')+(?!')} %{'} %{'(?!')|\$} group
+ add-highlighter shared/$r/string/ fill string
+ add-highlighter shared/$r/string/escape regex "''" 0:+b
+
+ add-highlighter shared/$r/directive region \{\\$[a-zA-Z] \} fill meta
+
+ # comments (https://www.freepascal.org/docs-html/ref/refse2.html)
+ add-highlighter shared/$r/comment_old region -recurse \(\* \(\* \*\) fill comment
+ add-highlighter shared/$r/comment_new region -recurse \{ \{ \} fill comment
+ add-highlighter shared/$r/comment_oneline region // $ fill comment
+EOF
+ done
+
+
+ # The types "string" and "file", the value "nil", and the modifiers
+ # "bitpacked" and "packed" are not included.
+ reserved='and array as asm begin case class const constructor cppclass
+ destructor dispinterface div do downto else end except exports
+ finalization finally for function goto if implementation in
+ inherited initialization interface is label library mod not
+ object of operator or otherwise procedure program property raise
+ record repeat resourcestring set shl shr then threadvar to try
+ type unit until uses var while with xor'
+
+ # These are not reserved words in Free Pascal, but for consistency
+ # with other programming languages are highlighted as reserved words:
+ keywords='Break Continue Exit at on package'
+ # "at" is used in Delphi, e.g. 'raise object at address'
+
+ # https://www.freepascal.org/docs-html/ref/refsu3.html and
+ # https://wiki.freepascal.org/Reserved_words
+ # "name" and "alias" are not included beacuse they produce too many
+ # false positives. Some modifiers like "index" are only highlighted in
+ # certain places.
+ modifiers='absolute abstract assembler automated bitpacked cdecl contains
+ cppdecl cvar default deprecated dispid dynamic enumerator
+ experimental export external far far16 final forward generic
+ helper inline interrupt iocheck local near noreturn
+ nostackframe oldfpccall overload override packed pascal
+ platform private protected public published readonly register
+ reintroduce requires safecall saveregisters softfloat specialize
+ static stdcall strict syscall unaligned unimplemented varargs
+ virtual winapi writeonly'
+
+ # https://wiki.freepascal.org/Category:Data_types
+ # not highlighted for consistency with not built-in types
+ types='AnsiChar AnsiString Boolean Boolean16 Boolean32 Boolean64 Buffer
+ Byte ByteBool Cardinal Char Comp Currency Double DWord Extended
+ Int16 Int32 Int64 Int8 Integer IUnknown LongBool LongInt Longword
+ NativeInt NativeUInt OleVariant PAnsiChar PAnsiString PBoolean PByte
+ PByteArray PCardinal PChar PComp PCurrency PDate PDateTime PDouble
+ PDWord PExtended PHandle PInt64 PInteger PLongInt PLongWord Pointer
+ PPointer PQWord PShortInt PShortString PSingle PSmallInt PString
+ PUnicodeChar PUnicodeString PVariant PWideChar PWideString PWord
+ PWordArray PWordBool QWord QWordBool RawBytestring Real Real48
+ ShortInt ShortString Single SmallInt TClass TDate TDateTime Text
+ TextFile THandle TObject TTime UInt16 UInt32 UInt8 UnicodeChar
+ UnicodeString UTF16String UTF8String Variant WideChar WideString
+ Word WordBool file string'
+
+ constants='False True nil MaxInt Self'
+
+ # Add the language's grammar to the static completion list
+ echo declare-option str-list static_words $reserved $keywords $modifiers \
+ $types $constants name index result constref out read write implements \
+ nodefault stored message
+
+ # Replace spaces with a pipe
+ join() { eval set -- $1; IFS='|'; echo "$*"; }
+
+ cat <<EOF
+ add-highlighter shared/pascal/code/keywords regex \
+ (?i)(?<!&)\b($(join "$reserved $keywords")|file\s+of)\b 0:keyword
+ add-highlighter shared/pascal/code/modifiers regex \
+ (?i)(?<!\.)\b($(join "$modifiers"))\b(?!\()|message\s+(?!:) 0:attribute
+ add-highlighter shared/pascal/code/index regex \
+ '\b(?i)(index)\s+\w+\s*;' 1:attribute
+EOF
+
+ for r in code routine/parameters/default routine/default property/default simple_property; do
+ cat <<EOF
+ add-highlighter shared/pascal/$r/ regex '[.:=<>@^*/+-]' 0:operator
+ add-highlighter shared/pascal/$r/constants regex \
+ \b(?i)($(join "$constants"))\b 0:value
+
+ # numbers (https://www.freepascal.org/docs-html/ref/refse6.html)
+ add-highlighter shared/pascal/$r/decimal regex \b\d+([eE][+-]?\d+)?\b 0:value
+ add-highlighter shared/pascal/$r/hex regex \\\$[\da-fA-F]+\b 0:value
+ add-highlighter shared/pascal/$r/octal regex &[0-7]+\b 0:value
+ add-highlighter shared/pascal/$r/binary regex \%[01]+\b 0:value
+ add-highlighter shared/pascal/$r/char regex '#\d+\b' 0:value
+EOF
+ done
+
+define-command -hidden pascal-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden pascal-indent-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after certain keywords
+ try %{ execute-keys -draft kx<a-k>(?i)(asm|begin|const|else|except|exports|finalization|finally|label|of|otherwise|private|property|public|protected|published|record|repeat|resourcestring|threadvar|try|type|uses|var|:)\h*$<ret>j<a-gt> }
+ }
+}
+
+# Other syntax highlighters for reference:
+# https://github.com/pygments/pygments/blob/master/pygments/lexers/pascal.py
+# https://github.com/codemirror/CodeMirror/blob/master/mode/pascal/pascal.js
+# https://github.com/vim/vim/blob/master/runtime/syntax/pascal.vim
+# https://github.com/highlightjs/highlight.js/blob/master/src/languages/delphi.js
diff --git a/autoload/filetype/perl.kak b/autoload/filetype/perl.kak
new file mode 100644
index 0000000..2fcb026
--- /dev/null
+++ b/autoload/filetype/perl.kak
@@ -0,0 +1,146 @@
+# https://www.perl.org/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(t|p[lm])$ %{
+ set-option buffer filetype perl
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=perl %{
+ require-module perl
+
+ set-option window static_words %opt{perl_static_words}
+
+ # cleanup trailing whitespaces when exiting insert mode
+ hook window ModeChange pop:insert:.* -group perl-trim-indent %{ try %{ execute-keys -draft xs^\h+$<ret>d } }
+ hook window InsertChar \n -group perl-insert perl-insert-on-new-line
+ hook window InsertChar \n -group perl-indent perl-indent-on-new-line
+ hook window InsertChar \{ -group perl-indent perl-indent-on-opening-curly-brace
+ hook window InsertChar \} -group perl-indent perl-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window perl-.+ }
+}
+
+hook -group perl-highlight global WinSetOption filetype=perl %{
+ add-highlighter window/perl ref perl
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/perl }
+}
+
+provide-module perl %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/perl regions
+add-highlighter shared/perl/code default-region group
+add-highlighter shared/perl/command region (?<!\$)(?<!\\)` (?<!\\)(\\\\)*` fill meta
+add-highlighter shared/perl/double_string region (?<!\$)" (?<!\\)(\\\\)*" fill string
+add-highlighter shared/perl/single_string region (?<!\$)(?<!\\\\)' (?<!\\)(\\\\)*' fill string
+add-highlighter shared/perl/comment region (?<!\$)(?<!\\)# $ fill comment
+
+add-highlighter shared/perl/regex region m?(?<!/)(?<!qr)/[^/\n]+(?=/) /\w* fill meta
+add-highlighter shared/perl/sregex region s/[^/\n]+/[^/\n]+(?=/) /\w* fill meta
+add-highlighter shared/perl/bregex region s\{[^\}\n]+\}\{[^\}\n]*(?=\}) \}\w* fill meta
+
+add-highlighter shared/perl/quote_brace region -recurse \{ \bq[qrwx]?\{ \} fill string
+add-highlighter shared/perl/quote_paren region -recurse \( \bq[qrwx]?\( \) fill string
+add-highlighter shared/perl/quote_brack region -recurse \[ \bq[qrwx]?\[ \] fill string
+add-highlighter shared/perl/quote_angle region -recurse < \bq[qrwx]?< > fill string
+add-highlighter shared/perl/quote_punct region -match-capture '\bq[qwx]?([:;!@#$%^&*|,.?/~=+-])' '(.)' fill string
+add-highlighter shared/perl/quote_regex region -match-capture '\bqr([:;!@#$%^&*|,.?/~=+-])' '(.)' fill meta
+
+add-highlighter shared/perl/double_heredoc region -match-capture <<~?\h*'(\w*)' ^\t*(\w*)$ fill string
+add-highlighter shared/perl/single_heredoc region -match-capture <<~?\h*"(\w*)" ^\t*(\w*)$ fill string
+add-highlighter shared/perl/bare_heredoc region -match-capture <<~?(\w+) ^\t*(\w+)$ fill string
+add-highlighter shared/perl/pod region ^=\w+ ^=cut\b fill string
+
+evaluate-commands %sh{
+ # Grammar
+ keywords="else lock qw elsif lt qx eq exp ne sub for no my not tr goto and foreach or break exit unless cmp ge package until continue gt while if qq xor do le qr return"
+ attributes="END AUTOLOAD BEGIN CHECK UNITCHECK INIT DESTROY
+ length setpgrp endgrent link setpriority endhostent listen setprotoent endnetent local setpwent
+ endprotoent localtime setservent endpwent log setsockopt endservent lstat shift eof map shmctl eval mkdir shmget exec msgctl shmread
+ exists msgget shmwrite msgrcv shutdown fcntl msgsnd sin fileno sleep flock next socket fork socketpair format oct sort
+ formline open splice getc opendir split getgrent ord sprintf getgrgid our sqrt getgrnam pack srand gethostbyaddr pipe stat gethostbyname
+ pop state gethostent pos study getlogin print substr getnetbyaddr printf symlink abs getnetbyname prototype syscall accept getnetent
+ push sysopen alarm getpeername quotemeta sysread atan2 getpgrp rand sysseek getppid read system getpriority readdir syswrite bind
+ getprotobyname readline tell binmode getprotobynumber readlink telldir bless getprotoent readpipe tie getpwent recv tied caller
+ getpwnam redo time chdir getpwuid ref times getservbyname rename truncate chmod getservbyport require uc chomp getservent reset ucfirst
+ chop getsockname umask chown getsockopt reverse undef chr glob rewinddir chroot gmtime rindex unlink close rmdir unpack
+ closedir grep say unshift connect hex scalar untie cos index seek use crypt seekdir utime dbmclose int select values dbmopen ioctl semctl
+ vec defined join semget wait delete keys semop waitpid kill send wantarray die last setgrent warn dump lc sethostent write each lcfirst setnetent"
+ values="ARGV STDERR STDOUT ARGVOUT STDIN __DATA__ __END__ __FILE__ __LINE__ __PACKAGE__"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list perl_static_words $(join "${keywords} ${attributes} ${values}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/perl/code/ regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/perl/code/ regex \b($(join "${attributes}" '|'))\b 0:attribute
+ add-highlighter shared/perl/code/ regex \b($(join "${values}" '|'))\b 0:value
+ "
+}
+
+add-highlighter shared/perl/code/ regex (?!\$)-?([0-9]*\.(?!0[xXbB]))?\b([0-9]+|0[xX][0-9a-fA-F]+|0[bb][01_]+)\.?([eE][+-]?[0-9]+)?i?\b 0:value
+add-highlighter shared/perl/code/ regex %{\$!|\$"|\$#|\$\$|\$%|\$&|\$'|\$\(|\$\)|\$\*|\$\+|\$,|\$_|\$-|\$`|\$\.|\$/|\$:|\$;|\$<|\$=|\$>|\$\?|\$@|\$\[|\$\\|\$\]|\$\^|\$\||\$~|%!|@\+|@-|@_} 0:value
+add-highlighter shared/perl/code/ regex (%ENV|%INC|%OVERLOAD|%SIG|@ARGV|@INC|@LAST_MATCH_START) 0:value
+add-highlighter shared/perl/code/ regex %{%\^(H)\b} 0:value
+add-highlighter shared/perl/code/ regex \$\^(S|T|V|W|X|A|C|D|E|F|H|I|L|M|N|O|P|R)\b 0:value
+add-highlighter shared/perl/code/ regex \$\^(RE_TRIE_MAXBUF|TAINT|UNICODE|UTF8LOCALE|WARNING_BITS|WIDE_SYSTEM_CALLS|CHILD_ERROR_NATIVE|ENCODING|OPEN|RE_DEBUG_FLAGS)\b 0:value
+
+add-highlighter shared/perl/code/ regex \$[0-9]+ 0:attribute
+add-highlighter shared/perl/code/ regex \b-(B|b|C|c|d|e|f|g|k|l|M|O|o|p|r|R|S|s|T|t|u|w|W|X|x|z)\b 0:attribute
+
+add-highlighter shared/perl/code/ regex \$[a-zA-Z_][a-zA-Z0-9_]* 0:variable
+
+add-highlighter shared/perl/code/ regex \$(a|b|LAST_REGEXP_CODE_RESULT|LIST_SEPARATOR|MATCH|MULTILINE_MATCHING|NR|OFMT|OFS|ORS|OS_ERROR|OSNAME|OUTPUT_AUTO_FLUSH|OUTPUT_FIELD_SEPARATOR|OUTPUT_RECORD_SEPARATOR)\b 0:value
+add-highlighter shared/perl/code/ regex \$(LAST_REGEXP_CODE_RESULT|LIST_SEPARATOR|MATCH|MULTILINE_MATCHING|NR|OFMT|OFS|ORS|OS_ERROR|OSNAME|OUTPUT_AUTO_FLUSH|OUTPUT_FIELD_SEPARATOR|OUTPUT_RECORD_SEPARATOR|PERL_VERSION|ACCUMULATOR|PERLDB|ARG|PID|ARGV|POSTMATCH|PREMATCH|BASETIME|PROCESS_ID|CHILD_ERROR|PROGRAM_NAME|COMPILING|REAL_GROUP_ID|DEBUGGING|REAL_USER_ID|EFFECTIVE_GROUP_ID|RS|EFFECTIVE_USER_ID|SUBSCRIPT_SEPARATOR|EGID|SUBSEP|ERRNO|SYSTEM_FD_MAX|EUID|UID|EVAL_ERROR|WARNING|EXCEPTIONS_BEING_CAUGHT|EXECUTABLE_NAME|EXTENDED_OS_ERROR|FORMAT_FORMFEED|FORMAT_LINE_BREAK_CHARACTERS|FORMAT_LINES_LEFT|FORMAT_LINES_PER_PAGE|FORMAT_NAME|FORMAT_PAGE_NUMBER|FORMAT_TOP_NAME|GID|INPLACE_EDIT|INPUT_LINE_NUMBER|INPUT_RECORD_SEPARATOR|LAST_MATCH_END|LAST_PAREN_MATCH)\b 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden perl-insert-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # copy # comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K#\h* <ret> y<c-o>P<esc> }
+ =
+~
+
+define-command -hidden perl-indent-on-new-line %~
+ evaluate-commands -draft -itersel %=
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with { or (
+ try %[ execute-keys -draft kx <a-k> [{(]\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # align to opening paren of previous line
+ try %{ execute-keys -draft [( <a-k> \A\([^\n]+\n[^\n]*\n?\z <ret> s \A\(\h*.|.\z <ret> '<a-;>' & }
+ # indent after a switch's case/default statements
+ try %[ execute-keys -draft kx <a-k> ^\h*(case|default).*:$ <ret> j<a-gt> ]
+ # indent after if|else|while|for
+ try %[ execute-keys -draft <semicolon><a-F>)MB <a-k> \A(if|else|while|for)\h*\(.*\)\h*\n\h*\n?\z <ret> s \A|.\z <ret> 1<a-&>1<a-,><a-gt> ]
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> ]
+ =
+~
+
+define-command -hidden perl-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{) <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden perl-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
diff --git a/autoload/filetype/php.kak b/autoload/filetype/php.kak
new file mode 100644
index 0000000..62ba6dd
--- /dev/null
+++ b/autoload/filetype/php.kak
@@ -0,0 +1,107 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](phpt?) %{
+ set-option buffer filetype php
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=php %{
+ require-module php
+
+ hook window ModeChange pop:insert:.* -group php-trim-indent php-trim-indent
+ hook window InsertChar .* -group php-indent php-indent-on-char
+ hook window InsertChar \n -group php-insert php-insert-on-new-line
+ hook window InsertChar \n -group php-indent php-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window php-.+ }
+}
+
+hook -group php-highlight global WinSetOption filetype=php %{
+ add-highlighter window/php-file ref php-file
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/php-file }
+}
+
+provide-module php %§
+require-module html
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/php regions
+add-highlighter shared/php/code default-region group
+add-highlighter shared/php/double_string region '"' (?<!\\)(\\\\)*" group
+add-highlighter shared/php/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/php/doc_comment region /// '$' group
+add-highlighter shared/php/doc_comment2 region /\*\* \*/ ref php/doc_comment
+add-highlighter shared/php/comment1 region // '$' fill comment
+add-highlighter shared/php/comment2 region /\* \*/ fill comment
+add-highlighter shared/php/comment3 region '#' '$' fill comment
+add-highlighter shared/php/heredoc region -match-capture '<<<(.*?)$' '^\h*(.*?);' fill string
+
+
+add-highlighter shared/php/code/ regex &?\$\w* 0:variable
+add-highlighter shared/php/code/ regex \b(false|null|parent|self|this|true)\b 0:value
+add-highlighter shared/php/code/ regex "(\b|-)[0-9]*\.?[0-9]+\b" 0:value
+add-highlighter shared/php/code/ regex \b((string|int|bool)|[A-Z][a-z].*?)\b 0:type
+add-highlighter shared/php/code/ regex \B/[^\n/]+/[gimy]* 0:meta
+add-highlighter shared/php/code/ regex '<\?(php)?|\?>' 0:meta
+
+add-highlighter shared/php/double_string/ fill string
+add-highlighter shared/php/double_string/ regex (?<!\\)(\\\\)*(\$\w+)(->\w+)* 0:variable
+add-highlighter shared/php/double_string/ regex \{(?<!\\)(\\\\)*(\$\w+)(->\w+)*\} 0:variable
+
+# Highlight doc comments
+add-highlighter shared/php/doc_comment/ fill string
+add-highlighter shared/php/doc_comment/ regex '`.*`' 0:module
+add-highlighter shared/php/doc_comment/ regex '@\w+' 0:meta
+
+# Keywords are collected at
+# http://php.net/manual/en/reserved.keywords.php
+add-highlighter shared/php/code/ regex \b(__halt_compiler|abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|new|or|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__CLASS__|__DIR__|__FILE__|__FUNCTION__|__LINE__|__METHOD__|__NAMESPACE__|__TRAIT__)\b 0:keyword
+
+# Highlighter for html with php tags in it, i.e. the structure of conventional php files
+add-highlighter shared/php-file regions
+add-highlighter shared/php-file/html default-region ref html
+add-highlighter shared/php-file/php region '<\?(php)?' '\?>' ref php
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden php-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden php-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %/ execute-keys -draft <a-h> <a-k> ^\h+[\]}]$ <ret> m s \A|.\z <ret> 1<a-&> /
+ >
+>
+
+define-command -hidden php-insert-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # copy // comments or docblock * prefix and following white spaces
+ try %{ execute-keys -draft s [^/] <ret> k x s ^\h*\K(?://|[*][^/])\h* <ret> y gh j P }
+ # append " * " on lines starting a multiline /** or /* comment
+ try %{ execute-keys -draft k x s ^\h*/[*][* ]? <ret> j gi i <space>*<space> }
+ >
+>
+
+define-command -hidden php-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : php-trim-indent <ret> }
+ # indent after lines beginning / ending with opener token
+ try %_ execute-keys -draft k x <a-k> ^\h*[[{]|[[{]$ <ret> j <a-gt> _
+ # deindent closer token(s) when after cursor
+ try %_ execute-keys -draft x <a-k> ^\h*[})] <ret> gh / [})] <ret> m <a-S> 1<a-&> _
+ >
+>
+
diff --git a/autoload/filetype/pony.kak b/autoload/filetype/pony.kak
new file mode 100644
index 0000000..6a91688
--- /dev/null
+++ b/autoload/filetype/pony.kak
@@ -0,0 +1,155 @@
+# http://ponylang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](pony) %{
+ set-option buffer filetype pony
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=pony %{
+ require-module pony
+
+ set-option window static_words %opt{pony_static_words}
+
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group pony-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+ hook window InsertChar \n -group pony-indent pony-indent-on-new-line
+ hook window InsertChar \n -group pony-insert pony-insert-on-new-line
+
+ set-option buffer extra_word_chars '_' "'"
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window pony-.+ }
+}
+
+hook -group pony-highlight global WinSetOption filetype=pony %{
+ add-highlighter window/pony ref pony
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter pony }
+}
+
+provide-module pony %§
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/pony regions
+add-highlighter shared/pony/code default-region group
+
+add-highlighter shared/pony/comment region '/\*' '\*/' fill comment
+add-highlighter shared/pony/line_comment region '//' '$' fill comment
+
+# String literals
+add-highlighter shared/pony/string region -match-capture '("""|")' '(?<!\\)(?:\\\\)*("""|")' group
+add-highlighter shared/pony/string/ fill string
+add-highlighter shared/pony/string/ regex '(?:\\a|\\b|\\e|\\f|\\n|\\r|\\t||\\v|\\\\|\\''|\\0|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u[0-9a-fA-F]{6})' 0:meta
+
+# Character literals
+# negative lookbehind to account for variables with prime such as myvar' or myvar''
+add-highlighter shared/pony/character region "(?<![a-z0-9'])'" (?<!\\)(\\\\)*' group
+add-highlighter shared/pony/character/ fill string
+add-highlighter shared/pony/character/ regex '(?:\\a|\\b|\\e|\\f|\\n|\\r|\\t||\\v|\\\\|\\''|\\0|\\x[0-9a-fA-F]{2})' 0:meta
+
+# Operators
+add-highlighter shared/pony/code/ regex '=|\+|-|\*|/|%|%%|<<|>>|==|!=|<|<=|>=|>|=>|;' 0:operator
+add-highlighter shared/pony/code/ regex '(\+|-|\*|/|%|%%|<<|>>|==|!=|<|<=|>=|>)~' 0:operator
+add-highlighter shared/pony/code/ regex '(\+|-|\*|/|%|%%)\?' 0:operator
+add-highlighter shared/pony/code/ regex '^\h*|' 0:operator
+add-highlighter shared/pony/code/ regex '\b_\b' 0:operator
+
+# Integer literals
+add-highlighter shared/pony/code/ regex '\b[0-9](_?[0-9])*\b' 0:value
+add-highlighter shared/pony/code/ regex '\b0x[0-9a-fA-F](_?[0-9a-fA-F])*\b' 0:value
+add-highlighter shared/pony/code/ regex '\b0b[01](_?[01])*\b' 0:value
+
+# Float literals
+add-highlighter shared/pony/code/ regex '\b[0-9]+\.[0-9]+(?:[eE][-+]?[0-9]+)?\b' 0:value
+add-highlighter shared/pony/code/ regex '\b[0-9]+[eE][-+]?[0-9]+\b' 0:value
+
+# Type literals
+add-highlighter shared/pony/code/ regex '\b_?[A-Z][A-Za-z0-9]*\b' 0:type
+
+# Literal words are highlighted below to also allow for autocompletion by appending
+# them to static_words.
+evaluate-commands %sh{
+ # Grammar
+ values="true|false|this"
+ meta='use'
+ # Keyword list is collected using `keyword.kwlist` from `keyword`
+ keywords="as|break|match|continue|else|elseif|try|in|return|end|for|is"
+ keywords="${keywords}|recover|consume|error|do|then|if|while|where|with"
+ keywords="${keywords}|class|struct|object|actor|interface|trait|primitive"
+ keywords="${keywords}|type|var|let|embed|repeat|until|addressof"
+ func_decl="new|fun|be"
+ capabilities="iso|ref|box|tag|val|trn"
+ operators="and|or|xor|not"
+ builtin_types="String|None|Any|Array"
+
+ # Add the language's grammar to the static completion list
+ static_words="${values} ${meta} ${keywords} ${types_decl} ${capabilities}"
+ static_words="${static_words} ${operators} ${builtin_types}"
+ printf %s\\n "declare-option str-list pony_static_words ${static_words}" | tr '|' ' '
+
+ # Highlight keywords
+ # The (?!') ensures that keywords with prime are highlighted as usual, for example try'
+ printf %s "
+ add-highlighter shared/pony/code/ regex \b(${values})\b(?!') 0:value
+ add-highlighter shared/pony/code/ regex \b(${operators})\b(?!') 0:operator
+ add-highlighter shared/pony/code/ regex \b(${meta})\b(?!') 0:meta
+ add-highlighter shared/pony/code/ regex \b(${func_decl})(\s+(${capabilities}))?(\s+\w+)[\[(] 1:keyword 3:attribute 4:function
+ add-highlighter shared/pony/code/ regex \b(${func_decl})\b(?!') 0:keyword
+ add-highlighter shared/pony/code/ regex \b(${keywords})\b(?!') 0:keyword
+ add-highlighter shared/pony/code/ regex \b(${capabilities})\b(?!')(!|\\^)? 1:attribute 2:attribute
+ "
+
+ # Highlight types and attributes
+ printf %s "
+ add-highlighter shared/pony/code/ regex '@[\w_]+\b' 0:attribute
+ "
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden pony-insert-on-new-line %<
+ evaluate-commands -no-hooks -draft -itersel %<
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K//\h* <ret> y jgi P }
+
+ # wisely add `end` keyword
+ evaluate-commands -save-regs x %<
+ try %{ execute-keys -draft k x s ^ \h + <ret> \" x y } catch %{ reg x '' }
+ try %<
+ evaluate-commands -draft %<
+ # Check if previous line opens a block
+ execute-keys -draft kx <a-k>^<c-r>x(try|match|recover|repeat|object|.+\bdo$|.+\bthen$)[^0-9A-Za-z_']<ret>
+ # Check that we didn't put an end on the previous line that closes the block
+ execute-keys -draft kx <a-K> \bend$<ret>
+ # Check that we do not already have an end for this indent level which is first set via `pony-indent-on-new-line` hook
+ execute-keys -draft }i J x <a-K> ^<c-r>x(end|else|elseif|until)[^0-9A-Za-z_']<ret>
+ >
+ execute-keys -draft o<c-r>xend<esc> # insert a new line with containing end
+ >
+ >
+ >
+>
+
+define-command -hidden pony-indent-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft , K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with: =>, do, try, then, else, =; or
+ # starting with: actor, class, struct, trait, interface, if, elseif, recover, repeat
+ # excluding: primitive, type -- because they are often written in a single line
+ try %{ execute-keys -draft , k x <a-k> (\b(?:do|try|then|else|recover|repeat)|=>|=)$ | ^\h*(actor|class|struct|trait|interface|object)\b <ret> j <a-gt> }
+ # else, end are always de-indented
+ try %{ execute-keys -draft , k x <a-k> \b(else|end):$ <ret> k x s ^\h* <ret> y j x <a-k> ^<c-r>" <ret> J <a-lt> }
+ }
+}
+
diff --git a/autoload/filetype/prolog.kak b/autoload/filetype/prolog.kak
new file mode 100644
index 0000000..fa183c1
--- /dev/null
+++ b/autoload/filetype/prolog.kak
@@ -0,0 +1,49 @@
+# Prolog
+# ----------------------
+
+# Adapted from rc/filetype/erlang.kak
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+hook global BufCreate .*[.](pl|P) %{
+ set-option buffer filetype prolog
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+hook global WinSetOption filetype=prolog %{
+ require-module prolog
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window prolog-.+ }
+}
+
+hook -group prolog-highlight global WinSetOption filetype=prolog %{
+ add-highlighter window/prolog ref prolog
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/prolog }
+}
+
+provide-module prolog %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/prolog regions
+add-highlighter shared/prolog/default default-region group
+
+add-highlighter shared/prolog/comment region '(?<!)%' '$' fill comment
+add-highlighter shared/prolog/attribute_atom_single_quoted region %{-'} %{(?<!\\)(?:\\\\)*'(?=[\( \.])} fill builtin
+add-highlighter shared/prolog/attribute region '\b-[a-z][\w@]*(?=[\( \.])' '\K' fill builtin
+add-highlighter shared/prolog/atom_single_quoted region %{'} %{(?<!\\)(?:\\\\)*'} fill type
+add-highlighter shared/prolog/char_list region %{"} %{(?<!\\)(?:\\\\)*"} fill string
+
+# default-region regex highlighters
+add-highlighter shared/prolog/default/atom regex '\b[a-z]\w*\b' 0:type
+add-highlighter shared/prolog/default/pred_call regex '\b[a-z]\w*(?=\()' 0:function
+add-highlighter shared/prolog/default/keywords regex '\b(div|rem|is)\b' 0:keyword
+add-highlighter shared/prolog/default/variable_name regex '\b(?<!\?)[A-Z_][\w]*\b' 0:variable
+
+add-highlighter shared/prolog/default/base_number regex '\b(\d[_\d]*(?<!_)#[a-zA-Z0-9][a-z_A-Z0-9]*(?<!_)(?!\{))\b' 1:value
+add-highlighter shared/prolog/default/float regex '\b(?<![\.])(\d[\d_]*(?<!_)\.\d[\d_]*(?<!_)(?:e[+-]?\d[\d_]*(?<!_))?)\b' 1:value
+add-highlighter shared/prolog/default/integer regex '\b(?<!/)(\d[\d_]*)(?<!_)\b' 1:value
+
+]
diff --git a/autoload/filetype/protobuf.kak b/autoload/filetype/protobuf.kak
new file mode 100644
index 0000000..b85295e
--- /dev/null
+++ b/autoload/filetype/protobuf.kak
@@ -0,0 +1,109 @@
+# https://developers.google.com/protocol-buffers/
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.proto$ %{
+ set-option buffer filetype protobuf
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=protobuf %[
+ require-module protobuf
+
+ set-option window static_words %opt{protobuf_static_words}
+
+ hook window ModeChange pop:insert:.* -group protobuf-trim-indent protobuf-trim-indent
+ hook -group protobuf-insert window InsertChar \n protobuf-insert-on-newline
+ hook -group protobuf-indent window InsertChar \n protobuf-indent-on-newline
+ hook -group protobuf-indent window InsertChar \{ protobuf-indent-on-opening-curly-brace
+ hook -group protobuf-indent window InsertChar \} protobuf-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window protobuf-.+ }
+]
+
+hook -group protobuf-highlight global WinSetOption filetype=protobuf %{
+ add-highlighter window/protobuf ref protobuf
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/protobuf }
+}
+
+provide-module protobuf %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/protobuf regions
+add-highlighter shared/protobuf/code default-region group
+add-highlighter shared/protobuf/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/protobuf/comment region /\* \*/ fill comment
+add-highlighter shared/protobuf/comment_line region '//' $ fill comment
+
+add-highlighter shared/protobuf/code/ regex %{(0x)?[0-9]+\b} 0:value
+
+evaluate-commands %sh{
+ # Grammer
+ keywords='default deprecated enum extend import message oneof option
+ package service syntax'
+ attributes='optional repeated required'
+ types='double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64
+ sfixed32 sfixed64 bool string bytes rpc'
+ values='false true'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammer to the static completion list
+ printf %s\\n "declare-option str-list protobuf_static_words $(join "${keywords} ${attributes} ${types} ${values}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/protobuf/code/keywords regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/protobuf/code/attributes regex \b($(join "${attributes}" '|'))\b 0:attribute
+ add-highlighter shared/protobuf/code/types regex \b($(join "${types}" '|'))\b 0:type
+ add-highlighter shared/protobuf/code/values regex \b($(join "${values}" '|'))\b 0:value
+ "
+}
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden protobuf-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden protobuf-insert-on-newline %{
+ evaluate-commands -draft -itersel %[
+ # copy // comments prefix
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K/{2,}(\h*(?=\S))? <ret> y<c-o>P<esc> }
+ ]
+}
+
+define-command -hidden protobuf-indent-on-newline %~
+ evaluate-commands -draft -itersel %[
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ # indent after lines ending with {
+ try %[ execute-keys -draft kx <a-k> \{\h*$ <ret> j<a-gt> ]
+ # cleanup trailing white spaces on the previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ # deindent closing brace(s) when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ ]
+~
+
+define-command -hidden protobuf-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+]
+
+define-command -hidden protobuf-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -itersel -draft <a-h><a-k>^\h+\}$<ret>hms\A|.\z<ret>1<a-&> ]
+]
+
+]
diff --git a/autoload/filetype/pug.kak b/autoload/filetype/pug.kak
new file mode 100644
index 0000000..db05c9b
--- /dev/null
+++ b/autoload/filetype/pug.kak
@@ -0,0 +1,81 @@
+# Note: jade is changing its name to pug (https://github.com/pugjs/pug/issues/2184)
+# This appears to be a work in progress -- the pug-lang domain is parked, while
+# the jade-lang one is active. This highlighter will recognize .pug and .jade extensions,
+
+# http://jade-lang.com (will be http://pug-lang.com)
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](pug|jade) %{
+ set-option buffer filetype pug
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=pug %{
+ require-module pug
+
+ hook window ModeChange pop:insert:.* -group pug-trim-indent pug-trim-indent
+ hook window InsertChar \n -group pug-indent pug-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window pug-.+ }
+}
+
+hook -group pug-highlight global WinSetOption filetype=pug %{
+ add-highlighter window/pug ref pug
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/pug }
+}
+
+
+provide-module pug %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/pug regions
+add-highlighter shared/pug/code default-region group
+add-highlighter shared/pug/text region ^\h*\|\s $ regex \h*(\|) 1:meta
+add-highlighter shared/pug/text2 region '^\h*([A-Za-z][A-Za-z0-9_-]*)?(#[A-Za-z][A-Za-z0-9_-]*)?((?:\.[A-Za-z][A-Za-z0-9_-]*)*)?(?<!\t)(?<! )(?<!\n)\h+\K.*' $ regex \h*(\|) 1:meta
+add-highlighter shared/pug/javascript region ^\h*[-=!] $ ref javascript
+add-highlighter shared/pug/double_string region '"' (?:(?<!\\)(\\\\)*"|$) fill string
+add-highlighter shared/pug/single_string region "'" (?:(?<!\\)(\\\\)*'|$) fill string
+add-highlighter shared/pug/comment region // $ fill comment
+add-highlighter shared/pug/attribute region -recurse \( \( \) group
+add-highlighter shared/pug/puglang region ^\h*\b(\block|extends|include|append|prepend|if|unless|else|case|when|default|each|while|mixin)\b $ group
+
+# Filters
+# ‾‾‾‾‾‾‾
+
+add-highlighter shared/pug/attribute/ ref javascript
+add-highlighter shared/pug/attribute/ regex [()=] 0:operator
+add-highlighter shared/pug/puglang/ ref javascript
+add-highlighter shared/pug/puglang/ regex \b(\block|extends|include|append|prepend|if|unless|else|case|when|default|each|while|mixin|of|in)\b 0:keyword
+add-highlighter shared/pug/code/ regex ^\h*([A-Za-z][A-Za-z0-9_-]*) 1:type
+add-highlighter shared/pug/code/ regex '(#[A-Za-z][A-Za-z0-9_-]*)' 1:variable
+add-highlighter shared/pug/code/ regex ((?:\.[A-Za-z][A-Za-z0-9_-]*)*) 1:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden pug-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden pug-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : pug-trim-indent <ret> }
+ # copy '//', '|', '-' or '(!)=' prefix and following whitespace
+ try %{ execute-keys -draft k x s ^\h*\K[/|!=-]{1,2}\h* <ret> y gh j P }
+ # indent unless we copied something above
+ try %{ execute-keys -draft <a-gt> , b s \S <ret> g l <a-lt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/purescript.kak b/autoload/filetype/purescript.kak
new file mode 100644
index 0000000..8945ae6
--- /dev/null
+++ b/autoload/filetype/purescript.kak
@@ -0,0 +1,120 @@
+# http://purescript.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+# Syntax reference
+# https://github.com/purescript/documentation/blob/master/language/Syntax.md
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](purs) %{
+ set-option buffer filetype purescript
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=purescript %{
+ require-module purescript
+
+ set-option buffer extra_word_chars '_' "'"
+ hook window ModeChange pop:insert:.* -group purescript-trim-indent purescript-trim-indent
+ hook window InsertChar \n -group purescript-insert purescript-insert-on-new-line
+ hook window InsertChar \n -group purescript-indent purescript-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window purescript-.+ }
+}
+
+hook -group purescript-highlight global WinSetOption filetype=purescript %{
+ add-highlighter window/purescript ref purescript
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/purescript }
+}
+
+
+provide-module purescript %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/purescript regions
+add-highlighter shared/purescript/code default-region group
+add-highlighter shared/purescript/string region (?<!'\\)(?<!')" (?<!\\)(\\\\)*" fill string
+add-highlighter shared/purescript/comment region -recurse \{- \{- -\} fill comment
+add-highlighter shared/purescript/line_comment region --(?:[^!#$%&*+./<>?@\\\^|~=]|$) $ fill comment
+
+add-highlighter shared/purescript/code/ regex (?<!')\b0x+[A-Fa-f0-9]+ 0:value
+add-highlighter shared/purescript/code/ regex (?<!')\b\d+([.]\d+)? 0:value
+add-highlighter shared/purescript/code/ regex (?<!')\b(import|hiding|module)(?!')\b 0:keyword
+add-highlighter shared/purescript/code/ regex (?<!')\b(import)(?!')\b[^\n]+(?<!')\b(as)(?!')\b 2:keyword
+add-highlighter shared/purescript/code/ regex (?<!')\b(class|data|default|derive|infix|infixl|infixr|instance|module|newtype|pattern|type|where)(?!')\b 0:keyword
+add-highlighter shared/purescript/code/ regex (?<!')\b(case|do|else|if|in|let|mdo|of|proc|rec|then)(?!')\b 0:attribute
+add-highlighter shared/purescript/code/ regex (?<!')\b(type|data)\b\s+(\bfamily\b)?(?!') 0:keyword
+
+# The complications below is because period has many uses:
+# As function composition operator (possibly without spaces) like "." and "f.g"
+# Hierarchical modules like "Data.Maybe"
+# Qualified imports like "Data.Maybe.Just", "Data.Maybe.maybe", "Control.Applicative.<$>"
+# Quantifier separator in "forall a . [a] -> [a]"
+# Enum comprehensions like "[1..]" and "[a..b]" (making ".." and "Module..." illegal)
+
+# matches uppercase identifiers: Monad Control.Monad
+# not non-space separated dot: Just.const
+add-highlighter shared/purescript/code/ regex \b([A-Z]['\w]*\.)*[A-Z]['\w]*(?!['\w])(?![.a-z]) 0:variable
+
+# matches infix identifier: `mod` `Apa._T'M`
+add-highlighter shared/purescript/code/ regex `\b([A-Z]['\w]*\.)*[\w]['\w]*` 0:operator
+# matches imported operators: M.! M.. Control.Monad.>>
+# not operator keywords: M... M.->
+add-highlighter shared/purescript/code/ regex \b[A-Z]['\w]*\.[~<=>|:!?/.@$*&#%+\^\-\\]+ 0:operator
+# matches dot: .
+# not possibly incomplete import: a.
+# not other operators: !. .!
+add-highlighter shared/purescript/code/ regex (?<![\w~<=>|:!?/.@$*&#%+\^\-\\])\.(?![~<=>|:!?/.@$*&#%+\^\-\\]) 0:operator
+# matches other operators: ... > < <= ^ <*> <$> etc
+# not dot: .
+# not operator keywords: @ .. -> :: ~
+add-highlighter shared/purescript/code/ regex (?<![~<=>|:!?/.@$*&#%+\^\-\\])[~<=>|:!?/.@$*&#%+\^\-\\]+ 0:operator
+
+# matches operator keywords: @ ->
+add-highlighter shared/purescript/code/ regex (?<![~<=>|:!?/.@$*&#%+\^\-\\])(@|~|<-|->|=>|::|=|:|[|])(?![~<=>|:!?/.@$*&#%+\^\-\\]) 1:keyword
+# matches: forall [..variables..] .
+# not the variables
+add-highlighter shared/purescript/code/ regex \b(forall|∀)\b[^.\n]*?(\.) 1:keyword 2:keyword
+
+# matches 'x' '\\' '\'' '\n' '\0'
+# not incomplete literals: '\'
+# not valid identifiers: w' _'
+add-highlighter shared/purescript/code/ regex \B'([^\\]|[\\]['"\w\d\\])' 0:string
+# this has to come after operators so '-' etc is correct
+
+# matches function names in type signatures
+add-highlighter shared/purescript/code/ regex ^\s*(?:where\s+|let\s+|default\s+)?([_a-z]['\w]*#?(?:,\s*[_a-z]['\w]*#?)*)\s+::\s 1:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden purescript-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden purescript-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy -- comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K--\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden purescript-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # align to first clause
+ try %{ execute-keys -draft <semicolon> k x X s ^\h*(if|then|else)?\h*(([\w']+\h+)+=)?\h*(case\h+[\w']+\h+of|do|let|where)\h+\K.* <ret> s \A|.\z <ret> & }
+ # filter previous line
+ try %{ execute-keys -draft k : purescript-trim-indent <ret> }
+ # indent after lines beginning with condition or ending with expression or =(
+ try %{ execute-keys -draft <semicolon> k x <a-k> ^\h*if|[=(]$|\b(case\h+[\w']+\h+of|do|let|where)$ <ret> j <a-gt> }
+ }
+}
+
diff --git a/autoload/filetype/python.kak b/autoload/filetype/python.kak
new file mode 100644
index 0000000..65da1a8
--- /dev/null
+++ b/autoload/filetype/python.kak
@@ -0,0 +1,199 @@
+# http://python.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](py) %{
+ set-option buffer filetype python
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=python %{
+ require-module python
+
+ set-option window static_words %opt{python_static_words}
+
+ hook window InsertChar \n -group python-insert python-insert-on-new-line
+ hook window InsertChar \n -group python-indent python-indent-on-new-line
+ # cleanup trailing whitespaces on current line insert end
+ hook window ModeChange pop:insert:.* -group python-trim-indent %{ try %{ execute-keys -draft <semicolon> x s ^\h+$ <ret> d } }
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window python-.+ }
+}
+
+hook -group python-highlight global WinSetOption filetype=python %{
+ add-highlighter window/python ref python
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/python }
+}
+
+provide-module python %§
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/python regions
+add-highlighter shared/python/code default-region group
+add-highlighter shared/python/docstring region -match-capture ^\h*("""|''') (?<!\\)(?:\\\\)*("""|''') regions
+
+add-highlighter shared/python/documentation region '##' '$' fill documentation
+add-highlighter shared/python/comment region '#' '$' fill comment
+
+# String interpolation
+add-highlighter shared/python/f_triple_string region -match-capture [fF]("""|''') (?<!\\)(?:\\\\)*("""|''') group
+add-highlighter shared/python/f_triple_string/ fill string
+add-highlighter shared/python/f_triple_string/ regex \{.*?\} 0:value
+
+add-highlighter shared/python/f_double_string region '[fF]"' (?<!\\)(\\\\)*" group
+add-highlighter shared/python/f_double_string/ fill string
+add-highlighter shared/python/f_double_string/ regex \{.*?\} 0:value
+
+add-highlighter shared/python/f_single_string region "[fF]'" (?<!\\)(\\\\)*' group
+add-highlighter shared/python/f_single_string/ fill string
+add-highlighter shared/python/f_single_string/ regex \{.*?\} 0:value
+
+
+# Regular string
+add-highlighter shared/python/triple_string region -match-capture ("""|''') (?<!\\)(?:\\\\)*("""|''') fill string
+add-highlighter shared/python/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/python/single_string region "'" (?<!\\)(\\\\)*' fill string
+
+# Integer formats
+add-highlighter shared/python/code/ regex '(?i)\b0b[01]+l?\b' 0:value
+add-highlighter shared/python/code/ regex '(?i)\b0x[\da-f]+l?\b' 0:value
+add-highlighter shared/python/code/ regex '(?i)\b0o?[0-7]+l?\b' 0:value
+add-highlighter shared/python/code/ regex '(?i)\b([1-9]\d*|0)l?\b' 0:value
+# Float formats
+add-highlighter shared/python/code/ regex '\b\d+[eE][+-]?\d+\b' 0:value
+add-highlighter shared/python/code/ regex '(\b\d+)?\.\d+\b' 0:value
+add-highlighter shared/python/code/ regex '\b\d+\.' 0:value
+# Imaginary formats
+add-highlighter shared/python/code/ regex '\b\d+\+\d+[jJ]\b' 0:value
+
+add-highlighter shared/python/docstring/ default-region fill documentation
+add-highlighter shared/python/docstring/ region '(>>>|\.\.\.) \K' (?=''')|(?=""") ref python
+
+evaluate-commands %sh{
+ # Grammar
+ values="True False None self inf"
+ meta="import from"
+
+ # attributes and methods list based on https://docs.python.org/3/reference/datamodel.html
+ attributes="__annotations__ __closure__ __code__ __defaults__ __dict__ __doc__
+ __globals__ __kwdefaults__ __module__ __name__ __qualname__"
+ methods="__abs__ __add__ __aenter__ __aexit__ __aiter__ __and__ __anext__
+ __await__ __bool__ __bytes__ __call__ __complex__ __contains__
+ __del__ __delattr__ __delete__ __delitem__ __dir__ __divmod__
+ __enter__ __eq__ __exit__ __float__ __floordiv__ __format__
+ __ge__ __get__ __getattr__ __getattribute__ __getitem__
+ __gt__ __hash__ __iadd__ __iand__ __ifloordiv__ __ilshift__
+ __imatmul__ __imod__ __imul__ __index__ __init__
+ __init_subclass__ __int__ __invert__ __ior__ __ipow__
+ __irshift__ __isub__ __iter__ __itruediv__ __ixor__ __le__
+ __len__ __length_hint__ __lshift__ __lt__ __matmul__
+ __missing__ __mod__ __mul__ __ne__ __neg__ __new__ __or__
+ __pos__ __pow__ __radd__ __rand__ __rdivmod__ __repr__
+ __reversed__ __rfloordiv__ __rlshift__ __rmatmul__ __rmod__
+ __rmul__ __ror__ __round__ __rpow__ __rrshift__ __rshift__
+ __rsub__ __rtruediv__ __rxor__ __set__ __setattr__
+ __setitem__ __set_name__ __slots__ __str__ __sub__
+ __truediv__ __xor__"
+
+ # built-in exceptions https://docs.python.org/3/library/exceptions.html
+ exceptions="ArithmeticError AssertionError AttributeError BaseException BlockingIOError
+ BrokenPipeError BufferError BytesWarning ChildProcessError
+ ConnectionAbortedError ConnectionError ConnectionRefusedError
+ ConnectionResetError DeprecationWarning EOFError Exception
+ FileExistsError FileNotFoundError FloatingPointError FutureWarning
+ GeneratorExit ImportError ImportWarning IndentationError
+ IndexError InterruptedError IsADirectoryError KeyboardInterrupt
+ KeyError LookupError MemoryError ModuleNotFoundError NameError
+ NotADirectoryError NotImplementedError OSError OverflowError
+ PendingDeprecationWarning PermissionError ProcessLookupError
+ RecursionError ReferenceError ResourceWarning RuntimeError
+ RuntimeWarning StopAsyncIteration StopIteration SyntaxError
+ SyntaxWarning SystemError SystemExit TabError TimeoutError TypeError
+ UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError
+ UnicodeTranslateError UnicodeWarning UserWarning ValueError Warning
+ ZeroDivisionError"
+
+ # Keyword list is collected using `keyword.kwlist` from `keyword`
+ keywords="and as assert async await break class continue def del elif else except exec
+ finally for global if in is lambda nonlocal not or pass print
+ raise return try while with yield"
+
+ # Collected from `keyword.softkwlist`
+ soft_keywords="_ case match"
+
+ types="bool buffer bytearray bytes complex dict file float frozenset int
+ list long memoryview object set str tuple unicode xrange"
+
+ functions="abs all any ascii bin breakpoint callable chr classmethod compile complex
+ delattr dict dir divmod enumerate eval exec filter
+ format frozenset getattr globals hasattr hash help
+ hex id __import__ input isinstance issubclass iter
+ len locals map max memoryview min next oct open ord
+ pow print property range repr reversed round
+ setattr slice sorted staticmethod sum super type vars zip"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list python_static_words $(join "${values} ${meta} ${attributes} ${methods} ${exceptions} ${keywords} ${types} ${functions}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/python/code/ regex '\b($(join "${values}" '|'))\b' 0:value
+ add-highlighter shared/python/code/ regex '\b($(join "${meta}" '|'))\b' 0:meta
+ add-highlighter shared/python/code/ regex '\b($(join "${attributes}" '|'))\b' 0:attribute
+ add-highlighter shared/python/code/ regex '\bdef\s+($(join "${methods}" '|'))\b' 1:function
+ add-highlighter shared/python/code/ regex '\b($(join "${exceptions}" '|'))\b' 0:function
+ add-highlighter shared/python/code/ regex '\b($(join "${keywords} ${soft_keywords}" '|'))\b' 0:keyword
+ add-highlighter shared/python/code/ regex '\b($(join "${functions}" '|'))\b\(' 1:builtin
+ add-highlighter shared/python/code/ regex '\b($(join "${types}" '|'))\b' 0:type
+ add-highlighter shared/python/code/ regex '^\h*(@[\w_.]+))' 1:attribute
+ "
+}
+
+add-highlighter shared/python/code/ regex (?<=[\w\s\d\)\]'"_])(<=|<<|>>|>=|<>?|>|!=|==|\||\^|&|\+|-|\*\*?|//?|%|~) 0:operator
+add-highlighter shared/python/code/ regex (?<=[\w\s\d'"_])((?<![=<>!]):?=(?![=])|[+*-]=) 0:builtin
+add-highlighter shared/python/code/ regex ^\h*(?:from|import)\h+(\S+) 1:module
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden python-insert-on-new-line %{ evaluate-commands -itersel -draft %{
+ execute-keys <semicolon>
+ try %{
+ evaluate-commands -draft -save-regs '/"' %{
+ # copy the commenting prefix
+ execute-keys -save-regs '' k x1s^\h*(#+\h*)<ret> y
+ try %{
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*#+\h*$<ret> jxs^\h*<ret>P
+ } catch %{
+ # if there is no text in the previous comment, remove it completely
+ execute-keys d
+ }
+ }
+
+ # trim trailing whitespace on the previous line
+ try %{ execute-keys -draft k x s\h+$<ret> d }
+ }
+} }
+
+define-command -hidden python-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # cleanup trailing whitespaces from previous line
+ try %{ execute-keys -draft k x s \h+$ <ret> d }
+ # indent after line ending with :
+ try %{ execute-keys -draft , k x <a-k> :$ <ret> <a-K> ^\h*# <ret> j <a-gt> }
+ # deindent closing brace/bracket when after cursor (for arrays and dictionaries)
+ try %< execute-keys -draft x <a-k> ^\h*[}\]] <ret> gh / [}\]] <ret> m <a-S> 1<a-&> >
+ >
+>
+
diff --git a/autoload/filetype/r.kak b/autoload/filetype/r.kak
new file mode 100644
index 0000000..d3d9854
--- /dev/null
+++ b/autoload/filetype/r.kak
@@ -0,0 +1,155 @@
+# http://kakoune.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate (.*/)?(\.Rprofile|.*\.[rR]) %{
+ set-option buffer filetype r
+}
+
+provide-module r %§
+
+# Highlighters & Completion
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/r regions
+add-highlighter shared/r/code default-region group
+add-highlighter shared/r/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/r/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/r/comment region '#' '$' fill comment
+add-highlighter shared/r/identifier region '`' (?<!\\)(\\\\)*` fill attribute
+
+# see base::NumericConstants
+add-highlighter shared/r/code/ regex '(?i)\b(?<![.\d])\d+(\.\d*)?(e[-+]?\d+)?(?I)[iL]?(?![\d.e\w])' 0:value
+add-highlighter shared/r/code/ regex '(?i)(?<![.\d\w])\.\d+(e[-+]?\d+)?(?I)[iL]?(?![\d.e])' 0:value
+add-highlighter shared/r/code/ regex '(?i)\b(?<![.\d])0x[0-9a-f]+?(p[-+]?\d+)?(?I)[iL]?(?![\d.e\w])' 0:value
+
+evaluate-commands %sh{
+ # see base::Reserved
+ values="TRUE|FALSE|NULL|Inf|NaN|NA|NA_integer_|NA_real_|NA_complex_|NA_character_|\.{3}|\.{2}\d+|"
+ keywords="if|else|repeat|while|function|for|in|next|break"
+
+ # see base::Ops and methods::Ops
+ math_functions="abs|sign|sqrt|floor|ceiling|trunc|round|signif|exp|log|expm1|log1p|cos|sin|tan|cospi|sinpi|tanpi|acos|asin|atan|cosh|sinh|tanh|acosh|asinh|atanh|lgamma|gamma|digamma|trigamma"
+ summary_functions="all|any|sum|prod|min|max|range"
+ complex_functions="Arg|Conj|Im|Mod|Re"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "hook global WinSetOption filetype=python %{
+ set-option window static_words ${values} ${keywords} ${math_functions} ${summary_functions} ${complex_functions}
+ }" | tr '|' ' '
+
+ printf %s "
+ add-highlighter shared/r/code/ regex '\b(${values})\b' 0:value
+ add-highlighter shared/r/code/ regex '\b(${keywords})\b' 0:keyword
+ add-highlighter shared/r/code/ regex '\b(${math_functions}|${summary_functions}|${complex_functions})\b' 0:function
+ "
+}
+
+# see base::Syntax
+add-highlighter shared/r/code/ regex (?<=[\w\s\d'"_)])(\$|@|\^|-|\+|%[^%^\n]+%|\*|/|<|>|<=|>=|!|&{1,2}|\|{1,2}|~|\?|:{1,3}|\[{1,2}|\]{1,2}|={1,2}|<{1,2}-|->{1,2}|!=|%%) 0:operator
+
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden r-trim-indent %{
+ # remove the line if it's empty when leaving the insert mode
+ try %{ execute-keys -draft x 1s^(\h+)$<ret> d }
+}
+
+define-command -hidden r-indent-on-newline %< evaluate-commands -draft -itersel %<
+ execute-keys <semicolon>
+ try %<
+ # if previous line closed a paren (possibly followed by words and a comment),
+ # copy indent of the opening paren line
+ execute-keys -draft kx 1s(\))(\h+\w+)*\h*(\;\h*)?(?:#[^\n]+)?\n\z<ret> m<a-semicolon>J <a-S> 1<a-&>
+ > catch %<
+ # else indent new lines with the same level as the previous one
+ execute-keys -draft K <a-&>
+ >
+ # remove previous empty lines resulting from the automatic indent
+ try %< execute-keys -draft k x <a-k>^\h+$<ret> Hd >
+ # indent after an opening brace or parenthesis at end of line
+ try %< execute-keys -draft k x s[{(]\h*$<ret> j <a-gt> >
+ # indent after a statement not followed by an opening brace
+ try %< execute-keys -draft k x s\)\h*(?:#[^\n]+)?\n\z<ret> \
+ <a-semicolon>mB <a-k>\A\b(if|for|while)\b<ret> <a-semicolon>j <a-gt> >
+ try %< execute-keys -draft k x s \belse\b\h*(?:#[^\n]+)?\n\z<ret> \
+ j <a-gt> >
+ # deindent after a single line statement end
+ try %< execute-keys -draft K x <a-k>\;\h*(#[^\n]+)?$<ret> \
+ K x s\)(\h+\w+)*\h*(#[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ MB <a-k>\A\b(if|for|while)\b<ret> <a-S>1<a-&> >
+ try %< execute-keys -draft K x <a-k>\;\h*(#[^\n]+)?$<ret> \
+ K x s \belse\b\h*(?:#[^\n]+)?\n([^\n]*\n){2}\z<ret> \
+ <a-S>1<a-&> >
+ # align to the opening parenthesis or opening brace (whichever is first)
+ # on a previous line if its followed by text on the same line
+ try %< evaluate-commands -draft %<
+ # Go to opening parenthesis and opening brace, then select the most nested one
+ try %< execute-keys [c [({],[)}] <ret> >
+ # Validate selection and get first and last char
+ execute-keys <a-k>\A[{(](\h*\S+)+\n<ret> <a-K>"(([^"]*"){2})*<ret> <a-K>'(([^']*'){2})*<ret> <a-:><a-semicolon>L <a-S>
+ # Remove possibly incorrect indent from new line which was copied from previous line
+ try %< execute-keys -draft , <a-h> s\h+<ret> d >
+ # Now indent and align that new line with the opening parenthesis/brace
+ execute-keys 1<a-&> &
+ > >
+> >
+
+define-command -hidden r-indent-on-opening-curly-brace %[
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft -itersel h<a-F>)M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> <a-S> 1<a-&> ]
+]
+
+define-command -hidden r-indent-on-closing-curly-brace %[
+ # align to opening curly brace when alone on a line
+ try %[
+ # in case open curly brace follows a closing paren, align indent with opening paren
+ execute-keys -itersel -draft <a-h><a-:><a-k>^\h+\}$<ret>hm <a-F>)M <a-k> \A\(.*\)\h\{.*\}\z <ret> <a-S>1<a-&>
+ ] catch %[
+ # otherwise align with open curly brace
+ execute-keys -itersel -draft <a-h><a-:><a-k>^\h+\}$<ret>hm<a-S>1<a-&>
+ ] catch %[]
+]
+
+define-command -hidden r-insert-on-newline %[ evaluate-commands -itersel -draft %[
+ execute-keys <semicolon>
+ try %[
+ evaluate-commands -draft -save-regs '/"' %[
+ # copy the commenting prefix
+ execute-keys -save-regs '' k x1s^\h*(#+\h*)<ret> y
+ try %[
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*#+\h*$<ret> jxs^\h*<ret>P
+ ] catch %[
+ # if there is no text in the previous comment, remove it completely
+ execute-keys d
+ ]
+ ]
+ ]
+] ]
+
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook -group r-highlight global WinSetOption filetype=r %{
+ require-module r
+ add-highlighter window/r ref r
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/r }
+}
+
+hook global WinSetOption filetype=r %~
+ require-module r
+ hook window ModeChange pop:insert:.* r-trim-indent
+ hook window InsertChar \n r-insert-on-newline
+ hook window InsertChar \n r-indent-on-newline
+ hook window InsertChar \{ r-indent-on-opening-curly-brace
+ hook window InsertChar \} r-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window r-.+ }
+~
diff --git a/autoload/filetype/ragel.kak b/autoload/filetype/ragel.kak
new file mode 100644
index 0000000..e0a77db
--- /dev/null
+++ b/autoload/filetype/ragel.kak
@@ -0,0 +1,84 @@
+# http://complang.org/ragel
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# ragel.kak does not try to detect host language.
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](ragel|rl) %{
+ set-option buffer filetype ragel
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=ragel %{
+ require-module ragel
+
+ hook window ModeChange pop:insert:.* -group ragel-trim-indent ragel-trim-indent
+ hook window InsertChar .* -group ragel-indent ragel-indent-on-char
+ hook window InsertChar \n -group ragel-insert ragel-insert-on-new-line
+ hook window InsertChar \n -group ragel-indent ragel-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window ragel-.+ }
+}
+
+hook -group ragel-highlight global WinSetOption filetype=ragel %{
+ add-highlighter window/ragel ref ragel
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/ragel }
+}
+
+provide-module ragel %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/ragel regions
+add-highlighter shared/ragel/code default-region group
+add-highlighter shared/ragel/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/ragel/single_string region "'" "'" fill string
+add-highlighter shared/ragel/comment region '#' '$' fill comment
+
+add-highlighter shared/ragel/code/ regex \b(true|false)\b 0:value
+add-highlighter shared/ragel/code/ regex '%%\{|\}%%|<\w+>' 0:variable
+add-highlighter shared/ragel/code/ regex :=|=>|->|:>|:>>|<: 0:operator
+add-highlighter shared/ragel/code/ regex \b(action|alnum|alpha|any|ascii|case|cntrl|contained|context|data|digit|empty|eof|err|error|exec|export|exports|extend|fblen|fbreak|fbuf|fc|fcall|fcurs|fentry|fexec|fgoto|fhold|first_final|fnext|fpc|fret|from|fstack|ftargs|graph|import|include|init|inwhen|lerr|lower|machine|nocs|noend|noerror|nofinal|noprefix|outwhen|postpop|prepush|print|punct|range|space|start|to|upper|when|write|xdigit|zlen)\b 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden ragel-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden ragel-indent-on-char %<
+ evaluate-commands -draft -itersel %<
+ # align closer token to its opener when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h+[\]})]$ <ret> m s \A|.\z <ret> 1<a-&> >
+ try %< execute-keys -draft <a-h> <a-k> ^\h+ [*]$ <ret> <a-?> [*]$ <ret> s \A|.\z <ret> 1<a-&> >
+ >
+>
+
+define-command -hidden ragel-insert-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # copy _#_ comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ >
+>
+
+define-command -hidden ragel-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : ragel-trim-indent <ret> }
+ # indent after lines ending with opener token
+ try %< execute-keys -draft k x <a-k> [[{(*]$ <ret> j <a-gt> >
+ # align closer token to its opener when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*[})\]] <ret> gh / [})\]] <ret> m <a-S> 1<a-&> >
+ >
+>
+
diff --git a/autoload/filetype/restructuredtext.kak b/autoload/filetype/restructuredtext.kak
new file mode 100644
index 0000000..7853b30
--- /dev/null
+++ b/autoload/filetype/restructuredtext.kak
@@ -0,0 +1,92 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](rst) %{
+ set-option buffer filetype restructuredtext
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=restructuredtext %{
+ require-module restructuredtext
+}
+
+hook -group restructuredtext-load-languages global WinSetOption filetype=restructuredtext %{
+ restructuredtext-load-languages '%'
+}
+
+hook -group restructuredtext-load-languages global WinSetOption filetype=restructuredtext %{
+ hook -group restructuredtext-load-languages window NormalIdle .* %{restructuredtext-load-languages gtGbGl}
+ hook -group restructuredtext-load-languages window InsertIdle .* %{restructuredtext-load-languages gtGbGl}
+}
+
+hook -group restructuredtext-highlight global WinSetOption filetype=restructuredtext %{
+ add-highlighter window/restructuredtext ref restructuredtext
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/restructuredtext }
+}
+
+provide-module restructuredtext %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/restructuredtext regions
+add-highlighter shared/restructuredtext/content default-region group
+add-highlighter shared/restructuredtext/code region ::\h*\n ^(?=\S) fill meta
+
+# Setext-style header
+# Valid header characters:
+# # ! " $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
+
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(#{3,}\n)?[^\n]+\n(#{3,})$ 0:title
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(!{3,}\n)?[^\n]+\n(!{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)("{3,}\n)?[^\n]+\n("{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\${3,}\n)?[^\n]+\n(\${3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(%{3,}\n)?[^\n]+\n(%{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(&{3,}\n)?[^\n]+\n(&{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)('{3,}\n)?[^\n]+\n('{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\({3,}\n)?[^\n]+\n(\({3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\){3,}\n)?[^\n]+\n(\){3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\*{3,}\n)?[^\n]+\n(\*{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\+{3,}\n)?[^\n]+\n(\+{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(,{3,}\n)?[^\n]+\n(,{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(-{3,}\n)?[^\n]+\n(-{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\.{3,}\n)?[^\n]+\n(\.{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(/{3,}\n)?[^\n]+\n(/{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(:{3,}\n)?[^\n]+\n(:{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\;{3,}\n)?[^\n]+\n(\;{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(<{3,}\n)?[^\n]+\n(<{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(={3,}\n)?[^\n]+\n(={3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(>{3,}\n)?[^\n]+\n(>{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\?{3,}\n)?[^\n]+\n(\?{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(@{3,}\n)?[^\n]+\n(@{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\[{3,}\n)?[^\n]+\n(\[{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\\{3,}\n)?[^\n]+\n(\\{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\]{3,}\n)?[^\n]+\n(\]{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\^{3,}\n)?[^\n]+\n(\^{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(_{3,}\n)?[^\n]+\n(_{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(`{3,}\n)?[^\n]+\n(`{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\{{3,}\n)?[^\n]+\n(\{{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\|{3,}\n)?[^\n]+\n(\|{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(\}{3,}\n)?[^\n]+\n(\}{3,})$ 0:header
+add-highlighter shared/restructuredtext/content/ regex (\A|\n\n)(~{3,}\n)?[^\n]+\n(~{3,})$ 0:header
+
+# Inline markup
+add-highlighter shared/restructuredtext/content/ regex [^*](\*\*([^\s*]|([^\s*][^*]*[^\s*]))\*\*)[^*] 1:+b
+add-highlighter shared/restructuredtext/content/ regex [^*](\*([^\s*]|([^\s*][^*]*[^\s*]))\*)[^*] 1:+i
+add-highlighter shared/restructuredtext/content/ regex [^`](``([^\s`]|([^\s`][^`]*[^\s`]))``)[^`] 1:mono
+
+define-command restructuredtext-load-languages -params 1 %{
+ evaluate-commands -draft %{ try %{
+ execute-keys "%arg{1}s^\.\.\h*code-block::\h*\K\w+<ret>"
+ evaluate-commands -itersel %{ try %{
+ require-module %val{selection}
+ add-highlighter "shared/restructuredtext/%val{selection}" region "\.\.\h*code-block::\h*%val{selection}\h*\n" '^(?=\S)' regions
+ add-highlighter "shared/restructuredtext/%val{selection}/" default-region fill meta
+ add-highlighter "shared/restructuredtext/%val{selection}/inner" region \A\.\.\h*code-block::[^\n]*\K '^(?=\S)' ref %val{selection}
+ }}
+ }}
+}
+
+}
diff --git a/autoload/filetype/ruby.kak b/autoload/filetype/ruby.kak
new file mode 100644
index 0000000..0a57264
--- /dev/null
+++ b/autoload/filetype/ruby.kak
@@ -0,0 +1,194 @@
+# http://ruby-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*(([.](rb))|(irbrc)|(pryrc)|(Brewfile)|(Capfile|[.]cap)|(Gemfile|[.]gemspec)|(Guardfile)|(Rakefile|[.]rake)|(Thorfile|[.]thor)|(Vagrantfile)) %{
+ set-option buffer filetype ruby
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=ruby %{
+ require-module ruby
+
+ set-option window static_words %opt{ruby_static_words}
+
+ hook window ModeChange pop:insert:.* -group ruby-trim-indent ruby-trim-indent
+ hook window InsertChar .* -group ruby-indent ruby-indent-on-char
+ hook window InsertChar \n -group ruby-indent ruby-indent-on-new-line
+ hook window InsertChar \n -group ruby-insert ruby-insert-on-new-line
+
+ alias window alt ruby-alternative-file
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-hooks window ruby-.+
+ unalias window alt ruby-alternative-file
+ }
+}
+
+hook -group ruby-highlight global WinSetOption filetype=ruby %{
+ add-highlighter window/ruby ref ruby
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/ruby }
+}
+
+provide-module ruby %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/ruby regions
+add-highlighter shared/ruby/code default-region group
+add-highlighter shared/ruby/double_symbol region ':"' (?<!\\)(\\\\)*" regions
+add-highlighter shared/ruby/single_symbol region ":'" (?<!\\)(\\\\)*' fill variable
+add-highlighter shared/ruby/double_string region '"' (?<!\\)(\\\\)*" regions
+add-highlighter shared/ruby/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/ruby/backtick region '(?<![$:])`' (?<!\\)(\\\\)*` regions
+add-highlighter shared/ruby/regex region '(?<![$:])/' (?<!\\)(\\\\)*/[imox]* regions
+add-highlighter shared/ruby/ region '#' '$' fill comment
+add-highlighter shared/ruby/ region ^=begin ^=end fill comment
+add-highlighter shared/ruby/ region -recurse \( '%[qwQW]?\(' \) fill string
+add-highlighter shared/ruby/ region -recurse \{ '%[qwQW]?\{' \} fill string
+add-highlighter shared/ruby/ region -recurse \[ '%[qwQW]?\[' \] fill string
+add-highlighter shared/ruby/ region -recurse < '%[qwQW]?<' > fill string
+add-highlighter shared/ruby/ region -recurse \( '%[isIS]\(' \) fill variable
+add-highlighter shared/ruby/ region -recurse \{ '%[isIS]\{' \} fill variable
+add-highlighter shared/ruby/ region -recurse \[ '%[isIS]\[' \] fill variable
+add-highlighter shared/ruby/ region -recurse < '%[isIS]<' > fill variable
+add-highlighter shared/ruby/ region -recurse \( '%[rxRX]\(' \) fill meta
+add-highlighter shared/ruby/ region -recurse \{ '%[rxRX]\{' \} fill meta
+add-highlighter shared/ruby/ region -recurse \[ '%[rxRX]\[' \] fill meta
+add-highlighter shared/ruby/ region -recurse < '%[rxRX]<' > fill meta
+add-highlighter shared/ruby/ region -match-capture '%[qwQW]?([^\s0-9A-Za-z\(\{\[<>\]\}\)])' ([^\s0-9A-Za-z\(\{\[<>\]\}\)]) fill string
+add-highlighter shared/ruby/ region -match-capture '%[isIS]([^\s0-9A-Za-z\(\{\[<>\]\}\)])' ([^\s0-9A-Za-z\(\{\[<>\]\}\)]) fill variable
+add-highlighter shared/ruby/ region -match-capture '%[rxRX]([^\s0-9A-Za-z\(\{\[<>\]\}\)])' ([^\s0-9A-Za-z\(\{\[<>\]\}\)]) fill meta
+add-highlighter shared/ruby/heredoc region -match-capture '<<[-~]?(?!self)(\w+)' '^\h*(\w+)$' fill string
+add-highlighter shared/ruby/division region '[\w\)\]]\K(/|(\h+/\h+))' '\w' group # Help Kakoune to better detect /…/ literals
+
+# Regular expression flags are: i → ignore case, m → multi-lines, o → only interpolate #{} blocks once, x → extended mode (ignore white spaces)
+# Literals are: i → array of symbols, q → string, r → regular expression, s → symbol, w → array of words, x → capture shell result
+
+add-highlighter shared/ruby/double_string/ default-region fill string
+add-highlighter shared/ruby/double_string/interpolation region -recurse \{ \Q#{ \} fill meta
+
+add-highlighter shared/ruby/double_symbol/ default-region fill variable
+add-highlighter shared/ruby/double_symbol/interpolation region -recurse \{ \Q#{ \} fill meta
+
+add-highlighter shared/ruby/backtick/ default-region fill meta
+add-highlighter shared/ruby/backtick/interpolation region -recurse \{ \Q#{ \} fill meta
+
+add-highlighter shared/ruby/regex/ default-region fill meta
+add-highlighter shared/ruby/regex/interpolation region -recurse \{ \Q#{ \} fill meta
+
+evaluate-commands %sh{
+ # Grammar
+ # Keywords are collected searching for keywords at
+ # https://github.com/ruby/ruby/blob/trunk/parse.y
+ keywords="alias|and|begin|break|case|class|def|defined|do|else|elsif|end"
+ keywords="${keywords}|ensure|false|for|if|in|module|next|nil|not|or|private|protected|public|redo"
+ keywords="${keywords}|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield"
+ attributes="attr_reader|attr_writer|attr_accessor"
+ values="false|true|nil"
+ meta="require|require_relative|include|extend"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list ruby_static_words ${keywords} ${attributes} ${values} ${meta}" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/ruby/code/ regex \b(${keywords})[^0-9A-Za-z_!?] 1:keyword
+ add-highlighter shared/ruby/code/ regex \b(${attributes})\b 0:attribute
+ add-highlighter shared/ruby/code/ regex \b(${values})\b 0:value
+ add-highlighter shared/ruby/code/ regex \b(${meta})\b 0:meta
+ "
+}
+
+add-highlighter shared/ruby/code/ regex \b(\w+:(?!:))|(:?(\$(-[0FIKWadilpvw]|["'`/~&+=!$*,:.\;<>?@\\])|(\$|@@?)\w+))|((?<!:):(![~=]|=~|>[=>]?|<((=>?)|<)?|[+\-]@?|\*\*?|===?|[/`%&!^|~]|(\w+[=?!]?)|(\[\]=?)))|([A-Z]\w*|^|\h)\K::(?=[A-Z]) 0:variable
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command ruby-alternative-file -docstring 'Jump to the alternate file (implementation ↔ test)' %{ evaluate-commands %sh{
+ case $kak_buffile in
+ *spec/*_spec.rb)
+ altfile=$(eval echo $(echo $kak_buffile | sed s+spec/+'*'/+';'s/_spec//))
+ [ ! -f $altfile ] && echo "fail 'implementation file not found'" && exit
+ ;;
+ *test/*_test.rb)
+ altfile=$(eval echo $(echo $kak_buffile | sed s+test/+'*'/+';'s/_test//))
+ [ ! -f $altfile ] && echo "fail 'implementation file not found'" && exit
+ ;;
+ *.rb)
+ altfile=""
+ altdir=""
+ path=$kak_buffile
+ dirs=$(while [ $path ]; do echo $path; path=${path%/*}; done | tail -n +2)
+ for dir in $dirs; do
+ altdir=$dir/spec && suffix=spec
+ [ ! -d $altdir ] && altdir=$dir/test && suffix=test
+ if [ -d $altdir ]; then
+ altfile=$altdir/$(realpath $kak_buffile --relative-to $dir | sed s+[^/]'*'/++';'s/.rb$/_${suffix}.rb/)
+ break
+ fi
+ done
+ [ ! -d "$altdir" ] && echo "fail 'spec/ and test/ not found'" && exit
+ ;;
+ *)
+ echo "fail 'alternative file not found'" && exit
+ ;;
+ esac
+ echo "edit $altfile"
+}}
+
+define-command -hidden ruby-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden ruby-indent-on-char %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # align middle and end structures to start
+ try %{ execute-keys -draft x <a-k> ^ \h * (else) $ <ret> <a-a> i <a-semicolon> <a-?> ^ \h * (if|case) <ret> <a-S> 1<a-&> }
+ try %{ execute-keys -draft x <a-k> ^ \h * (elsif) $ <ret> <a-a> i <a-semicolon> <a-?> ^ \h * (if) <ret> <a-S> 1<a-&> }
+ try %{ execute-keys -draft x <a-k> ^ \h * (when) $ <ret> <a-a> i <a-semicolon> <a-?> ^ \h * (case) <ret> <a-S> 1<a-&> }
+ try %{ execute-keys -draft x <a-k> ^ \h * (rescue) $ <ret> <a-a> i <a-semicolon> <a-?> ^ \h * (begin|def) <ret> <a-S> 1<a-&> }
+ }
+}
+
+define-command -hidden ruby-indent-on-new-line %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : ruby-trim-indent <ret> }
+ # indent after start structure
+ try %{ execute-keys -draft k x <a-k> ^ \h * (begin|case|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while|.+\bdo$|.+\bdo\h\|.+(?=\|)) [^0-9A-Za-z_!?] <ret> j <a-gt> }
+ }
+}
+
+define-command -hidden ruby-insert-on-new-line %[
+ evaluate-commands -no-hooks -draft -itersel %[
+ # copy _#_ comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y jgi P }
+ # wisely add end structure
+ evaluate-commands -save-regs x %[
+ try %{ execute-keys -draft k x s ^ \h + <ret> \" x y } catch %{ reg x '' }
+ try %[
+ evaluate-commands -draft %[
+ # Check if previous line opens a block
+ execute-keys -draft kx <a-k>^<c-r>x(begin|case|class|def|for|if|module|unless|until|while|.+\bdo$|.+\bdo\h\|.+(?=\|))[^0-9A-Za-z_!?]<ret>
+ # Check that we do not already have an end for this indent level which is first set via `ruby-indent-on-new-line` hook
+ execute-keys -draft }i J x <a-K> ^<c-r>x(end|else|elsif|rescue|when)[^0-9A-Za-z_!?]<ret>
+ ]
+ execute-keys -draft o<c-r>xend<esc> # insert a new line with containing end
+ ]
+ ]
+ ]
+]
+
diff --git a/autoload/filetype/rust.kak b/autoload/filetype/rust.kak
new file mode 100644
index 0000000..3aacc73
--- /dev/null
+++ b/autoload/filetype/rust.kak
@@ -0,0 +1,209 @@
+# http://rust-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](rust|rs) %{
+ set-option buffer filetype rust
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=rust %<
+ require-module rust
+ hook window ModeChange pop:insert:.* -group rust-trim-indent rust-trim-indent
+ hook window InsertChar \n -group rust-indent rust-indent-on-new-line
+ hook window InsertChar \{ -group rust-indent rust-indent-on-opening-curly-brace
+ hook window InsertChar [)}\]] -group rust-indent rust-indent-on-closing
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window rust-.+ }
+>
+
+hook -group rust-highlight global WinSetOption filetype=rust %{
+ add-highlighter window/rust ref rust
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/rust }
+}
+
+provide-module rust %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/rust regions
+add-highlighter shared/rust/code default-region group
+add-highlighter shared/rust/string region %{(?<!')"} (?<!\\)(\\\\)*" fill string
+add-highlighter shared/rust/raw_string region -match-capture %{(?<!')r(#*)"} %{"(#*)} fill string
+
+add-highlighter shared/rust/line_doctest region ^\h*//[!/]\h*```($|should_panic|no_run|ignore|allow_fail|rust|test_harness|compile_fail|E\d{4}|edition201[58]) ^\h*//[!/]\h*```$ regions
+add-highlighter shared/rust/line_doctest/marker region ``` $ group
+add-highlighter shared/rust/line_doctest/marker/fence regex ``` 0:meta
+add-highlighter shared/rust/line_doctest/marker/keywords regex [\d\w] 0:meta # already matched above, just ignore comma
+add-highlighter shared/rust/line_doctest/inner region '^\h*//[!/]( #(?= ))?' '$| ' group
+add-highlighter shared/rust/line_doctest/inner/comment regex //[!/] 0:documentation
+add-highlighter shared/rust/line_doctest/inner/hidden regex '#' 0:meta
+add-highlighter shared/rust/line_doctest/code default-region ref rust
+add-highlighter shared/rust/line_code_rest region ^\h*//[!/]\h*``` ^\h*//[!/]\h*```$ fill documentation # reset invalid doctest
+add-highlighter shared/rust/line_comment2 region //[!/]{2} $ fill comment
+add-highlighter shared/rust/line_doc region //[!/] $ fill documentation
+add-highlighter shared/rust/line_comment1 region // $ group
+add-highlighter shared/rust/line_comment1/comment fill comment
+add-highlighter shared/rust/line_comment1/todo regex (TODO|NOTE|FIXME): 1:meta
+
+add-highlighter shared/rust/block_comment2 region -recurse /\*\*\* /\*\*\* \*/ fill comment
+add-highlighter shared/rust/block_doc region -recurse /\*\* /\*\* \*/ regions
+add-highlighter shared/rust/block_doc/doctest region ```($|should_panic|no_run|ignore|allow_fail|rust|test_harness|compile_fail|E\d{4}|edition201[58]) ```$ regions
+add-highlighter shared/rust/block_doc/doctest/marker region ``` $ group
+add-highlighter shared/rust/block_doc/doctest/marker/fence regex ``` 0:meta
+add-highlighter shared/rust/block_doc/doctest/marker/keywords regex [\d\w] 0:meta # already matched above, just ignore comma
+add-highlighter shared/rust/block_doc/doctest/inner default-region group
+add-highlighter shared/rust/block_doc/doctest/inner/hidden regex '^\h*\**\h*#' 0:meta
+add-highlighter shared/rust/block_doc/doctest/inner/comment regex ^\h*\* 0:documentation
+add-highlighter shared/rust/block_doc/doctest/inner/code ref rust
+add-highlighter shared/rust/block_doc/code_rest region ``` ``` fill documentation
+add-highlighter shared/rust/block_doc/doc default-region fill documentation
+add-highlighter shared/rust/block_comment1 region -recurse /\* /\* \*/ group
+add-highlighter shared/rust/block_comment1/comment fill comment
+add-highlighter shared/rust/block_comment1/todo regex (TODO|NOTE|FIXME): 1:meta
+
+add-highlighter shared/rust/macro_attributes region -recurse "\[" "#!?\[" "\]" regions
+add-highlighter shared/rust/macro_attributes/ default-region fill meta
+add-highlighter shared/rust/macro_attributes/string region %{(?<!')"} (?<!\\)(\\\\)*" fill string
+add-highlighter shared/rust/macro_attributes/raw_string region -match-capture %{(?<!')r(#*)"} %{"(#*)} fill string
+
+add-highlighter shared/rust/code/operators_arithmetic regex (\+|-|/|\*|=|\^|&|\||!|>|<|%)=? 0:operator
+add-highlighter shared/rust/code/operators_as regex \bas\b 0:operator
+add-highlighter shared/rust/code/ref_ref regex (&\h+[&~@*])[^)=\s\t\r\n] 1:type
+add-highlighter shared/rust/code/ref regex ([&~@*])[^)=\s\t\r\n] 1:type
+add-highlighter shared/rust/code/operators_logic regex &&|\|\| 0:operator
+
+add-highlighter shared/rust/code/lifetime_or_loop_label regex ('([a-zA-Z]\w+|_\w+))\b 1:meta
+add-highlighter shared/rust/code/namespace regex \b[a-zA-Z](\w+)?(\h+)?(?=::) 0:module
+add-highlighter shared/rust/code/mod_path_sep regex :: 0:meta
+add-highlighter shared/rust/code/question_mark regex \? 0:meta
+# the language keywords are defined here, but many of them are reserved and unused yet:
+# https://doc.rust-lang.org/reference/keywords.html
+add-highlighter shared/rust/code/function_call regex _?[a-zA-Z]\w*\s*(?=\() 0:function
+add-highlighter shared/rust/code/generic_function_call regex _?[a-zA-Z]\w*\s*(?=::<) 0:function
+add-highlighter shared/rust/code/function_declaration regex (?:fn\h+)(_?\w+)(?:<[^>]+?>)?\( 1:function
+add-highlighter shared/rust/code/keywords regex \b(?:as|break|continue|crate|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|pub|return|self|Self|struct|super|trait|true|type|union|unsafe|use|where|while|async|await|dyn|abstract|become|box|do|try)\b 0:keyword
+add-highlighter shared/rust/code/storage regex \b(move|mut|ref|static|const)\b 0:type
+add-highlighter shared/rust/code/pub_with_scope regex \b(pub)\h*(\()\h*(crate|super|self|in\h+[\w:]+)\h*(\)) 1:keyword 2:meta 4:meta
+# after let can be an arbitrary pattern match
+add-highlighter shared/rust/code/macro regex \b\w+! 0:meta
+# the number literals syntax is defined here:
+# https://doc.rust-lang.org/reference/tokens.html#numb ers
+add-highlighter shared/rust/code/values regex \b(?:self|true|false|[0-9][_0-9]*(?:\.[0-9][_0-9]*|(?:\.[0-9][_0-9]*)?E[\+\-][_0-9]+)(?:f(?:32|64))?|(?:0x[_0-9a-fA-F]+|0o[_0-7]+|0b[_01]+|[0-9][_0-9]*)(?:(?:i|u|f)(?:8|16|32|64|128|size))?)\b 0:value
+add-highlighter shared/rust/code/char_character regex "'([^\\]|\\(.|x[0-9a-fA-F]{2}|u\{[0-9a-fA-F]{1,6}\}))'" 0:value
+# TODO highlight error for unicode or single escape by te character
+add-highlighter shared/rust/code/byte_character regex b'([\x00-\x5B\x5D-\x7F]|\\(.|x[0-9a-fA-F]{2}))' 0:value
+add-highlighter shared/rust/code/builtin_types regex \b(?:u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize|f32|f64|bool|char|str|Self)\b 0:type
+add-highlighter shared/rust/code/return regex \breturn\b 0:meta
+
+add-highlighter shared/rust/code/enum regex \b(Option|Result)\b 0:type
+add-highlighter shared/rust/code/enum_variant regex \b(Some|None|Ok|Err)\b 0:value
+add-highlighter shared/rust/code/std_traits regex \b(Copy|Send|Sized|Sync|Drop|Fn|FnMut|FnOnce|Box|ToOwned|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator|SliceConcatExt|String|ToString|Vec)\b 0:type
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden rust-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden rust-indent-on-new-line %~
+ evaluate-commands -draft -itersel %@
+ try %{
+ try %[ # line comment
+ evaluate-commands -draft -save-regs '/"' %[
+ # copy the commenting prefix
+ execute-keys -save-regs '' k x s ^\h*//[!/]{0,2}\h* <ret> y
+ try %[
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*//[!/]{0,2}$<ret> jxs^\h*<ret>P
+ ] catch %[
+ # TODO figure out a way to not delete empty comment in current line
+ # if there is no space and text in the previous comment, remove it completely
+ execute-keys s //.*<ret> d
+ ]
+ ]
+ ] catch %[ # block comment
+ # if the previous line isn't within a comment scope, break
+ execute-keys -draft kx <a-k>^(\h*/\*|\h+\*(?!/))<ret>
+
+ # find comment opening, validate it was not closed, and check its using star prefixes
+ execute-keys -draft <a-?>/\*<ret><a-H> <a-K>\*/<ret> <a-k>\A\h*/\*([^\n]*\n\h*\*)*[^\n]*\n\h*.\z<ret>
+
+ try %[
+ # if the previous line is opening the comment, insert star preceeded by space
+ execute-keys -draft kx<a-k>^\h*/\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the next line is a comment line insert a star
+ execute-keys -draft jx<a-k>^\h+\*<ret>
+ execute-keys -draft i*<space><esc>
+ ] catch %[
+ try %[
+ # if the previous line is an empty comment line, close the comment scope
+ execute-keys -draft kx<a-k>^\h+\*\h+$<ret> x1s\*(\h*)<ret>c/<esc>
+ ] catch %[
+ # if the previous line is a non-empty comment line, add a star
+ execute-keys -draft i*<space><esc>
+ ]
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft s\h+$<ret> d ]
+ # align the new star with the previous one
+ execute-keys Kx1s^[^*]*(\*)<ret>&
+ ]
+ } catch %`
+ # re-indent previous line if it starts with where to match previous block
+ # string literal parsing within extern does not handle escape
+ try %% execute-keys -draft k x <a-k> ^\h*where\b <ret> hh <a-?> ^\h*\b(impl|((|pub\ |pub\((crate|self|super|in\ (::)?([a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)(::[a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)*)\)\ )((async\ |const\ )?(unsafe\ )?(extern\ ("[^"]*"\ )?)?fn|struct|enum|union)))\b <ret> <a-S> 1<a-&> %
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # indent after lines ending with [{([].+ and move first parameter to own line
+ try %< execute-keys -draft [c[({[],[)}\]] <ret> <a-k> \A[({[][^\n]+\n[^\n]*\n?\z <ret> L i<ret><esc> <gt> <a-S> <a-&> >
+ # indent after non-empty lines not starting with operator and not ending with , or ; or {
+ # XXX simplify this into a single <a-k> without s
+ try %< execute-keys -draft k x s [^\h].+ <ret> <a-K> \A[-+*/&|^})<gt><lt>#] <ret> <a-K> [,<semicolon>{](\h*/[/*].*|)$ <ret> j <a-gt> >
+ # indent after lines ending with {
+ try %+ execute-keys -draft k x <a-k> \{$ <ret> j <a-gt> +
+ # dedent after lines starting with . and ending with } or ) or , or ; or .await (} or ) or .await maybe with ?)
+ try %_ execute-keys -draft k x <a-k> ^\h*\. <ret> <a-k> ([,<semicolon>]|(([})]|\.await)\?*))\h*$ <ret> j <a-lt> _
+ # dedent after lines ending with " => {}" - part of empty match
+ try %# execute-keys -draft k x <a-k> \ =>\ \{\}\h*$ <ret> j <a-lt> #
+ # align to opening curly brace or paren when newline is inserted before a single closing
+ try %< execute-keys -draft <a-h> <a-k> ^\h*[)}] <ret> h m <a-S> 1<a-&> >
+ # todo dedent additional unmatched parenthesis
+ # try %& execute-keys -draft k x s \((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\) l Gl s\) %sh{
+ # count previous selections length
+ # printf "j $(echo $kak_selections_length | wc -w) <a-lt>"
+ # } &
+ `
+ # filter previous line
+ try %{ execute-keys -draft k : rust-trim-indent <ret> }
+ @
+~
+
+define-command -hidden rust-indent-on-opening-curly-brace %[
+ evaluate-commands -draft -itersel %~
+ # align indent with opening paren when { is entered on a new line after the closing paren
+ try %[ execute-keys -draft h <a-F> ) M <a-k> \A\(.*\)\h*\n\h*\{\z <ret> s \A|.\z <ret> 1<a-&> ]
+ # dedent standalone { after impl and related block without any { in between
+ try %@ execute-keys -draft hh <a-?> ^\h*\b(impl|((|pub\ |pub\((crate|self|super|in\ (::)?([a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)(::[a-zA-Z][a-zA-Z0-9_]*|_[a-zA-Z0-9_]+)*)\)\ )((async\ |const\ )?(unsafe\ )?(extern\ ("[^"]*"\ )?)?fn|struct|enum|union))|if|for)\b <ret> <a-K> \{ <ret> <a-semicolon> <semicolon> ll x <a-k> ^\h*\{$ <ret> <a-lt> @
+ ~
+]
+
+define-command -hidden rust-indent-on-closing %~
+ evaluate-commands -draft -itersel %_
+ # align to opening curly brace or paren when alone on a line
+ try %< execute-keys -draft <a-h> <a-k> ^\h*[)}\]]$ <ret> h m <a-S> 1<a-&> >
+ _
+~
+
diff --git a/autoload/filetype/sass.kak b/autoload/filetype/sass.kak
new file mode 100644
index 0000000..5f2cce0
--- /dev/null
+++ b/autoload/filetype/sass.kak
@@ -0,0 +1,86 @@
+# http://sass-lang.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](sass) %{
+ set-option buffer filetype sass
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=sass %<
+ require-module sass
+
+ hook window ModeChange pop:insert:.* -group sass-trim-indent sass-trim-indent
+ hook window InsertChar \} -group sass-indent sass-indent-on-closing-brace
+ hook window InsertChar \n -group sass-insert sass-insert-on-new-line
+ hook window InsertChar \n -group sass-indent sass-indent-on-new-line
+ set-option buffer extra_word_chars '_' '-'
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window sass-.+ }
+>
+
+hook -group sass-highlight global WinSetOption filetype=sass %{
+ add-highlighter window/sass ref sass
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/sass }
+}
+
+
+provide-module sass %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/sass regions
+add-highlighter shared/sass/code default-region group
+add-highlighter shared/sass/single_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/sass/double_string region "'" "'" fill string
+add-highlighter shared/sass/comment region '//' '$' fill comment
+add-highlighter shared/sass/css_comment region /[*] [*]/ fill comment
+
+add-highlighter shared/sass/code/ regex [*]|[#.][A-Za-z][A-Za-z0-9_-]* 0:variable
+add-highlighter shared/sass/code/ regex &|@[A-Za-z][A-Za-z0-9_-]* 0:meta
+add-highlighter shared/sass/code/ regex (#[0-9A-Fa-f]+)|((\d*\.)?\d+(em|px)) 0:value
+add-highlighter shared/sass/code/ regex ([A-Za-z][A-Za-z0-9_-]*)\h*: 1:keyword
+add-highlighter shared/sass/code/ regex :(before|after) 0:attribute
+add-highlighter shared/sass/code/ regex !important 0:keyword
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden sass-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden sass-indent-on-closing-brace %<
+ evaluate-commands -draft -itersel %<
+ # align closing brace to same indentation as the line that the opening brace resides on
+ try %[ execute-keys -draft <a-h> <a-k> ^\h+\}$ <ret> m <a-S> 1<a-&> ]
+ >
+>
+
+define-command -hidden sass-insert-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # copy // comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K/{2,}\h* <ret> y gh j P }
+ >
+>
+
+define-command -hidden sass-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : sass-trim-indent <ret> }
+ # avoid indent after properties and comments
+ try %{ execute-keys -draft k x <a-K> [:/] <ret> j <a-gt> }
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ >
+>
+
diff --git a/autoload/filetype/scala.kak b/autoload/filetype/scala.kak
new file mode 100644
index 0000000..05d7b28
--- /dev/null
+++ b/autoload/filetype/scala.kak
@@ -0,0 +1,90 @@
+# http://scala-lang.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](scala|sbt|sc) %{
+ set-option buffer filetype scala
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=scala %[
+ require-module scala
+
+ hook window ModeChange pop:insert:.* -group scala-trim-indent scala-trim-indent
+ hook window InsertChar \n -group scala-insert scala-insert-on-new-line
+ hook window InsertChar \n -group scala-indent scala-indent-on-new-line
+ hook window InsertChar \} -group scala-indent scala-indent-on-closing-curly-brace
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window scala-.+ }
+]
+
+hook -group scala-highlight global WinSetOption filetype=scala %{
+ add-highlighter window/scala ref scala
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/scala }
+}
+
+
+provide-module scala %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/scala regions
+add-highlighter shared/scala/code default-region group
+add-highlighter shared/scala/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/scala/literal region ` ` fill variable
+add-highlighter shared/scala/comment region -recurse /[*] /[*] [*]/ fill comment
+add-highlighter shared/scala/line_comment region // $ fill comment
+
+# Keywords are collected at
+# http://tutorialspoint.com/scala/scala_basic_syntax.htm
+
+add-highlighter shared/scala/code/ regex (?:\b|\W)(@\w+|import|package)\b 0:meta
+add-highlighter shared/scala/code/ regex \b(true|false|null)\b 0:value
+add-highlighter shared/scala/code/ regex \b(?:class|extends|with)\s+(\w+) 0:type
+add-highlighter shared/scala/code/ regex \b([A-Z]\w*)\b 0:type
+add-highlighter shared/scala/code/ regex (?:def|var|val)\s+(\w+) 0:variable
+add-highlighter shared/scala/code/ regex \b(become|case|catch|class|def|do|else|extends|final|finally|for|forSome|goto|if|initialize|macro|match|new|object|onTransition|return|startWith|stay|this|super|throw|trait|try|unbecome|using|val|var|when|while|with|yield)\b 0:keyword
+add-highlighter shared/scala/code/ regex \b(abstract|final|implicit|implicitly|lazy|override|private|protected|require|sealed)\b 0:attribute
+add-highlighter shared/scala/code/ regex (\[|\]|=>|<:|:>|=:=|::|&&|\|\|) 0:operator
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden scala-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden scala-insert-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # copy // comments prefix and following white spaces
+ try %{ execute-keys -draft <semicolon><c-s>kx s ^\h*\K#\h* <ret> y<c-o>P<esc> }
+ ]
+]
+
+define-command -hidden scala-indent-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # preserve previous line indent
+ try %[ execute-keys -draft <semicolon> K <a-&> ]
+ # filter previous line
+ try %[ execute-keys -draft k : scala-trim-indent <ret> ]
+ # indent after lines ending with {
+ try %[ execute-keys -draft k x <a-k> \{$ <ret> j <a-gt> ]
+ # deindent closing brace when after cursor
+ try %[ execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> ]
+ ]
+]
+
+define-command -hidden scala-indent-on-closing-curly-brace %[
+ evaluate-commands -draft -itersel %[
+ # align to opening curly brace when alone on a line
+ try %[ execute-keys -draft <a-h> <a-k> ^\h+\}$ <ret> m s \A|.\z <ret> 1<a-&> ]
+ ]
+]
+
+]
diff --git a/autoload/filetype/scheme.kak b/autoload/filetype/scheme.kak
new file mode 100644
index 0000000..5e2e97c
--- /dev/null
+++ b/autoload/filetype/scheme.kak
@@ -0,0 +1,170 @@
+# http://www.scheme-reports.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate (.*/)?(.*\.(scm|ss|sld|sps|sls)) %{
+ set-option buffer filetype scheme
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=scheme %{
+ require-module scheme
+
+ set-option window static_words %opt{scheme_static_words}
+
+ set-option buffer extra_word_chars '!' '$' '%' '&' '*' '+' '-' '.' '/' ':' '<' '=' '>' '?' '@' '^' '_' '~'
+ hook window ModeChange pop:insert:.* -group scheme-trim-indent lisp-trim-indent
+ hook window InsertChar \n -group scheme-indent lisp-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window scheme-.+ }
+}
+
+hook -group scheme-highlight global WinSetOption filetype=scheme %{
+ add-highlighter window/scheme ref scheme
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/scheme }
+}
+
+provide-module scheme %{
+
+require-module lisp
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/scheme regions
+add-highlighter shared/scheme/code default-region group
+
+add-highlighter shared/scheme/string region %{(?<!#\\)"} (?<!\\)(\\\\)*" fill string
+add-highlighter shared/scheme/comment region %{(?<!#\\);} '$' fill comment
+add-highlighter shared/scheme/comment-form region -recurse "\(" "#;\(" "\)" fill comment
+add-highlighter shared/scheme/comment-block region "#\|" "\|#" fill comment
+add-highlighter shared/scheme/quoted-form region -recurse "\(" "'\(" "\)" fill variable
+
+add-highlighter shared/scheme/code/ regex (#t|#f) 0:value
+
+# Numbers
+add-highlighter shared/scheme/code/ regex '(#[bB]#[eiEI]|#[eiEI]#[bB]|#[bB])[01]+' 0:value
+add-highlighter shared/scheme/code/ regex '(#[oO]#[eiEI]|#[eiEI]#[oO]|#[oO])[0-7]+' 0:value
+add-highlighter shared/scheme/code/ regex '(#[dD](#[eiEI])?|#[eiEI]#[dD]|#[eiEI])(\d+(?:\.\d*)?|\.\d+)([esfdlESFDL][-+]?\d+)?' 0:value
+add-highlighter shared/scheme/code/ regex '(#[xX]#[eiEI]|#[eiEI]#[xX]|#[xX])[0-9a-fA-F]+' 0:value
+
+add-highlighter shared/scheme/code/ regex (#\\((\w+)|(.))) 0:value
+
+add-highlighter shared/scheme/code/ regex '#!(?:no-)?fold-case\b' 0:meta
+
+evaluate-commands %sh{ exec awk -f - <<'EOF'
+ BEGIN {
+ split("and begin call-with-current-continuation call/cc case case-lambda cond define "\
+ "define-record-type define-values delay delay-force do else guard if lambda "\
+ "let let* let-values let*-values letrec letrec* or set! unless when", keywords);
+
+ # Macro expressions, imports/exports/library
+ split("begin-syntax cond-expand define-library define-syntax export import include "\
+ "include-ci include-library-declarations let-syntax letrec-syntax quote "\
+ "quasiquote syntax-rules syntax-case unquote unquote-splicing", meta);
+
+ # Basic operators.
+ split("* + - ... / < <= = => > >=", operators);
+
+ # Procedures that create a base type and their predicates (for easier type checking)
+ split("list vector bytevector cons string boolean? list? pair? vector? bytevector? "\
+ "string? char? complex? eof-object eof-object? input-port? null? number? "\
+ "output-port? port? procedure? symbol?", types);
+
+ # R7RS available procedures
+ split("abs acos angle append apply asin assoc assq assv atan boolean=? "\
+ "bytevector-append bytevector-copy bytevector-copy! bytevector-length "\
+ "bytevector-u8-ref bytevector-u8-set! caaaar caaadr caaar caadar caaddr caadr "\
+ "caar cadaar cadadr cadar caddar cadddr caddr cadr call-with-input-file "\
+ "call-with-output-file call-with-values car cdaaar cdaadr cdaar cdadar cdaddr "\
+ "cdadr cdar cddaar cddadr cddar cdddar cddddr cdddr cddr cdr ceiling "\
+ "char->integer char-alphabetic? char-ci<=? char-ci<? char-ci=? char-ci>=? "\
+ "char-ci>? char-downcase char-foldcase char-lower-case? char-numeric? "\
+ "char-ready? char-upcase char-upper-case? char-whitespace? char<=? char<? "\
+ "char=? char>=? char>? close-input-port close-output-port close-port cons cos "\
+ "current-input-port current-output-port denominator digit-value display "\
+ "dynamic-wind eq? equal? eqv? error error-object-irritants "\
+ "error-object-message error-object? eval even? exact exact->inexact "\
+ "exact-integer? exact-integer-sqrt exact? exp expt features file-error? "\
+ "finite? floor floor/ floor-quotient floor-remainder for-each force "\
+ "flush-output-port gcd get-output-bytevector get-output-string guard imag-part "\
+ "inexact->exact inexact inexact? infinite? input-port-open? integer->char "\
+ "integer? interaction-environment lcm length list list-copy list-set! "\
+ "list->string list->vector list-ref list-tail load log magnitude "\
+ "make-bytevector make-list make-parameter make-polar make-promise "\
+ "make-rectangular make-string make-vector map max member memq memv min modulo "\
+ "nan? negative? newline not null-environment number->string numerator odd? "\
+ "open-input-bytevector open-input-file open-input-string "\
+ "open-output-bytevector open-output-file open-output-string output-port-open? "\
+ "or parameterize peek-char peek-u8 positive? promise? quotient raise "\
+ "raise-continuable rational? rationalize read read-bytevector read-bytevector! "\
+ "read-char read-error? read-line read-string read-u8 real-part real? remainder "\
+ "reverse round scheme-report-environment set-car! set-cdr! sin square sqrt "\
+ "string->list string->number string->symbol string->utf8 string->vector "\
+ "string-append string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>? "\
+ "string-copy string-copy! string-downcase string-fill! string-foldcase "\
+ "string-for-each string-length string-map string-ref string-set! string-upcase "\
+ "string<=? string<? string=? string>=? string>? substring symbol=? "\
+ "symbol->string syntax-error tan textual-port? truncate truncate/ "\
+ "truncate-quotient truncate-remainder u8-ready? unless utf8->string values "\
+ "vector vector->list vector->string vector-append vector-copy vector-copy! "\
+ "vector-for-each vector-fill! vector-length vector-map vector-ref vector-set! "\
+ "when with-exception-handler with-input-from-file with-output-to-file write "\
+ "write-bytevector write-char write-string write-u8 zero?", builtins);
+
+ non_word_chars="['\"\\s\\(\\)\\[\\]\\{\\};]";
+
+ normal_identifiers="-!$%&\\*\\+\\./:<=>\\?@\\^_~a-zA-Z0-9";
+ identifier_chars="[" normal_identifiers "][" normal_identifiers ",#]*";
+ }
+ function kak_escape(s) {
+ gsub(/'/, "''", s);
+ return "'" s "'";
+ }
+ function add_highlighter(regex, highlight) {
+ printf("add-highlighter shared/scheme/code/ regex %s %s\n", kak_escape(regex), highlight);
+ }
+ function quoted_join(words, quoted, first) {
+ first=1
+ for (i in words) {
+ if (!first) { quoted=quoted "|"; }
+ quoted=quoted "\\Q" words[i] "\\E";
+ first=0;
+ }
+ return quoted;
+ }
+ function add_word_highlighter(words, face, regex) {
+ regex = "(?<![" normal_identifiers "])(" quoted_join(words) ")(?![" normal_identifiers "])";
+ add_highlighter(regex, "1:" face);
+ }
+ function print_words(words) {
+ for (i in words) { printf(" %s", words[i]); }
+ }
+
+ BEGIN {
+ printf("declare-option str-list scheme_static_words ");
+ print_words(keywords); print_words(meta); print_words(operators); print_words(builtins);
+ printf("\n");
+
+ add_word_highlighter(keywords, "keyword");
+ add_word_highlighter(meta, "meta");
+ add_word_highlighter(operators, "operator");
+ add_word_highlighter(builtins, "function");
+ add_word_highlighter(types, "type");
+ add_highlighter(non_word_chars "+('" identifier_chars ")", "1:attribute");
+ add_highlighter("\\(define\\W+\\((" identifier_chars ")", "1:function");
+ add_highlighter("\\(define\\W+(" identifier_chars ")\\W+\\(lambda", "1:function");
+
+ # unprefixed decimals
+ add_highlighter("(?<![" normal_identifiers "])(\\d+(\\.\\d*)?|\\.\\d+)(?:[esfdlESFDL][-+]?\\d+)?(?![" normal_identifiers "])", "0:value");
+ # inf and nan
+ add_highlighter("(?<![" normal_identifiers "])[+-](?:inf|nan)\\.0(?![" normal_identifiers "])", "0:value");
+ }
+EOF
+}
+
+}
diff --git a/autoload/filetype/scss.kak b/autoload/filetype/scss.kak
new file mode 100644
index 0000000..a4ac7b0
--- /dev/null
+++ b/autoload/filetype/scss.kak
@@ -0,0 +1,55 @@
+# http://sass-lang.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](scss) %{
+ set-option buffer filetype scss
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=scss %[
+ require-module scss
+
+ hook window ModeChange pop:insert:.* -group scss-trim-indent scss-trim-indent
+ hook window InsertChar \n -group scss-indent scss-insert-on-new-line
+ hook window InsertChar \n -group scss-indent scss-indent-on-new-line
+ hook window InsertChar \} -group scss-indent scss-indent-on-closing-curly-brace
+ set-option buffer extra_word_chars '_' '-'
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window scss-.+ }
+]
+
+hook -group scss-highlight global WinSetOption filetype=scss %{
+ add-highlighter window/scss ref scss
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/scss }
+}
+
+
+provide-module scss %[
+
+require-module css
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/scss regions
+add-highlighter shared/scss/core default-region group
+add-highlighter shared/scss/comment region ^\h*// $ fill comment
+
+add-highlighter shared/scss/core/ ref css
+add-highlighter shared/scss/core/ regex & 0:keyword
+add-highlighter shared/scss/core/ regex \$[A-Za-z][A-Za-z0-9_-]* 0:variable
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden scss-trim-indent css-trim-indent
+define-command -hidden scss-insert-on-new-line css-insert-on-new-line
+define-command -hidden scss-indent-on-new-line css-indent-on-new-line
+define-command -hidden scss-indent-on-closing-curly-brace css-indent-on-closing-curly-brace
+
+]
diff --git a/autoload/filetype/sh.kak b/autoload/filetype/sh.kak
new file mode 100644
index 0000000..9a9f51e
--- /dev/null
+++ b/autoload/filetype/sh.kak
@@ -0,0 +1,206 @@
+hook global BufCreate .*\.((z|ba|c|k|mk)?sh(rc|_profile)?|profile) %{
+ set-option buffer filetype sh
+}
+
+hook global WinSetOption filetype=sh %{
+ require-module sh
+ set-option window static_words %opt{sh_static_words}
+
+ hook window ModeChange pop:insert:.* -group sh-trim-indent sh-trim-indent
+ hook window InsertChar \n -group sh-insert sh-insert-on-new-line
+ hook window InsertChar \n -group sh-indent sh-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window sh-.+ }
+}
+
+hook -group sh-highlight global WinSetOption filetype=sh %{
+ add-highlighter window/sh ref sh
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/sh }
+}
+
+# using non-ascii characters here so that we can use the '[' command
+provide-module sh %§
+
+add-highlighter shared/sh regions
+add-highlighter shared/sh/code default-region group
+add-highlighter shared/sh/arithmetic region -recurse \(.*?\( (\$|(?<=for)\h*)\(\( \)\) group
+add-highlighter shared/sh/double_string region %{(?<!\\)(?:\\\\)*\K"} %{(?<!\\)(?:\\\\)*"} group
+add-highlighter shared/sh/single_string region %{(?<!\\)(?:\\\\)*\K'} %{'} fill string
+add-highlighter shared/sh/expansion region -recurse (?<!\\)(?:\\\\)*\K\$\{ (?<!\\)(?:\\\\)*\K\$\{ \}|\n fill value
+add-highlighter shared/sh/comment region (?<!\\)(?:\\\\)*(?:^|\h)\K# '$' fill comment
+add-highlighter shared/sh/heredoc region -match-capture '<<-?\h*''?(\w+)''?' '^\t*(\w+)$' fill string
+
+add-highlighter shared/sh/arithmetic/expansion ref sh/double_string/expansion
+add-highlighter shared/sh/double_string/fill fill string
+
+evaluate-commands %sh{
+ # Grammar
+ # Generated with `compgen -k` in bash
+ keywords="if then else elif fi case esac for select while until do done in
+ function time coproc"
+
+ # Generated with `compgen -b` in bash
+ builtins="alias bg bind break builtin caller cd command compgen complete
+ compopt continue declare dirs disown echo enable eval exec
+ exit export false fc fg getopts hash help history jobs kill
+ let local logout mapfile popd printf pushd pwd read readarray
+ readonly return set shift shopt source suspend test times trap
+ true type typeset ulimit umask unalias unset wait"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list sh_static_words $(join "${keywords}" ' ') $(join "${builtins}" ' ')"
+
+ # Highlight keywords
+ printf %s\\n "add-highlighter shared/sh/code/ regex (?<!-)\b($(join "${keywords}" '|'))\b(?!-) 0:keyword"
+
+ # Highlight builtins
+ printf %s "add-highlighter shared/sh/code/builtin regex (?<!-)\b($(join "${builtins}" '|'))\b(?!-) 0:builtin"
+}
+
+add-highlighter shared/sh/code/operators regex [\[\]\(\)&|]{1,2} 0:operator
+add-highlighter shared/sh/code/variable regex ((?<![-:])\b\w+)= 1:variable
+add-highlighter shared/sh/code/alias regex \balias(\h+[-+]\w)*\h+([\w-.]+)= 2:variable
+add-highlighter shared/sh/code/function regex ^\h*(\S+(?<!=))\h*\(\) 1:function
+
+add-highlighter shared/sh/code/unscoped_expansion regex (?<!\\)(?:\\\\)*\K\$(\w+|#|@|\?|\$|!|-|\*) 0:value
+add-highlighter shared/sh/double_string/expansion regex (?<!\\)(?:\\\\)*\K\$(\w+|#|@|\?|\$|!|-|\*|\{.+?\}) 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden sh-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+# This is at best an approximation, since shell syntax is very complex.
+# Also note that this targets plain sh syntax, not bash - bash adds a whole
+# other level of complexity. If your bash code is fairly portable this will
+# probably work.
+#
+# Of necessity, this is also fairly opinionated about indentation styles.
+# Doing it "properly" would require far more context awareness than we can
+# bring to this kind of thing.
+define-command -hidden sh-insert-on-new-line %[
+ evaluate-commands -draft -itersel %[
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ ]
+]
+
+# Use custom object matching to copy indentation for the various logical
+# blocks.
+#
+# Note that we're using a weird non-ascii character instead of [ or { here
+# because the '[' and '{' characters need to be available for the commands.
+define-command -hidden sh-indent-on-new-line %¶
+ evaluate-commands -draft -itersel %@
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : sh-trim-indent <ret> }
+
+ # Indent loop syntax, e.g.:
+ # for foo in bar; do
+ # things
+ # done
+ #
+ # or:
+ #
+ # while foo; do
+ # things
+ # done
+ #
+ # or equivalently:
+ #
+ # while foo
+ # do
+ # things
+ # done
+ #
+ # indent after do
+ try %{ execute-keys -draft , k x <a-k> \bdo$ <ret> j <a-gt> }
+ # copy the indentation of the matching for/when - matching on the do
+ # statement, so we don't need to duplicate this for the two loop
+ # structures.
+ try %{ execute-keys -draft , k x <a-k> \bdone$ <ret> gh [c\bdo\b,\bdone\b <ret> x <a-S> 1<a-&> , j K <a-&> }
+
+ # Indent if/then/else syntax, e.g.:
+ # if [ $foo = $bar ]; then
+ # things
+ # else
+ # other_things
+ # fi
+ #
+ # or equivalently:
+ # if [ $foo = $bar ]
+ # then
+ # things
+ # else
+ # other_things
+ # fi
+ #
+ # indent after then
+ try %{ execute-keys -draft , k x <a-k> \bthen$ <ret> j <a-gt> }
+ # copy the indentation of the matching if
+ try %{ execute-keys -draft , k x <a-k> \bfi$ <ret> gh [c\bif\b,\bfi\b <ret> x <a-S> 1<a-&> , j K <a-&> }
+ # copy the indentation of the matching if, and then re-indent afterwards
+ try %{ execute-keys -draft , k x <a-k> \belse$ <ret> gh [c\bif\b,\bfi\b <ret> x <a-S> 1<a-&> , j K <a-&> j <a-gt> }
+
+ # Indent case syntax, e.g.:
+ # case "$foo" in
+ # bar) thing1;;
+ # baz)
+ # things
+ # ;;
+ # *)
+ # default_things
+ # ;;
+ # esac
+ #
+ # or equivalently:
+ # case "$foo"
+ # in
+ # bar) thing1;;
+ # esac
+ #
+ # indent after in
+ try %{ execute-keys -draft , k x <a-k> \bin$ <ret> j <a-gt> }
+ # copy the indentation of the matching case
+ try %{ execute-keys -draft , k x <a-k> \besac$ <ret> gh [c\bcase\b,\besac\b <ret> x <a-S> 1<a-&> , j K <a-&> }
+ # indent after )
+ try %{ execute-keys -draft , k x <a-k> ^\s*\(?[^(]+[^)]\)$ <ret> j <a-gt> }
+ # deindent after ;;
+ try %{ execute-keys -draft , k x <a-k> ^\s*\;\;$ <ret> j <a-lt> }
+
+ # Indent compound commands as logical blocks, e.g.:
+ # {
+ # thing1
+ # thing2
+ # }
+ #
+ # or in a function definition:
+ # foo () {
+ # thing1
+ # thing2
+ # }
+ #
+ # We don't handle () delimited compond commands - these are technically very
+ # similar, but the use cases are quite different and much less common.
+ #
+ # Note that in this context the '{' and '}' characters are reserved
+ # words, and hence must be surrounded by a token separator - typically
+ # white space (including a newline), though technically it can also be
+ # ';'. Only vertical white space makes sense in this context, though,
+ # since the syntax denotes a logical block, not a simple compound command.
+ try %= execute-keys -draft , k x <a-k> (\s|^)\{$ <ret> j <a-gt> =
+ # deindent closing }
+ try %= execute-keys -draft , k x <a-k> ^\s*\}$ <ret> <a-lt> j K <a-&> =
+ # deindent closing } when after cursor
+ try %= execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> =
+
+ @
+
diff --git a/autoload/filetype/sml.kak b/autoload/filetype/sml.kak
new file mode 100644
index 0000000..961d58d
--- /dev/null
+++ b/autoload/filetype/sml.kak
@@ -0,0 +1,86 @@
+# https://smlfamily.github.io
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(sml|fun|sig) %{
+ set-option buffer filetype sml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=sml %{
+ require-module sml
+ set-option buffer extra_word_chars '_' "'"
+ set-option window static_words %opt{sml_static_words}
+}
+
+hook -group sml-highlight global WinSetOption filetype=sml %{
+ add-highlighter window/sml ref sml
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/sml }
+}
+
+provide-module sml %[
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/sml regions
+add-highlighter shared/sml/code default-region group
+add-highlighter shared/sml/string region '#?"' '(?<!\\)(\\\\)*"' fill string
+add-highlighter shared/sml/comment region -recurse '\(\*' '\(\*' '\*\)' fill comment
+
+evaluate-commands %sh{
+ keywords='abstype and andalso as case datatype do else end exception fn fun
+ handle if in infix infixr let local nonfix of op open orelse raise
+ rec then type val with withtype while eqtype functor include
+ sharing sig signature struct structure'
+ types='unit exn ref'
+ ops='before ignore o
+ div mod quot rem abs
+ not chr ord ceil floor round trunc
+ andb orb xorb notb'
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ printf %s\\n "declare-option str-list sml_static_words $(join "${keywords} ${types} ${ops}" ' ')"
+
+ printf %s "
+ add-highlighter shared/sml/code/ regex \b($(join "${keywords}" '|'))\b 0:keyword
+ add-highlighter shared/sml/code/ regex \b($(join "${types}" '|'))\b 0:builtin
+ add-highlighter shared/sml/code/ regex \b($(join "${ops}" '|'))\b 0:operator
+ "
+}
+
+# Symbolic identifiers
+add-highlighter shared/sml/code/ regex "[!*/+\-~\^@=<>%%&$?`\\#:|]+" 0:operator
+
+# Record projection functions
+add-highlighter shared/sml/code/ regex "(?<![!*/+\-~\^@=<>%%&$?`\\#:|])#([\w']+)?(?![!*/+\-~\^@=<>%%&$?`\\#:|])" 0:function
+
+# Symbolic keywords
+add-highlighter shared/sml/code/ regex "(?<![!*/+\-~\^@=<>%%&$?`\\#:|])(=>|=|\*|->|:>|:|;|\.\.\.|\b_\b|\|)(?![!*/+\-~\^@=<>%%&$?`\\#:|])" 0:keyword
+
+# Type variables
+add-highlighter shared/sml/code/ regex "(?<![\w'])'[\w']+(?![\w'])" 0:variable
+
+# Structure identifiers and value constructors
+add-highlighter shared/sml/code/ regex "(?<![\w'])([A-Z][\w']*\.?)" 0:type
+
+# Signature identifiers and all-caps value constructors
+add-highlighter shared/sml/code/ regex "(?<![\w'])[A-Z]{2}[A-Z0-9_']+(?![\w'])" 0:attribute
+
+# Constants
+add-highlighter shared/sml/code/ regex "(?<![\w'])(true|false|nil)\b" 0:value
+
+# Numeric literals
+add-highlighter shared/sml/code/ regex "(?<![\w'])0w[0-9]+\b" 0:value
+add-highlighter shared/sml/code/ regex "(?<![\w'])(0wx|0xw)[0-9a-fA-F]+\b" 0:value
+add-highlighter shared/sml/code/ regex "(?<![\w'])(0wb|0bw)[01]+\b" 0:value
+add-highlighter shared/sml/code/ regex "(~|(?<![\w']))0x[0-9a-fA-F]+\b" 0:value
+add-highlighter shared/sml/code/ regex "(~|(?<![\w']))0b[01]+\b" 0:value
+add-highlighter shared/sml/code/ regex "(?<!#)(~|(?<![\w']))[0-9]+(\.[0-9]+)?([eE]~?[0-9]+)?\b" 0:value
+
+]
diff --git a/autoload/filetype/sql.kak b/autoload/filetype/sql.kak
new file mode 100644
index 0000000..befa9de
--- /dev/null
+++ b/autoload/filetype/sql.kak
@@ -0,0 +1,115 @@
+# https://www.w3schools.com/sql/default.asp
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*/?(?i)sql %{
+ set-option buffer filetype sql
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=sql %{
+ require-module sql
+ set-option window static_words %opt{sql_static_words}
+}
+
+hook -group sql-highlight global WinSetOption filetype=sql %{
+ add-highlighter window/sql ref sql
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/sql }
+}
+
+
+provide-module sql %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/sql regions
+add-highlighter shared/sql/code default-region group
+add-highlighter shared/sql/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/sql/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/sql/comment1 region '--' '$' fill comment
+add-highlighter shared/sql/comment2 region '#' '$' fill comment
+add-highlighter shared/sql/comment3 region '/\*' '\*/' fill comment
+
+evaluate-commands %sh{
+ # Keywords
+ keywords="ALTER|AS|ASC|AUTO_INCREMENT|CHECK|CONSTRAINT|CREATE|DATABASE|DEFAULT|DELETE|DESC|DISTINCT|DROP"
+ keywords="${keywords}|EXISTS|FOREIGN KEY|FROM|FULL JOIN|FULL OUTER JOIN|GROUP BY|HAVING|INDEX|INNER JOIN"
+ keywords="${keywords}|INSERT INTO|INTO|JOIN|LEFT JOIN|LEFT OUTER JOIN|LIMIT|MODIFY|NOT NULL|ON|ORDER BY|PRIMARY KEY"
+ keywords="${keywords}|REFERENCES|RIGHT JOIN|RIGHT OUTER JOIN|SELECT|SELECT TOP|SET|TABLE|TRUNCATE|UNION|UNIQUE"
+ keywords="${keywords}|UPDATE|VALUES|VIEW|WHERE"
+
+ # Operators
+ operators="ALL|AND|ANY|BETWEEN|EXISTS|IN|IS|LIKE|NOT|OR|SOME"
+
+ # MySQL functions
+ functions="ABS|ACOS|ADDDATE|ADDTIME|ASCII|ASIN|ATAN|AVG|BIN|BINARY|CASE|CAST|CEIL|CEILING"
+ functions="${functions}|CHARACTER_LENGTH|CHAR_LENGTH|COALESCE|CONCAT|CONCAT_WS|CONNECTION_ID|CONV|CONVERT"
+ functions="${functions}|COS|COT|COUNT|CURDATE|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER"
+ functions="${functions}|CURTIME|DATABASE|DATE|DATE_ADD|DATEDIFF|DATE_FORMAT|DATE_SUB|DAY|DAYNAME"
+ functions="${functions}|DAYOFMONTH|DAYOFWEEK|DAYOFYEAR|DEGREES|DIV|EXP|EXTRACT|FIELD|FIND_IN_SET|FLOOR"
+ functions="${functions}|FORMAT|FROM_DAYS|GREATEST|HOUR|IF|IFNULL|INSERT|INSTR|ISNULL|LAST_DAY"
+ functions="${functions}|LAST_INSERT_ID|LCASE|LEAST|LEFT|LENGTH|LN|LOCALTIME|LOCALTIMESTAMP|LOCATE|LOG"
+ functions="${functions}|LOWER|LPAD|LTRIM|MAKEDATE|MAKETIME|MAX|MICROSECOND|MID|MIN|MINUTE|MOD|MONTH"
+ functions="${functions}|MONTHNAME|NOW|NULLIF|PERIOD_ADD|PERIOD_DIFF|PI|POSITION|POW|POWER|QUARTER|RADIANS"
+ functions="${functions}|RAND|REPEAT|REPLACE|REVERSE|RIGHT|ROUND|RPAD|RTRIM|SECOND|SEC_TO_TIME|SESSION_USER"
+ functions="${functions}|SIGN|SIN|SPACE|SQRT|STRCMP|STR_TO_DATE|SUBDATE|SUBSTR|SUBSTRING|SUBSTRING_INDEX"
+ functions="${functions}|SUBTIME|SUM|SYSDATE|SYSTEM_USER|TAN|TIME|TIMEDIFF|TIME_FORMAT|TIMESTAMP"
+ functions="${functions}|TIME_TO_SEC|TO_DAYS|TRIM|TRUNCATE|UCASE|UPPER|USER|VERSION|WEEK|WEEKDAY|WEEKOFYEAR"
+ functions="${functions}|YEAR|YEARWEEK"
+
+ # SQL Server functions
+ functions="${functions}|CHAR|CHARINDEX|DATALENGTH|DATEADD|DATENAME|DATEPART|GETDATE|GETUTCDATE|ISDATE"
+ functions="${functions}|ISNUMERIC|LEN|NCHAR|PATINDEX|SESSIONPROPERTY|STR|STUFF|USER_NAME"
+
+ # MS Access functions
+ functions="${functions}|Abs|Asc|Atn|Avg|Chr|Cos|Count|CurDir|CurrentUser|Date|DateAdd|DateDiff|DatePart"
+ functions="${functions}|DateSerial|DateValue|Day|Environ|Exp|Fix|Format|Hour|InStr|InstrRev|Int|IsDate"
+ functions="${functions}|IsNull|IsNumeric|LCase|Left|Len|LTrim|Max|Mid|Min|Minute|Month|MonthName|Now"
+ functions="${functions}|Randomize|Replace|Right|Rnd|Round|RTrim|Second|Sgn|Space|Split|Sqr|Str|StrComp"
+ functions="${functions}|StrConv|StrReverse|Sum|Time|TimeSerial|TimeValue|Trim|UCase|Val|Weekday"
+ functions="${functions}|WeekdayName|Year"
+
+ # Oracle functions
+ functions="${functions}|ADD_MONTHS|ASCIISTR|BITAND|CHR|COMPOSE|COSH|DBTIMEZONE|DECOMPOSE|DUMP|INITCAP|INSTRB"
+ functions="${functions}|INSTRC|LENGTHB|LENGTHC|MEDIAN|MONTHS_BETWEEN|NCHR|NEW_TIME|NEXT_DAY|REGEXP_COUNT"
+ functions="${functions}|REGEXP_INSTR|REGEXP_REPLACE|REGEXP_SUBSTR|REMAINDER|ROWNUM|SESSIONTIMEZONE|SOUNDEX"
+ functions="${functions}|SYSTIMESTAMP|TANH|TRANSLATE|TRUNC|TZ_OFFSET|VSIZE"
+
+ # MySQL data types
+ data_types="LONGBLOB|LONGTEXT|MEDIUMBLOB|MEDIUMTEXT|SET|TEXT|TINYTEXT"
+ data_types_fn="BIGINT|BLOB|CHAR|DATE|DATETIME|DECIMAL|DOUBLE|ENUM|FLOAT|INT"
+ data_types_fn="${data_types_fn}|MEDIUMINT|SMALLINT|TIME|TIMESTAMP|TINYINT|VARCHAR|YEAR"
+
+ # SQL Server data types
+ data_types="${data_types}|bigint|bit|cursor|date|datetime|datetime2|datetimeoffset|image|int|money|nchar|ntext"
+ data_types="${data_types}|nvarchar|real|smalldatetime|smallint|smallmoney|sql_variant|table|text|time"
+ data_types="${data_types}|timestamp|tinyint|uniqueidentifier|varbinary|xml"
+ data_types_fn="${data_types_fn}|binary|char|decimal|float|numeric|nvarchar|varbinary|varchar|varchar"
+
+ # MS Access data types
+ data_types="${data_types}|Text|Memo|Byte|Integer|Long|Single|Double|Currency|AutoNumber|Date"
+ data_types="${data_types}|Time|Ole Object|Hyperlink|Lookup Wizard"
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list sql_static_words ${keywords} ${operators} ${functions} ${data_types} ${data_types_fn} NULL" | tr '|' ' '
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/sql/code/ regex '(?i)\b(${functions})\(.*?\)' 0:function
+ add-highlighter shared/sql/code/ regex '(?i)\b(${data_types_fn})\(.*?\)' 0:type
+ add-highlighter shared/sql/code/ regex '(?i)\b(${keywords})\b' 0:keyword
+ add-highlighter shared/sql/code/ regex '(?i)\b(${operators})\b' 0:operator
+ add-highlighter shared/sql/code/ regex '(?i)\b(${data_types})\b' 0:type
+ "
+}
+
+add-highlighter shared/sql/code/ regex '\+|-|\*|/|%|&|\||^|=|>|<|>=|<=|<>|\+=|-=|\*=|/=|%=|&=|^-=|\|\*=' 0:operator
+add-highlighter shared/sql/code/ regex \bNULL\b 0:value
+add-highlighter shared/sql/code/ regex \b\d+(?:\.\d+)?\b 0:value
+
+}
diff --git a/autoload/filetype/svelte.kak b/autoload/filetype/svelte.kak
new file mode 100644
index 0000000..a4469f2
--- /dev/null
+++ b/autoload/filetype/svelte.kak
@@ -0,0 +1,35 @@
+hook global BufCreate .*\.svelte %[
+ set-option buffer filetype svelte
+]
+
+hook global WinSetOption filetype=(svelte) %{
+ require-module html
+
+ hook window ModeChange pop:insert:.* -group "svelte-trim-indent" html-trim-indent
+ hook window InsertChar '>' -group "svelte-indent" html-indent-on-greater-than
+ hook window InsertChar \n -group "svelte-indent" html-indent-on-new-line
+
+ hook -once -always window WinSetOption "filetype=.*" "
+ remove-hooks window ""svelte-.+""
+ "
+}
+
+hook -group svelte-highlight global WinSetOption filetype=(svelte) %{
+ add-highlighter "window/svelte" ref svelte
+ hook -once -always window WinSetOption "filetype=.*" "
+ remove-highlighter ""window/svelte""
+ "
+}
+
+add-highlighter shared/svelte regions
+add-highlighter shared/svelte/comment region <!-- --> fill comment
+add-highlighter shared/svelte/tag region < > regions
+add-highlighter shared/svelte/style region <style\b.*?>\K (?=</style>) ref css
+add-highlighter shared/svelte/script region <script\b.*?>\K (?=</script>) ref javascript
+
+add-highlighter shared/svelte/block region \{((#|:|/)\w+)? \} regions
+add-highlighter shared/svelte/block/ default-region fill meta
+add-highlighter shared/svelte/block/inner region -recurse \{ \{((#|:|/)\w+)?\K (?=\}) ref javascript
+
+add-highlighter shared/svelte/tag/base default-region ref html/tag
+add-highlighter shared/svelte/tag/block region -recurse \{ \{ \} ref svelte/block
diff --git a/autoload/filetype/swift.kak b/autoload/filetype/swift.kak
new file mode 100644
index 0000000..10c2b77
--- /dev/null
+++ b/autoload/filetype/swift.kak
@@ -0,0 +1,37 @@
+hook global BufCreate .*\.(swift) %{
+ set-option buffer filetype swift
+}
+
+hook global WinSetOption filetype=swift %{
+ require-module swift
+}
+
+hook -group swift-highlight global WinSetOption filetype=swift %{
+ add-highlighter window/swift ref swift
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/swift }
+}
+
+
+provide-module swift %{
+
+add-highlighter shared/swift regions
+add-highlighter shared/swift/code default-region group
+add-highlighter shared/swift/string_multiline region %{(?<!')"""} %{(?<!\\)(\\\\)*"""} ref swift/string
+add-highlighter shared/swift/string region %{(?<!')"} %{(?<!\\)(\\\\)*"} fill string
+add-highlighter shared/swift/comment region /\* \*/ group
+add-highlighter shared/swift/line_comment region // $ ref swift/comment
+
+add-highlighter shared/swift/comment/ fill comment
+
+add-highlighter shared/swift/code/ regex %{\b(true|false|nil)\b|\b-?(?!\$)\d+[fdiu]?|'((\\.)?|[^'\\])'} 0:value
+add-highlighter shared/swift/code/ regex "\b(let|var|while|in|for|if|guard|else|do|switch|case|default|break|continue|return|try|catch|throw|new|delete|and|or|not|operator|explicit|func|import|return|init|deinit|get|set)\b" 0:keyword
+add-highlighter shared/swift/code/ regex "\bas\b[!?]?" 0:keyword
+add-highlighter shared/swift/code/ regex "(\$[0-9])\b" 0:keyword
+add-highlighter shared/swift/code/ regex "\b(const|mutable|auto|namespace|inline|static|volatile|class|struct|enum|union|extension|open|public|protected|private|fileprivate|internal|typedef|virtual|friend|extern|typename|override|final|required|convenience|dynamic)\b" 0:attribute
+
+add-highlighter shared/swift/code/ regex "\b(self|nil|id|super)\b" 0:value
+add-highlighter shared/swift/code/ regex "\b(Bool|String|UInt|UInt16|UInt32|UInt64|UInt8)\b" 0:type
+add-highlighter shared/swift/code/ regex "\b(IBAction|IBOutlet)\b" 0:attribute
+add-highlighter shared/swift/code/ regex "@\w+\b" 0:attribute
+
+}
diff --git a/autoload/filetype/systemd.kak b/autoload/filetype/systemd.kak
new file mode 100644
index 0000000..3810d00
--- /dev/null
+++ b/autoload/filetype/systemd.kak
@@ -0,0 +1,14 @@
+# https://freedesktop.org/wiki/Software/systemd/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*/systemd/.+\.(automount|conf|link|mount|network|path|service|slice|socket|target|timer) %{
+ set-option buffer filetype ini
+
+ # NOTE: INI files define the commenting character to be `;`, which won't work in `systemd` files
+ hook -once buffer BufSetOption comment_line=.+ %{
+ set-option buffer comment_line "#"
+ }
+}
diff --git a/autoload/filetype/taskpaper.kak b/autoload/filetype/taskpaper.kak
new file mode 100644
index 0000000..187aa09
--- /dev/null
+++ b/autoload/filetype/taskpaper.kak
@@ -0,0 +1,63 @@
+# https://www.taskpaper.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.taskpaper %{
+ set-option buffer filetype taskpaper
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=taskpaper %{
+ require-module taskpaper
+
+ hook window ModeChange pop:insert:.* -group taskpaper-trim-indent taskpaper-trim-indent
+ hook window InsertChar \n -group taskpaper-indent taskpaper-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window taskpaper-.+ }
+}
+
+hook -group taskpaper-highlight global WinSetOption filetype=taskpaper %{
+ add-highlighter window/taskpaper ref taskpaper
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/taskpaper }
+}
+
+
+provide-module taskpaper %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/taskpaper group
+
+add-highlighter shared/taskpaper/ regex ^\h*([^:\n]+):\h*\n 1:header
+add-highlighter shared/taskpaper/ regex \h@\w+(?:\(([^)]*)\))? 0:variable 1:value
+add-highlighter shared/taskpaper/ regex ^\h*([^-:\n]+)\n 1:+i
+add-highlighter shared/taskpaper/ regex ^\h*-\h+[^\n]*@done[^\n]* 0:+d
+add-highlighter shared/taskpaper/ regex \b(([a-z]+://\S+)|((mailto:)[\w+-]+@\S+)) 0:link
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden taskpaper-trim-indent %{
+ evaluate-commands -no-hooks -draft -itersel %{
+ execute-keys x
+ # remove trailing white spaces
+ try %{ execute-keys -draft s \h + $ <ret> d }
+ }
+}
+
+define-command -hidden taskpaper-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon>K<a-&> }
+ ## If the line above is a project indent with a tab
+ try %{ execute-keys -draft Z kx <a-k>^\h*([^:\n]+):<ret> z i<tab> }
+ # cleanup trailing white spaces on previous line
+ try %{ execute-keys -draft kx s \h+$ <ret>d }
+ }
+}
+
+}
diff --git a/autoload/filetype/tcl.kak b/autoload/filetype/tcl.kak
new file mode 100644
index 0000000..5823eb5
--- /dev/null
+++ b/autoload/filetype/tcl.kak
@@ -0,0 +1,79 @@
+hook global BufCreate .*[.](tcl) %{
+ set-option buffer filetype tcl
+}
+
+hook global WinSetOption filetype=tcl %{
+ require-module tcl
+
+ hook window ModeChange pop:insert:.* -group tcl-trim-indent tcl-trim-indent
+ hook window InsertChar \n -group tcl-insert tcl-insert-on-new-line
+ hook window InsertChar \n -group tcl-indent tcl-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window tcl-.+ }
+}
+
+hook -group tcl-highlight global WinSetOption filetype=tcl %{
+ add-highlighter window/tcl ref tcl
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/tcl }
+}
+
+# Using non-ascii characters here so that we can use the '[' command
+provide-module tcl %§
+
+add-highlighter shared/tcl regions
+add-highlighter shared/tcl/code default-region group
+add-highlighter shared/tcl/comment region (?<!\\)(?:\\\\)*(?:^|\h)\K# '$' fill comment
+add-highlighter shared/tcl/double_string region %{(?<!\\)(?:\\\\)*\K"} %{(?<!\\)(?:\\\\)*"} group
+add-highlighter shared/tcl/double_string/fill fill string
+
+evaluate-commands %sh{
+ # Tcl does not have keywords, as everything in Tcl is a command.
+ # Highlight all builtin commads does not make a lot of sense as there are plenty of them.
+ # What is more, sometimes user defines varaibles have the same names as commands.
+ # On the other hand, highlight no commads makes the code harder to read.
+ # The approach for builtin commads highlighting is very simply.
+ # Highlight only two types of commands:
+ # 1. "Control statement" like commands, for exmaple, "if", "break", "return", etc.
+ # 2. Commands defining new scope, for exmaple, "proc", "namespace".
+ keywords="break catch continue default else elseif error exit for foreach if return switch while proc namespace"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Highlight keywords
+ printf %s\\n "add-highlighter shared/tcl/code/ regex (?<!-)\b($(join "${keywords}" '|'))\b(?!-) 0:keyword"
+}
+
+add-highlighter shared/tcl/code/function regex ^\h*proc\h+((\w|-)+) 1:function
+add-highlighter shared/tcl/code/brackets regex [\[\]]{1,2} 0:operator
+add-highlighter shared/tcl/code/parameters regex \s-\w+\b 0:attribute
+add-highlighter shared/tcl/code/variable regex \$(\w|:)+ 0:value
+add-highlighter shared/tcl/code/numbers regex '\b\d+\.?' 0:value
+
+add-highlighter shared/tcl/double_string/variable regex \$(\w|:)+ 0:value
+add-highlighter shared/tcl/double_string/brackets regex [\[\]]{1,2} 0:operator
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden tcl-trim-indent %{
+ # Remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden tcl-insert-on-new-line %[
+ # Copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+]
+
+define-command -hidden tcl-indent-on-new-line %¶
+ evaluate-commands -draft -itersel %@
+ # Preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+
+ # Filter previous line
+ try %{ execute-keys -draft k : tcl-trim-indent <ret> }
+
+ # Indent after {
+ try %= execute-keys -draft , k x <a-k> (\s|^)\{$ <ret> j <a-gt> =
+ @
diff --git a/autoload/filetype/terraform.kak b/autoload/filetype/terraform.kak
new file mode 100644
index 0000000..3ad975a
--- /dev/null
+++ b/autoload/filetype/terraform.kak
@@ -0,0 +1,120 @@
+# Terraform configuration language
+# https://www.terraform.io/docs/configuration/
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](tf|tfvars) %{
+ set-option buffer filetype terraform
+}
+
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=terraform %{
+ require-module terraform
+
+ set-option window static_words %opt{terraform_static_words}
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window terraform-.+ }
+}
+
+
+hook -group terraform-highlight global WinSetOption filetype=terraform %{
+ add-highlighter window/terraform ref terraform
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/terraform }
+}
+
+
+provide-module terraform %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/terraform regions
+add-highlighter shared/terraform/code default-region group
+
+add-highlighter shared/terraform/comment1 region '#' '$' fill comment
+add-highlighter shared/terraform/comment2 region '\\' '$' fill comment
+add-highlighter shared/terraform/comment3 region /\* \*/ fill comment
+
+# Strings can contain interpolated terraform expressions, which can contain
+# strings. Currently, we cannot support nesting of the same type of delimiter,
+# so instead we render the full interpolation as a value (otherwise, it
+# looks bad).
+# See https://github.com/mawww/kakoune/issues/1670
+add-highlighter shared/terraform/string region '"' '(?<!\\)(?:\\\\)*"' group
+add-highlighter shared/terraform/string/fill fill string
+add-highlighter shared/terraform/string/inter regex \$\{.+?\} 0:value
+
+add-highlighter shared/terraform/heredoc region -match-capture '<<-?(\w+)' '^\h*(\w+)$' regions
+add-highlighter shared/terraform/heredoc/fill default-region fill string
+add-highlighter shared/terraform/heredoc/inter region -recurse \{ (?<!\\)(\\\\)*\$\{ \} ref terraform
+
+
+add-highlighter shared/terraform/code/valueDec regex '\b[0-9]+([kKmMgG]b?)?\b' 0:value
+add-highlighter shared/terraform/code/valueHex regex '\b0x[0-9a-f]+([kKmMgG]b?)?\b' 0:value
+
+add-highlighter shared/terraform/code/operators regex [\[\]] 0:operator
+
+add-highlighter shared/terraform/code/field regex '^\h+(\w+)\s*(=)' 1:variable 2:keyword
+
+evaluate-commands %sh{
+ blocks="connection content data dynamic locals module output provider
+ provisioner resource terraform variable"
+
+ constants="true false null"
+
+ keywords="for for_each if in"
+
+ types="bool list map number object set string tuple"
+
+ var_subs="local module var"
+
+ # Builtin functions
+ fun_num="abs ceil floor log max min parseint pow signum"
+
+ fun_str="chomp format formatlist indent join lower regex regexall replace
+ split strrev substr title trimspace upper"
+
+ fun_coll="chunklist coalesce coalescelist compact concat contains
+ distinct element flatten index keys length lookup
+ matchkeys merge range reverse setintersection setproduct
+ setunion slice sort transpose values zipmap"
+
+ fun_enc="base64decode base64encode base64gzip csvdecode jsondecode
+ jsonencode urlencode yamldecode yamlencode"
+
+ fun_file="abspath dirname pathexpand basename file fileexists fileset
+ filebase64 templatefile"
+
+ fun_dt="formatdate timeadd timestamp"
+
+ fun_crypt="base64sha256 base64sha512 bcrypt filebase64sha256
+ filebase64sha512 filemd5 filesha1 filesha256 filesha512 md5
+ rsadecrypt sha1 sha256 sha512 uuid uuidv5"
+
+ fun_net="cidrhost cidrnetmask cidrsubnet"
+
+ fun_cast="tobool tolist tomap tonumber toset tostring"
+
+ functions="$fun_num $fun_str $fun_coll $fun_enc $fun_file $fun_dt $fun_crypt $fun_net $fun_cast"
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add grammar elements to the static completion list
+ printf %s\\n "declare-option str-list terraform_static_words $(join "$blocks $keywords $constants $types $var_subs $functions" ' ')"
+
+ # Highlight grammar elements
+ printf %s "
+ add-highlighter shared/terraform/code/ regex '\b($(join "$blocks" '|'))\b[^.]' 1:keyword
+ add-highlighter shared/terraform/code/ regex '\b($(join "$keywords" '|'))\b' 1:keyword
+ add-highlighter shared/terraform/code/ regex '\b($(join "$constants" '|'))\b' 1:value
+ add-highlighter shared/terraform/code/ regex '\b($(join "$types" '|'))\b' 1:type
+ add-highlighter shared/terraform/code/ regex '\b($(join "$var_subs" '|'))\b\.' 1:meta
+ add-highlighter shared/terraform/code/ regex '\b($(join "$functions" '|'))\s*\(' 1:builtin
+ "
+}
+
diff --git a/autoload/filetype/toml.kak b/autoload/filetype/toml.kak
new file mode 100644
index 0000000..d02e074
--- /dev/null
+++ b/autoload/filetype/toml.kak
@@ -0,0 +1,76 @@
+# https://github.com/toml-lang/toml/tree/v0.4.0
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.(toml) %{
+ set-option buffer filetype toml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=toml %{
+ require-module toml
+
+ hook window ModeChange pop:insert:.* -group toml-trim-indent toml-trim-indent
+ hook window InsertChar \n -group toml-insert toml-insert-on-new-line
+ hook window InsertChar \n -group toml-indent toml-indent-on-new-line
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window toml-.+ }
+}
+
+hook -group toml-highlight global WinSetOption filetype=toml %{
+ add-highlighter window/toml ref toml
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/toml }
+}
+
+
+provide-module toml %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/toml regions
+add-highlighter shared/toml/code default-region group
+add-highlighter shared/toml/comment region '#' $ fill comment
+add-highlighter shared/toml/string1 region '"""' (?<!\\)(\\\\)*"""(?!") fill string
+add-highlighter shared/toml/string2 region "'''" "'''(?!')" fill string
+add-highlighter shared/toml/string3 region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/toml/string4 region "'" "'" fill string
+
+add-highlighter shared/toml/code/ regex \
+ "^\h*\[\[?([A-Za-z0-9._-]*)\]\]?" 1:title
+add-highlighter shared/toml/code/ regex \
+ (?<!\w)[+-]?[0-9](_?\d)*(\.[0-9](_?\d)*)?([eE][+-]?[0-9](_?\d)*)?\b 0:value
+add-highlighter shared/toml/code/ regex \
+ true|false 0:value
+add-highlighter shared/toml/code/ regex \
+ '\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(.\d+)?([Zz]|[+-]\d{2}:\d{2})' 0:value
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden toml-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden toml-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy # comments prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden toml-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : toml-trim-indent <ret> }
+ }
+}
+
+}
diff --git a/autoload/filetype/troff.kak b/autoload/filetype/troff.kak
new file mode 100644
index 0000000..205c288
--- /dev/null
+++ b/autoload/filetype/troff.kak
@@ -0,0 +1,51 @@
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*\.\d+ %{
+ set-option buffer filetype troff
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=troff %{
+ require-module troff
+}
+
+hook -group troff-highlight global WinSetOption filetype=troff %{
+ add-highlighter window/troff ref troff
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/troff }
+}
+
+provide-module troff %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/troff regions
+
+add-highlighter shared/troff/text default-region group
+add-highlighter shared/troff/text/ regex '(^\.)?\\".*?\n' 0:comment
+add-highlighter shared/troff/text/ regex '\\f[A-Z]' 0:attribute
+add-highlighter shared/troff/text/ regex '\\fB(.+?)\\f[A-Z]' 1:+b
+add-highlighter shared/troff/text/ regex '\\fI(.+?)\\f[A-Z]' 1:+i
+add-highlighter shared/troff/text/ regex '^\.[a-zA-Z]{1,2}\b' 0:meta
+add-highlighter shared/troff/text/ regex '^\.(PSPIC|PDFPIC|pdfhref)\b' 0:meta
+add-highlighter shared/troff/text/ regex '^\.\.$' 0:meta
+add-highlighter shared/troff/text/ regex '^\.TH\s+[^\n]+' 0:title
+add-highlighter shared/troff/text/ regex '^\.NH(\s+\d+(\s+\d+)?)?\s*\n' 0:header
+add-highlighter shared/troff/text/ regex '^\.SH(\s+\d+)?\s*\n' 0:header
+add-highlighter shared/troff/text/ regex '^\.IR\s+(\S+)' 1:+i
+add-highlighter shared/troff/text/ regex '^\.BR\s+(\S+)' 1:+b
+add-highlighter shared/troff/text/ regex '^\.I\s+([^\n]+)' 1:+i
+add-highlighter shared/troff/text/ regex '^\.B\s+([^\n]+)' 1:+b
+add-highlighter shared/troff/text/ regex '(ftp:|http:|https:|www\.)+[^\s]+[\w]' 0:link
+
+add-highlighter shared/troff/pic region '^\.PS\b' '^\.PE\b' group
+add-highlighter shared/troff/pic/ regex '^(\.PS\b|\.PE\b)' 1:meta
+add-highlighter shared/troff/pic/ regex '^(copy)\s+' 1:keyword
+add-highlighter shared/troff/pic/ regex '\b(arc|arrow|box|circle|ellipse|line|move|spline)\b' 1:type
+add-highlighter shared/troff/pic/ regex '\b(above|at|below|by|center|chop|dashed|diam|diameter|down|dotted|fill|from|ht|height|invis|left|ljust|rad|radius|right|rjust|solid|then|to|up|wid|width|with)\b' 1:attribute
+add-highlighter shared/troff/pic/ regex '(\s+|\+|-|\*|/)(\d+(\.\d+)?)' 2:value
+add-highlighter shared/troff/pic/ regex '"[^"]*"' 0:string
+}
diff --git a/autoload/filetype/tupfile.kak b/autoload/filetype/tupfile.kak
new file mode 100644
index 0000000..33e570d
--- /dev/null
+++ b/autoload/filetype/tupfile.kak
@@ -0,0 +1,40 @@
+# http://gittup.org/tup/
+#
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*/?Tup(file|rules)(\.\w+)?$ %{
+ set-option buffer filetype tupfile
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook -group tupfile-highlight global WinSetOption filetype=tupfile %{
+ require-module tupfile
+
+ add-highlighter window/tupfile ref tupfile
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/tupfile }
+}
+
+
+provide-module tupfile %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/tupfile regions
+add-highlighter shared/tupfile/code default-region group
+add-highlighter shared/tupfile/string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/tupfile/comment region '#' $ fill comment
+
+add-highlighter shared/tupfile/code/ regex '%[fbBeoOdg]\b' 0:value
+add-highlighter shared/tupfile/code/ regex '[$@]\([\w_]+\)' 0:value
+add-highlighter shared/tupfile/code/ regex '^\h*:\s*(foreach)\b' 1:keyword
+add-highlighter shared/tupfile/code/ regex '^\h*(\.gitignore)\b' 1:keyword
+add-highlighter shared/tupfile/code/ regex '^\h*\b(ifn?eq|ifn?def|else|endif|error|include|include_rules|run|preload|export)\b' 0:keyword
+add-highlighter shared/tupfile/code/ regex '^\h*\b(&?[\w_]+)\s*[:+]?=' 1:keyword
+add-highlighter shared/tupfile/code/ regex '`[^`\n]+`' 0:meta
+
+}
diff --git a/autoload/filetype/twig.kak b/autoload/filetype/twig.kak
new file mode 100644
index 0000000..2f5ef18
--- /dev/null
+++ b/autoload/filetype/twig.kak
@@ -0,0 +1,89 @@
+# https://twig.symfony.com/doc/3.x/templates.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](twig) %{
+ set-option buffer filetype twig
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=twig %[
+ require-module twig
+
+ hook window ModeChange pop:insert:.* -group twig-trim-indent twig-trim-indent
+ hook window InsertChar \n -group twig-insert twig-insert-on-new-line
+ hook window InsertChar \n -group twig-indent twig-indent-on-new-line
+ hook window InsertChar '>' -group twig-indent twig-indent-on-greater-than
+ hook window InsertChar '#' -group twig-auto-close twig-auto-close-delim
+ hook window InsertChar '%' -group twig-auto-close twig-auto-close-delim
+ set-option buffer extra_word_chars '_' '-'
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window twig-.+ }
+]
+
+hook -group twig-highlight global WinSetOption filetype=twig %{
+ add-highlighter window/twig ref twig
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/twig }
+}
+
+
+provide-module twig %[
+
+require-module html
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/twig regions
+add-highlighter shared/twig/core default-region group
+add-highlighter shared/twig/comment region \{# [#]\} fill comment
+add-highlighter shared/twig/delim region \{([%]-?|\{) (-?[%]|\})\} regions
+
+add-highlighter shared/twig/core/ ref html
+
+add-highlighter shared/twig/delim/base default-region group
+add-highlighter shared/twig/delim/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/twig/delim/single_string region "'" (?<!\\)(\\\\)*' fill string
+add-highlighter shared/twig/delim/base/ regex (\w+)\h= 1:variable
+
+add-highlighter shared/twig/delim/base/functions regex \b(\w+)\( 1:function
+
+add-highlighter shared/twig/delim/base/filters regex \b(abs|batch|capitalize|column|convert_encoding|country_name|currency_name|currency_symbol|data_uri|date|date_modify|default|e|escape|filter|first|format|format_currency|format_date|format_datetime|format_number|format_time|html_to_markdown|inline_css|inky_to_html|join|json_encode|keys|language_name|last|length|locale_name|lower|map|markdown_to_html|merge|nl2br|number_format|raw|reduce|replace|reverse|round|slice|slug|sort|spaceless|split|striptags|timezone_name|title|trim|u|upper|url_encode)(\()?\b 1:operator
+
+add-highlighter shared/twig/delim/base/tags regex \b((extends|deprecated|do|flush|import|from|elseif|else|include|set|use)|(end)?(apply|autoescape|block|cache|embed|for|if|macro|sandbox|set|verbatim|with))\b 0:keyword 0:+i
+
+
+add-highlighter shared/twig/delim/base/delimiter_code regex (\{[%]|[%]\}) 0:function
+add-highlighter shared/twig/delim/base/delimiter_output regex (\{\{|\}\}) 0:operator
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden twig-trim-indent html-trim-indent
+define-command -hidden twig-indent-on-new-line html-indent-on-new-line
+define-command -hidden twig-indent-on-greater-than html-indent-on-greater-than
+
+define-command -hidden twig-auto-close-delim %[
+ evaluate-commands -itersel %[
+ try %[
+ execute-keys -draft <semicolon>hH<a-k>\h*\{<ret>lyp
+ execute-keys <esc>hi<space><esc>hi<space>
+ ]
+ ]
+]
+
+define-command -hidden twig-insert-on-new-line %[
+ evaluate-commands -draft -itersel %/
+ execute-keys <semicolon>
+ try %[
+ execute-keys -draft kx<a-k>^\h*\{\[%#\{\]\h+$<ret>
+ execute-keys -draft jghd
+ ]
+ /
+]
+
+]
diff --git a/autoload/filetype/vhdl.kak b/autoload/filetype/vhdl.kak
new file mode 100644
index 0000000..71aa7ed
--- /dev/null
+++ b/autoload/filetype/vhdl.kak
@@ -0,0 +1,410 @@
+# Based on IEEE Std 1076‐2019
+
+# Detection
+hook global BufCreate .*[.](vhd[l]?) %[
+ set-option buffer filetype vhdl
+]
+
+# Initialization
+hook global WinSetOption filetype=vhdl %[
+ require-module vhdl
+ set-option window static_words %opt{vhdl_static_words}
+ hook -group vhdl-indent window InsertChar \n vhdl-indent-on-new-line
+ hook -group vhdl-indent window InsertChar \) vhdl-indent-on-closing-parenthesis
+ hook -group vhdl-insert window InsertChar \n vhdl-insert-on-new-line
+ # Cleanup trailing whitespaces on current line insert end.
+ hook -group vhdl-trim-indent window ModeChange pop:insert:.* %[ try %[ execute-keys -draft <semicolon> x s ^\h+$ <ret> d ] ]
+ hook -once -always window WinSetOption filetype=.* %[ remove-hooks window vhdl-.+ ]
+]
+
+hook -group vhdl-highlight global WinSetOption filetype=vhdl %[
+ add-highlighter window/vhdl ref vhdl
+ hook -once -always window WinSetOption filetype=.* %[ remove-highlighter window/vhdl ]
+]
+
+provide-module vhdl %§
+
+# Highlighters & Completion
+add-highlighter shared/vhdl regions
+add-highlighter shared/vhdl/code default-region group
+add-highlighter shared/vhdl/comment_line region '--' $ fill comment
+add-highlighter shared/vhdl/comment region /\* \*/ fill comment
+
+# Integer formats
+add-highlighter shared/vhdl/code/ regex '(?i)\b0b[01]+l?\b' 0:value
+add-highlighter shared/vhdl/code/ regex '(?i)\b0x[\da-f]+l?\b' 0:value
+add-highlighter shared/vhdl/code/ regex '(?i)\b0o?[0-7]+l?\b' 0:value
+add-highlighter shared/vhdl/code/ regex '(?i)\b([1-9]\d*|0)l?\b' 0:value
+# Float formats
+add-highlighter shared/vhdl/code/ regex '\b\d+[eE][+-]?\d+\b' 0:value
+add-highlighter shared/vhdl/code/ regex '(\b\d+)?\.\d+\b' 0:value
+add-highlighter shared/vhdl/code/ regex '\b\d+\.' 0:value
+# Imaginary formats
+add-highlighter shared/vhdl/code/ regex '\b\d+\+\d+[jJ]\b' 0:value
+
+evaluate-commands %sh[
+ values="true false note warning error failure nul"
+
+ # LRM 5.2.4.1
+ units="fs ps ns us ms sec min hr Å nm um mm cm m km"
+
+ # LRM 16.2
+ predefined_attributes="
+ base left right high low ascending length range reverse_range
+ subtype image pos succ pred leftof rightof value val
+ designated_subtype reflect high low index element delayed
+ stable quiet transaction event active last_event last_active
+ last_value driving driving_value simple_name instance_name
+ path_name record signal converse
+ "
+
+ libraries="ieee std work"
+
+ packages="
+ math_real math_complex std_logic_1164 std_logic_textio numeric_bit numeric_std
+ numeric_bit_unsigned numeric_std_unsigned fixed_float_types fixed_generic_pkg
+ fixed_pkg float_generic_pkg float_pkg
+ standard textio env
+ "
+
+ # LRM 15.10
+ reserved_words="
+ abs access after alias all and architecture array assert assume assume_guarantee attribute
+ begin block body buffer bus
+ case component configuration constant context cover
+ default disconnect downto
+ else elsif end entity exit
+ fairness file for force function
+ generate generic group guarded
+ if impure in inertial inout is
+ label library linkage literal loop
+ map mod
+ nand new next nor not null
+ of on open or others out
+ package parameter port postponed procedure process property protected pure
+ range record register reject release rem report restrict restrict_guarantee return rol ror
+ select sequence severity signal shared sla sll sra srl strong subtype
+ then to transport type
+ unaffected units until use
+ variable view vpkg vmode vprop vunit
+ wait when while with
+ xnor xor
+ "
+
+ types="
+ bit bit_vector
+ boolean boolean_vector
+ character
+ file_open_state file_origin_kind
+ integer integer_vector
+ natural positive
+ line line_vector
+ real real_vector
+ std_logic std_logic_vector
+ std_ulogic std_ulogic_vector
+ side
+ signed unsigned
+ string text
+ time time_vector
+ "
+
+ functions="
+ find_leftmost find_rightmost divide reciprocal remainder modulo minimum maximum
+ std_match add_carry scalb
+ resize to_ufixed to_sfixed to_unsigned to_signed to_real to_integer to_slv
+ to_std_logic_vector to_stdlogicvector to_sulv to_std_ulogic_vector to_std_ulogicvector
+ to_01 is_x to_x01 to_ux01 to_x01z
+ ufixed_high ufixed_low sfixed_high sfixed_low to_ufix to_sfix ufix_high ufix_low
+ sfix_high sfix_low
+ write read bwrite binary_write bread binary_read owrite oread octal_write octal_read
+ hwrite hread hex_write hex_read to_string to_bstring to_binary_string to_ostring
+ to_octal_string to_hstring to_hex_string from_string from_bstring from_binary_string
+ from_ostring from_octal_string from_hstring from_hex_string
+ rising_edge falling_edge
+ "
+
+ join() { sep=$2; eval set -- $1; IFS="$sep"; echo "$*"; }
+
+ # Add the language's grammar to the static completion list
+ printf %s\\n "declare-option str-list vhdl_static_words $(join "${values} ${units} ${predefined_attributes} ${libraries} ${packages} ${reserved_words} ${types} ${functions}" ' ')"
+
+ # Highlight keywords
+ printf %s "
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${values}" '|'))\b' 0:value
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${units}" '|'))\b' 0:meta
+ add-highlighter shared/vhdl/code/ regex \"'(?i)\b($(join "${predefined_attributes}" '|'))\b\" 0:attribute
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${libraries}" '|'))\b' 0:builtin
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${packages}" '|'))\b' 0:builtin
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${reserved_words}" '|'))\b' 0:keyword
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${functions}" '|'))\b\(' 1:builtin
+ add-highlighter shared/vhdl/code/ regex '(?i)\b($(join "${types}" '|'))\b' 0:type
+ add-highlighter shared/vhdl/code/ regex '^\h*(@[\w_.]+))' 1:attribute
+ "
+]
+
+add-highlighter shared/vhdl/code/ regex \(|\)|\;|\.|,|:|\| 0:attribute
+
+add-highlighter shared/vhdl/code/ regex \?\?|=|/=|<|<=|>|>=|\?=|\?/=|\?<|\?<=|\?>|\?>=|\+|-|&|\*|/|:= 0:operator
+
+# Meta values highlight.
+# The values 'U', 'X', 'W', and '–' are metalogical values; they define the behavior of the model itself rather than the behavior of the hardware being synthesized.
+add-highlighter shared/vhdl/code/ regex "(?i)'[U|X|W|-]'" 0:meta
+# Highlight other logical values.
+add-highlighter shared/vhdl/code/ regex "(?i)'[0|1|Z|L|H]'" 0:value
+
+# String
+add-highlighter shared/vhdl/code/ regex '"[^"]*"' 0:string
+
+# Binary vector.
+add-highlighter shared/vhdl/code/ regex '[bB]"[01_]*"' 0:value
+
+# Octal vector.
+add-highlighter shared/vhdl/code/ regex '[oO]"[01234567_]*"' 0:value
+
+# Hex vector.
+add-highlighter shared/vhdl/code/ regex '(?i)x"[0123456789abcdef_]*"' 0:value
+
+define-command -hidden vhdl-insert-on-new-line %[
+ # Handle comment lines.
+ evaluate-commands -itersel %[
+ # Copy '--' comment prefix and following white spaces.
+ try %[
+ # <a-lt> is needed because of "Preserve previous line indent" command.
+ try %[ execute-keys -draft k x s ^\h*--\h* <ret> y j <a-lt> gh P ]
+ ]
+ ]
+
+ evaluate-commands -save-regs x %[
+ # Save previous line indent in register x.
+ try %[ execute-keys -draft kxs^\h+<ret>"xy ] catch %[ reg x '' ]
+
+ # All "wisely add" commands share the same concept.
+ # Only "end if" has extra comments.
+ # Wisely add "end if;".
+ evaluate-commands %[
+ try %[
+ # Validate previous line and that it is not closed yet.
+ execute-keys -draft kx <a-k>^\h*(?i)((then|(.*:\h*)?if\b.*\bthen)$)<ret> j}ijx <a-K>^<c-r>x(?i)(elsif|else|end)\b<ret>
+ # Don't add for "if ... generate", it requires "end generate;".
+ execute-keys -draft kx <a-K>(?i)\bgenerate\b<ret>
+ execute-keys -draft o<c-r>xend<space>if<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end generate;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i).*\bgenerate$<ret> j}ijx <a-K>^<c-r>x(?i)(begin|elsif|else|end)\b<ret>
+ # Don't add in case of comment line.
+ execute-keys -draft kx <a-K>^\h*--<ret>
+ execute-keys -draft o<c-r>xend<space>generate<semicolon><esc>
+ ]
+ ]
+ # Wisely add "when" and "end case;".
+ evaluate-commands %[
+ try %[
+ # TODO: Case needs special handling.
+ execute-keys -draft kx <a-k>^\h*(?i)(case|.*\h*:\h*case)\b<ret> jwx <a-K>^<c-r>x(?i)(end|when)<ret>
+ execute-keys -draft <c-r>xo<c-r>xend<space>case<semicolon><esc>kAwhen<space><esc>
+ ]
+ ]
+ # Wisely add "begin" and "end block;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)((block|.*:\h*block)\b)<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>block<semicolon><esc>
+ ]
+ ]
+ # Wisely add "begin" and "end process;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(.*:\h*)?(postponed\h+)?process\b<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>process<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end loop;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(.*\bloop|.*\h*:\h*(for|loop))$<ret> j}ijx <a-K>^<c-r>x(?i)(end)<ret>
+ execute-keys -draft o<c-r>xend<space>loop<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end protected;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(type\b.*\bis\h+protected)$<ret> j}ijx <a-K>^<c-r>x(?i)(end)<ret>
+ execute-keys -draft o<c-r>xend<space>protected<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end protected body;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(\h*type\h+\w+\h+is\h+protected\h+body$)<ret> j}ijx <a-K>^<c-r>x(?i)end\h+protected\h+body\b<ret>
+ execute-keys -draft o<c-r>xend<space>protected<space>body<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end record;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(type\b.*\bis\h+record\h*)$<ret> j}ijx <a-K>^<c-r>x(?i)(end)<ret>
+ execute-keys -draft o<c-r>xend<space>record<semicolon><esc>
+ ]
+ ]
+ # Wisely add ");" for "type ... is (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(type\b.*\bis\h+\(\h*)$<ret> j}ijx <a-K>^<c-r>x(\))<ret>
+ execute-keys -draft o<c-r>x)<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end entity;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)\h*entity\b.*\bis$<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xend<space>entity<semicolon><esc>
+ ]
+ ]
+ # Wisely add "begin" and "end function;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(\h*\)?\h*return\b.*\bis$)<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>function<semicolon><esc>
+ ]
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(\h*((pure|impure)\h+)?function\b.*\bis$)<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>function<semicolon><esc>
+ ]
+ ]
+ # Wisely add "begin" and "end procedure;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(\h*procedure\b.*\bis$)<ret> j}ijx <a-K>^<c-r>x(?i)\b(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>procedure<semicolon><esc>
+ ]
+ try %[
+ execute-keys -draft kx <a-k>^(?i)\h*\)\h*\bis$<ret> j}ijx <a-K>^<c-r>x(?i)\b(begin|end)\b<ret>
+ # Verify that line with opening parenthesis contains "procedure" keyword.
+ execute-keys -draft kx s\)<ret> <a-m><semicolon> x<a-k> (?i)\bprocedure\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>procedure<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end package;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(package\b)<ret> j}ijx <a-K>^<c-r>x(?i)(end)\b<ret>
+ # Make sure it is not package body.
+ execute-keys -draft kx<a-K>(?i)\bbody\b<ret>
+ execute-keys -draft oend<space>package<semicolon><esc>
+ ]
+ ]
+ # Wisely add "end package body;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)(package\h+body\b)<ret> j}ijx <a-K>^<c-r>x(?i)(end)\b<ret>
+ execute-keys -draft oend<space>package<space>body<semicolon><esc>
+ ]
+ ]
+ # Wisely add "begin" and "end architecture;".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^(?i)\h*architecture\b<ret> j}ijx <a-K>^<c-r>x(?i)(begin|end)\b<ret>
+ execute-keys -draft o<c-r>xbegin<ret><c-r>xend<space>architecture<semicolon><esc>
+ ]
+ ]
+ # Wisely add ");" for "port (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)port\h*\($<ret> j}ijx <a-K>^<c-r>x(\)\;)<ret>
+ execute-keys -draft o<c-r>x)<semicolon><esc>
+ ]
+ ]
+ # Wisely add ");" for "port map (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)port\h+map\h*\($<ret> j}ijx <a-K>^<c-r>x(\)\;)<ret>
+ execute-keys -draft o<c-r>x)<semicolon><esc>
+ ]
+ ]
+ # Wisely add ");" for "generic (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)generic\h*\($<ret> j}ijx <a-K>^<c-r>x(\)\;)<ret>
+ execute-keys -draft o<c-r>x)<semicolon><esc>
+ ]
+ ]
+ # Wisely add ")" for "generic map (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)generic\h+map\h*\($<ret> j}ijx <a-K>^<c-r>x(\))<ret>
+ execute-keys -draft o<c-r>x)<esc>
+ ]
+ ]
+ # Wisely add ") return ;" for "[pure|impure] function ... (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)(pure\b|impure\b)?\h*function\b.*\h*\($<ret> j}ijx <a-K>^<c-r>x(\)\h*return.*)<ret>
+ execute-keys -draft o<c-r>x)<space>return<space><semicolon><esc>
+ ]
+ ]
+ # Wisely add ");" for "procedure ... (".
+ evaluate-commands %[
+ try %[
+ execute-keys -draft kx <a-k>^\h*(?i)procedure\b.*\h*\($<ret> j}ijx <a-K>^<c-r>x(\)\h*\;)<ret>
+ execute-keys -draft o<c-r>x)<semicolon><esc>
+ ]
+ ]
+ ]
+]
+
+define-command -hidden vhdl-indent-on-new-line %{
+ evaluate-commands -itersel %{
+ # Align "then" to previous "if|elsif".
+ evaluate-commands -itersel -save-regs x %[
+ try %[
+ execute-keys -draft k x <a-k> (?i)^\h*then$ <ret>
+ try %[ execute-keys -draft <a-/>(?i)\b(if|elsif)\b<ret>xs^\h+<ret>"xy ] catch %[ reg x '' ]
+ try %[ execute-keys -draft k xs^\h+<ret>d ] catch %[ ]
+ execute-keys -draft kgh i<c-r>x<esc>
+ ]
+ ]
+
+ # Align "generate" to previous "if|for".
+ evaluate-commands -itersel -save-regs x %[
+ try %[
+ execute-keys -draft k x <a-k> (?i)^\h*generate$ <ret>
+ try %[ execute-keys -draft <a-/>(?i)\b(if|for)\b<ret>xs^\h+<ret>"xy ] catch %[ reg x '' ]
+ try %[ execute-keys -draft k xs^\h+<ret>d ] catch %[ ]
+ execute-keys -draft kgh i<c-r>x<esc>
+ ]
+ ]
+
+ # Preserve previous line indent.
+ try %[ execute-keys -draft <semicolon> K <a-&> ]
+
+ # Cleanup trailing whitespaces from previous line.
+ try %[ execute-keys -draft k x s \h+$ <ret> d ]
+
+ # Increase indent after some keywords.
+ try %[
+ execute-keys -draft kx<a-k> (?i)\b(begin|block|body|else|for|generate|if|is|loop|process|protected|record|select|then)$ <ret>
+ # Do not indent if in comment line.
+ execute-keys -draft kx<a-K>(?i)^\h*--<ret>
+ # Do not indent for "case ... is".
+ execute-keys -draft kx<a-K>^\h*(?i)(case|.*\h*:\h*case)\b<ret>
+ execute-keys -draft <semicolon> <a-gt>
+ ]
+ # Copy the indentation of the matching if.
+ try %{ execute-keys -draft , k x <a-k> ^\h*(elsif\b|else$) <ret> gh [c^\h*(\S*\h*:\h*)?if\b,\bend\sif\b <ret> x <a-S> 1<a-&> , j K <a-&> }
+
+ # Increase indent after some operators.
+ try %[ execute-keys -draft <semicolon> k x <a-k> (\(|=>|<=|:=)$ <ret> j <a-gt> ]
+ }
+}
+
+define-command vhdl-indent-on-closing-parenthesis %[
+ evaluate-commands -itersel %[
+ # Decrease indent after ")" at the beginning of line.
+ try %[ execute-keys -draft x <a-k> (^\h+\)$) <ret> <a-lt> ]
+ ]
+]
+
diff --git a/autoload/filetype/void-linux.kak b/autoload/filetype/void-linux.kak
new file mode 100644
index 0000000..23a0206
--- /dev/null
+++ b/autoload/filetype/void-linux.kak
@@ -0,0 +1,4 @@
+# Void Linux package template
+hook global BufCreate .*/?srcpkgs/.+/template %{
+ set-option buffer filetype sh
+}
diff --git a/autoload/filetype/yaml.kak b/autoload/filetype/yaml.kak
new file mode 100644
index 0000000..db6a4f9
--- /dev/null
+++ b/autoload/filetype/yaml.kak
@@ -0,0 +1,73 @@
+# http://yaml.org
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](ya?ml) %{
+ set-option buffer filetype yaml
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=yaml %{
+ require-module yaml
+
+ hook window ModeChange pop:insert:.* -group yaml-trim-indent yaml-trim-indent
+ hook window InsertChar \n -group yaml-insert yaml-insert-on-new-line
+ hook window InsertChar \n -group yaml-indent yaml-indent-on-new-line
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window yaml-.+ }
+}
+
+hook -group yaml-highlight global WinSetOption filetype=yaml %{
+ add-highlighter window/yaml ref yaml
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/yaml }
+}
+
+
+provide-module yaml %{
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/yaml regions
+add-highlighter shared/yaml/code default-region group
+add-highlighter shared/yaml/double_string region '"' (?<!\\)(\\\\)*" fill string
+add-highlighter shared/yaml/single_string region "'" "'" fill string
+add-highlighter shared/yaml/comment region '(?:^| )#' $ fill comment
+
+add-highlighter shared/yaml/code/ regex ^(---|\.\.\.)$ 0:meta
+add-highlighter shared/yaml/code/ regex ^(\h*:\w*) 0:keyword
+add-highlighter shared/yaml/code/ regex \b(true|false|null)\b 0:value
+add-highlighter shared/yaml/code/ regex ^\h*-?\h*(\S+): 1:attribute
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden yaml-trim-indent %{
+ # remove trailing white spaces
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden yaml-insert-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # copy '#' comment prefix and following white spaces
+ try %{ execute-keys -draft k x s ^\h*\K#\h* <ret> y gh j P }
+ }
+}
+
+define-command -hidden yaml-indent-on-new-line %{
+ evaluate-commands -draft -itersel %{
+ # preserve previous line indent
+ try %{ execute-keys -draft <semicolon> K <a-&> }
+ # filter previous line
+ try %{ execute-keys -draft k : yaml-trim-indent <ret> }
+ # indent after :
+ try %{ execute-keys -draft , k x <a-k> :$ <ret> j <a-gt> }
+ # indent after -
+ try %{ execute-keys -draft , k x <a-k> ^\h*- <ret> j <a-gt> }
+ }
+}
+
+}
diff --git a/autoload/filetype/zig.kak b/autoload/filetype/zig.kak
new file mode 100644
index 0000000..b322c82
--- /dev/null
+++ b/autoload/filetype/zig.kak
@@ -0,0 +1,144 @@
+# zig syntax highlighting for kakoune (https://ziglang.org)
+#
+# based off of https://github.com/ziglang/zig.vim/blob/master/syntax/zig.vim
+# as well as https://ziglang.org/documentation/master/#Grammar
+
+# Detection
+# ‾‾‾‾‾‾‾‾‾
+
+hook global BufCreate .*[.](zig|zon) %{
+ set-option buffer filetype zig
+}
+
+# Initialization
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+hook global WinSetOption filetype=zig %<
+ require-module zig
+ hook window ModeChange pop:insert:.* -group zig-trim-indent zig-trim-indent
+ hook window InsertChar \n -group zig-insert zig-insert-on-new-line
+ hook window InsertChar \n -group zig-indent zig-indent-on-new-line
+ hook window InsertChar \} -group zig-indent zig-indent-on-closing
+
+ hook -once -always window WinSetOption filetype=.* %< remove-hooks window zig-.+ >
+>
+
+hook -group zig-highlight global WinSetOption filetype=zig %{
+ require-module zig
+ add-highlighter window/zig ref zig
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/zig }
+}
+
+provide-module zig %§
+
+# Highlighters
+# ‾‾‾‾‾‾‾‾‾‾‾‾
+
+add-highlighter shared/zig regions
+add-highlighter shared/zig/code default-region group
+
+add-highlighter shared/zig/doc_comment region '///(?=[^/])' '$' fill documentation
+add-highlighter shared/zig/comment region '//' '$' fill comment
+
+# strings and characters
+add-highlighter shared/zig/string region '"' (?<!\\)(\\\\)*" group
+add-highlighter shared/zig/string/ fill string
+add-highlighter shared/zig/string/ regex '(?:\\n|\\r|\\t|\\\\|\\''|\\"|\\x[0-9a-fA-F]{2}|\\u\{[0-9a-fA-F]+\})' 0:meta
+
+add-highlighter shared/zig/character region "'" (?<!\\)(\\\\)*' group
+add-highlighter shared/zig/character/ fill string
+add-highlighter shared/zig/character/ regex '(?:\\n|\\r|\\t|\\\\|\\''|\\"|\\x[0-9a-fA-F]{2}|\\u\{[0-9a-fA-F]+\})' 0:meta
+
+add-highlighter shared/zig/multiline_string region '\\\\' '$' fill string
+
+# attributes
+add-highlighter shared/zig/code/ regex '\b(?:const|var|extern|packed|export|pub|noalias|inline|noinline|comptime|callconv|volatile|allowzero|align|linksection|threadlocal)\b' 0:attribute
+# structures
+add-highlighter shared/zig/code/ regex '\b(?:struct|enum|union|error|opaque)\b' 0:attribute
+
+# statement
+add-highlighter shared/zig/code/ regex '\b(?:break|return|continue|asm|defer|errdefer|unreachable|try|catch|async|noasync|await|suspend|nosuspend|resume)\b' 0:keyword
+# conditional
+add-highlighter shared/zig/code/ regex '\b(?:if|else|switch|and|or|orelse)\b' 0:keyword
+# repeat
+add-highlighter shared/zig/code/ regex '\b(?:while|for)\b' 0:keyword
+# other keywords
+add-highlighter shared/zig/code/ regex '\b(?:fn|usingnamespace|test)\b' 0:keyword
+
+# types
+add-highlighter shared/zig/code/ regex '\b(?:bool|f16|f32|f64|f80|f128|void|noreturn|type|anyerror|anyframe|anytype|anyopaque)\b' 0:type
+add-highlighter shared/zig/code/ regex '\b(?:i0|u0|isize||usize|comptime_int|comptime_float)\b' 0:type
+add-highlighter shared/zig/code/ regex '\b(?:[iu][1-9]\d*)\b' 0:type
+add-highlighter shared/zig/code/ regex '\b(?:c_char|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|c_longlong|c_ulonglong|c_longdouble)\b' 0:type
+
+# primitive values
+add-highlighter shared/zig/code/ regex '\b(?:true|false|null|undefined)\b' 0:value
+
+# integer literals
+add-highlighter shared/zig/code/ regex '\b[0-9](_?[0-9])*\b' 0:value
+add-highlighter shared/zig/code/ regex '\b0x[0-9a-fA-F](_?[0-9a-fA-F])*\b' 0:value
+add-highlighter shared/zig/code/ regex '\b0o[0-7](_?[0-7])*\b' 0:value
+add-highlighter shared/zig/code/ regex '\b0b[01](_?[01])*\b' 0:value
+
+# float literals
+add-highlighter shared/zig/code/ regex '\b[0-9]+\.[0-9]+(?:[eE][-+]?[0-9]+)?\b' 0:value
+add-highlighter shared/zig/code/ regex '\b0x[0-9a-fA-F]+\.[0-9a-fA-F]+(?:[pP][-+]?[0-9a-fA-F]+)?\b' 0:value
+add-highlighter shared/zig/code/ regex '\b[0-9]+\.?[eE][-+]?[0-9]+\b' 0:value
+add-highlighter shared/zig/code/ regex '\b0x[0-9a-fA-F]+\.?[eE][-+]?[0-9a-fA-F]+\b' 0:value
+
+# operators
+add-highlighter shared/zig/code/ regex '(?:\+|-|\*|/|%|=|<|>|&|\||\^|~|\?|!)' 0:operator
+
+# builtin functions
+add-highlighter shared/zig/code/ regex "@(?:addWithOverflow|as|atomicLoad|atomicStore|bitCast|breakpoint|trap|alignCast|alignOf|cDefine|cImport|cInclude|cUndef|clz|cmpxchgWeak|cmpxchgStrong|compileError|compileLog|constCast|ctz|popCount|divExact|divFloor|divTrunc|embedFile|export|extern|tagName|TagType|errorName|call|errorReturnTrace|fence|fieldParentPtr|field|unionInit|frameAddress|import|inComptime|newStackCall|asyncCall|ptrFromInt|max|min|memcpy|memset|mod|mulAdd|mulWithOverflow|splat|src|bitOffsetOf|byteOffsetOf|offsetOf|OpaqueType|panic|prefetch|ptrCast|intFromPtr|rem|returnAddress|setCold|Type|shuffle|reduce|select|setRuntimeSafety|setEvalBranchQuota|setFloatMode|shlExact|This|hasDecl|hasField|shlWithOverflow|shrExact|sizeOf|bitSizeOf|sqrt|byteSwap|subWithOverflow|intCast|floatCast|floatFromInt|intFromFloat|intFromBool|errSetCast|truncate|typeInfo|typeName|TypeOf|atomicRmw|errorFromInt|intFromError|enumFromInt|intFromEnum|setAlignStack|frame|Frame|frameSize|bitReverse|Vector|volatileCast|sin|cos|tan|exp|exp2|log|log2|log10|abs|floor|ceil|trunc|wasmMemorySize|wasmMemoryGrow|round)\b" 0:meta
+
+# Commands
+# ‾‾‾‾‾‾‾‾
+
+define-command -hidden zig-trim-indent %{
+ # delete trailing whitespace
+ try %{ execute-keys -draft -itersel x s \h+$ <ret> d }
+}
+
+define-command -hidden zig-insert-on-new-line %<
+ try %[
+ evaluate-commands -draft -save-regs '/"' %[
+ # copy // or /// comments prefix or \\ string literal prefix and following whitespace
+ execute-keys -save-regs '' k x1s^\h*((///?|\\\\)+\h*)<ret> y
+ try %[
+ # if the previous comment isn't empty, create a new one
+ execute-keys x<a-K>^\h*//+\h*$<ret> jxs^\h*<ret>P
+ ] catch %[
+ # if there is no text in the previous comment, remove it completely
+ execute-keys d
+ ]
+ ]
+
+ # trim trailing whitespace on the previous line
+ try %[ execute-keys -draft k x s\h+$<ret> d ]
+ ]
+>
+
+define-command -hidden zig-indent-on-new-line %<
+ evaluate-commands -draft -itersel %<
+ # preserve indent level
+ try %< execute-keys -draft <semicolon> K <a-&> >
+ try %<
+ # only if we didn't copy a comment or multiline string
+ execute-keys -draft x <a-K> ^\h*(//|\\\\) <ret>
+ # indent after lines ending in {
+ try %< execute-keys -draft k x <a-k> \{\h*$ <ret> j <a-gt> >
+ # deindent closing } when after cursor
+ try %< execute-keys -draft x <a-k> ^\h*\} <ret> gh / \} <ret> m <a-S> 1<a-&> >
+ >
+ # filter previous line
+ try %< execute-keys -draft k : zig-trim-indent <ret> >
+ >
+>
+
+define-command -hidden zig-indent-on-closing %<
+ # align lone } to indent level of opening line
+ try %< execute-keys -draft -itersel <a-h> <a-k> ^\h*\}$ <ret> h m <a-S> 1<a-&> >
+>
+
diff --git a/autoload/kak-rainbow b/autoload/kak-rainbow
new file mode 160000
+Subproject 9c3d0aa62514134ee5cb86e80855d9712c4e8c4
diff --git a/autoload/kakoune-gdb b/autoload/kakoune-gdb
new file mode 160000
+Subproject e4264aa1ba133f6e7188a6e55c2b47ffb40180b
diff --git a/autoload/tools/autorestore.asciidoc b/autoload/tools/autorestore.asciidoc
new file mode 100644
index 0000000..528fcf5
--- /dev/null
+++ b/autoload/tools/autorestore.asciidoc
@@ -0,0 +1,13 @@
+= Automatically restore unsaved work after a crash.
+
+When Kakoune crashes, it automatically writes out unsaved changes to backup
+files with predictable names. When you edit a file, if such a backup file
+exists, this plugin will automatically load the content of the backup file
+instead.
+
+By default, backup files are deleted when restored. You can set the
+`autorestore_purge_restored` option to `false` to prevent this.
+
+If you don't want backups to be restored automatically, use the
+`autorestore-disable` command to disable the feature for the current session,
+or put it in your `kakrc` to disable the feature forever.
diff --git a/autoload/tools/autorestore.kak b/autoload/tools/autorestore.kak
new file mode 100644
index 0000000..52febe3
--- /dev/null
+++ b/autoload/tools/autorestore.kak
@@ -0,0 +1,93 @@
+declare-option -docstring %{
+ Remove backups once they've been restored
+
+ See `:doc autorestore` for details.
+ } \
+ bool autorestore_purge_restored true
+
+## Insert the content of the backup file into the current buffer, if a suitable one is found
+define-command autorestore-restore-buffer \
+ -docstring %{
+ Restore the backup for the current file if it exists
+
+ See `:doc autorestore` for details.
+ } \
+%{
+ evaluate-commands %sh{
+ buffer_basename="${kak_buffile##*/}"
+ buffer_dirname=$(dirname "${kak_buffile}")
+
+ if [ -f "${kak_buffile}" ]; then
+ newer=$(find "${buffer_dirname}"/".${buffer_basename}.kak."* -newer "${kak_buffile}" -exec ls -1t {} + 2>/dev/null | head -n 1)
+ older=$(find "${buffer_dirname}"/".${buffer_basename}.kak."* \! -newer "${kak_buffile}" -exec ls -1t {} + 2>/dev/null | head -n 1)
+ else
+ # New buffers that were never written to disk.
+ newer=$(ls -1t "${buffer_dirname}"/".${buffer_basename}.kak."* 2>/dev/null | head -n 1)
+ older=""
+ fi
+
+ if [ -z "${newer}" ]; then
+ if [ -n "${older}" ]; then
+ printf %s\\n "
+ echo -debug Old backup file(s) found: will not restore ${older} .
+ "
+ fi
+ exit
+ fi
+
+ printf %s\\n "
+ ## Replace the content of the buffer with the content of the backup file
+ echo -debug Restoring file: ${newer}
+
+ execute-keys -draft %{%d!cat<space>\"${newer}\"<ret>jd}
+
+ ## If the backup file has to be removed, issue the command once
+ ## the current buffer has been saved
+ ## If the autorestore_purge_restored option has been unset right after the
+ ## buffer was restored, do not remove the backup
+ hook -group autorestore buffer BufWritePost '${kak_buffile}' %{
+ nop %sh{
+ if [ \"\${kak_opt_autorestore_purge_restored}\" = true ];
+ then
+ rm -f \"${buffer_dirname}/.${buffer_basename}.kak.\"*
+ fi
+ }
+ }
+ "
+ }
+}
+
+## Remove all the backups that have been created for the current buffer
+define-command autorestore-purge-backups \
+ -docstring %{
+ Remove all the backups of the current buffer
+
+ See `:doc autorestore` for details.
+ } \
+%{
+ evaluate-commands %sh{
+ [ ! -f "${kak_buffile}" ] && exit
+
+ buffer_basename="${kak_bufname##*/}"
+ buffer_dirname=$(dirname "${kak_bufname}")
+
+ rm -f "${buffer_dirname}/.${buffer_basename}.kak."*
+
+ printf %s\\n "
+ echo -markup {Information}Backup files removed.
+ "
+ }
+}
+
+## If for some reason, backup files need to be ignored
+define-command autorestore-disable \
+ -docstring %{
+ Disable automatic backup recovering
+
+ See `:doc autorestore` for details.
+ } \
+%{
+ remove-hooks global autorestore
+}
+
+hook -group autorestore global BufCreate .* %{ autorestore-restore-buffer }
diff --git a/autoload/tools/autowrap.kak b/autoload/tools/autowrap.kak
new file mode 100644
index 0000000..d742f6e
--- /dev/null
+++ b/autoload/tools/autowrap.kak
@@ -0,0 +1,50 @@
+declare-option -docstring "maximum amount of characters per line, after which a newline character will be inserted" \
+ int autowrap_column 80
+
+declare-option -docstring %{
+ when enabled, paragraph formatting will reformat the whole paragraph in which characters are being inserted
+ This can potentially break formatting of documents containing markup (e.g. markdown)
+} bool autowrap_format_paragraph no
+declare-option -docstring %{
+ command to which the paragraphs to wrap will be passed
+ all occurences of '%c' are replaced with `autowrap_column`
+} str autowrap_fmtcmd 'fold -s -w %c'
+
+define-command -hidden autowrap-cursor %{ evaluate-commands -save-regs '/"|^@m' %{
+ try %{
+ ## if the line isn't too long, do nothing
+ execute-keys -draft "x<a-k>^[^\n]{%opt{autowrap_column},}[^\n]<ret>"
+
+ try %{
+ reg m "%val{selections_desc}"
+
+ ## if we're adding characters past the limit, just wrap them around
+ execute-keys -draft "<a-h><a-k>.{%opt{autowrap_column}}\h*[^\s]*<ret>1s(\h+)[^\h]*\z<ret>c<ret>"
+ } catch %{
+ ## if we're adding characters in the middle of a sentence, use
+ ## the `fmtcmd` command to wrap the entire paragraph
+ evaluate-commands %sh{
+ if [ "${kak_opt_autowrap_format_paragraph}" = true ] \
+ && [ -n "${kak_opt_autowrap_fmtcmd}" ]; then
+ format_cmd=$(printf %s "${kak_opt_autowrap_fmtcmd}" \
+ | sed "s/%c/${kak_opt_autowrap_column}/g")
+ printf %s "
+ evaluate-commands -draft %{
+ execute-keys '<a-]>px<a-j>|${format_cmd}<ret>'
+ try %{ execute-keys s\h+$<ret> d }
+ }
+ select '${kak_main_reg_m}'
+ "
+ fi
+ }
+ }
+ }
+} }
+
+define-command autowrap-enable -docstring "Automatically wrap the lines in which characters are inserted" %{
+ hook -group autowrap window InsertChar [^\n] autowrap-cursor
+}
+
+define-command autowrap-disable -docstring "Disable automatic line wrapping" %{
+ remove-hooks window autowrap
+}
diff --git a/autoload/tools/clang.kak b/autoload/tools/clang.kak
new file mode 100644
index 0000000..41f6c55
--- /dev/null
+++ b/autoload/tools/clang.kak
@@ -0,0 +1,196 @@
+hook -once global BufSetOption filetype=(c|cpp) %{
+ require-module clang
+}
+
+provide-module clang %[
+
+declare-option -docstring "options to pass to the `clang` shell command" \
+ str clang_options
+
+declare-option -docstring "directory from which to invoke clang" \
+ str clang_directory
+
+declare-option -hidden completions clang_completions
+declare-option -hidden line-specs clang_flags
+declare-option -hidden line-specs clang_errors
+
+define-command -params ..1 \
+ -docstring %{
+ Parse the contents of the current buffer
+ The syntaxic errors detected during parsing are shown when auto-diagnostics are enabled
+ } clang-parse %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-clang.XXXXXXXX)
+ mkfifo ${dir}/fifo
+ printf %s\\n "
+ evaluate-commands -no-hooks write -sync -method replace ${dir}/buf
+ evaluate-commands -draft %{
+ edit! -fifo ${dir}/fifo -debug *clang-output*
+ set-option buffer filetype make
+ set-option buffer jump_current_line 0
+ hook -once -always buffer BufCloseFifo .* %{ nop %sh{ rm -r ${dir} } }
+ }"
+
+ # this runs in a detached shell, asynchronously, so that kakoune does
+ # not hang while clang is running. As completions references a cursor
+ # position and a buffer timestamp, only valid completions should be
+ # displayed.
+ ((
+ trap - INT QUIT
+ until [ -f ${dir}/buf ]; do :; done # wait for the buffer to be written
+
+ if [ -n "$kak_opt_clang_directory" ]; then
+ cd "$kak_opt_clang_directory"
+ fi
+ case ${kak_opt_filetype} in
+ (c) ft=c ;;
+ (cpp) ft=c++ ;;
+ (obj-c) ft=objective-c ;;
+ (*) ft=c++ ;;
+ esac
+
+ if [ "$1" = "-complete" ]; then
+ pos=-:${kak_cursor_line}:${kak_cursor_column}
+ header="${kak_cursor_line}.${kak_cursor_column}@${kak_timestamp}"
+ compl=$(clang++ -x ${ft} -fsyntax-only ${kak_opt_clang_options} \
+ -Xclang -code-completion-brief-comments -Xclang -code-completion-at=${pos} - < ${dir}/buf 2> ${dir}/stderr |
+ awk -F ': ' '
+ /^COMPLETION:/ && $2 !~ /[(,](Hidden|Inaccessible)[),]/ {
+ candidate=$3
+ gsub(/[[<{]#[^#]*#[]>}]/, "", candidate)
+ gsub(/~/, "~~", candidate)
+ gsub(/\|/, "\\|", candidate)
+
+ gsub(/[[{<]#|#[]}>]/, " ", $3)
+ gsub(/:: /, "::", $3)
+ gsub(/ ,/, ",", $3)
+ gsub(/^ +| +$/, "", $3)
+ docstring=$4 ? $3 "\n" $4 : $3
+
+ gsub(/~|!/, "&&", docstring)
+ gsub(/\|/, "\\|", docstring)
+ if (candidate in candidates)
+ candidates[candidate]=candidates[candidate] "\n" docstring
+ else
+ candidates[candidate]=docstring
+ }
+ END {
+ for (candidate in candidates) {
+ menu=candidate
+ gsub(/(^|[^[:alnum:]_])(operator|new|delete)($|[^{}_[:alnum:]]+)/, "{keyword}&{}", menu)
+ gsub(/(^|[[:space:]])(int|size_t|bool|char|unsigned|signed|long)($|[[:space:]])/, "{type}&{}", menu)
+ gsub(/[^{}_[:alnum:]]+/, "{operator}&{}", menu)
+ printf "%%~%s|info -style menu %!%s!|%s~ ", candidate, candidates[candidate], menu
+ }
+ }')
+ printf %s\\n "evaluate-commands -client ${kak_client} echo 'clang completion done'
+ set-option 'buffer=${kak_buffile}' clang_completions ${header} ${compl}" | kak -p ${kak_session}
+ else
+ clang++ -x ${ft} -fsyntax-only ${kak_opt_clang_options} - < ${dir}/buf 2> ${dir}/stderr
+ printf %s\\n "evaluate-commands -client ${kak_client} echo 'clang parsing done'" | kak -p ${kak_session}
+ fi
+
+ flags=$(cat ${dir}/stderr | sed -Ene "
+ /^<stdin>:[0-9]+:([0-9]+:)? (fatal )?error/ { s/^<stdin>:([0-9]+):.*/'\1|{red}█'/; p }
+ /^<stdin>:[0-9]+:([0-9]+:)? warning/ { s/^<stdin>:([0-9]+):.*/'\1|{yellow}█'/; p }
+ " | paste -s -d ' ' -)
+
+ errors=$(cat ${dir}/stderr | sed -Ene "
+ /^<stdin>:[0-9]+:([0-9]+:)? ((fatal )?error|warning)/ {
+ s/'/''/g; s/^<stdin>:([0-9]+):([0-9]+:)? (.*)/'\1|\3'/; p
+ }" | sort -n | paste -s -d ' ' -)
+
+ sed -e "s|<stdin>|${kak_bufname}|g" < ${dir}/stderr > ${dir}/fifo
+
+ printf %s\\n "set-option 'buffer=${kak_buffile}' clang_flags ${kak_timestamp} ${flags}
+ set-option 'buffer=${kak_buffile}' clang_errors ${kak_timestamp} ${errors}" | kak -p ${kak_session}
+ ) & ) > /dev/null 2>&1 < /dev/null
+ }
+}
+
+define-command clang-complete -docstring "Complete the current selection" %{ clang-parse -complete }
+
+define-command -hidden clang-show-completion-info %[ try %[
+ evaluate-commands -draft %[
+ execute-keys ,{( <a-k> ^\( <ret> b <a-k> \A\w+\z <ret>
+ evaluate-commands %sh[
+ desc=$(printf %s\\n "${kak_opt_clang_completions}" | sed -e "{ s/\([^\\]\):/\1\n/g }" | sed -ne "/^${kak_selection}|/ { s/^[^|]\+|//; s/|.*$//; s/\\\:/:/g; p }")
+ if [ -n "$desc" ]; then
+ printf %s\\n "evaluate-commands -client $kak_client %{info -anchor ${kak_cursor_line}.${kak_cursor_column} -style above %{${desc}}}"
+ fi
+ ] ]
+] ]
+
+define-command clang-enable-autocomplete -docstring "Enable automatic clang completion" %{
+ set-option window completers "option=clang_completions" %opt{completers}
+ hook window -group clang-autocomplete InsertIdle .* %{
+ try %{
+ execute-keys -draft <a-h><a-k>(\.|->|::).\z<ret>
+ echo 'completing...'
+ clang-complete
+ }
+ clang-show-completion-info
+ }
+ alias window complete clang-complete
+}
+
+define-command clang-disable-autocomplete -docstring "Disable automatic clang completion" %{
+ evaluate-commands %sh{ printf "set-option window completers %s\n" $(printf %s "${kak_opt_completers}" | sed -e "s/'option=clang_completions'//g") }
+ remove-hooks window clang-autocomplete
+ unalias window complete clang-complete
+}
+
+define-command -hidden clang-show-error-info %{
+ update-option buffer clang_errors # Ensure we are up to date with buffer changes
+ evaluate-commands %sh{
+ eval "set -- ${kak_quoted_opt_clang_errors}"
+ shift # skip timestamp
+ desc=$(for error in "$@"; do
+ if [ "${error%%|*}" = "$kak_cursor_line" ]; then
+ printf '%s\n' "${error##*|}"
+ fi
+ done)
+ if [ -n "$desc" ]; then
+ desc=$(printf %s "${desc}" | sed "s/'/''/g")
+ printf "info -anchor %d.%d '%s'\n" "${kak_cursor_line}" "${kak_cursor_column}" "${desc}"
+ fi
+ } }
+
+define-command clang-enable-diagnostics -docstring %{
+ Activate automatic error reporting and diagnostics
+ Information about the analysis will be shown after the buffer has been parsed with the clang-parse function
+} %{
+ add-highlighter window/clang_flags flag-lines default clang_flags
+ hook window -group clang-diagnostics NormalIdle .* %{ clang-show-error-info }
+ hook window -group clang-diagnostics WinSetOption ^clang_errors=.* %{ info; clang-show-error-info }
+}
+
+define-command clang-disable-diagnostics -docstring "Disable automatic error reporting and diagnostics" %{
+ remove-highlighter window/clang_flags
+ remove-hooks window clang-diagnostics
+}
+
+define-command clang-diagnostics-next -docstring "Jump to the next line that contains an error" %{
+ update-option buffer clang_errors # Ensure we are up to date with buffer changes
+ evaluate-commands %sh{
+ eval "set -- ${kak_quoted_opt_clang_errors}"
+ shift # skip timestamp
+ unset line
+ unset first_line
+ for error in "$@"; do
+ candidate=${error%%|*}
+ first_line=${first_line-$candidate}
+ if [ "$candidate" -gt $kak_cursor_line ]; then
+ line=$candidate
+ break
+ fi
+ done
+ line=${line-$first_line}
+ if [ -n "$line" ]; then
+ printf %s\\n "execute-keys ${line} g"
+ else
+ echo "fail no next clang diagnostic"
+ fi
+ } }
+
+]
diff --git a/autoload/tools/comment.kak b/autoload/tools/comment.kak
new file mode 100644
index 0000000..b610073
--- /dev/null
+++ b/autoload/tools/comment.kak
@@ -0,0 +1,218 @@
+# Line comments
+# If the language has no line comments, set to ''
+declare-option -docstring "characters inserted at the beginning of a commented line" \
+ str comment_line '#'
+
+# Block comments
+declare-option -docstring "characters inserted before a commented block" \
+ str comment_block_begin
+declare-option -docstring "characters inserted after a commented block" \
+ str comment_block_end
+
+# Default comments for all languages
+hook global BufSetOption filetype=asciidoc %{
+ set-option buffer comment_line '//'
+ set-option buffer comment_block_begin '////'
+ set-option buffer comment_block_end '////'
+}
+
+hook global BufSetOption filetype=(c|cpp|dart|gluon|go|java|javascript|objc|odin|php|pony|protobuf|rust|sass|scala|scss|swift|typescript|groovy) %{
+ set-option buffer comment_line '//'
+ set-option buffer comment_block_begin '/*'
+ set-option buffer comment_block_end '*/'
+}
+
+hook global BufSetOption filetype=(cabal|haskell|moon|idris|elm|dhall|purescript) %{
+ set-option buffer comment_line '--'
+ set-option buffer comment_block_begin '{-'
+ set-option buffer comment_block_end '-}'
+}
+
+hook global BufSetOption filetype=clojure %{
+ set-option buffer comment_line '#_'
+ set-option buffer comment_block_begin '(comment '
+ set-option buffer comment_block_end ')'
+}
+
+hook global BufSetOption filetype=janet %{
+ set-option buffer comment_line '#'
+ set-option buffer comment_block_begin '(comment '
+ set-option buffer comment_block_end ')'
+}
+
+hook global BufSetOption filetype=coffee %{
+ set-option buffer comment_block_begin '###'
+ set-option buffer comment_block_end '###'
+}
+
+hook global BufSetOption filetype=conf %{
+ set-option buffer comment_line '#'
+}
+
+hook global BufSetOption filetype=css %{
+ set-option buffer comment_line ''
+ set-option buffer comment_block_begin '/*'
+ set-option buffer comment_block_end '*/'
+}
+
+hook global BufSetOption filetype=d %{
+ set-option buffer comment_line '//'
+ set-option buffer comment_block_begin '/+'
+ set-option buffer comment_block_end '+/'
+}
+
+hook global BufSetOption filetype=(fennel|gas|ini) %{
+ set-option buffer comment_line ';'
+}
+
+hook global BufSetOption filetype=haml %{
+ set-option buffer comment_line '-#'
+}
+
+hook global BufSetOption filetype=(html|xml) %{
+ set-option buffer comment_line ''
+ set-option buffer comment_block_begin '<!--'
+ set-option buffer comment_block_end '-->'
+}
+
+hook global BufSetOption filetype=(latex|mercury) %{
+ set-option buffer comment_line '%'
+}
+
+hook global BufSetOption filetype=ledger %{
+ set-option buffer comment_line ';'
+}
+
+hook global BufSetOption filetype=(lisp|scheme) %{
+ set-option buffer comment_line ';'
+ set-option buffer comment_block_begin '#|'
+ set-option buffer comment_block_end '|#'
+}
+
+hook global BufSetOption filetype=lua %{
+ set-option buffer comment_line '--'
+ set-option buffer comment_block_begin '--[['
+ set-option buffer comment_block_end ']]'
+}
+
+hook global BufSetOption filetype=markdown %{
+ set-option buffer comment_line ''
+ set-option buffer comment_block_begin '[//]: # "'
+ set-option buffer comment_block_end '"'
+}
+
+hook global BufSetOption filetype=(ocaml|coq) %{
+ set-option buffer comment_line ''
+ set-option buffer comment_block_begin '(* '
+ set-option buffer comment_block_end ' *)'
+}
+
+hook global BufSetOption filetype=((free|object)?pascal|delphi) %{
+ set-option buffer comment_line '//'
+ set-option buffer comment_block_begin '{'
+ set-option buffer comment_block_end '}'
+}
+
+hook global BufSetOption filetype=perl %{
+ set-option buffer comment_block_begin '#['
+ set-option buffer comment_block_end ']'
+}
+
+hook global BufSetOption filetype=(pug|zig|cue|hare) %{
+ set-option buffer comment_line '//'
+}
+
+hook global BufSetOption filetype=python %{
+ set-option buffer comment_block_begin "'''"
+ set-option buffer comment_block_end "'''"
+}
+
+hook global BufSetOption filetype=r %{
+ set-option buffer comment_line '#'
+}
+
+hook global BufSetOption filetype=ragel %{
+ set-option buffer comment_line '%%'
+ set-option buffer comment_block_begin '%%{'
+ set-option buffer comment_block_end '}%%'
+}
+
+hook global BufSetOption filetype=ruby %{
+ set-option buffer comment_block_begin '^begin='
+ set-option buffer comment_block_end '^=end'
+}
+
+hook global BufSetOption filetype=sql %{
+ set-option buffer comment_line '--'
+ set-option buffer comment_block_begin '/*'
+ set-option buffer comment_block_end '*/'
+}
+
+define-command comment-block -docstring '(un)comment selections using block comments' %{
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_comment_block_begin}" ] || [ -z "${kak_opt_comment_block_end}" ]; then
+ echo "fail \"The 'comment_block' options are empty, could not comment the selection\""
+ fi
+ }
+ evaluate-commands -save-regs '"/' -draft %{
+ # Keep non-empty selections
+ execute-keys <a-K>\A\s*\z<ret>
+
+ try %{
+ # Assert that the selection has been commented
+ set-register / "\A\Q%opt{comment_block_begin}\E.*\Q%opt{comment_block_end}\E\n*\z"
+ execute-keys "s<ret>"
+ # Uncomment it
+ set-register / "\A\Q%opt{comment_block_begin}\E|\Q%opt{comment_block_end}\E\n*\z"
+ execute-keys s<ret>d
+ } catch %{
+ # Comment the selection
+ set-register '"' "%opt{comment_block_begin}"
+ execute-keys -draft P
+ set-register '"' "%opt{comment_block_end}"
+ execute-keys p
+ }
+ }
+}
+
+define-command comment-line -docstring '(un)comment selected lines using line comments' %{
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_comment_line}" ]; then
+ echo "fail \"The 'comment_line' option is empty, could not comment the line\""
+ fi
+ }
+ evaluate-commands -save-regs '"/' -draft %{
+ # Select the content of the lines, without indentation
+ execute-keys <a-s>gi<a-l>
+
+ try %{
+ # Keep non-empty lines
+ execute-keys <a-K>\A\s*\z<ret>
+ }
+
+ try %{
+ set-register / "\A\Q%opt{comment_line}\E\h?"
+
+ try %{
+ # See if there are any uncommented lines in the selection
+ execute-keys -draft <a-K><ret>
+
+ # There are uncommented lines, so comment everything
+ set-register '"' "%opt{comment_line} "
+ align-selections-left
+ execute-keys P
+ } catch %{
+ # All lines were commented, so uncomment everything
+ execute-keys s<ret>d
+ }
+ }
+ }
+}
+
+define-command align-selections-left -docstring 'extend selections to the left to align with the leftmost selected column' %{
+ evaluate-commands %sh{
+ leftmost_column=$(echo "$kak_selections_desc" | tr ' ' '\n' | cut -d',' -f1 | cut -d'.' -f2 | sort -n | head -n1)
+ aligned_selections=$(echo "$kak_selections_desc" | sed -E "s/\.[0-9]+,/.$leftmost_column,/g")
+ echo "select $aligned_selections"
+ }
+}
diff --git a/autoload/tools/ctags.kak b/autoload/tools/ctags.kak
new file mode 100644
index 0000000..9ad9cbb
--- /dev/null
+++ b/autoload/tools/ctags.kak
@@ -0,0 +1,168 @@
+# Kakoune CTags support script
+#
+# This script requires the readtags command available in universal-ctags
+
+declare-option -docstring "minimum characters before triggering autocomplete" \
+ int ctags_min_chars 3
+
+declare-option -docstring "list of paths to tag files to parse when looking up a symbol" \
+ str-list ctagsfiles 'tags'
+
+declare-option -hidden completions ctags_completions
+
+declare-option -docstring "shell command to run" str readtagscmd "readtags"
+
+define-command -params ..1 \
+ -shell-script-candidates %{
+ realpath() { ( cd "$(dirname "$1")"; printf "%s/%s\n" "$(pwd -P)" "$(basename "$1")" ) }
+ eval "set -- $kak_quoted_opt_ctagsfiles"
+ for candidate in "$@"; do
+ [ -f "$candidate" ] && realpath "$candidate"
+ done | awk '!x[$0]++;' | # remove duplicates
+ while read -r tags; do
+ namecache="${tags%/*}/.kak.${tags##*/}.namecache"
+ if [ -z "$(find "$namecache" -prune -newer "$tags")" ]; then
+ cut -f 1 "$tags" | grep -v '^!' | uniq > "$namecache"
+ fi
+ cat "$namecache"
+ done
+ } \
+ -docstring %{
+ ctags-search [<symbol>]: jump to a symbol's definition
+ If no symbol is passed then the current selection is used as symbol name
+ } \
+ ctags-search %[ require-module menu; evaluate-commands %sh[
+ realpath() { ( cd "$(dirname "$1")"; printf "%s/%s\n" "$(pwd -P)" "$(basename "$1")" ) }
+ export tagname="${1:-${kak_selection}}"
+ eval "set -- $kak_quoted_opt_ctagsfiles"
+ for candidate in "$@"; do
+ [ -f "$candidate" ] && realpath "$candidate"
+ done | awk '!x[$0]++' | # remove duplicates
+ while read -r tags; do
+ printf '!TAGROOT\t%s\n' "$(realpath "${tags%/*}")/"
+ ${kak_opt_readtagscmd} -t "$tags" "$tagname"
+ done | awk -F '\t|\n' '
+ /^!TAGROOT\t/ { tagroot=$2 }
+ /[^\t]+\t[^\t]+\t\/\^.*\$?\// {
+ line = $0; sub(".*\t/\\^", "", line); sub("\\$?/$", "", line);
+ menu_info = line; gsub("!", "!!", menu_info); gsub(/^[\t ]+/, "", menu_info); gsub(/\t/, " ", menu_info);
+ keys = line; gsub(/</, "<lt>", keys); gsub(/\t/, "<c-v><c-i>", keys); gsub("!", "!!", keys); gsub("&", "&&", keys); gsub("#", "##", keys); gsub("\\|", "||", keys); gsub("\\\\/", "/", keys);
+ menu_item = $2; gsub("!", "!!", menu_item);
+ edit_path = path($2); gsub("&", "&&", edit_path); gsub("#", "##", edit_path); gsub("\\|", "||", edit_path);
+ select = $1; gsub(/</, "<lt>", select); gsub(/\t/, "<c-v><c-i>", select); gsub("!", "!!", select); gsub("&", "&&", select); gsub("#", "##", select); gsub("\\|", "||", select);
+ out = out "%!" menu_item ": " menu_info "! %!evaluate-commands %# try %& edit -existing %|" edit_path "|; execute-keys %|/\\Q" keys "<ret>vc| & catch %& fail unable to find tag &; try %& execute-keys %|s\\Q" select "<ret>| & # !"
+ }
+ /[^\t]+\t[^\t]+\t[0-9]+/ {
+ menu_item = $2; gsub("!", "!!", menu_item);
+ select = $1; gsub(/</, "<lt>", select); gsub(/\t/, "<c-v><c-i>", select); gsub("!", "!!", select); gsub("&", "&&", select); gsub("#", "##", select); gsub("\\|", "||", select);
+ menu_info = $3; gsub("!", "!!", menu_info);
+ edit_path = path($2); gsub("!", "!!", edit_path); gsub("#", "##", edit_path); gsub("&", "&&", edit_path); gsub("\\|", "||", edit_path);
+ line_number = $3;
+ out = out "%!" menu_item ": " menu_info "! %!evaluate-commands %# try %& edit -existing %|" edit_path "|; execute-keys %|" line_number "gx| & catch %& fail unable to find tag &; try %& execute-keys %|s\\Q" select "<ret>| & # !"
+ }
+ END { print ( length(out) == 0 ? "fail no such tag " ENVIRON["tagname"] : "menu -markup -auto-single " out ) }
+ # Ensure x is an absolute file path, by prepending with tagroot
+ function path(x) { return x ~/^\// ? x : tagroot x }'
+ ]]
+
+define-command ctags-complete -docstring "Complete the current selection" %{
+ nop %sh{
+ (
+ header="${kak_cursor_line}.${kak_cursor_column}@${kak_timestamp}"
+ compl=$(
+ eval "set -- $kak_quoted_opt_ctagsfiles"
+ for ctagsfile in "$@"; do
+ ${kak_opt_readtagscmd} -p -t "$ctagsfile" ${kak_selection}
+ done | awk '{ uniq[$1]++ } END { for (elem in uniq) printf " %s||%s", elem, elem }'
+ )
+ printf %s\\n "evaluate-commands -client ${kak_client} set-option buffer=${kak_bufname} ctags_completions ${header}${compl}" | \
+ kak -p ${kak_session}
+ ) > /dev/null 2>&1 < /dev/null &
+ }
+}
+
+define-command ctags-funcinfo -docstring "Display ctags information about a selected function" %{
+ evaluate-commands -draft %{
+ try %{
+ execute-keys '[(;B<a-k>[a-zA-Z_]+\(<ret><a-;>'
+ evaluate-commands %sh{
+ f=${kak_selection%?}
+ sig='\tsignature:(.*)'
+ csn='\t(class|struct|namespace):(\S+)'
+ sigs=$(${kak_opt_readtagscmd} -e -Q '(eq? $kind "f")' "${f}" | sed -Ee "s/^.*${csn}.*${sig}$/\3 [\2::${f}]/ ;t ;s/^.*${sig}$/\1 [${f}]/")
+ if [ -n "$sigs" ]; then
+ printf %s\\n "evaluate-commands -client ${kak_client} %{info -anchor $kak_cursor_line.$kak_cursor_column -style above '$sigs'}"
+ fi
+ }
+ }
+ }
+}
+
+define-command ctags-enable-autoinfo -docstring "Automatically display ctags information about function" %{
+ hook window -group ctags-autoinfo NormalIdle .* ctags-funcinfo
+ hook window -group ctags-autoinfo InsertIdle .* ctags-funcinfo
+}
+
+define-command ctags-disable-autoinfo -docstring "Disable automatic ctags information displaying" %{ remove-hooks window ctags-autoinfo }
+
+declare-option -docstring "shell command to run" \
+ str ctagscmd "ctags -R --fields=+S"
+declare-option -docstring "path to the directory in which the tags file will be generated" str ctagspaths "."
+
+define-command ctags-generate -docstring 'Generate tag file asynchronously' %{
+ echo -markup "{Information}launching tag generation in the background"
+ nop %sh{ (
+ trap - INT QUIT
+ while ! mkdir .tags.kaklock 2>/dev/null; do sleep 1; done
+ trap 'rmdir .tags.kaklock' EXIT
+
+ if ${kak_opt_ctagscmd} -f .tags.kaktmp ${kak_opt_ctagspaths}; then
+ mv .tags.kaktmp tags
+ msg="tags generation complete"
+ else
+ msg="tags generation failed"
+ fi
+
+ printf %s\\n "evaluate-commands -client $kak_client echo -markup '{Information}${msg}'" | kak -p ${kak_session}
+ ) > /dev/null 2>&1 < /dev/null & }
+}
+
+define-command ctags-update-tags -docstring 'Update tags for the given file' %{
+ nop %sh{ (
+ trap - INT QUIT
+ while ! mkdir .tags.kaklock 2>/dev/null; do sleep 1; done
+ trap 'rmdir .tags.kaklock' EXIT
+
+ if ${kak_opt_ctagscmd} -f .file_tags.kaktmp $kak_bufname; then
+ export LC_COLLATE=C LC_ALL=C # ensure ASCII sorting order
+ # merge the updated tags tags with the general tags (filtering out out of date tags from it) into the target file
+ grep -Fv "$(printf '\t%s\t' "$kak_bufname")" tags | grep -v '^!' | sort --merge - .file_tags.kaktmp >> .tags.kaktmp
+ rm .file_tags.kaktmp
+ mv .tags.kaktmp tags
+ msg="tags updated for $kak_bufname"
+ else
+ msg="tags update failed for $kak_bufname"
+ fi
+
+ printf %s\\n "evaluate-commands -client $kak_client echo -markup '{Information}${msg}'" | kak -p ${kak_session}
+ ) > /dev/null 2>&1 < /dev/null & }
+}
+
+define-command ctags-enable-autocomplete -docstring "Enable automatic ctags completion" %{
+ set-option window completers "option=ctags_completions" %opt{completers}
+ hook window -group ctags-autocomplete InsertIdle .* %{
+ try %{
+ evaluate-commands -draft %{ # select previous word >= ctags_min_chars
+ execute-keys ",b_<a-k>.{%opt{ctags_min_chars},}<ret>"
+ ctags-complete # run in draft context to preserve selection
+ }
+ }
+ }
+}
+
+define-command ctags-disable-autocomplete -docstring "Disable automatic ctags completion" %{
+ evaluate-commands %sh{
+ printf "set-option window completers %s\n" $(printf %s "${kak_opt_completers}" | sed -e "s/'option=ctags_completions'//g")
+ }
+ remove-hooks window ctags-autocomplete
+}
diff --git a/autoload/tools/doc.asciidoc b/autoload/tools/doc.asciidoc
new file mode 100644
index 0000000..bb2e262
--- /dev/null
+++ b/autoload/tools/doc.asciidoc
@@ -0,0 +1,45 @@
+= Kakoune's online documentation
+
+This is Kakoune's online documentation system.
+
+To see what documentation topics are available, type `:doc` and look at the
+completion menu. To view a particular topic, type its name or select it
+from the completion menu. Then hit Enter.
+
+Documentation will be displayed in the client named in the `docsclient` option.
+
+== Using the documentation browser
+
+Documentation buffers are like any other buffer, so you can scroll through
+them as normal, search within a topic with `/`, etc. However, they can also
+contain links: <<doc#demonstration-target,like this>>. Links can be followed
+by moving the cursor onto them and pressing Enter. If a link takes you to
+a different documentation topic, you can return to the original by using the
+`:buffer` command.
+
+== Writing documentation
+
+Documentation must be in AsciiDoc format, with the extension `.asciidoc`.
+It must be stored somewhere within <<doc#sources,the documentation search
+path>>. Kakoune's built-in documentation renderer does not necessarily
+support every feature, so don't go overboard with formatting.
+
+To create a link to another documentation topic, the URL should be the topic's
+name, just like `:doc` uses. Because topics are identified only by their
+basename, you should take care that your topic's name does not conflict with
+any of the names used either by other plugins or by Kakoune's standard library.
+
+== Sources
+
+The `:doc` command searches within the following locations for
+documents in the AsciiDoc format (`*.asciidoc`):
+
+* The user plugin directory, `"%val{config}/autoload"`
+* The system documentation directory, `"%val{runtime}/doc"`
+* The system plugin directory, `"%val{runtime}/rc"`
+
+It searches recursively, and follows symlinks.
+
+== Demonstration target
+
+Well done! You can <<doc#using-the-documentation-browser,go back now>>!
diff --git a/autoload/tools/doc.kak b/autoload/tools/doc.kak
new file mode 100644
index 0000000..4b6afe3
--- /dev/null
+++ b/autoload/tools/doc.kak
@@ -0,0 +1,195 @@
+declare-option -docstring "name of the client in which documentation is to be displayed" \
+ str docsclient
+
+declare-option -hidden range-specs doc_render_ranges
+declare-option -hidden range-specs doc_links
+declare-option -hidden range-specs doc_anchors
+
+define-command -hidden -params 4 doc-render-regex %{
+ evaluate-commands -draft %{ try %{
+ execute-keys <percent> s %arg{1} <ret>
+ execute-keys -draft s %arg{2} <ret> d
+ execute-keys "%arg{3}"
+ evaluate-commands %sh{
+ face="$4"
+ eval "set -- $kak_quoted_selections_desc"
+ ranges=""
+ for desc in "$@"; do ranges="$ranges '$desc|$face'"; done
+ echo "update-option buffer doc_render_ranges"
+ echo "set-option -add buffer doc_render_ranges $ranges"
+ }
+ } }
+}
+
+define-command -hidden doc-parse-links %{
+ evaluate-commands -draft %{ try %{
+ execute-keys <percent> s <lt><lt>(.*?),.*?<gt><gt> <ret>
+ execute-keys -draft s <lt><lt>.*,|<gt><gt> <ret> d
+ execute-keys H
+ set-option buffer doc_links %val{timestamp}
+ update-option buffer doc_render_ranges
+ evaluate-commands -itersel %{
+ set-option -add buffer doc_links "%val{selection_desc}|%reg{1}"
+ set-option -add buffer doc_render_ranges "%val{selection_desc}|default+u"
+ }
+ } }
+}
+
+define-command -hidden doc-parse-anchors %{
+ evaluate-commands -draft %{ try %{
+ set-option buffer doc_anchors %val{timestamp}
+ # Find sections as add them as imlicit anchors
+ execute-keys <percent> s ^={2,}\h+([^\n]+)$ <ret>
+ evaluate-commands -itersel %{
+ set-option -add buffer doc_anchors "%val{selection_desc}|%sh{printf '%s' ""$kak_main_reg_1"" | tr '[A-Z ]' '[a-z-]'}"
+ }
+
+ # Parse explicit anchors and remove their text
+ execute-keys <percent> s \[\[(.*?)\]\]\s* <ret>
+ evaluate-commands -itersel %{
+ set-option -add buffer doc_anchors "%val{selection_desc}|%reg{1}"
+ }
+ execute-keys d
+ update-option buffer doc_anchors
+ } }
+}
+
+define-command -hidden doc-jump-to-anchor -params 1 %{
+ update-option buffer doc_anchors
+ evaluate-commands %sh{
+ anchor="$1"
+ eval "set -- $kak_quoted_opt_doc_anchors"
+
+ shift
+ for range in "$@"; do
+ if [ "${range#*|}" = "$anchor" ]; then
+ printf '%s\n' "select '${range%|*}'; execute-keys vv"
+ exit
+ fi
+ done
+ printf "fail No such anchor '%s'\n" "${anchor}"
+ }
+}
+
+define-command -hidden doc-follow-link %{
+ update-option buffer doc_links
+ evaluate-commands %sh{
+ eval "set -- $kak_quoted_opt_doc_links"
+ for link in "$@"; do
+ printf '%s\n' "$link"
+ done | awk -v FS='[.,|#]' '
+ BEGIN {
+ l=ENVIRON["kak_cursor_line"];
+ c=ENVIRON["kak_cursor_column"];
+ }
+ l >= $1 && c >= $2 && l <= $3 && c <= $4 {
+ if (NF == 6) {
+ print "doc " $5
+ if ($6 != "") {
+ print "doc-jump-to-anchor %{" $6 "}"
+ }
+ } else {
+ print "doc-jump-to-anchor %{" $5 "}"
+ }
+ exit
+ }
+ '
+ }
+}
+
+define-command -params 1 -hidden doc-render %{
+ edit! -scratch "*doc-%sh{basename $1 .asciidoc}*"
+ execute-keys "!cat '%arg{1}'<ret>gg"
+
+ doc-parse-anchors
+
+ # Join paragraphs together
+ try %{
+ execute-keys -draft '%S\n{2,}|(?<lt>=\+)\n|^[^\n]+::\n|^\h*[*-]\h+<ret>' \
+ <a-K>^\h*-{2,}(\n|\z)<ret> S\n\z<ret> <a-k>\n<ret> <a-j>
+ }
+
+ # Remove some line end markers
+ try %{ execute-keys -draft <percent> s \h*(\+|:{2,})$ <ret> d }
+
+ # Setup the doc_render_ranges option
+ set-option buffer doc_render_ranges %val{timestamp}
+ doc-render-regex \B(?<!\\)\*(?=\S)[^\n]+?(?<=\S)(?<!\\)\*\B \A|.\z 'H' default+b
+ doc-render-regex \b(?<!\\)_(?=\S)[^\n]+?(?<=\S)(?<!\\)_\b \A|.\z 'H' default+i
+ doc-render-regex \B(?<!\\)`(?=\S)[^\n]+?(?<=\S)`\B \A|.\z 'H' mono
+ doc-render-regex ^=\h+[^\n]+ ^=\h+ '~' title
+ doc-render-regex ^={2,}\h+[^\n]+ ^={2,}\h+ '' header
+ doc-render-regex ^\h*-{2,}\n\h*.*?^\h*-{2,}\n ^\h*-{2,}\n '' block
+
+ doc-parse-links
+
+ # Remove escaping of * and `
+ try %{ execute-keys -draft <percent> s \\((?=\*)|(?=`)) <ret> d }
+ # Go to beginning of file
+ execute-keys 'gg'
+
+ set-option buffer readonly true
+ add-highlighter buffer/ ranges doc_render_ranges
+ add-highlighter buffer/ wrap -word -indent
+ map buffer normal <ret> :doc-follow-link<ret>
+}
+
+define-command doc -params 0..2 -docstring %{
+ doc <topic> [<keyword>]: open a buffer containing documentation about a given topic
+ An optional keyword argument can be passed to the function, which will be automatically selected in the documentation
+
+ See `:doc doc` for details.
+ } %{
+ evaluate-commands %sh{
+ topic="doc"
+ if [ $# -ge 1 ]; then
+ topic="$1"
+ fi
+ page=$(
+ find -L \
+ "${kak_config}/autoload/" \
+ "${kak_runtime}/doc/" \
+ "${kak_runtime}/rc/" \
+ -type f -name "$topic.asciidoc" 2>/dev/null |
+ head -1
+ )
+ if [ -f "${page}" ]; then
+ jump_cmd=""
+ if [ $# -eq 2 ]; then
+ jump_cmd="doc-jump-to-anchor '$2'"
+ fi
+ printf %s\\n "evaluate-commands -try-client %opt{docsclient} %{ doc-render ${page}; ${jump_cmd} }"
+ else
+ printf 'fail No such doc file: %s\n' "$topic.asciidoc"
+ fi
+ }
+}
+
+complete-command -menu doc shell-script-candidates %{
+ case "$kak_token_to_complete" in
+ 0)
+ find -L \
+ "${kak_config}/autoload/" \
+ "${kak_runtime}/doc/" \
+ "${kak_runtime}/rc/" \
+ -type f -name "*.asciidoc" 2>/dev/null |
+ sed 's,.*/,,; s/\.[^.]*$//';;
+ 1)
+ page=$(
+ find -L \
+ "${kak_config}/autoload/" \
+ "${kak_runtime}/doc/" \
+ "${kak_runtime}/rc/" \
+ -type f -name "$1.asciidoc" 2>/dev/null |
+ head -1
+ )
+ if [ -f "${page}" ]; then
+ awk '
+ /^==+ +/ { sub(/^==+ +/, ""); print }
+ /^\[\[[^\]]+\]\]/ { sub(/^\[\[/, ""); sub(/\]\].*/, ""); print }
+ ' < $page | tr '[A-Z ]' '[a-z-]'
+ fi;;
+ esac | sort
+}
+
+alias global help doc
diff --git a/autoload/tools/format.kak b/autoload/tools/format.kak
new file mode 100644
index 0000000..2435af0
--- /dev/null
+++ b/autoload/tools/format.kak
@@ -0,0 +1,38 @@
+declare-option -docstring "shell command used for the 'format-selections' and 'format-buffer' commands" \
+ str formatcmd
+
+define-command format-buffer -docstring "Format the contents of the buffer" %{
+ evaluate-commands -draft %{
+ execute-keys '%'
+ format-selections
+ }
+}
+
+define-command format-selections -docstring "Format the selections individually" %{
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_formatcmd}" ]; then
+ echo "fail 'The option ''formatcmd'' must be set'"
+ fi
+ }
+ evaluate-commands -draft -no-hooks -save-regs 'e|' %{
+ set-register e nop
+ set-register '|' %{
+ format_in="$(mktemp "${TMPDIR:-/tmp}"/kak-formatter.XXXXXX)"
+ format_out="$(mktemp "${TMPDIR:-/tmp}"/kak-formatter.XXXXXX)"
+
+ cat > "$format_in"
+ eval "$kak_opt_formatcmd" < "$format_in" > "$format_out"
+ if [ $? -eq 0 ]; then
+ cat "$format_out"
+ else
+ echo "set-register e fail formatter returned an error (exit code $?)" >"$kak_command_fifo"
+ cat "$format_in"
+ fi
+ rm -f "$format_in" "$format_out"
+ }
+ execute-keys '|<ret>'
+ %reg{e}
+ }
+}
+
+alias global format format-buffer
diff --git a/autoload/tools/git.kak b/autoload/tools/git.kak
new file mode 100644
index 0000000..def591d
--- /dev/null
+++ b/autoload/tools/git.kak
@@ -0,0 +1,787 @@
+declare-option -docstring "name of the client in which documentation is to be displayed" \
+ str docsclient
+
+declare-option -docstring "git diff added character" \
+ str git_diff_add_char "▏"
+
+declare-option -docstring "git diff modified character" \
+ str git_diff_mod_char "▏"
+
+declare-option -docstring "git diff deleted character" \
+ str git_diff_del_char "_"
+
+declare-option -docstring "git diff top deleted character" \
+ str git_diff_top_char "‾"
+
+hook -group git-log-highlight global WinSetOption filetype=git-log %{
+ add-highlighter window/git-log group
+ add-highlighter window/git-log/ regex '^([*|\\ /_.-])*' 0:keyword
+ add-highlighter window/git-log/ regex '^( ?[*|\\ /_.-])*\h{,3}(commit )?(\b[0-9a-f]{4,40}\b)' 2:keyword 3:comment
+ add-highlighter window/git-log/ regex '^( ?[*|\\ /_.-])*\h{,3}([a-zA-Z_-]+:) (.*?)$' 2:variable 3:value
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-log }
+}
+
+hook global WinSetOption filetype=diff %{
+ try %{
+ execute-keys -draft %{/^diff --git\b<ret>}
+ evaluate-commands %sh{
+ if [ -n "$(git ls-files -- "${kak_buffile}")" ]; then
+ echo fail
+ fi
+ }
+ set-option buffer filetype git-diff
+ }
+}
+
+hook -group git-diff-highlight global WinSetOption filetype=(git-diff|git-log) %{
+ require-module diff
+ add-highlighter %exp{window/%val{hook_param_capture_1}-ref-diff} ref diff
+ hook -once -always window WinSetOption filetype=.* %exp{
+ remove-highlighter window/%val{hook_param_capture_1}-ref-diff
+ }
+}
+
+hook global WinSetOption filetype=(?:git-diff|git-log) %{
+ map buffer normal <ret> %exp{:git-diff-goto-source # %val{hook_param}<ret>} -docstring 'Jump to source from git diff'
+ hook -once -always window WinSetOption filetype=.* %exp{
+ unmap buffer normal <ret> %%{:git-diff-goto-source # %val{hook_param}<ret>}
+ }
+}
+
+hook -group git-status-highlight global WinSetOption filetype=git-status %{
+ add-highlighter window/git-status group
+ add-highlighter window/git-status/ regex '^## ' 0:comment
+ add-highlighter window/git-status/ regex '^## (\S*[^\s\.@])' 1:green
+ add-highlighter window/git-status/ regex '^## (\S*[^\s\.@])(\.\.+)(\S*[^\s\.@])' 1:green 2:comment 3:red
+ add-highlighter window/git-status/ regex '^(##) (No commits yet on) (\S*[^\s\.@])' 1:comment 2:Default 3:green
+ add-highlighter window/git-status/ regex '^## \S+ \[[^\n]*ahead (\d+)[^\n]*\]' 1:green
+ add-highlighter window/git-status/ regex '^## \S+ \[[^\n]*behind (\d+)[^\n]*\]' 1:red
+ add-highlighter window/git-status/ regex '^(?:([Aa])|([Cc])|([Dd!?])|([MUmu])|([Rr])|([Tt]))[ !\?ACDMRTUacdmrtu]\h' 1:green 2:blue 3:red 4:yellow 5:cyan 6:cyan
+ add-highlighter window/git-status/ regex '^[ !\?ACDMRTUacdmrtu](?:([Aa])|([Cc])|([Dd!?])|([MUmu])|([Rr])|([Tt]))\h' 1:green 2:blue 3:red 4:yellow 5:cyan 6:cyan
+ add-highlighter window/git-status/ regex '^R[ !\?ACDMRTUacdmrtu] [^\n]+( -> )' 1:cyan
+ add-highlighter window/git-status/ regex '^\h+(?:((?:both )?modified:)|(added:|new file:)|(deleted(?: by \w+)?:)|(renamed:)|(copied:))(?:.*?)$' 1:yellow 2:green 3:red 4:cyan 5:blue 6:magenta
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-status }
+}
+
+hook -group git-show-branch-highlight global WinSetOption filetype=git-show-branch %{
+ add-highlighter window/git-show-branch group
+ add-highlighter window/git-show-branch/ regex '(\*)|(\+)|(!)' 1:red 2:green 3:green
+ add-highlighter window/git-show-branch/ regex '(!\D+\{0\}\])|(!\D+\{1\}\])|(!\D+\{2\}\])|(!\D+\{3\}\])' 1:red 2:green 3:yellow 4:blue
+ add-highlighter window/git-show-branch/ regex '(\B\+\D+\{0\}\])|(\B\+\D+\{1\}\])|(\B\+\D+\{2\}\])|(\B\+\D+\{3\}\])|(\B\+\D+\{1\}\^\])' 1:red 2:green 3:yellow 4:blue 5:magenta
+
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/git-show-branch}
+}
+
+declare-option -hidden line-specs git_blame_flags
+declare-option -hidden line-specs git_blame_index
+declare-option -hidden str git_blame
+declare-option -hidden str git_blob
+declare-option -hidden line-specs git_diff_flags
+declare-option -hidden int-list git_hunk_list
+
+define-command -params 1.. \
+ -docstring %{
+ git [<arguments>]: git wrapping helper
+ All the optional arguments are forwarded to the git utility
+ Available commands:
+ add
+ apply - alias for "patch git apply"
+ blame - toggle blame annotations
+ blame-jump - show the commit that added the line at cursor
+ checkout
+ commit
+ diff
+ edit
+ grep
+ hide-diff
+ init
+ log
+ next-hunk
+ prev-hunk
+ reset
+ rm
+ show
+ show-branch
+ show-diff
+ status
+ update-diff
+ } -shell-script-candidates %{
+ if [ $kak_token_to_complete -eq 0 ]; then
+ printf %s\\n \
+ apply \
+ blame \
+ blame-jump \
+ checkout \
+ commit \
+ diff \
+ edit \
+ grep \
+ hide-diff \
+ init \
+ log \
+ next-hunk \
+ prev-hunk \
+ reset \
+ rm \
+ show \
+ show-branch \
+ show-diff \
+ status \
+ update-diff \
+ ;
+ else
+ case "$1" in
+ commit) printf -- "--amend\n--no-edit\n--all\n--reset-author\n--fixup\n--squash\n"; git ls-files -m ;;
+ add) git ls-files -dmo --exclude-standard ;;
+ apply) printf -- "--reverse\n--cached\n--index\n--3way\n" ;;
+ grep|edit) git ls-files -c --recurse-submodules ;;
+ esac
+ fi
+ } \
+ git %{ evaluate-commands %sh{
+ cd_bufdir() {
+ dirname_buffer="${kak_buffile%/*}"
+ cd "${dirname_buffer}" 2>/dev/null || {
+ printf 'fail Unable to change the current working directory to: %s\n' "${dirname_buffer}"
+ exit 1
+ }
+ }
+ kakquote() {
+ printf "%s" "$1" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"
+ }
+
+ show_git_cmd_output() {
+ local filetype
+
+ case "$1" in
+ diff) filetype=git-diff ;;
+ show) filetype=git-log ;;
+ show-branch) filetype=git-show-branch ;;
+ log) filetype=git-log ;;
+ status) filetype=git-status ;;
+ *) return 1 ;;
+ esac
+ output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-git.XXXXXXXX)/fifo
+ mkfifo ${output}
+ ( trap - INT QUIT; git "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
+
+ printf %s "evaluate-commands -try-client '$kak_opt_docsclient' '
+ edit! -fifo ${output} *git*
+ set-option buffer filetype ${filetype}
+ $(hide_blame)
+ set-option buffer git_blob %{}
+ hook -always -once buffer BufCloseFifo .* ''
+ nop %sh{ rm -r $(dirname ${output}) }
+ $(printf %s "${on_close_fifo}" | sed "s/'/''''/g")
+ ''
+ '"
+ }
+
+ hide_blame() {
+ printf %s "
+ set-option buffer git_blame_flags $kak_timestamp
+ set-option buffer git_blame_index $kak_timestamp
+ set-option buffer git_blame %{}
+ try %{ remove-highlighter window/git-blame }
+ unmap window normal <ret> %{:git blame-jump<ret>}
+ "
+ }
+
+ prepare_git_blame_args='
+ if [ -n "${kak_opt_git_blob}" ]; then {
+ contents_fifo=/dev/null
+ set -- "$@" "${kak_opt_git_blob%%:*}" -- "${kak_opt_git_blob#*:}"
+ } else {
+ contents_fifo=$(mktemp -d "${TMPDIR:-/tmp}"/kak-git.XXXXXXXX)/fifo
+ mkfifo ${contents_fifo}
+ echo >${kak_command_fifo} "evaluate-commands -save-regs | %{
+ set-register | %{
+ contents=\$(cat; printf .)
+ ( printf %s \"\${contents%.}\" >${contents_fifo} ) >/dev/null 2>&1 &
+ }
+ execute-keys -client ${kak_client} -draft %{%<a-|><ret>}
+ }"
+ set -- "$@" --contents - -- "${kak_buffile}"
+ } fi
+ '
+
+ blame_toggle() {
+ echo >${kak_command_fifo} "try %{
+ add-highlighter window/git-blame flag-lines Information git_blame_flags
+ echo -to-file ${kak_response_fifo}
+ } catch %{
+ echo -to-file ${kak_response_fifo} 'hide_blame; exit'
+ }"
+ eval $(cat ${kak_response_fifo})
+ if [ -z "${kak_opt_git_blob}" ] && {
+ [ "${kak_opt_filetype}" = git-diff ] || [ "${kak_opt_filetype}" = git-log ]
+ } then {
+ echo 'try %{ remove-highlighter window/git-blame }'
+ printf >${kak_command_fifo} %s '
+ evaluate-commands -client '${kak_client}' -draft %{
+ try %{
+ execute-keys <a-l><semicolon><a-?>^commit<ret><a-semicolon>
+ } catch %{
+ # Missing commit line, assume it is an uncommitted change.
+ execute-keys <a-l><semicolon>Gg<a-semicolon>
+ }
+ require-module diff
+ try %{
+ diff-parse END %{
+ my $line = $file_line;
+ if (not defined $commit) {
+ $commit = "HEAD";
+ $line = $other_file_line;
+ if ($diff_line_text =~ m{^\+}) {
+ print "echo -to-file '${kak_response_fifo}' -quoting shell "
+ . "%{git blame: blame from HEAD does not work on added lines}";
+ exit;
+ }
+ } elsif ($diff_line_text =~ m{^[-]}) {
+ $commit = "$commit~";
+ $line = $other_file_line;
+ }
+ $line = $line or 1;
+ printf "echo -to-file '${kak_response_fifo}' -quoting shell %s %s %d %d",
+ $commit, quote($file), $line, ('${kak_cursor_column}' - 1);
+ }
+ } catch %{
+ echo -to-file '${kak_response_fifo}' -quoting shell -- %val{error}
+ }
+ }
+ '
+ n=$#
+ eval set -- "$(cat ${kak_response_fifo})" "$@"
+ if [ $# -eq $((n+1)) ]; then
+ echo fail -- "$(kakquote "$1")"
+ exit
+ fi
+ commit=$1
+ file=${2#"$PWD/"}
+ cursor_line=$3
+ cursor_column=$4
+ shift 4
+ # Log commit and file name because they are only echoed briefly
+ # and not shown elsewhere (we don't have a :messages buffer).
+ message="Blaming $file as of $(git rev-parse --short $commit)"
+ echo "echo -debug -- $(kakquote "$message")"
+ on_close_fifo="
+ execute-keys -client ${kak_client} ${cursor_line}g<a-h>${cursor_column}lh
+ evaluate-commands -client ${kak_client} %{
+ set-option buffer git_blob $(kakquote "$commit:$file")
+ git blame $(for arg; do kakquote "$arg"; printf " "; done)
+ hook -once window NormalIdle .* %{
+ execute-keys vv
+ echo -markup -- $(kakquote "{Information}{\\}$message. Press <ret> to jump to blamed commit")
+ }
+ }
+ " show_git_cmd_output show "$commit:$file"
+ exit
+ } fi
+ eval "$prepare_git_blame_args"
+ echo 'map window normal <ret> %{:git blame-jump<ret>}'
+ echo 'echo -markup {Information}Press <ret> to jump to blamed commit'
+ (
+ trap - INT QUIT
+ cd_bufdir
+ printf %s "evaluate-commands -client '$kak_client' %{
+ set-option buffer=$kak_bufname git_blame_flags '$kak_timestamp'
+ set-option buffer=$kak_bufname git_blame_index '$kak_timestamp'
+ set-option buffer=$kak_bufname git_blame ''
+ }" | kak -p ${kak_session}
+ if ! stderr=$({ git blame --incremental "$@" <${contents_fifo} | perl -wne '
+ use POSIX qw(strftime);
+ sub quote {
+ my $SQ = "'\''";
+ my $token = shift;
+ $token =~ s/$SQ/$SQ$SQ/g;
+ return "$SQ$token$SQ";
+ }
+ sub send_flags {
+ my $is_last_call = shift;
+ if (not defined $line) {
+ if ($is_last_call) { exit 1; }
+ return;
+ }
+ my $text = substr($sha,0,7) . " " . $dates{$sha} . " " . $authors{$sha};
+ $text =~ s/~/~~/g;
+ for ( my $i = 0; $i < $count; $i++ ) {
+ $flags .= " %~" . ($line+$i) . "|$text~";
+ }
+ $now = time();
+ # Send roughly one update per second, to avoid creating too many kak processes.
+ if (!$is_last_call && defined $last_sent && $now - $last_sent < 1) {
+ return
+ }
+ open CMD, "|-", "kak -p $ENV{kak_session}";
+ print CMD "set-option -add buffer=$ENV{kak_bufname} git_blame_flags $flags;";
+ print CMD "set-option -add buffer=$ENV{kak_bufname} git_blame_index $index;";
+ print CMD "set-option -add buffer=$ENV{kak_bufname} git_blame " . quote $raw_blame;
+ close(CMD);
+ $flags = "";
+ $index = "";
+ $raw_blame = "";
+ $last_sent = $now;
+ }
+ $raw_blame .= $_;
+ chomp;
+ if (m/^([0-9a-f]+) ([0-9]+) ([0-9]+) ([0-9]+)/) {
+ send_flags(0);
+ $sha = $1;
+ $line = $3;
+ $count = $4;
+ for ( my $i = 0; $i < $count; $i++ ) {
+ $index .= " " . ($line+$i) . "|$.,$i";
+ }
+ }
+ if (m/^author /) {
+ $authors{$sha} = substr($_,7);
+ $authors{$sha} = "Not Committed Yet" if $authors{$sha} eq "External file (--contents)";
+ }
+ if (m/^author-time ([0-9]*)/) { $dates{$sha} = strftime("%F %T", localtime $1) }
+ END { send_flags(1); }'
+ } 2>&1); then
+ escape2() { printf %s "$*" | sed "s/'/''''/g"; }
+ echo "evaluate-commands -client ${kak_client} '
+ evaluate-commands -draft %{
+ buffer %{${kak_buffile}}
+ git hide-blame
+ }
+ echo -debug failed to run git blame
+ echo -debug git stderr: <<<
+ echo -debug ''$(escape2 "$stderr")>>>''
+ hook -once buffer NormalIdle .* %{
+ echo -markup %{{Error}failed to run git blame, see *debug* buffer}
+ }
+ '" | kak -p ${kak_session}
+ fi
+ if [ "$contents_fifo" != /dev/null ]; then
+ rm -r $(dirname $contents_fifo)
+ fi
+ ) > /dev/null 2>&1 < /dev/null &
+ }
+
+ run_git_cmd() {
+ if git "${@}" > /dev/null 2>&1; then
+ printf %s "echo -markup '{Information}git $1 succeeded'"
+ else
+ printf 'fail git %s failed\n' "$1"
+ fi
+ }
+
+ update_diff() {
+ (
+ cd_bufdir
+ git --no-pager diff --no-ext-diff -U0 "$kak_buffile" | perl -e '
+ use utf8;
+ $flags = $ENV{"kak_timestamp"};
+ $add_char = $ENV{"kak_opt_git_diff_add_char"};
+ $del_char = $ENV{"kak_opt_git_diff_del_char"};
+ $top_char = $ENV{"kak_opt_git_diff_top_char"};
+ $mod_char = $ENV{"kak_opt_git_diff_mod_char"};
+ foreach $line (<STDIN>) {
+ if ($line =~ /@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?/) {
+ $from_line = $1;
+ $from_count = ($2 eq "" ? 1 : $2);
+ $to_line = $3;
+ $to_count = ($4 eq "" ? 1 : $4);
+
+ if ($from_count == 0 and $to_count > 0) {
+ for $i (0..$to_count - 1) {
+ $line = $to_line + $i;
+ $flags .= " $line|\{green\}$add_char";
+ }
+ }
+ elsif ($from_count > 0 and $to_count == 0) {
+ if ($to_line == 0) {
+ $flags .= " 1|\{red\}$top_char";
+ } else {
+ $flags .= " $to_line|\{red\}$del_char";
+ }
+ }
+ elsif ($from_count > 0 and $from_count == $to_count) {
+ for $i (0..$to_count - 1) {
+ $line = $to_line + $i;
+ $flags .= " $line|\{blue\}$mod_char";
+ }
+ }
+ elsif ($from_count > 0 and $from_count < $to_count) {
+ for $i (0..$from_count - 1) {
+ $line = $to_line + $i;
+ $flags .= " $line|\{blue\}$mod_char";
+ }
+ for $i ($from_count..$to_count - 1) {
+ $line = $to_line + $i;
+ $flags .= " $line|\{green\}$add_char";
+ }
+ }
+ elsif ($to_count > 0 and $from_count > $to_count) {
+ for $i (0..$to_count - 2) {
+ $line = $to_line + $i;
+ $flags .= " $line|\{blue\}$mod_char";
+ }
+ $last = $to_line + $to_count - 1;
+ $flags .= " $last|\{blue+u\}$mod_char";
+ }
+ }
+ }
+ print "set-option buffer git_diff_flags $flags"
+ ' )
+ }
+
+ jump_hunk() {
+ direction=$1
+ set -- ${kak_opt_git_diff_flags}
+ shift
+
+ if [ $# -lt 1 ]; then
+ echo "fail 'no git hunks found, try \":git show-diff\" first'"
+ exit
+ fi
+
+ # Update hunk list if required
+ if [ "$kak_timestamp" != "${kak_opt_git_hunk_list%% *}" ]; then
+ hunks=$kak_timestamp
+
+ prev_line="-1"
+ for line in "$@"; do
+ line="${line%%|*}"
+ if [ "$((line - prev_line))" -gt 1 ]; then
+ hunks="$hunks $line"
+ fi
+ prev_line="$line"
+ done
+ echo "set-option buffer git_hunk_list $hunks"
+ hunks=${hunks#* }
+ else
+ hunks=${kak_opt_git_hunk_list#* }
+ fi
+
+ prev_hunk=""
+ next_hunk=""
+ for hunk in ${hunks}; do
+ if [ "$hunk" -lt "$kak_cursor_line" ]; then
+ prev_hunk=$hunk
+ elif [ "$hunk" -gt "$kak_cursor_line" ]; then
+ next_hunk=$hunk
+ break
+ fi
+ done
+
+ wrapped=false
+ if [ "$direction" = "next" ]; then
+ if [ -z "$next_hunk" ]; then
+ next_hunk=${hunks%% *}
+ wrapped=true
+ fi
+ if [ -n "$next_hunk" ]; then
+ echo "select $next_hunk.1,$next_hunk.1"
+ fi
+ elif [ "$direction" = "prev" ]; then
+ if [ -z "$prev_hunk" ]; then
+ wrapped=true
+ prev_hunk=${hunks##* }
+ fi
+ if [ -n "$prev_hunk" ]; then
+ echo "select $prev_hunk.1,$prev_hunk.1"
+ fi
+ fi
+
+ if [ "$wrapped" = true ]; then
+ echo "echo -markup '{Information}git hunk search wrapped around buffer'"
+ fi
+ }
+
+ commit() {
+ # Handle case where message needs not to be edited
+ if grep -E -q -e "-m|-F|-C|--message=.*|--file=.*|--reuse-message=.*|--no-edit|--fixup.*|--squash.*"; then
+ if git commit "$@" > /dev/null 2>&1; then
+ echo 'echo -markup "{Information}Commit succeeded"'
+ else
+ echo 'fail Commit failed'
+ fi
+ exit
+ fi <<-EOF
+ $@
+ EOF
+
+ # fails, and generate COMMIT_EDITMSG
+ GIT_EDITOR='' EDITOR='' git commit "$@" > /dev/null 2>&1
+ msgfile="$(git rev-parse --git-dir)/COMMIT_EDITMSG"
+ printf %s "edit '$msgfile'
+ hook buffer BufWritePost '.*\Q$msgfile\E' %{ evaluate-commands %sh{
+ if git commit -F '$msgfile' --cleanup=strip $* > /dev/null; then
+ printf %s 'evaluate-commands -client $kak_client echo -markup %{{Information}Commit succeeded}; delete-buffer'
+ else
+ printf 'evaluate-commands -client %s fail Commit failed\n' "$kak_client"
+ fi
+ } }"
+ }
+
+ blame_jump() {
+ echo >${kak_command_fifo} "echo -to-file ${kak_response_fifo} -- %opt{git_blame}"
+ blame_info=$(cat < ${kak_response_fifo})
+ blame_index=
+ cursor_column=${kak_cursor_column}
+ cursor_line=${kak_cursor_line}
+ if [ -n "$blame_info" ]; then {
+ echo >${kak_command_fifo} "
+ update-option buffer git_blame_index
+ echo -to-file ${kak_response_fifo} -- %opt{git_blame_index}
+ "
+ blame_index=$(cat < ${kak_response_fifo})
+ } elif [ "${kak_opt_filetype}" = git-diff ] || [ "${kak_opt_filetype}" = git-log ]; then {
+ printf >${kak_command_fifo} %s '
+ evaluate-commands -draft %{
+ try %{
+ execute-keys <a-l><semicolon><a-?>^commit<ret><a-semicolon>
+ } catch %{
+ # Missing commit line, assume it is an uncommitted change.
+ execute-keys <a-l><semicolon><a-?>\A<ret><a-semicolon>
+ }
+ require-module diff
+ try %{
+ diff-parse BEGIN %{
+ $version = "-";
+ } END %{
+ if ($diff_line_text !~ m{^[ -]}) {
+ print "set-register e fail git blame-jump: recursive blame only works on context or deleted lines";
+ } else {
+ if (not defined $commit) {
+ $commit = "HEAD";
+ } else {
+ $commit = "$commit~" if $diff_line_text =~ m{^[- ]};
+ }
+ printf "echo -to-file '${kak_response_fifo}' -quoting shell %s %s %d %d",
+ $commit, quote($file), $file_line, ('$cursor_column' - 1);
+ }
+ }
+ } catch %{
+ echo -to-file '${kak_response_fifo}' -quoting shell -- %val{error}
+ }
+ }
+ '
+ eval set -- "$(cat ${kak_response_fifo})"
+ if [ $# -eq 1 ]; then
+ echo fail -- "$(kakquote "$1")"
+ exit
+ fi
+ starting_commit=$1
+ file=$2
+ cursor_line=$3
+ cursor_column=$4
+ blame_info=$(git blame --porcelain "$starting_commit" -L"$cursor_line,$cursor_line" -- "$file")
+ if [ $? -ne 0 ]; then
+ echo 'echo -markup %{{Error}failed to run git blame, see *debug* buffer}'
+ exit
+ fi
+ } else {
+ set --
+ eval "$prepare_git_blame_args"
+ blame_info=$(git blame --porcelain -L"$cursor_line,$cursor_line" "$@" <${contents_fifo})
+ status=$?
+ if [ "$contents_fifo" != /dev/null ]; then
+ rm -r $(dirname $contents_fifo)
+ fi
+ if [ $status -ne 0 ]; then
+ echo 'echo -markup %{{Error}failed to run git blame, see *debug* buffer}'
+ exit
+ fi
+ } fi
+ eval "$(printf '%s\n---\n%s' "$blame_index" "$blame_info" |
+ client=${kak_opt_docsclient:-$kak_client} \
+ cursor_line=$cursor_line cursor_column=$cursor_column \
+ perl -wne '
+ BEGIN {
+ use POSIX qw(strftime);
+ our $SQ = "'\''";
+ sub escape {
+ return shift =~ s/$SQ/$SQ$SQ/gr
+ }
+ sub quote {
+ my $token = escape shift;
+ return "$SQ$token$SQ";
+ }
+ sub shellquote {
+ my $token = shift;
+ $token =~ s/$SQ/$SQ\\$SQ$SQ/g;
+ return "$SQ$token$SQ";
+ }
+ sub perlquote {
+ my $token = shift;
+ $token =~ s/\\/\\\\/g;
+ $token =~ s/$SQ/\\$SQ/g;
+ return "$SQ$token$SQ";
+ }
+ $target = $ENV{"cursor_line"};
+ $state = "index";
+ }
+ chomp;
+ if ($state eq "index") {
+ if ($_ eq "---") {
+ $state = "blame";
+ next;
+ }
+ @blame_index = split;
+ next unless @blame_index;
+ shift @blame_index;
+ foreach (@blame_index) {
+ $_ =~ m{(\d+)\|(\d+),(\d+)} or die "bad blame index flag: $_";
+ my $buffer_line = $1;
+ if ($buffer_line == $target) {
+ $target_in_blame = $2;
+ $target_offset = $3;
+ last;
+ }
+ }
+ defined $target_in_blame and next, or last;
+ }
+ if (m/^([0-9a-f]+) ([0-9]+) ([0-9]+) ([0-9]+)/) {
+ if ($done) {
+ last;
+ }
+ $sha = $1;
+ $old_line = $2;
+ $new_line = $3;
+ $count = $4;
+ if (defined $target_in_blame) {
+ if ($target_in_blame == $. - 2) {
+ $old_line += $target_offset;
+ $done = 1;
+ }
+ } else {
+ if ($new_line <= $target and $target < $new_line + $count) {
+ $old_line += $target - $new_line;
+ $done = 1;
+ }
+ }
+ }
+ if (m/^filename /) { $old_filenames{$sha} = substr($_,9) }
+ if (m/^author /) { $authors{$sha} = substr($_,7) }
+ if (m/^author-time ([0-9]*)/) { $dates{$sha} = strftime("%F", localtime $1) }
+ if (m/^summary /) { $summaries{$sha} = substr($_,8) }
+ END {
+ if (@blame_index and not defined $target_in_blame) {
+ print "echo fail git blame-jump: line has no blame information;";
+ exit;
+ }
+ if (not defined $sha) {
+ print "echo fail git blame-jump: missing blame info";
+ exit;
+ }
+ if (not $done) {
+ print "echo \"fail git blame-jump: line not found in annotations (blame still loading?)\"";
+ exit;
+ }
+ $info = "{Information}{\\}";
+ if ($sha =~ m{^0+$}) {
+ $old_filename = $ENV{"kak_buffile"};
+ $old_filename = substr $old_filename, length($ENV{"PWD"}) + 1;
+ $show_diff = "diff HEAD";
+ $info .= "Not committed yet";
+ } else {
+ $old_filename = $old_filenames{$sha};
+ $author = $authors{$sha};
+ $date = $dates{$sha};
+ $summary = $summaries{$sha};
+ $show_diff = "show $sha";
+ $info .= "$date $author \"$summary\"";
+ }
+ $on_close_fifo = "
+ evaluate-commands -draft $SQ
+ execute-keys <percent>
+ require-module diff
+ diff-parse BEGIN %{
+ \$in_file = " . escape(perlquote($old_filename)) . ";
+ \$in_file_line = $old_line;
+ } END $SQ$SQ
+ print \"execute-keys -client $ENV{client} \${diff_line}g<a-h>$ENV{cursor_column}l;\";
+ printf \"evaluate-commands -client $ENV{client} $SQ$SQ$SQ$SQ
+ hook -once window NormalIdle .* $SQ$SQ$SQ$SQ$SQ$SQ$SQ$SQ
+ execute-keys vv
+ echo -markup -- %s
+ $SQ$SQ$SQ$SQ$SQ$SQ$SQ$SQ
+ $SQ$SQ$SQ$SQ ;\"," . escape(escape(perlquote(escape(escape(quote($info)))))) . ";
+ $SQ$SQ
+ $SQ
+ ";
+ printf "on_close_fifo=%s show_git_cmd_output %s",
+ shellquote($on_close_fifo), $show_diff;
+ }
+ ')"
+ }
+
+ case "$1" in
+ apply)
+ shift
+ enquoted="$(printf '"%s" ' "$@")"
+ echo "require-module patch"
+ echo "patch git apply $enquoted"
+ ;;
+ show|show-branch|log|diff|status)
+ show_git_cmd_output "$@"
+ ;;
+ blame)
+ shift
+ blame_toggle "$@"
+ ;;
+ blame-jump)
+ blame_jump
+ ;;
+ hide-blame)
+ hide_blame
+ ;;
+ show-diff)
+ echo 'try %{ add-highlighter window/git-diff flag-lines Default git_diff_flags }'
+ update_diff
+ ;;
+ hide-diff)
+ echo 'try %{ remove-highlighter window/git-diff }'
+ ;;
+ update-diff) update_diff ;;
+ next-hunk) jump_hunk next ;;
+ prev-hunk) jump_hunk prev ;;
+ commit)
+ shift
+ commit "$@"
+ ;;
+ init)
+ shift
+ git init "$@" > /dev/null 2>&1
+ ;;
+ add|rm)
+ cmd="$1"
+ shift
+ run_git_cmd $cmd "${@:-"${kak_buffile}"}"
+ ;;
+ reset|checkout)
+ run_git_cmd "$@"
+ ;;
+ grep)
+ shift
+ enquoted="$(printf '"%s" ' "$@")"
+ printf %s "try %{
+ set-option current grepcmd 'git grep -n --column'
+ grep $enquoted
+ set-option current grepcmd '$kak_opt_grepcmd'
+ }"
+ ;;
+ edit)
+ shift
+ enquoted="$(printf '"%s" ' "$@")"
+ printf %s "edit -existing -- $enquoted"
+ ;;
+ *)
+ printf "fail unknown git command '%s'\n" "$1"
+ exit
+ ;;
+ esac
+}}
+
+# Works within :git diff and :git show
+define-command git-diff-goto-source \
+ -docstring 'Navigate to source by pressing the enter key in hunks when git diff is displayed. Works within :git diff and :git show' %{
+ require-module diff
+ diff-jump %sh{ git rev-parse --show-toplevel }
+}
diff --git a/autoload/tools/go/gopls.kak b/autoload/tools/go/gopls.kak
new file mode 100644
index 0000000..1e295ef
--- /dev/null
+++ b/autoload/tools/go/gopls.kak
@@ -0,0 +1,98 @@
+# gopls.kak: gopls bindings for kakoune
+
+define-command -params 1 -docstring %{
+gopls <command>: gopls command wrapper
+
+All commands are forwarded to gopls utility
+Available commands are:
+ format
+ imports
+ definition
+ references
+} -shell-script-candidates %{
+ printf "format\nimports\ndefinition\nreferences\n"
+} \
+gopls %{
+ require-module gopls
+ evaluate-commands %sh{
+ case "$1" in
+ format|imports)
+ printf %s\\n "gopls-cmd $1"
+ ;;
+ definition)
+ printf %s\\n "gopls-def"
+ ;;
+ references)
+ printf %s\\n "gopls-ref"
+ ;;
+ *)
+ printf "fail Unknown gopls command '%s'\n" "$1"
+ exit
+ ;;
+ esac
+ }
+}
+
+provide-module gopls %§
+
+evaluate-commands %sh{
+ if ! command -v gopls > /dev/null 2>&1; then
+ echo "fail Please install gopls or add to PATH!"
+ fi
+}
+
+# Temp dir preparation
+declare-option -hidden str gopls_tmp_dir
+define-command -hidden -params 0 gopls-prepare %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-gopls.XXXXXXXX)
+ printf %s\\n "set-option buffer gopls_tmp_dir ${dir}"
+ }
+}
+
+# gopls format/imports
+define-command -hidden -params 1 gopls-cmd %{
+ gopls-prepare
+ evaluate-commands %sh{
+ dir=${kak_opt_gopls_tmp_dir}
+ gopls "$1" -w "${kak_buffile}" 2> "${dir}/stderr"
+ if [ $? -ne 0 ]; then
+ # show error messages in *debug* buffer
+ printf %s\\n "echo -debug %file{${dir}/stderr}"
+ fi
+ }
+ edit!
+ nop %sh{ rm -rf "${kak_opt_gopls_tmp_dir}" }
+}
+
+# gopls definition
+define-command -hidden -params 0 gopls-def %{
+ evaluate-commands %sh{
+ jump=$( gopls definition "${kak_buffile}:${kak_cursor_line}:${kak_cursor_column}" 2> /dev/null \
+ |sed -e 's/-[0-9]\+:.*//; s/:/ /g; q' )
+ if [ -n "${jump}" ]; then
+ printf %s\\n "evaluate-commands -try-client '${kak_opt_jumpclient}' %{
+ edit ${jump}
+ }"
+ fi
+ }
+}
+
+# gopls references
+define-command -hidden -params 0 gopls-ref %{
+ gopls-prepare
+ evaluate-commands %sh{
+ dir=${kak_opt_gopls_tmp_dir}
+ mkfifo "${dir}/fifo"
+ ( { trap - INT QUIT; gopls references "${kak_buffile}:${kak_cursor_line}:${kak_cursor_column}"
+ } > "${dir}/fifo" 2> /dev/null & ) > /dev/null 2>&1 < /dev/null
+ # using filetype=grep for nice hilight and <ret> mapping
+ printf %s\\n "evaluate-commands -try-client '${kak_opt_toolsclient}' %{
+ edit! -fifo '${dir}/fifo' *gopls-refs*
+ set-option buffer filetype grep
+ hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -r '${dir}' } }
+ }"
+ }
+}
+
diff --git a/autoload/tools/grep.kak b/autoload/tools/grep.kak
new file mode 100644
index 0000000..13b5b9c
--- /dev/null
+++ b/autoload/tools/grep.kak
@@ -0,0 +1,66 @@
+declare-option -docstring "shell command run to search for subtext in a file/directory" \
+ str grepcmd 'grep -RHn'
+
+provide-module grep %{
+
+require-module jump
+
+define-command -params .. -docstring %{
+ grep [<arguments>]: grep utility wrapper
+ All optional arguments are forwarded to the grep utility
+ Passing no argument will perform a literal-string grep for the current selection
+} grep %{ evaluate-commands %sh{
+ if [ $# -eq 0 ]; then
+ case "$kak_opt_grepcmd" in
+ ag\ * | git\ grep\ * | grep\ * | rg\ * | ripgrep\ * | ugrep\ * | ug\ *)
+ set -- -F "${kak_selection}"
+ ;;
+ ack\ *)
+ set -- -Q "${kak_selection}"
+ ;;
+ *)
+ set -- "${kak_selection}"
+ ;;
+ esac
+ fi
+
+ output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-grep.XXXXXXXX)/fifo
+ mkfifo ${output}
+ ( { trap - INT QUIT; ${kak_opt_grepcmd} "$@" 2>&1 | tr -d '\r'; } > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
+
+ printf %s\\n "evaluate-commands -try-client '$kak_opt_toolsclient' %{
+ edit! -fifo ${output} *grep*
+ set-option buffer filetype grep
+ set-option buffer jump_current_line 0
+ hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -r $(dirname ${output}) } }
+ }"
+}}
+complete-command grep file
+
+hook -group grep-highlight global WinSetOption filetype=grep %{
+ add-highlighter window/grep group
+ add-highlighter window/grep/ regex "^([^:\n]+):(\d+):(\d+)?" 1:cyan 2:green 3:green
+ add-highlighter window/grep/ line %{%opt{jump_current_line}} default+b
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/grep }
+}
+
+hook global WinSetOption filetype=grep %{
+ hook buffer -group grep-hooks NormalKey <ret> jump
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks buffer grep-hooks }
+}
+
+define-command -hidden grep-jump %{
+ jump
+}
+
+define-command grep-next-match -docstring %{alias for "jump-next *grep*"} %{
+ jump-next -matching \*grep(-.*)?\*
+}
+
+define-command grep-previous-match -docstring %{alias for "jump-previous *grep*"} %{
+ jump-previous -matching \*grep(-.*)?\*
+}
+
+}
+
+hook -once global KakBegin .* %{ require-module grep }
diff --git a/autoload/tools/jump.kak b/autoload/tools/jump.kak
new file mode 100644
index 0000000..60d777d
--- /dev/null
+++ b/autoload/tools/jump.kak
@@ -0,0 +1,70 @@
+declare-option -docstring "name of the client in which all source code jumps will be executed" \
+ str jumpclient
+declare-option -docstring "name of the client in which utilities display information" \
+ str toolsclient
+
+provide-module jump %{
+
+declare-option -hidden int jump_current_line 0
+
+define-command -hidden jump %{
+ evaluate-commands -save-regs a %{ # use evaluate-commands to ensure jumps are collapsed
+ try %{
+ evaluate-commands -draft %{
+ execute-keys ',xs^([^:\n]+):(\d+):(\d+)?<ret>'
+ set-register a %reg{1} %reg{2} %reg{3}
+ }
+ set-option buffer jump_current_line %val{cursor_line}
+ evaluate-commands -try-client %opt{jumpclient} -verbatim -- edit -existing -- %reg{a}
+ try %{ focus %opt{jumpclient} }
+ }
+ }
+}
+
+define-command jump-next -params 1.. -docstring %{
+ jump-next <bufname>: jump to next location listed in the given *grep*-like location list buffer.
+} %{
+ evaluate-commands -try-client %opt{jumpclient} -save-regs / %{
+ buffer %arg{@}
+ jump-select-next
+ jump
+ }
+ try %{
+ evaluate-commands -client %opt{toolsclient} %{
+ buffer %arg{@}
+ execute-keys gg %opt{jump_current_line}g
+ }
+ }
+}
+complete-command jump-next buffer
+define-command -hidden jump-select-next %{
+ # First jump to end of buffer so that if jump_current_line == 0
+ # 0g<a-l> will be a no-op and we'll jump to the first result.
+ # Yeah, thats ugly...
+ execute-keys ge %opt{jump_current_line}g<a-l> /^[^:\n]+:\d+:<ret>
+}
+
+define-command jump-previous -params 1.. -docstring %{
+ jump-previous <bufname>: jump to previous location listed in the given *grep*-like location list buffer.
+} %{
+ evaluate-commands -try-client %opt{jumpclient} -save-regs / %{
+ buffer %arg{@}
+ jump-select-previous
+ jump
+ }
+ try %{
+ evaluate-commands -client %opt{toolsclient} %{
+ buffer %arg{@}
+ execute-keys gg %opt{jump_current_line}g
+ }
+ }
+}
+complete-command jump-previous buffer
+define-command -hidden jump-select-previous %{
+ # See comment in jump-select-next
+ execute-keys ge %opt{jump_current_line}g<a-h> <a-/>^[^:\n]+:\d+:<ret>
+}
+
+}
+
+hook -once global KakBegin .* %{ require-module jump }
diff --git a/autoload/tools/lint.asciidoc b/autoload/tools/lint.asciidoc
new file mode 100644
index 0000000..469b1e5
--- /dev/null
+++ b/autoload/tools/lint.asciidoc
@@ -0,0 +1,26 @@
+= Integrate with tools that check files for problems.
+
+Many file-formats have "lint" tools that check for common problems and point out
+where they occur. Most of these tools produce output in the traditional message
+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.
+
+The `:lint-buffer` and `:lint-selections` commands will run the shell command
+specified in the `lintcmd` option, passing it the path to a temporary file
+containing the text to be linted. The results are collected in the
+`*lint-output*` buffer, and analyze it. If `toolsclient` is set, the
+`*lint-output*` buffer will be displayed in the named client.
+
+Each reported error or warning causes a marker to appear in the left-hand
+margin of the buffer that was checked. When the main cursor moves onto that
+line, the associated messages are displayed. If they get distracting, you can
+turn off the markers and messages with the `:lint-hide-diagnostics` command.
+
+You can also use `:lint-next-message` and `:lint-previous-message` to jump
+between the lines with messages.
diff --git a/autoload/tools/lint.kak b/autoload/tools/lint.kak
new file mode 100644
index 0000000..471edc9
--- /dev/null
+++ b/autoload/tools/lint.kak
@@ -0,0 +1,452 @@
+# require-module jump
+
+declare-option \
+ -docstring %{
+ The shell command used by lint-buffer and lint-selections.
+
+ See `:doc lint` for details.
+ } \
+ str lintcmd
+
+declare-option -hidden line-specs lint_flags
+declare-option -hidden line-specs lint_messages
+declare-option -hidden int lint_error_count
+declare-option -hidden int lint_warning_count
+
+define-command -hidden -params 1 lint-open-output-buffer %{
+ evaluate-commands -try-client %opt{toolsclient} %{
+ edit! -fifo "%arg{1}/fifo" -debug *lint-output*
+ set-option buffer filetype make
+ set-option buffer jump_current_line 0
+ }
+}
+
+define-command \
+ -hidden \
+ -params 1 \
+ -docstring %{
+ lint-cleaned-selections <linter>: Check each selection with <linter>.
+
+ Assumes selections all have anchor before cursor, and that
+ %val{selections} and %val{selections_desc} are in the same order.
+ } \
+ lint-cleaned-selections \
+%{
+ # Create a temporary directory to keep all our state.
+ evaluate-commands %sh{
+ # This is going to come in handy later.
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ # Before we clobber our arguments,
+ # let's record the lintcmd we were given.
+ lintcmd="$1"
+
+ # Some linters care about the name or extension
+ # of the file being linted, so we'll store the text we want to lint
+ # in a file with the same name as the original buffer.
+ filename="${kak_buffile##*/}"
+
+ # A directory to keep all our temporary data.
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-lint.XXXXXXXX)
+
+ # Write all the selection descriptions to files.
+ eval set -- "$kak_selections_desc"
+ i=0
+ for desc; do
+ mkdir -p "$dir"/sel-"$i"
+ printf "%s" "$desc" > "$dir"/sel-$i/desc
+ i=$(( i + 1 ))
+ done
+
+ # Write all the selection contents to files.
+ eval set -- "$kak_quoted_selections"
+ i=0
+ for text; do
+ # The selection text needs to be stored in a subdirectory,
+ # so we can be sure the filename won't clash with one of ours.
+ mkdir -p "$dir"/sel-"$i"/text/
+ printf "%s" "$text" > "$dir"/sel-$i/text/"$filename"
+ i=$(( i + 1 ))
+ done
+
+ # We do redirection trickiness to record stderr from
+ # this background task and route it back to Kakoune,
+ # but shellcheck isn't a fan.
+ # shellcheck disable=SC2094
+ ({ # do the parsing in the background and when ready send to the session
+ trap - INT QUIT
+
+ for selpath in "$dir"/sel-*; do
+ # Read in the line and column offset of this selection.
+ IFS=".," read -r start_line start_byte _ < "$selpath"/desc
+
+ # Run the linter, and record the exit-code.
+ eval "$lintcmd '$selpath/text/$filename'" |
+ sort -t: -k2,2 -n |
+ awk \
+ -v line_offset=$(( start_line - 1 )) \
+ -v first_line_byte_offset=$(( start_byte - 1 )) \
+ '
+ BEGIN { OFS=":"; FS=":" }
+
+ /:[1-9][0-9]*:[1-9][0-9]*:/ {
+ $1 = ENVIRON["kak_bufname"]
+ if ( $2 == 1 ) {
+ $3 += first_line_byte_offset
+ }
+ $2 += line_offset
+ print $0
+ }
+ ' >>"$dir"/result
+ done
+
+ # Load all the linter messages into Kakoune options.
+ # Inside this block, shellcheck warns us that the shell doesn't
+ # need backslash-continuation chars in a single-quoted string,
+ # but awk still needs them.
+ # shellcheck disable=SC1004
+ awk -v file="$kak_buffile" -v stamp="$kak_timestamp" -v client="$kak_client" '
+ function kakquote(text) {
+ # \x27 is apostrophe, escaped for shell-quoting reasons.
+ gsub(/\x27/, "\x27\x27", text)
+ return "\x27" text "\x27"
+ }
+
+ BEGIN {
+ OFS=":"
+ FS=":"
+ error_count = 0
+ warning_count = 0
+ }
+
+ /:[1-9][0-9]*:[1-9][0-9]*:/ {
+ # Remember that an error or a warning occurs on this line..
+ if ($4 ~ /[Ee]rror/) {
+ # We definitely have an error on this line.
+ flags_by_line[$2] = "{Error}x"
+ error_count++
+ } else if (flags_by_line[$2] ~ /Error/) {
+ # We have a warning on this line,
+ # but we already have an error, so do nothing.
+ warning_count++
+ } else {
+ # We have a warning on this line,
+ # and no previous error.
+ flags_by_line[$2] = "{Information}!"
+ warning_count++
+ }
+
+ # The message starts with the severity indicator.
+ msg = substr($4, 2)
+
+ # fix case where $5 is not the last field
+ # because of extra colons in the message
+ for (i=5; i<=NF; i++) msg = msg ":" $i
+
+ # Mention the column where this problem occurs,
+ # so that information is not lost.
+ msg = msg "(col " $3 ")"
+
+ # Messages will be stored in a line-specs option,
+ # and each record in the option uses "|"
+ # as a field delimiter, so we need to escape them.
+ gsub(/\|/, "\\|", msg)
+
+ if ($2 in messages_by_line) {
+ # We already have a message on this line,
+ # so append our new message.
+ messages_by_line[$2] = messages_by_line[$2] "\n" msg
+ } else {
+ # A brand-new message on this line.
+ messages_by_line[$2] = msg
+ }
+ }
+
+ END {
+ printf("set-option %s lint_flags %s", kakquote("buffer=" file), stamp);
+ for (line in flags_by_line) {
+ flag = flags_by_line[line]
+ printf(" %s", kakquote(line "|" flag));
+ }
+ printf("\n");
+
+ printf("set-option %s lint_messages %s", kakquote("buffer=" file), stamp);
+ for (line in messages_by_line) {
+ msg = messages_by_line[line]
+ printf(" %s", kakquote(line "|" msg));
+ }
+ printf("\n");
+
+ print "set-option " \
+ kakquote("buffer=" file) " " \
+ "lint_error_count " \
+ error_count
+ print "set-option " \
+ kakquote("buffer=" file) " " \
+ "lint_warning_count " \
+ warning_count
+ }
+ ' "$dir"/result | kak -p "$kak_session"
+
+ # Send any linting errors to the debug buffer,
+ # for visibility.
+ if [ -s "$dir"/stderr ]; then
+ # Errors were detected!"
+ printf "echo -debug Linter errors: <<<\n"
+ while read -r LINE; do
+ printf "echo -debug %s\n" "$(kakquote " $LINE")"
+ done < "$dir"/stderr
+ printf "echo -debug >>>\n"
+ # FIXME: When #3254 is fixed, this can become a "fail"
+ printf "eval -client %s echo -markup {Error}%s\n" \
+ "$kak_client" \
+ "lint failed, see *debug* for details"
+ else
+ # No errors detected, show the results.
+ printf "eval -client %s 'lint-show-diagnostics; lint-show-counters'" \
+ "$kak_client"
+ fi | kak -p "$kak_session"
+
+ # A fifo to send the results back to a Kakoune buffer.
+ mkfifo "$dir"/fifo
+ # Send the results to kakoune if the session is still valid.
+ if printf 'lint-open-output-buffer %s' "$(kakquote "$dir")" | kak -p "$kak_session"; then
+ cat "$dir"/result > "$dir"/fifo
+ fi
+ # Clean up.
+ rm -rf "$dir"
+
+ } & ) >"$dir"/stderr 2>&1 </dev/null
+ }
+}
+
+define-command \
+ -params 0..2 \
+ -docstring %{
+ lint-selections [<switches>]: Check each selection with a linter.
+
+ Switches:
+ -command <cmd> Use the given linter.
+ If not given, the lintcmd option is used.
+
+ See `:doc lint` for details.
+ } \
+ lint-selections \
+%{
+ evaluate-commands -draft %{
+ # Make sure all the selections are "forward" (anchor before cursor)
+ execute-keys <a-:>
+
+ # Make sure the selections are in document order.
+ evaluate-commands %sh{
+ printf "select "
+ printf "%s\n" "$kak_selections_desc" |
+ tr ' ' '\n' |
+ sort -n -t. |
+ tr '\n' ' '
+ }
+
+ evaluate-commands %sh{
+ # This is going to come in handy later.
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ if [ "$1" = "-command" ]; then
+ if [ -z "$2" ]; then
+ echo 'fail -- -command option requires a value'
+ exit 1
+ fi
+ lintcmd="$2"
+ elif [ -n "$1" ]; then
+ echo "fail -- Unrecognised parameter $(kakquote "$1")"
+ exit 1
+ elif [ -z "${kak_opt_lintcmd}" ]; then
+ echo 'fail The lintcmd option is not set'
+ exit 1
+ else
+ lintcmd="$kak_opt_lintcmd"
+ fi
+
+ printf 'lint-cleaned-selections %s\n' "$(kakquote "$lintcmd")"
+ }
+ }
+}
+
+define-command \
+ -docstring %{
+ lint-buffer: Check the current buffer with a linter.
+
+ See `:doc lint` for details.
+ } \
+ lint-buffer \
+%{
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_lintcmd}" ]; then
+ echo 'fail The lintcmd option is not set'
+ exit 1
+ fi
+ }
+ evaluate-commands -draft %{
+ execute-keys '%'
+ lint-cleaned-selections %opt{lintcmd}
+ }
+}
+
+alias global lint lint-buffer
+
+define-command -hidden lint-show-current-line %{
+ update-option buffer lint_messages
+ evaluate-commands %sh{
+ # This is going to come in handy later.
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ eval set -- "${kak_quoted_opt_lint_messages}"
+ shift # skip the timestamp
+
+ while [ $# -gt 0 ]; do
+ lineno=${1%%|*}
+ msg=${1#*|}
+
+ if [ "$lineno" -eq "$kak_cursor_line" ]; then
+ printf "info -anchor %d.%d %s\n" \
+ "$kak_cursor_line" \
+ "$kak_cursor_column" \
+ "$(kakquote "$msg")"
+ break
+ fi
+ shift
+ done
+ }
+}
+
+define-command -hidden lint-show-counters %{
+ echo -markup "linting results: {Error} %opt{lint_error_count} error(s) {Information} %opt{lint_warning_count} warning(s) "
+}
+
+define-command -hidden lint-show-diagnostics %{
+ try %{
+ # Assume that if the highlighter is set, then hooks also are
+ add-highlighter window/lint flag-lines default lint_flags
+ hook window -group lint-diagnostics NormalIdle .* %{ lint-show-current-line }
+ hook window -group lint-diagnostics WinSetOption lint_flags=.* %{ info; lint-show-current-line }
+ }
+}
+
+define-command lint-hide-diagnostics -docstring "Hide line markers and disable automatic diagnostic displaying" %{
+ remove-highlighter window/lint
+ remove-hooks window lint-diagnostics
+}
+
+# FIXME: Is there some way we can re-use make-next-error
+# instead of re-implementing it?
+define-command \
+ -docstring "Jump to the next line that contains a lint message" \
+ lint-next-message \
+%{
+ update-option buffer lint_messages
+
+ evaluate-commands %sh{
+ # This is going to come in handy later.
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ eval "set -- ${kak_quoted_opt_lint_messages}"
+ shift
+
+ if [ "$#" -eq 0 ]; then
+ printf 'fail no lint messages'
+ exit
+ fi
+
+ first_lineno=""
+ first_msg=""
+
+ for lint_message; do
+ lineno="${lint_message%%|*}"
+ msg="${lint_message#*|}"
+
+ if [ -z "$first_lineno" ]; then
+ first_lineno=$lineno
+ first_msg=$msg
+ fi
+
+ if [ "$lineno" -gt "$kak_cursor_line" ]; then
+ printf "execute-keys %dg\n" "$lineno"
+ printf "info -anchor %d.%d %s\n" \
+ "$lineno" "1" "$(kakquote "$msg")"
+ exit
+ fi
+ done
+
+ # We didn't find any messages after the current line,
+ # let's wrap around to the beginning.
+ printf "execute-keys %dg\n" "$first_lineno"
+ printf "info -anchor %d.%d %s\n" \
+ "$first_lineno" "1" "$(kakquote "$first_msg")"
+ printf "echo -markup \
+ {Information}lint message search wrapped around buffer\n"
+
+ }
+}
+
+# FIXME: Is there some way we can re-use make-previous-error
+# instead of re-implementing it?
+define-command \
+ -docstring "Jump to the previous line that contains a lint message" \
+ lint-previous-message \
+%{
+ update-option buffer lint_messages
+
+ evaluate-commands %sh{
+ # This is going to come in handy later.
+ kakquote() { printf "%s" "$*" | sed "s/'/''/g; 1s/^/'/; \$s/\$/'/"; }
+
+ eval "set -- ${kak_quoted_opt_lint_messages}"
+ shift
+
+ if [ "$#" -eq 0 ]; then
+ printf 'fail no lint messages'
+ exit
+ fi
+
+ prev_lineno=""
+ prev_msg=""
+
+ for lint_message; do
+ lineno="${lint_message%%|*}"
+ msg="${lint_message#*|}"
+
+ # If this message comes on or after the cursor position...
+ if [ "$lineno" -ge "${kak_cursor_line}" ]; then
+ # ...and we had a previous message...
+ if [ -n "$prev_lineno" ]; then
+ # ...then go to the previous message and display it.
+ printf "execute-keys %dg\n" "$prev_lineno"
+ printf "info -anchor %d.%d %s\n" \
+ "$lineno" "1" "$(kakquote "$prev_msg")"
+ exit
+
+ # We are after the cursor position, but there has been
+ # no previous message; we'll need to do something else.
+ else
+ break
+ fi
+ fi
+
+ # We have not yet reached the cursor position, stash this message
+ # and try the next.
+ prev_lineno="$lineno"
+ prev_msg="$msg"
+ done
+
+ # There is no message before the cursor position,
+ # let's wrap around to the end.
+ shift $(( $# - 1 ))
+ last_lineno="${1%%|*}"
+ last_msg="${1#*|}"
+
+ printf "execute-keys %dg\n" "$last_lineno"
+ printf "info -anchor %d.%d %s\n" \
+ "$last_lineno" "1" "$(kakquote "$last_msg")"
+ printf "echo -markup \
+ {Information}lint message search wrapped around buffer\n"
+ }
+}
diff --git a/autoload/tools/make.kak b/autoload/tools/make.kak
new file mode 100644
index 0000000..61d9b6c
--- /dev/null
+++ b/autoload/tools/make.kak
@@ -0,0 +1,92 @@
+declare-option -docstring "shell command run to build the project" \
+ str makecmd make
+declare-option -docstring "pattern that describes lines containing information about errors in the output of the `makecmd` command. Capture groups must be: 1: filename 2: line number 3: optional column 4: optional error description" \
+ regex make_error_pattern "^([^:\n]+):(\d+):(?:(\d+):)? (?:fatal )?error:([^\n]+)?"
+
+provide-module make %{
+
+require-module jump
+
+define-command -params .. \
+ -docstring %{
+ make [<arguments>]: make utility wrapper
+ All the optional arguments are forwarded to the make utility
+ } make %{ evaluate-commands %sh{
+ output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-make.XXXXXXXX)/fifo
+ mkfifo ${output}
+ ( { trap - INT QUIT; eval "${kak_opt_makecmd}" "$@"; } > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
+
+ printf %s\\n "evaluate-commands -try-client '$kak_opt_toolsclient' %{
+ edit! -fifo ${output} -scroll *make*
+ set-option buffer filetype make
+ set-option buffer jump_current_line 0
+ hook -always -once buffer BufCloseFifo .* %{ nop %sh{ rm -r $(dirname ${output}) } }
+ }"
+}}
+
+add-highlighter shared/make group
+add-highlighter shared/make/ regex "^([^:\n]+):(\d+):(?:(\d+):)?\h+(?:((?:fatal )?error)|(warning)|(note)|(required from(?: here)?))?.*?$" 1:cyan 2:green 3:green 4:red 5:yellow 6:blue 7:yellow
+add-highlighter shared/make/ regex "^\h*(~*(?:(\^)~*)?)$" 1:green 2:cyan+b
+add-highlighter shared/make/ line '%opt{jump_current_line}' default+b
+
+hook -group make-highlight global WinSetOption filetype=make %{
+ add-highlighter window/make ref make
+ hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/make }
+}
+
+hook global WinSetOption filetype=make %{
+ alias buffer jump make-jump
+ alias buffer jump-select-next make-select-next
+ alias buffer jump-select-previous make-select-previous
+ hook buffer -group make-hooks NormalKey <ret> make-jump
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks buffer make-hooks }
+}
+
+define-command -hidden make-open-error -params 4 %{
+ evaluate-commands -try-client %opt{jumpclient} %{
+ edit -existing "%arg{1}" %arg{2} %arg{3}
+ echo -markup "{Information}{\}%arg{4}"
+ try %{ focus }
+ }
+}
+
+define-command -hidden make-jump %{
+ evaluate-commands -save-regs a/ %{
+ evaluate-commands -draft %{
+ execute-keys ,
+ try %{
+ execute-keys gl<a-?> "Entering directory" <ret><a-:>
+ # Try to parse the error into capture groups, failing on absolute paths
+ execute-keys s "Entering directory [`']([^']+)'.*\n([^:\n/][^:\n]*):(\d+):(?:(\d+):)?([^\n]+)\n?\z" <ret>l
+ set-option buffer jump_current_line %val{cursor_line}
+ set-register a "%reg{1}/%reg{2}" "%reg{3}" "%reg{4}" "%reg{5}"
+ } catch %{
+ set-register / %opt{make_error_pattern}
+ execute-keys <a-h><a-l> s<ret>l
+ set-option buffer jump_current_line %val{cursor_line}
+ set-register a "%reg{1}" "%reg{2}" "%reg{3}" "%reg{4}"
+ }
+ }
+ make-open-error %reg{a}
+ }
+}
+define-command -hidden make-select-next %{
+ set-register / %opt{make_error_pattern}
+ execute-keys "%opt{jump_current_line}ggl" "/<ret>"
+}
+define-command -hidden make-select-previous %{
+ set-register / %opt{make_error_pattern}
+ execute-keys "%opt{jump_current_line}g" "<a-/><ret>"
+}
+
+define-command make-next-error -docstring %{alias for "jump-next *make*"} %{
+ jump-next *make*
+}
+
+define-command make-previous-error -docstring %{alias for "jump-previous *make*"} %{
+ jump-previous *make*
+}
+
+}
+
+hook -once global KakBegin .* %{ require-module make }
diff --git a/autoload/tools/man.kak b/autoload/tools/man.kak
new file mode 100644
index 0000000..2fcc981
--- /dev/null
+++ b/autoload/tools/man.kak
@@ -0,0 +1,139 @@
+declare-option -docstring "name of the client in which documentation is to be displayed" \
+ str docsclient
+
+declare-option -hidden str-list manpage
+
+hook -group man-highlight global WinSetOption filetype=man %{
+ add-highlighter window/man-highlight group
+ # Sections
+ add-highlighter window/man-highlight/ regex ^\S.*?$ 0:title
+ # Subsections
+ add-highlighter window/man-highlight/ regex '^ {3}\S.*?$' 0:default+b
+ # Command line options
+ add-highlighter window/man-highlight/ regex '^ {7}-[^\s,]+(,\s+-[^\s,]+)*' 0:list
+ # References to other manpages
+ add-highlighter window/man-highlight/ regex [-a-zA-Z0-9_.]+\([a-z0-9]+\) 0:header
+
+ map window normal <ret> :man-jump<ret>
+
+ hook -once -always window WinSetOption filetype=.* %{
+ remove-highlighter window/man-highlight
+ unmap window normal <ret>
+ }
+}
+
+hook global WinSetOption filetype=man %{
+ hook -group man-hooks window WinResize .* %{ man-impl %opt{manpage} }
+ hook -once -always window WinSetOption filetype=.* %{ remove-hooks window man-hooks }
+}
+
+define-command -hidden -params ..3 man-impl %{ evaluate-commands %sh{
+ buffer_name="$1"
+ if [ -z "${buffer_name}" ]; then
+ exit
+ fi
+ shift
+ manout=$(mktemp "${TMPDIR:-/tmp}"/kak-man.XXXXXX)
+ manerr=$(mktemp "${TMPDIR:-/tmp}"/kak-man.XXXXXX)
+ colout=$(mktemp "${TMPDIR:-/tmp}"/kak-man.XXXXXX)
+ env MANWIDTH=${kak_window_range##* } man "$@" > "$manout" 2> "$manerr"
+ retval=$?
+ if command -v col >/dev/null; then
+ col -b -x > ${colout} < ${manout}
+ else
+ sed 's/.//g' > ${colout} < ${manout}
+ fi
+ rm ${manout}
+
+ if [ "${retval}" -eq 0 ]; then
+ printf %s\\n "
+ edit -scratch %{*$buffer_name ${*}*}
+ execute-keys '%|cat<space>${colout}<ret>gk'
+ nop %sh{ rm ${colout}; rm ${manerr} }
+ set-option buffer filetype man
+ set-option window manpage $buffer_name $*
+ "
+ else
+ printf '
+ fail %%{%s}
+ nop %%sh{ rm "%s"; rm "%s" }
+ ' "$(cat "$manerr")" "${colout}" "${manerr}"
+ fi
+} }
+
+define-command -params ..1 \
+ -shell-script-candidates %{
+ find /usr/share/man/ $(printf %s "${MANPATH}" |
+ sed 's/:/ /') -name '*.[1-8]*' |
+ sed 's,^.*/\(.*\)\.\([1-8][a-zA-Z]*\).*$,\1(\2),'
+ } \
+ -docstring %{
+ man [<page>]: manpage viewer wrapper
+ If no argument is passed to the command, the selection will be used as page
+ The page can be a word, or a word directly followed by a section number between parenthesis, e.g. kak(1)
+ } man %{ evaluate-commands %sh{
+ subject=${1-$kak_selection}
+
+ ## The completion suggestions display the page number, strip them if present
+ case "${subject}" in
+ *\([1-8]*\))
+ pagenum="${subject##*\(}"
+ pagenum="${pagenum%\)}"
+ subject="${subject%%\(*}"
+ ;;
+ *)
+ pagenum=""
+ ;;
+ esac
+
+ printf %s\\n "evaluate-commands -try-client %opt{docsclient} man-impl man $pagenum $subject"
+} }
+
+
+
+# The following section of code enables a user
+# to go to next or previous man page links and to follow man page links,
+# for example, apropos(1), that would normally appear in SEE ALSO sections.
+# The user would position the cursor on any character of the link
+# and then press <ret> to change to a buffer showing the man page.
+
+# Regex pattern defining a man page link.
+# Used for determining if a selection, which may just be a link, is a link.
+declare-option -hidden regex man_link1 \
+ [\w_.:-]+\(\d[a-z]*\)
+
+# Same as above but with lookbehind and lookahead patterns.
+# Used for searching for a man page link.
+declare-option -hidden regex man_link2 \
+ "(?:^|(?<=\W))%opt{man_link1}(?=\W)"
+
+# Define a useful command sequence for searching a given regex
+# and a given sequence of search keys.
+define-command -hidden man-search -params 2 %{
+ set-register / %arg[1]
+ try %{
+ execute-keys %arg[2]
+ } catch %{
+ fail "Could not find man page link"
+ }
+}
+
+define-command -docstring 'Go to next man page link' \
+man-link-next %{ man-search %opt[man_link2] n }
+
+define-command -docstring 'Go to previous man page link' \
+man-link-prev %{ man-search %opt[man_link2] <a-n> }
+
+define-command -docstring 'Try to jump to a man page' \
+man-jump %{
+ try %{ execute-keys <a-a><a-w> s %opt[man_link1] <ret> } catch %{ fail 'Not a valid man page link' }
+ try %{ man } catch %{ fail 'No man page link to follow' }
+}
+
+# Suggested keymaps for a user mode
+declare-user-mode man
+
+map global man 'g' -docstring 'Jump to a man page using selected man page link' :man-jump<ret>
+map global man 'j' -docstring 'Go to next man page link' :man-link-next<ret>
+map global man 'k' -docstring 'Go to previous man page link' :man-link-prev<ret>
+map global man 'm' -docstring 'Look up a man page' :man<space>
diff --git a/autoload/tools/menu.kak b/autoload/tools/menu.kak
new file mode 100644
index 0000000..4fd7dde
--- /dev/null
+++ b/autoload/tools/menu.kak
@@ -0,0 +1,85 @@
+provide-module menu %§§
+
+define-command menu -params 1.. -docstring %{
+ menu [<switches>] <name1> <commands1> <name2> <commands2>...: display a
+ menu and execute commands for the selected item
+
+ -auto-single instantly validate if only one item is available
+ -select-cmds each item specify an additional command to run when selected
+} %{
+ evaluate-commands %sh{
+ auto_single=false
+ select_cmds=false
+ stride=2
+ on_abort=
+ while true
+ do
+ case "$1" in
+ (-auto-single) auto_single=true ;;
+ (-select-cmds) select_cmds=true; stride=3 ;;
+ (-on-abort) on_abort="$2"; shift ;;
+ (-markup) ;; # no longer supported
+ (*) break ;;
+ esac
+ shift
+ done
+ if [ $(( $# % $stride )) -ne 0 ]; then
+ echo fail "wrong argument count"
+ exit
+ fi
+ if $auto_single && [ $# -eq $stride ]; then
+ printf %s "$2"
+ exit
+ fi
+ shellquote() {
+ printf "'%s'" "$(printf %s "$1" | sed "s/'/'\\\\''/g; s/§/§§/g; $2")"
+ }
+ cases=
+ select_cases=
+ completion=
+ nl=$(printf '\n.'); nl=${nl%.}
+ while [ $# -gt 0 ]; do
+ title=$1
+ command=$2
+ completion="${completion}${title}${nl}"
+ cases="${cases}
+ ($(shellquote "$title" s/¶/¶¶/g))
+ printf '%s\\n' $(shellquote "$command" s/¶/¶¶/g)
+ ;;"
+ if $select_cmds; then
+ select_command=$3
+ select_cases="${select_cases}
+ ($(shellquote "$title" s/¶/¶¶/g))
+ printf '%s\\n' $(shellquote "$select_command" s/¶/¶¶/g)
+ ;;"
+ fi
+ shift $stride
+ done
+ printf "\
+ prompt '' %%§
+ evaluate-commands %%sh¶
+ case \"\$kak_text\" in \
+ %s
+ (*) echo fail -- no such item: \"'\$(printf %%s \"\$kak_text\" | sed \"s/'/''/g\")'\" ;;
+ esac
+ ¶
+ §" "$cases"
+ if $select_cmds; then
+ printf " \
+ -on-change %%§
+ evaluate-commands %%sh¶
+ case \"\$kak_text\" in \
+ %s
+ (*) : ;;
+ esac
+ ¶
+ §" "$select_cases"
+ fi
+ if [ -n "$on_abort" ]; then
+ printf " -on-abort '%s'" "$(printf %s "$on_abort" | sed "s/'/''/g")"
+ fi
+ printf ' -menu -shell-script-candidates %%§
+ printf %%s %s
+ §\n' "$(shellquote "$completion")"
+ }
+}
diff --git a/autoload/tools/patch-range.pl b/autoload/tools/patch-range.pl
new file mode 100644
index 0000000..978f45c
--- /dev/null
+++ b/autoload/tools/patch-range.pl
@@ -0,0 +1,113 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+my $min_line = $ARGV[0];
+shift @ARGV;
+my $max_line = $ARGV[0];
+shift @ARGV;
+
+my $patch_cmd;
+if (defined $ARGV[0] and $ARGV[0] =~ m{^[^-]}) {
+ $patch_cmd = "@ARGV";
+} else {
+ $patch_cmd = "patch @ARGV";
+}
+my $reverse = grep /^(--reverse|-R)$/, @ARGV;
+
+my $lineno = 0;
+my $original = "";
+my $diff_header = "";
+my $wheat = "";
+my $chaff = "";
+my $state = undef;
+my $hunk_wheat = undef;
+my $hunk_chaff = undef;
+my $hunk_header = undef;
+my $hunk_remaining_lines = undef;
+my $signature = "";
+
+sub compute_hunk_header {
+ my $original_header = shift;
+ my $hunk = shift;
+ my $old_lines = 0;
+ my $new_lines = 0;
+ for (split /\n/, $hunk) {
+ $old_lines++ if m{^[ -]};
+ $new_lines++ if m{^[ +]};
+ }
+ my $updated_header = $original_header =~ s/^@@ -(\d+),\d+\s+\+(\d+),\d+ @@(.*)/@@ -$1,$old_lines +$2,$new_lines @\@$3/mr;
+ return $updated_header;
+}
+
+sub finish_hunk {
+ return unless defined $hunk_header;
+ if ($hunk_wheat =~ m{^[-+]}m) {
+ if ($diff_header) {
+ $wheat .= $diff_header;
+ $diff_header = "";
+ }
+ $wheat .= (compute_hunk_header $hunk_header, $hunk_wheat). $hunk_wheat;
+ }
+ $chaff .= (compute_hunk_header $hunk_header, $hunk_chaff) . $hunk_chaff . $signature;
+ $hunk_header = undef;
+}
+
+while (<STDIN>) {
+ ++$lineno;
+ $original .= $_;
+ if (m{^diff}) {
+ finish_hunk();
+ $state = "diff header";
+ $diff_header = "";
+ }
+ if ($state eq "signature") {
+ $signature .= $_;
+ next;
+ }
+ if (m{^@@ -\d+(?:,(\d)+)? \+\d+(?:,\d+)? @@}) {
+ $hunk_remaining_lines = $1 or 1;
+ finish_hunk();
+ $state = "diff hunk";
+ $hunk_header = $_;
+ $hunk_wheat = "";
+ $hunk_chaff = "";
+ $signature = "";
+ next;
+ }
+ if ($state eq "diff header") {
+ $diff_header .= $_;
+ $chaff .= $_;
+ next;
+ }
+ if ($hunk_remaining_lines == 0 and m{^-- $}) {
+ $state = "signature";
+ $signature .= $_;
+ next;
+ }
+ --$hunk_remaining_lines if m{^[ -]};
+ my $include = m{^ } || ($lineno >= $min_line && $lineno <= $max_line);
+ if ($include) {
+ $hunk_wheat .= $_;
+ $hunk_chaff .= $_ if m{^ };
+ if ($reverse ? m{^[-]} : m{^\+}) {
+ $hunk_chaff .= " " . substr $_, 1;
+ }
+ } else {
+ if ($reverse ? m{^\+} : m{^-}) {
+ $hunk_wheat .= " " . substr $_, 1;
+ }
+ $hunk_chaff .= $_;
+ }
+}
+finish_hunk();
+
+open PATCH_COMMAND, "|-", "$patch_cmd 1>&2" or die "patch-range.pl: error running '$patch_cmd': $!";
+print PATCH_COMMAND $wheat;
+if (not close PATCH_COMMAND) {
+ print $original;
+ print STDERR "patch-range.pl: error running:\n" . "\$ $patch_cmd << EOF\n$wheat" . "EOF\n";
+ exit 1;
+}
+print $chaff;
diff --git a/autoload/tools/patch.kak b/autoload/tools/patch.kak
new file mode 100644
index 0000000..a481ff4
--- /dev/null
+++ b/autoload/tools/patch.kak
@@ -0,0 +1,63 @@
+define-command patch -params .. -docstring %{
+ patch [<arguments>]: apply selections in diff to a file
+
+ Given some selections within a unified diff, apply the changed lines in
+ each selection by piping them to "patch <arguments> 1>&2"
+ (or "<arguments> 1>&2" if <arguments> starts with a non-option argument).
+ If successful, the in-buffer diff will be updated to reflect the applied
+ changes.
+ For selections that contain no newline, the entire enclosing diff hunk
+ is applied (unless the cursor is inside a diff header, in which case
+ the entire diff is applied).
+ To revert changes, <arguments> must contain "--reverse" or "-R".
+} %{
+ evaluate-commands -draft -itersel -save-regs aes|^ %{
+ try %{
+ execute-keys <a-k>\n<ret>
+ } catch %{
+ # The selection contains no newline.
+ execute-keys -save-regs '' Z
+ execute-keys <a-l><semicolon><a-?>^diff<ret>
+ try %{
+ execute-keys <a-k>^@@<ret>
+ # If the cursor is in a diff hunk, stage the entire hunk.
+ execute-keys z
+ execute-keys /.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x<semicolon><a-?>^@@<ret>
+ } catch %{
+ # If the cursor is in a diff header, stage the entire diff.
+ execute-keys <a-semicolon>?.*?(?:(?=\ndiff)|(?=\n\n)|\z)<ret>
+ }
+ }
+ # We want to apply only the selected lines. Remember them.
+ execute-keys <a-:>
+ set-register s %val{selection_desc}
+ # Select forward until the end of the last hunk.
+ execute-keys H?.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x
+ # Select backward to the beginning of the first hunk's diff header.
+ execute-keys <a-semicolon><a-L><a-?>^diff<ret>
+ # Move cursor to the beginning so we know the diff's offset within the buffer.
+ execute-keys <a-:><a-semicolon>
+ set-register a %arg{@}
+ set-register e nop
+ set-register | %{
+ # The selected range to apply.
+ IFS=' .,' read min_line _ max_line _ <<-EOF
+ $kak_reg_s
+ EOF
+ min_line=$((min_line - kak_cursor_line + 1))
+ max_line=$((max_line - kak_cursor_line + 1))
+
+ # Since registers are never empty, we get an empty arg even if
+ # there were no args. This does no harm because we pass it to
+ # a shell where it expands to nothing.
+ eval set -- "$kak_quoted_reg_a"
+
+ perl "${kak_runtime}"/rc/tools/patch-range.pl $min_line $max_line "$@" ||
+ echo >$kak_command_fifo "set-register e fail 'patch: failed to apply selections, see *debug* buffer'"
+ }
+ execute-keys |<ret>
+ %reg{e}
+ }
+}
+
+provide-module patch %§§
diff --git a/autoload/tools/python/jedi.kak b/autoload/tools/python/jedi.kak
new file mode 100644
index 0000000..82d799a
--- /dev/null
+++ b/autoload/tools/python/jedi.kak
@@ -0,0 +1,77 @@
+hook -once global BufSetOption filetype=python %{
+ require-module jedi
+}
+
+provide-module jedi %{
+
+declare-option -hidden str jedi_tmp_dir
+declare-option -hidden completions jedi_completions
+declare-option -docstring "colon separated list of path added to `python`'s $PYTHONPATH environment variable" \
+ str jedi_python_path
+
+define-command jedi-complete -docstring "Complete the current selection" %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-jedi.XXXXXXXX)
+ mkfifo ${dir}/fifo
+ printf %s\\n "set-option buffer jedi_tmp_dir ${dir}"
+ printf %s\\n "evaluate-commands -no-hooks write -sync ${dir}/buf"
+ }
+ evaluate-commands %sh{
+ dir=${kak_opt_jedi_tmp_dir}
+ printf %s\\n "evaluate-commands -draft %{ edit! -fifo ${dir}/fifo *jedi-output* }"
+ ((
+ trap - INT QUIT
+ cd $(dirname ${kak_buffile})
+
+ export PYTHONPATH="$kak_opt_jedi_python_path:$PYTHONPATH"
+ python 2> "${dir}/fifo" -c 'if 1:
+ import os
+ dir = os.environ["kak_opt_jedi_tmp_dir"]
+ buffile = os.environ["kak_buffile"]
+ line = int(os.environ["kak_cursor_line"])
+ column = int(os.environ["kak_cursor_column"])
+ timestamp = os.environ["kak_timestamp"]
+ client = os.environ["kak_client"]
+ pipe_escape = lambda s: s.replace("|", "\\|")
+ def quote(s):
+ c = chr(39) # single quote
+ return c + s.replace(c, c+c) + c
+ import jedi
+ script = jedi.Script(code=open(dir + "/buf", "r").read(), path=buffile)
+ completions = (
+ quote(
+ pipe_escape(str(c.name)) + "|" +
+ pipe_escape("info -style menu -- " + quote(c.docstring())) + "|" +
+ pipe_escape(str(c.name))
+ )
+ for c in script.complete(line=line, column=column-1)
+ )
+ header = str(line) + "." + str(column) + "@" + timestamp
+ cmds = [
+ "echo completed",
+ " ".join(("set-option", quote("buffer=" + buffile), "jedi_completions", header, *completions)),
+ ]
+ print("evaluate-commands -client", quote(client), quote("\n".join(cmds)))
+ ' | kak -p "${kak_session}"
+ rm -r ${dir}
+ ) & ) > /dev/null 2>&1 < /dev/null
+ }
+}
+
+define-command jedi-enable-autocomplete -docstring "Add jedi completion candidates to the completer" %{
+ set-option window completers option=jedi_completions %opt{completers}
+ hook window -group jedi-autocomplete InsertIdle .* %{ try %{
+ execute-keys -draft <a-h><a-k>\..\z<ret>
+ echo 'completing...'
+ jedi-complete
+ } }
+ alias window complete jedi-complete
+}
+
+define-command jedi-disable-autocomplete -docstring "Disable jedi completion" %{
+ set-option window completers %sh{ printf %s\\n "'${kak_opt_completers}'" | sed -e 's/option=jedi_completions://g' }
+ remove-hooks window jedi-autocomplete
+ unalias window complete jedi-complete
+}
+
+}
diff --git a/autoload/tools/rust/racer.kak b/autoload/tools/rust/racer.kak
new file mode 100644
index 0000000..ed85e3c
--- /dev/null
+++ b/autoload/tools/rust/racer.kak
@@ -0,0 +1,123 @@
+hook -once global BufSetOption filetype=rust %{
+ require-module racer
+}
+
+provide-module racer %{
+
+declare-option -hidden str racer_tmp_dir
+declare-option -hidden completions racer_completions
+
+define-command racer-complete -docstring "Complete the current selection with racer" %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-racer.XXXXXXXX)
+ printf %s\\n "set-option buffer racer_tmp_dir ${dir}"
+ printf %s\\n "evaluate-commands -no-hooks %{ write ${dir}/buf }"
+ }
+ evaluate-commands %sh{
+ dir=${kak_opt_racer_tmp_dir}
+ (
+ trap - INT QUIT
+ cursor="${kak_cursor_line} $((${kak_cursor_column} - 1))"
+ racer_data=$(racer --interface tab-text complete-with-snippet ${cursor} ${kak_buffile} ${dir}/buf)
+ compl=$(printf %s\\n "${racer_data}" | awk '
+ BEGIN { FS = "\t"; ORS = " " }
+ /^PREFIX/ {
+ column = ENVIRON["kak_cursor_column"] + $2 - $3
+ print ENVIRON["kak_cursor_line"] "." column "@@" ENVIRON["kak_timestamp"]
+ }
+ /^MATCH/ {
+ word = $2
+ desc = substr($9, 2, length($9) - 2)
+ gsub(/\|/, "\\|", desc)
+ gsub(/\\n/, "\n", desc)
+ gsub(/!/, "!!", desc)
+ info = $8
+ gsub(/\|/, "\\|", info)
+
+ candidate = word "|info -style menu %!" desc "!|" word " {MenuInfo}" info
+
+ gsub(/@/, "@@", candidate)
+ gsub(/~/, "~~", candidate)
+ print "%~" candidate "~"
+ }'
+ )
+ printf %s\\n "evaluate-commands -client '${kak_client}' %@ set-option 'buffer=${kak_bufname}' racer_completions ${compl%?} @" | kak -p ${kak_session}
+ rm -r ${dir}
+ ) > /dev/null 2>&1 < /dev/null &
+ }
+}
+
+define-command racer-enable-autocomplete -docstring "Add racer completion candidates to the completer" %{
+ set-option window completers option=racer_completions %opt{completers}
+ hook window -group racer-autocomplete InsertIdle .* %{ try %{
+ execute-keys -draft <a-h><a-k>([\w\.]|::).\z<ret>
+ racer-complete
+ } }
+ alias window complete racer-complete
+}
+
+define-command racer-disable-autocomplete -docstring "Disable racer completion" %{
+ evaluate-commands %sh{ printf "set-option window completers %s\n" $(printf %s "${kak_opt_completers}" | sed -e "s/'option=racer_completions'//g") }
+ remove-hooks window racer-autocomplete
+ unalias window complete racer-complete
+}
+
+define-command racer-go-definition -docstring "Jump to where the rust identifier below the cursor is defined" %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-racer.XXXXXXXX)
+ printf %s\\n "set-option buffer racer_tmp_dir ${dir}"
+ printf %s\\n "evaluate-commands -no-hooks %{ write ${dir}/buf }"
+ }
+ evaluate-commands %sh{
+ dir=${kak_opt_racer_tmp_dir}
+ cursor="${kak_cursor_line} $((${kak_cursor_column} - 1))"
+ racer_data=$(racer --interface tab-text find-definition ${cursor} "${kak_buffile}" "${dir}/buf" | head -n 1)
+
+ racer_match=$(printf %s\\n "$racer_data" | cut -f1 )
+ if [ "$racer_match" = "MATCH" ]; then
+ racer_line=$(printf %s\\n "$racer_data" | cut -f3 )
+ racer_column=$(printf %s\\n "$racer_data" | cut -f4 )
+ racer_file=$(printf %s\\n "$racer_data" | cut -f5 )
+ printf %s\\n "edit -existing '$racer_file' $racer_line $racer_column"
+ case ${racer_file} in
+ "${RUST_SRC_PATH}"* | "${CARGO_HOME:-$HOME/.cargo}"/registry/src/*)
+ printf %s\\n "set-option buffer readonly true";;
+ esac
+ else
+ printf %s\\n "echo -debug 'racer could not find a definition'"
+ fi
+ }
+}
+
+define-command racer-show-doc -docstring "Show the documentation about the rust identifier below the cursor" %{
+ evaluate-commands %sh{
+ dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-racer.XXXXXXXX)
+ printf %s\\n "set-option buffer racer_tmp_dir ${dir}"
+ printf %s\\n "evaluate-commands -no-hooks %{ write ${dir}/buf }"
+ }
+ evaluate-commands %sh{
+ dir=${kak_opt_racer_tmp_dir}
+ cursor="${kak_cursor_line} ${kak_cursor_column}"
+ racer_data=$(racer --interface tab-text complete-with-snippet ${cursor} "${kak_buffile}" "${dir}/buf" | sed -n 2p )
+ racer_match=$(printf %s\\n "$racer_data" | cut -f1)
+ if [ "$racer_match" = "MATCH" ]; then
+ racer_doc=$(
+ printf %s\\n "$racer_data" |
+ cut -f9 |
+ sed -e '
+
+ # Remove leading and trailing quotes
+ s/^"\(.*\)"$/\1/g
+
+ # Escape all @ so that it can be properly used in the string expansion
+ s/@/\\@/g
+
+ ')
+ printf "info %%@$racer_doc@"
+ else
+ printf %s\\n "echo -debug 'racer could not find a definition'"
+ fi
+ }
+}
+
+}
diff --git a/autoload/tools/spell.kak b/autoload/tools/spell.kak
new file mode 100644
index 0000000..4bd3305
--- /dev/null
+++ b/autoload/tools/spell.kak
@@ -0,0 +1,184 @@
+declare-option -hidden range-specs spell_regions
+declare-option -hidden str spell_last_lang
+
+declare-option -docstring "default language to use when none is passed to the spell-check command" str spell_lang
+
+define-command -params ..1 -docstring %{
+ spell [<language>]: spell check the current buffer
+
+ The first optional argument is the language against which the check will be performed (overrides `spell_lang`)
+ Formats of language supported:
+ - ISO language code, e.g. 'en'
+ - language code above followed by a dash or underscore with an ISO country code, e.g. 'en-US'
+ } spell %{
+ try %{ add-highlighter window/ ranges 'spell_regions' }
+ evaluate-commands %sh{
+ use_lang() {
+ if ! printf %s "$1" | grep -qE '^[a-z]{2,3}([_-][A-Z]{2})?$'; then
+ echo "fail 'Invalid language code (examples of expected format: en, en_US, en-US)'"
+ exit 1
+ else
+ options="-l '$1'"
+ printf 'set-option buffer spell_last_lang %s\n' "$1"
+ fi
+ }
+
+ if [ $# -ge 1 ]; then
+ use_lang "$1"
+ elif [ -n "${kak_opt_spell_lang}" ]; then
+ use_lang "${kak_opt_spell_lang}"
+ fi
+
+ printf 'eval -no-hooks write %s\n' "${kak_response_fifo}" > $kak_command_fifo
+
+ {
+ trap - INT QUIT
+ sed 's/^/^/' | eval "aspell --byte-offsets -a $options" 2>&1 | awk '
+ BEGIN {
+ line_num = 1
+ regions = ENVIRON["kak_timestamp"]
+ server_command = sprintf("kak -p \"%s\"", ENVIRON["kak_session"])
+ }
+
+ {
+ if (/^@\(#\)/) {
+ # drop the identification message
+ }
+
+ else if (/^\*/) {
+ # nothing
+ }
+
+ else if (/^[+-]/) {
+ # required to ignore undocumented aspell functionality
+ }
+
+ else if (/^$/) {
+ line_num++
+ }
+
+ else if (/^[#&]/) {
+ word_len = length($2)
+ word_pos = substr($0, 1, 1) == "&" ? substr($4, 1, length($4) - 1) : $3;
+ regions = regions " " line_num "." word_pos "+" word_len "|DiagnosticError"
+ }
+
+ else {
+ line = $0
+ gsub(/"/, "&&", line)
+ command = "fail \"" line "\""
+ exit
+ }
+ }
+
+ END {
+ if (!length(command))
+ command = "set-option \"buffer=" ENVIRON["kak_bufname"] "\" spell_regions " regions
+
+ print command | server_command
+ close(server_command)
+ }
+ '
+ } <$kak_response_fifo >/dev/null 2>&1 &
+ }
+}
+
+define-command spell-clear %{
+ unset-option buffer spell_regions
+}
+
+define-command spell-next %{ evaluate-commands %sh{
+ anchor_line="${kak_selection_desc%%.*}"
+ anchor_col="${kak_selection_desc%%,*}"
+ anchor_col="${anchor_col##*.}"
+
+ start_first="${kak_opt_spell_regions%%|*}"
+ start_first="${start_first#* }"
+
+ # Make sure properly formatted selection descriptions are in `%opt{spell_regions}`
+ if ! printf %s "${start_first}" | grep -qE '^[0-9]+\.[0-9]+,[0-9]+\.[0-9]+$'; then
+ exit
+ fi
+
+ printf %s "${kak_opt_spell_regions#* }" | awk -v start_first="${start_first}" \
+ -v anchor_line="${anchor_line}" \
+ -v anchor_col="${anchor_col}" '
+ BEGIN {
+ anchor_line = int(anchor_line)
+ anchor_col = int(anchor_col)
+ }
+
+ {
+ for (i = 1; i <= NF; i++) {
+ sel = $i
+ sub(/\|.+$/, "", sel)
+
+ start_line = sel
+ sub(/\..+$/, "", start_line)
+ start_line = int(start_line)
+
+ start_col = sel
+ sub(/,.+$/, "", start_col)
+ sub(/^.+\./, "", start_col)
+ start_col = int(start_col)
+
+ if (start_line < anchor_line \
+ || (start_line == anchor_line && start_col <= anchor_col))
+ continue
+
+ target_sel = sel
+ break
+ }
+ }
+
+ END {
+ if (!target_sel)
+ target_sel = start_first
+
+ printf "select %s\n", target_sel
+ }'
+} }
+
+define-command \
+ -docstring "Suggest replacement words for the current selection, against the last language used by the spell-check command" \
+ spell-replace %{
+ prompt \
+ -shell-script-candidates %{
+ options=""
+ if [ -n "$kak_opt_spell_last_lang" ]; then
+ options="-l '$kak_opt_spell_last_lang'"
+ fi
+ printf %s "$kak_selection" |
+ eval "aspell -a $options" |
+ sed -n -e '/^&/ { s/^[^:]*: //; s/, /\n/g; p;}'
+ } \
+ "Replace with: " \
+ %{
+ evaluate-commands -save-regs a %{
+ set-register a %val{text}
+ execute-keys c <c-r>a <esc>
+ }
+ }
+}
+
+
+define-command -params 0.. \
+ -docstring "Add the current selection to the dictionary" \
+ spell-add %{ evaluate-commands %sh{
+ options=""
+ if [ -n "$kak_opt_spell_last_lang" ]; then
+ options="-l '$kak_opt_spell_last_lang'"
+ fi
+ if [ $# -eq 0 ]; then
+ # use selections
+ eval set -- "$kak_quoted_selections"
+ fi
+ while [ $# -gt 0 ]; do
+ word="$1"
+ if ! printf '*%s\n#\n' "${word}" | eval "aspell -a $options" >/dev/null; then
+ printf 'fail "Unable to add word: %s"' "$(printf %s "${word}" | sed 's/"/&&/g')"
+ exit 1
+ fi
+ shift
+ done
+}}
diff --git a/autoload/windowing/appleterminal.kak b/autoload/windowing/appleterminal.kak
new file mode 100644
index 0000000..f47c83c
--- /dev/null
+++ b/autoload/windowing/appleterminal.kak
@@ -0,0 +1,44 @@
+# macOS Terminal.app
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module appleterminal %{
+
+# ensure that we're running in Terminal.app
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM_PROGRAM" = "Apple_Terminal" ] || echo 'fail Terminal.app not detected'
+}
+
+define-command appleterminal-terminal-window -params 1.. -docstring '
+appleterminal-terminal-window <program> [<arguments>]: create a new terminal as a Terminal.app window
+The program passed as argument will be executed in the new terminal'\
+%{
+ nop %sh{
+ # join the arguments as one string for the shell execution (see x11.kak)
+ quoted=$(
+ for i in exec env "PATH=$PATH" "TMPDIR=$TMPDIR" "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+
+ # since Terminal.app runs the command in the interactive shell, add initial space to prevent adding it to shell history
+ cmd=" $quoted"
+ echo "$cmd" | osascript \
+ -e 'set s to (do shell script "cat 0<&3")' \
+ -e 'tell application "Terminal" to do script s' >/dev/null 3<&0
+ }
+}
+complete-command appleterminal-terminal-window shell
+
+alias global terminal appleterminal-terminal-window
+
+define-command appleterminal-focus -params ..1 -docstring '
+appleterminal-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ fail 'Focusing Terminal.app client is unimplemented'
+}
+complete-command -menu appleterminal-focus client
+
+alias global focus appleterminal-focus
+
+}
diff --git a/autoload/windowing/detection.kak b/autoload/windowing/detection.kak
new file mode 100644
index 0000000..2fde81c
--- /dev/null
+++ b/autoload/windowing/detection.kak
@@ -0,0 +1,71 @@
+# Attempt to detect the windowing environment we're operating in
+#
+# We try to load modules from the windowing_modules str-list option in order,
+# stopping when one of the modules loads successfully. This ensures that only
+# a single module is loaded by default.
+#
+# On load each module must attempt to detect the environment it's appropriate
+# for, and if the environment isn't appropriate it must fail with an error.
+# In addition, each module must check for the length of the windowing_modules
+# str-list option defined below, and must /not/ check for an appropriate
+# environment if the list is empty. An example of this test:
+#
+# evaluate-commands %sh{
+# [ -z "${kak_opt_windowing_modules}" ] || [ -n "$TMUX" ] || echo 'fail tmux not detected'
+# }
+#
+# Each module is expected to define at least two aliases:
+# * terminal - create a new terminal with sensible defaults
+# * focus - focus the specified client, defaulting to the current client
+#
+
+declare-option -docstring \
+"Ordered list of windowing modules to try and load. An empty list disables
+both automatic module loading and environment detection, enabling complete
+manual control of the module loading." \
+str-list windowing_modules 'tmux' 'screen' 'zellij' 'kitty' 'iterm' 'appleterminal' 'sway' 'wayland' 'x11' 'wezterm'
+
+declare-option -docstring %{
+ windowing module to use in the 'terminal' command
+} str windowing_module
+
+declare-option -docstring %{
+ where to create new windows in the 'terminal' command.
+
+ Possible values:
+ - "window" (default) - new window
+ - "horizontal" - horizontal split (left/right)
+ - "vertical" - vertical split (top/bottom)
+ - "tab" - new tab besides current window
+} str windowing_placement window
+
+define-command terminal -params 1.. -docstring %{
+ terminal <program> [<arguments>]: create a new terminal using the preferred windowing environment and placement
+
+ This executes "%opt{windowing_module}-terminal-%opt{windowing_placement}" with the given arguments.
+ If the windowing module is 'wayland', 'sway' or 'x11', then the 'termcmd' option is used as terminal program.
+
+ Example usage:
+
+ terminal sh
+ evaluate-commands %{ set local windowing_placement horizontal; terminal sh }
+
+ See also the 'new' command.
+} %{
+ "%opt{windowing_module}-terminal-%opt{windowing_placement}" %arg{@}
+}
+complete-command terminal shell
+
+hook -group windowing global KakBegin .* %{
+ evaluate-commands %sh{
+ set -- ${kak_opt_windowing_modules}
+ if [ $# -gt 0 ]; then
+ echo 'try %{ '
+ while [ $# -ge 1 ]; do
+ echo "require-module ${1}; set-option global windowing_module ${1} } catch %{ "
+ shift
+ done
+ echo "echo -debug 'no windowing module detected' }"
+ fi
+ }
+}
diff --git a/autoload/windowing/iterm.kak b/autoload/windowing/iterm.kak
new file mode 100644
index 0000000..2ca46df
--- /dev/null
+++ b/autoload/windowing/iterm.kak
@@ -0,0 +1,124 @@
+# https://www.iterm2.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module iterm %{
+
+# ensure that we're running on iTerm
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM_PROGRAM" = "iTerm.app" ] || echo 'fail iTerm not detected'
+}
+
+define-command -hidden -params 2.. iterm-terminal-impl %{
+ nop %sh{
+ direction="$1"
+ shift
+ # join the arguments as one string for the shell execution (see x11.kak)
+ args=$(
+ for i in "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+
+ # go through another round of escaping for osascript
+ # \ -> \\
+ # " -> \"
+ do_esc() {
+ printf %s "$*" | sed -e 's|\\|\\\\|g; s|"|\\"|g'
+ }
+
+ escaped=$(do_esc "$args")
+ esc_path=$(do_esc "$PATH")
+ esc_tmp=$(do_esc "$TMPDIR")
+ cmd="env PATH='${esc_path}' TMPDIR='${esc_tmp}' $escaped"
+ if [ "$direction" = 'tab' ]; then
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " tell current window" \
+ -e " create tab with default profile command \"${cmd}\"" \
+ -e " end tell" \
+ -e "end tell" >/dev/null
+ elif [ "$direction" = 'window' ]; then
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " create window with default profile command \"${cmd}\"" \
+ -e "end tell" >/dev/null
+ else
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " tell current session of current window" \
+ -e " tell (split ${direction} with same profile command \"${cmd}\") to select" \
+ -e " end tell" \
+ -e "end tell" >/dev/null
+ fi
+ }
+}
+
+define-command iterm-terminal-vertical -params 1.. -docstring '
+iterm-terminal-vertical <program> [<arguments>]: create a new terminal as an iterm pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'vertically' %arg{@}
+}
+complete-command iterm-terminal-vertical shell
+
+define-command iterm-terminal-horizontal -params 1.. -docstring '
+iterm-terminal-horizontal <program> [<arguments>]: create a new terminal as an iterm pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'horizontally' %arg{@}
+}
+complete-command iterm-terminal-horizontal shell
+
+define-command iterm-terminal-tab -params 1.. -docstring '
+iterm-terminal-tab <program> [<arguments>]: create a new terminal as an iterm tab
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'tab' %arg{@}
+}
+complete-command iterm-terminal-tab shell
+
+define-command iterm-terminal-window -params 1.. -docstring '
+iterm-terminal-window <program> [<arguments>]: create a new terminal as an iterm window
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'window' %arg{@}
+}
+complete-command iterm-terminal-window shell
+
+define-command iterm-focus -params ..1 -docstring '
+iterm-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "evaluate-commands -client '$1' focus"
+ else
+ session="${kak_client_env_ITERM_SESSION_ID#*:}"
+ osascript \
+ -e "tell application \"iTerm\" to repeat with aWin in windows" \
+ -e " tell aWin to repeat with aTab in tabs" \
+ -e " tell aTab to repeat with aSession in sessions" \
+ -e " tell aSession" \
+ -e " if (unique id = \"${session}\") then" \
+ -e " tell aWin" \
+ -e " select" \
+ -e " end tell" \
+ -e " tell aTab" \
+ -e " select" \
+ -e " end tell" \
+ -e " select" \
+ -e " end if" \
+ -e " end tell" \
+ -e " end repeat" \
+ -e " end repeat" \
+ -e "end repeat"
+ fi
+ }
+}
+complete-command -menu iterm-focus client
+
+alias global focus iterm-focus
+
+}
diff --git a/autoload/windowing/kitty.kak b/autoload/windowing/kitty.kak
new file mode 100644
index 0000000..a81f969
--- /dev/null
+++ b/autoload/windowing/kitty.kak
@@ -0,0 +1,84 @@
+# https://sw.kovidgoyal.net/kitty/index.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module kitty %{
+
+# ensure that we're running on kitty
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM" = "xterm-kitty" ] || echo 'fail Kitty not detected'
+}
+
+declare-option -docstring %{window type that kitty creates on new and repl calls (window|os-window)} str kitty_window_type window
+
+define-command kitty-terminal-window -params 1.. -docstring '
+kitty-terminal-window <program> [<arguments>]: create a new terminal as a kitty window
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --type="$kak_opt_kitty_window_type" --cwd="$PWD" $match "$@"
+ }
+}
+complete-command kitty-terminal-window shell
+
+define-command kitty-terminal-tab -params 1.. -docstring '
+kitty-terminal-tab <program> [<arguments>]: create a new terminal as kitty tab
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --type=tab --cwd="$PWD" $match "$@"
+ }
+}
+complete-command kitty-terminal-tab shell
+
+define-command kitty-focus -params ..1 -docstring '
+kitty-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ else
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen focus-window --no-response $match
+ fi
+ }
+}
+complete-command -menu kitty-focus client
+
+alias global focus kitty-focus
+
+# deprecated
+define-command -hidden kitty-terminal -params 1.. %{
+ kitty-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/new-client.kak b/autoload/windowing/new-client.kak
new file mode 100644
index 0000000..f852b39
--- /dev/null
+++ b/autoload/windowing/new-client.kak
@@ -0,0 +1,9 @@
+define-command new -params .. -docstring '
+new [<commands>]: create a new Kakoune client
+The ''terminal'' command is used to determine the user''s preferred terminal emulator
+The optional arguments are passed as commands to the new client' \
+%{
+ terminal kak -c %val{session} -e "%arg{@}"
+}
+
+complete-command -menu new command
diff --git a/autoload/windowing/repl/dtach.kak b/autoload/windowing/repl/dtach.kak
new file mode 100644
index 0000000..02d11ec
--- /dev/null
+++ b/autoload/windowing/repl/dtach.kak
@@ -0,0 +1,38 @@
+provide-module dtach-repl %{
+
+# test if dtach is installed
+evaluate-commands %sh{
+ [ -n "$(command -v dtach)" ] || echo 'fail dtach not found'
+}
+
+declare-option -docstring "id of the REPL" str dtach_repl_id
+
+define-command -docstring %{
+ dtach-repl [<arguments>]: create a new terminal window for repl interaction
+ All optional parameters are forwarded to the new terminal window
+} \
+ -params .. \
+ dtach-repl %{ terminal sh -c %{
+ file="$(mktemp -u -t kak_dtach_repl.XXXXX)"
+ trap 'rm -f "${file}"' EXIT
+ printf "evaluate-commands -try-client $1 \
+ 'set-option current dtach_repl_id ${file}'" | kak -p "$2"
+ shift 2
+ dtach -c "${file}" -E sh -c "${@:-$SHELL}" || "${@:-$SHELL}"
+ } -- %val{client} %val{session} %arg{@}
+}
+complete-command dtach-repl shell
+
+define-command dtach-send-text -params 0..1 -docstring %{
+ dtach-send-text [text]: Send text to the REPL.
+ If no text is passed, then the selection is used
+ } %{
+ nop %sh{
+ printf "%s" "${@:-$kak_selection}" | dtach -p "$kak_opt_dtach_repl_id"
+ }
+}
+
+alias global repl-new dtach-repl
+alias global repl-send-text dtach-send-text
+
+}
diff --git a/autoload/windowing/repl/kitty.kak b/autoload/windowing/repl/kitty.kak
new file mode 100644
index 0000000..1f60366
--- /dev/null
+++ b/autoload/windowing/repl/kitty.kak
@@ -0,0 +1,56 @@
+hook global ModuleLoaded kitty %{
+ require-module kitty-repl
+}
+
+provide-module kitty-repl %{
+
+define-command -params .. \
+ -docstring %{
+ kitty-repl [<arguments>]: Create a new window for repl interaction.
+
+ All optional parameters are forwarded to the new window.
+ } \
+ kitty-repl %{
+ nop %sh{
+ if [ $# -eq 0 ]; then
+ cmd="${SHELL:-/bin/sh}"
+ else
+ cmd="$*"
+ fi
+
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --keep-focus --type="$kak_opt_kitty_window_type" --title=kak_repl_window --cwd="$PWD" $match $cmd
+ }
+}
+complete-command kitty-repl shell
+
+define-command -hidden -params 0..1 \
+ -docstring %{
+ kitty-send-text [text]: Send text to the REPL window.
+
+ If no text is passed, the selection is used.
+ } \
+ kitty-send-text %{
+ nop %sh{
+ if [ $# -eq 0 ]; then
+ text="$kak_selection"
+ else
+ text="$1"
+ fi
+ kitty @ send-text --match=title:kak_repl_window "$text"
+ }
+}
+
+alias global repl-new kitty-repl
+alias global repl-send-text kitty-send-text
+
+}
diff --git a/autoload/windowing/repl/tmux.kak b/autoload/windowing/repl/tmux.kak
new file mode 100644
index 0000000..5b77633
--- /dev/null
+++ b/autoload/windowing/repl/tmux.kak
@@ -0,0 +1,91 @@
+# http://tmux.github.io/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+# Tmux version >= 2 is required to use this module
+
+hook global ModuleLoaded tmux %{
+ require-module tmux-repl
+}
+
+provide-module tmux-repl %{
+
+declare-option -docstring "tmux pane id in which the REPL is running" str tmux_repl_id
+
+define-command -hidden -params 1.. tmux-repl-impl %{
+ evaluate-commands %sh{
+ if [ -z "$TMUX" ]; then
+ echo 'fail This command is only available in a tmux session'
+ exit
+ fi
+ tmux_args="$1"
+ if [ "${1%%-*}" = split ]; then
+ tmux_args="$tmux_args -t ${kak_client_env_TMUX_PANE}"
+ elif [ "${1%% *}" = new-window ]; then
+ session_id=$(tmux display-message -p -t ${kak_client_env_TMUX_PANE} '#{session_id}')
+ tmux_args="$tmux_args -t $session_id"
+ fi
+ shift
+ repl_pane_id=$(tmux $tmux_args -P -F '#{pane_id}' "$@")
+ printf "set-option current tmux_repl_id '%s'" "$repl_pane_id"
+ }
+}
+
+define-command tmux-repl-vertical -params 0.. -docstring "Create a new vertical pane for repl interaction" %{
+ tmux-repl-impl 'split-window -v' %arg{@}
+}
+complete-command tmux-repl-vertical shell
+
+define-command tmux-repl-horizontal -params 0.. -docstring "Create a new horizontal pane for repl interaction" %{
+ tmux-repl-impl 'split-window -h' %arg{@}
+}
+complete-command tmux-repl-horizontal shell
+
+define-command tmux-repl-window -params 0.. -docstring "Create a new window for repl interaction" %{
+ tmux-repl-impl 'new-window' %arg{@}
+}
+complete-command tmux-repl-window shell
+
+define-command -params 0..1 tmux-repl-set-pane -docstring %{
+ tmux-repl-set-pane [pane number]: Set an existing tmux pane for repl interaction
+ If the address of new pane is not given, next pane is used
+ (To get the pane number in tmux,
+ use 'tmux display-message -p '#{pane_id}'" in that pane)
+ } %{
+ evaluate-commands %sh{
+ if [ -z "$TMUX" ]; then
+ echo 'fail This command is only available in a tmux session'
+ exit
+ fi
+ if [ $# -eq 0 ]; then
+ curr_pane_no="${kak_client_env_TMUX_PANE#%}"
+ tgt_pane=$((curr_pane_no+1))
+ else
+ tgt_pane="$1"
+ fi
+ curr_win="$(tmux display-message -t ${kak_client_env_TMUX_PANE} -p '#{window_id}')"
+ if tmux list-panes -t "$curr_win" -F \#D | grep -Fxq "%"$tgt_pane; then
+ printf "set-option current tmux_repl_id '%s'" %$tgt_pane
+ else
+ echo 'fail The correct pane is not there. Activate using tmux-terminal-* or some other way'
+ fi
+ }
+}
+
+define-command -hidden tmux-send-text -params 0..1 -docstring %{
+ tmux-send-text [text]: Send text to the REPL pane.
+ If no text is passed, then the selection is used
+ } %{
+ evaluate-commands %sh{
+ if [ $# -eq 0 ]; then
+ tmux set-buffer -b kak_selection -- "${kak_selection}"
+ else
+ tmux set-buffer -b kak_selection -- "$1"
+ fi
+ tmux paste-buffer -b kak_selection -t "$kak_opt_tmux_repl_id" ||
+ echo 'fail tmux-send-text: failed to send text, see *debug* buffer for details'
+ }
+}
+
+alias global repl-new tmux-repl-horizontal
+alias global repl-send-text tmux-send-text
+
+}
diff --git a/autoload/windowing/repl/x11.kak b/autoload/windowing/repl/x11.kak
new file mode 100644
index 0000000..8f048a7
--- /dev/null
+++ b/autoload/windowing/repl/x11.kak
@@ -0,0 +1,41 @@
+hook global ModuleLoaded x11 %{
+ require-module x11-repl
+}
+
+provide-module x11-repl %{
+
+declare-option -docstring "window id of the REPL window" str x11_repl_id
+
+define-command -docstring %{
+ x11-repl [<arguments>]: create a new window for repl interaction
+ All optional parameters are forwarded to the new window
+} \
+ -params .. \
+ x11-repl %{ x11-terminal-window sh -c %{
+ winid="${WINDOWID:-$(xdotool search --pid ${PPID} | tail -1)}"
+ printf "evaluate-commands -try-client $1 \
+ 'set-option current x11_repl_id ${winid}'" | kak -p "$2"
+ shift 2;
+ [ "$1" ] && "$@" || "$SHELL"
+ } -- %val{client} %val{session} %arg{@}
+}
+complete-command x11-repl shell
+
+define-command x11-send-text -params 0..1 -docstring %{
+ x11-send-text [text]: Send text to the REPL window.
+ If no text is passed, then the selection is used
+ } %{
+ evaluate-commands %sh{
+ ([ "$#" -gt 0 ] && printf "%s" "$1" || printf "%s" "${kak_selection}" ) | xsel -i ||
+ echo 'fail x11-send-text: failed to run xsel, see *debug* buffer for details' &&
+ kak_winid=$(xdotool getactivewindow) &&
+ xdotool windowactivate "${kak_opt_x11_repl_id}" key --clearmodifiers Shift+Insert &&
+ xdotool windowactivate "${kak_winid}" ||
+ echo 'fail x11-send-text: failed to run xdotool, see *debug* buffer for details'
+ }
+}
+
+alias global repl-new x11-repl
+alias global repl-send-text x11-send-text
+
+}
diff --git a/autoload/windowing/screen.kak b/autoload/windowing/screen.kak
new file mode 100644
index 0000000..d5515c9
--- /dev/null
+++ b/autoload/windowing/screen.kak
@@ -0,0 +1,75 @@
+# http://gnu.org/software/screen/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+
+provide-module screen %{
+
+# ensure that we're running under screen
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$STY" ] || echo 'fail screen not detected'
+}
+
+define-command screen-terminal-impl -hidden -params 3.. %{
+ nop %sh{
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X eval "$1" "$2"
+ shift 2
+ # see x11.kak for what this achieves
+ args=$(
+ for i in "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+ screen -X screen sh -c "${args} ; screen -X remove" < "/dev/$tty"
+ }
+}
+
+define-command screen-terminal-vertical -params 1.. -docstring '
+screen-terminal-vertical <program> [<arguments>]: create a new terminal as a screen pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ screen-terminal-impl 'split -v' 'focus right' %arg{@}
+}
+complete-command screen-terminal-vertical shell
+
+define-command screen-terminal-horizontal -params 1.. -docstring '
+screen-terminal-horizontal <program> [<arguments>]: create a new terminal as a screen pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ screen-terminal-impl 'split -h' 'focus down' %arg{@}
+}
+complete-command screen-terminal-horizontal shell
+
+define-command screen-terminal-window -params 1.. -docstring '
+screen-terminal-window <program> [<arguments>]: create a new terminal as a screen window
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X screen "$@" < "/dev/$tty"
+ }
+}
+complete-command screen-terminal-window shell
+
+define-command screen-focus -params ..1 -docstring '
+screen-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "
+ evaluate-commands -client '$1' focus
+ "
+ elif [ -n "${kak_client_env_STY}" ]; then
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X select "${kak_client_env_WINDOW}" < "/dev/$tty"
+ fi
+ }
+}
+complete-command -menu screen-focus client
+
+alias global focus screen-focus
+
+}
diff --git a/autoload/windowing/sway.kak b/autoload/windowing/sway.kak
new file mode 100644
index 0000000..c4501c2
--- /dev/null
+++ b/autoload/windowing/sway.kak
@@ -0,0 +1,81 @@
+provide-module sway %{
+
+# Ensure we're actually in Sway
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] ||
+ [ -n "$SWAYSOCK" ] ||
+ echo 'fail SWAYSOCK is not set'
+}
+
+require-module 'wayland'
+
+alias global sway-terminal-window wayland-terminal-window
+
+define-command sway-terminal-vertical -params 1.. -docstring '
+ sway-terminal-vertical <program> [<arguments>]: create a new terminal as a Sway window
+ The current pane is split into two, top and bottom
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg split vertical}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-vertical shell
+
+define-command sway-terminal-horizontal -params 1.. -docstring '
+ sway-terminal-horizontal <program> [<arguments>]: create a new terminal as a Sway window
+ The current pane is split into two, left and right
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg split horizontal}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-horizontal shell
+
+define-command sway-terminal-tab -params 1.. -docstring '
+ sway-terminal-tab <program> [<arguments>]: create a new terminal as a Sway window
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg 'split horizontal; layout tabbed'}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-tab shell
+
+define-command sway-focus-pid -hidden %{
+ evaluate-commands %sh{
+ pid=$kak_client_pid
+
+ # Try to focus a window with the current PID, walking up the tree of
+ # parent processes until the focus eventually succeeds
+ while ! swaymsg [pid=$pid] focus > /dev/null 2> /dev/null ; do
+ # Replace the current PID with its parent PID
+ pid=$(ps -p $pid -o ppid=)
+
+ # If we couldn't get a PPID for some reason, or it's 1 or less, we
+ # should just fail.
+ if [ -z $pid ] || [ $pid -le 1 ]; then
+ echo "fail Can't find PID for Sway window to focus"
+ break
+ fi
+ done
+ }
+}
+
+define-command sway-focus -params ..1 -docstring '
+sway-focus [<kakoune_client>]: focus a given client''s window.
+If no client is passed, then the current client is used' \
+%{
+ # Quick branch to make sure we're calling sway-focus-pid from the client
+ # the user wants to focus on.
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' sway-focus-pid" "$1"
+ else
+ echo sway-focus-pid
+ fi
+ }
+}
+complete-command -menu sway-focus client
+
+alias global focus sway-focus
+
+}
diff --git a/autoload/windowing/tmux.kak b/autoload/windowing/tmux.kak
new file mode 100644
index 0000000..2d43db0
--- /dev/null
+++ b/autoload/windowing/tmux.kak
@@ -0,0 +1,82 @@
+# http://tmux.github.io/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module tmux %{
+
+# ensure we're running under tmux
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$TMUX" ] || echo 'fail tmux not detected'
+}
+
+define-command -hidden -params 2.. tmux-terminal-impl %{
+ evaluate-commands %sh{
+ tmux=${kak_client_env_TMUX:-$TMUX}
+ if [ -z "$tmux" ]; then
+ echo "fail 'This command is only available in a tmux session'"
+ exit
+ fi
+ tmux_args="$1"
+ if [ "${1%%-*}" = split ]; then
+ tmux_args="$tmux_args -t ${kak_client_env_TMUX_PANE}"
+ elif [ "${1%% *}" = new-window ]; then
+ session_id=$(tmux display-message -p -t ${kak_client_env_TMUX_PANE} '#{session_id}')
+ tmux_args="$tmux_args -t $session_id"
+ fi
+ shift
+ # ideally we should escape single ';' to stop tmux from interpreting it as a new command
+ # but that's probably too rare to care
+ if [ -n "$TMPDIR" ]; then
+ TMUX=$tmux tmux $tmux_args env TMPDIR="$TMPDIR" "$@"
+ else
+ TMUX=$tmux tmux $tmux_args "$@"
+ fi
+ }
+}
+
+define-command tmux-terminal-vertical -params 1.. -docstring '
+tmux-terminal-vertical <program> [<arguments>]: create a new terminal as a tmux pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'split-window -v' %arg{@}
+}
+complete-command tmux-terminal-vertical shell
+
+define-command tmux-terminal-horizontal -params 1.. -docstring '
+tmux-terminal-horizontal <program> [<arguments>]: create a new terminal as a tmux pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'split-window -h' %arg{@}
+}
+complete-command tmux-terminal-horizontal shell
+
+define-command tmux-terminal-window -params 1.. -docstring '
+tmux-terminal-window <program> [<arguments>]: create a new terminal as a tmux window
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'new-window' %arg{@}
+}
+complete-command tmux-terminal-window shell
+
+define-command tmux-focus -params ..1 -docstring '
+tmux-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ elif [ -n "${kak_client_env_TMUX}" ]; then
+ # select-pane makes the pane active in the window, but does not select the window. Both select-pane
+ # and select-window should be invoked in order to select a pane on a currently not focused window.
+ TMUX="${kak_client_env_TMUX}" tmux select-window -t "${kak_client_env_TMUX_PANE}" \; \
+ select-pane -t "${kak_client_env_TMUX_PANE}" > /dev/null
+ fi
+ }
+}
+complete-command -menu tmux-focus client
+
+## The default behaviour for the `new` command is to open an horizontal pane in a tmux session
+alias global focus tmux-focus
+
+}
diff --git a/autoload/windowing/wayland.kak b/autoload/windowing/wayland.kak
new file mode 100644
index 0000000..33737f7
--- /dev/null
+++ b/autoload/windowing/wayland.kak
@@ -0,0 +1,66 @@
+# wayland
+
+provide-module wayland %{
+
+# ensure that we're running in the right environment
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$WAYLAND_DISPLAY" ] || echo 'fail WAYLAND_DISPLAY is not set'
+}
+
+# termcmd should be set such as the next argument is the whole
+# command line to execute
+declare-option -docstring %{shell command run to spawn a new terminal
+A shell command is appended to the one set in this option at runtime} \
+ str termcmd %sh{
+ for termcmd in 'alacritty -e sh -c' \
+ 'kitty sh -c' \
+ 'foot sh -c' \
+ 'termite -e ' \
+ 'wterm -e sh -c' \
+ 'gnome-terminal -e ' \
+ 'xfce4-terminal -e ' \
+ 'konsole -e '; do
+ terminal=${termcmd%% *}
+ if command -v $terminal >/dev/null 2>&1; then
+ printf %s\\n "$termcmd"
+ exit
+ fi
+ done
+}
+
+define-command wayland-terminal-window -params 1.. -docstring '
+wayland-terminal-window <program> [<arguments>]: create a new terminal as a Wayland window
+The program passed as argument will be executed in the new terminal' \
+%{
+ evaluate-commands -save-regs 'a' %{
+ set-register a %arg{@}
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_termcmd}" ]; then
+ echo "fail 'termcmd option is not set'"
+ exit
+ fi
+ termcmd=$kak_opt_termcmd
+ args=$kak_quoted_reg_a
+ unset kak_opt_termcmd kak_quoted_reg_a
+ setsid ${termcmd} "$args" < /dev/null > /dev/null 2>&1 &
+ }
+ }
+}
+complete-command wayland-terminal-window shell
+
+define-command wayland-focus -params ..1 -docstring '
+wayland-focus [<kakoune_client>]: focus a given client''s window
+If no client is passed, then the current client is used' \
+%{
+ fail 'Focusing specific windows in most Wayland window managers is unsupported'
+}
+complete-command -menu wayland-focus client
+
+alias global focus wayland-focus
+
+# deprecated
+define-command -hidden wayland-terminal -params 1.. %{
+ wayland-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/wezterm.kak b/autoload/windowing/wezterm.kak
new file mode 100644
index 0000000..1e3fafd
--- /dev/null
+++ b/autoload/windowing/wezterm.kak
@@ -0,0 +1,70 @@
+# https://wezfurlong.org/wezterm/index.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+
+provide-module wezterm %{
+
+# ensure that we're running under screen
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$WEZTERM_UNIX_SOCKET" ] || echo 'fail wezterm not detected'
+}
+
+define-command wezterm-terminal-impl -hidden -params 2.. %{
+ nop %sh{
+ wezterm cli "$@"
+ }
+}
+
+define-command wezterm-terminal-vertical -params 1.. -docstring '
+wezterm-terminal-vertical <program> [<arguments>]: create a new terminal as a wezterm pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl split-pane --cwd "%val{client_env_PWD}" --bottom --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-vertical shell
+
+define-command wezterm-terminal-horizontal -params 1.. -docstring '
+wezterm-terminal-horizontal <program> [<arguments>]: create a new terminal as a wezterm pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl split-pane --cwd "%val{client_env_PWD}" --right --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-horizontal shell
+
+define-command wezterm-terminal-tab -params 1.. -docstring '
+wezterm-terminal-tab <program> [<arguments>]: create a new terminal as a wezterm tab
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl spawn --cwd "%val{client_env_PWD}" --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-tab shell
+
+define-command wezterm-terminal-window -params 1.. -docstring '
+wezterm-terminal-window <program> [<arguments>]: create a new terminal as a wezterm window
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl spawn --cwd "%val{client_env_PWD}" --new-window --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-window shell
+
+define-command wezterm-focus -params ..1 -docstring '
+wezterm-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "
+ evaluate-commands -client '$1' focus
+ "
+ elif [ -n "${kak_client_env_WEZTERM_PANE}" ]; then
+ wezterm cli activate-pane --pane-id "${kak_client_env_WEZTERM_PANE}" > /dev/null 2>&1
+ fi
+ }
+}
+complete-command -menu wezterm-focus client
+
+alias global focus wezterm-focus
+
+}
diff --git a/autoload/windowing/x11.kak b/autoload/windowing/x11.kak
new file mode 100644
index 0000000..f65718b
--- /dev/null
+++ b/autoload/windowing/x11.kak
@@ -0,0 +1,78 @@
+# x11
+
+provide-module x11 %{
+
+# ensure that we're running in the right environment
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$DISPLAY" ] || echo 'fail DISPLAY is not set'
+}
+
+# termcmd should be set such as the next argument is the whole
+# command line to execute
+declare-option -docstring %{shell command run to spawn a new terminal
+A shell command is appended to the one set in this option at runtime} \
+ str termcmd %sh{
+ for termcmd in 'alacritty -e sh -c' \
+ 'kitty sh -c' \
+ 'termite -e ' \
+ 'urxvt -e sh -c' \
+ 'rxvt -e sh -c' \
+ 'st -e sh -c' \
+ 'xterm -e sh -c' \
+ 'roxterm -e sh -c' \
+ 'mintty -e sh -c' \
+ 'sakura -x ' \
+ 'gnome-terminal -e ' \
+ 'xfce4-terminal -x sh -c' \
+ 'konsole -e '; do
+ terminal=${termcmd%% *}
+ if command -v $terminal >/dev/null 2>&1; then
+ printf %s\\n "$termcmd"
+ exit
+ fi
+ done
+}
+
+define-command x11-terminal-window -params 1.. -docstring '
+x11-terminal-window <program> [<arguments>]: create a new terminal as an X11 window
+The program passed as argument will be executed in the new terminal' \
+%{
+ evaluate-commands -save-regs 'a' %{
+ set-register a %arg{@}
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_termcmd}" ]; then
+ echo "fail 'termcmd option is not set'"
+ exit
+ fi
+ termcmd=$kak_opt_termcmd
+ args=$kak_quoted_reg_a
+ unset kak_opt_termcmd kak_quoted_reg_a
+ setsid ${termcmd} "$args" < /dev/null > /dev/null 2>&1 &
+ }
+ }
+}
+complete-command x11-terminal-window shell
+
+define-command x11-focus -params ..1 -docstring '
+x11-focus [<kakoune_client>]: focus a given client''s window
+If no client is passed, then the current client is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ else
+ xdotool windowactivate $kak_client_env_WINDOWID > /dev/null ||
+ echo 'fail failed to run x11-focus, see *debug* buffer for details'
+ fi
+ }
+}
+complete-command -menu x11-focus client
+
+alias global focus x11-focus
+
+# deprecated
+define-command -hidden x11-terminal -params 1.. %{
+ x11-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/zellij.kak b/autoload/windowing/zellij.kak
new file mode 100644
index 0000000..71f962e
--- /dev/null
+++ b/autoload/windowing/zellij.kak
@@ -0,0 +1,67 @@
+# https://zellij.dev/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module zellij %{
+
+# ensure we're running under zellij
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$ZELLIJ" -a -n "$ZELLIJ_SESSION_NAME" ] || echo 'fail zellij not detected'
+}
+
+define-command -params 1.. zellij-action %{ nop %sh{
+ zellij --session "$kak_client_env_ZELLIJ_SESSION_NAME" action "$@"
+}}
+define-command -hidden -params 2.. zellij-run %{ nop %sh{
+ zellij_run_options=$1
+ shift
+ zellij --session "$kak_client_env_ZELLIJ_SESSION_NAME" run $zellij_run_options -- "$@"
+}}
+
+define-command -hidden -params 1.. zellij-terminal-window %{
+ zellij-run "--close-on-exit" %arg{@}
+}
+complete-command zellij-terminal-window shell
+
+define-command zellij-terminal-vertical -params 1.. -docstring '
+zellij-terminal-vertical <program> [<arguments>]: create a new terminal as a zellij pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ zellij-run "--direction down" %arg{@}
+}
+complete-command zellij-terminal-vertical shell
+
+define-command zellij-terminal-horizontal -params 1.. -docstring '
+zellij-terminal-horizontal <program> [<arguments>]: create a new terminal as a zellij pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ zellij-run "--direction right" %arg{@}
+}
+complete-command zellij-terminal-horizontal shell
+
+define-command zellij-focus -params ..1 -docstring '
+zellij-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ elif [ -n "${kak_client_env_ZELLIJ}" ]; then
+ output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-zellij.XXXXXXXX)/dump-screen
+ pane_count=0
+ while [ $((pane_count+=1)) -lt 10 ]; do
+ if zellij action dump-screen "$output" && grep "${kak_client}@\[$kak_session\]" "$output" > /dev/null ; then
+ break;
+ fi
+ zellij action focus-next-pane
+ done
+ rm -r $(dirname $output)
+ fi
+}}
+complete-command -menu zellij-focus client
+
+## The default behaviour for the `new` command is to open an horizontal pane in a zellij session
+alias global focus zellij-focus
+
+}
+