# your linter should output in this format:
# {filename}:{line}:{column}: {kind}: {message}
decl str lintcmd

decl -hidden line-flags lint_flags
decl -hidden str        lint_errors

def lint -docstring 'Parse the current buffer with a linter' %{
    %sh{
        dir=$(mktemp -d -t kak-lint.XXXXXXXX)
        mkfifo "$dir"/fifo
        printf '%s\n' "eval -no-hooks write $dir/buf"

        printf '%s\n' "eval -draft %{
                  edit! -fifo $dir/fifo -debug *lint-output*
                  set buffer filetype make
                  set buffer make_current_error_line 0
                  hook -group fifo buffer BufCloseFifo .* %{
                      nop %sh{ rm -r '$dir' }
                      remove-hooks buffer fifo
                  }
              }"

        { # do the parsing in the background and when ready send to the session

        eval "$kak_opt_lintcmd '$dir'/buf" | sort -t: -k2,2 -n > "$dir"/stderr
        printf '%s\n' "eval -client $kak_client echo 'linting done'" | kak -p "$kak_session"

        # Flags for the gutter:
        #   line3|{red}:line11|{yellow}
        # Contextual error messages:
        #   l1,c1,err1
        #   ln,cn,err2
        awk -F: -v file="$kak_buffile" -v stamp="$kak_timestamp" '
            /:[0-9]+:[0-9]+: ([Ff]atal )?[Ee]rror/ {
                flags = flags $2 "|{red}█:"
            }
            /:[0-9]+:[0-9]+:/ {
                if ($4 !~ /[Ee]rror/) {
                    flags = flags $2 "|{yellow}█:"
                }
            }
            /:[0-9]+:[0-9]+:/ {
                errors = errors $2 "," $3 "," substr($4,2) ":"
                # fix case where $5 is not the last field because of extra :s in the message
                for (i=5; i<=NF; i++) errors = errors $i ":"
                errors = substr(errors, 1, length(errors)-1) " (col " $3 ")\n"
            }
            END {
                print "set \"buffer=" file "\" lint_flags  %{" stamp ":" substr(flags,  1, length(flags)-1)  "}"
                errors = substr(errors, 1, length(errors)-1)
                gsub("~", "\\~", errors)
                print "set \"buffer=" file "\" lint_errors %~" errors "~"
            }
        ' "$dir"/stderr | kak -p "$kak_session"

        cut -d: -f2- "$dir"/stderr | sed "s@^@$kak_bufname:@" > "$dir"/fifo

        } >/dev/null 2>&1 </dev/null &
    }
}

def -hidden lint-show %{ %sh{
    desc=$(printf '%s\n' "$kak_opt_lint_errors" | sed -ne "/^$kak_cursor_line,.*/ { s/^[[:digit:]]*,[[:digit:]]*,//g; s/'/\\\\'/g; p; }")
    if [ -n "$desc" ]; then
        printf '%s\n' "info -anchor $kak_cursor_line.$kak_cursor_column '$desc'"
    fi
}}

def lint-enable -docstring "Activate automatic diagnostics of the code" %{
    add-highlighter flag_lines default lint_flags
    hook window -group lint-diagnostics NormalIdle .* %{ lint-show }
}

def lint-disable -docstring "Disable automatic diagnostics of the code" %{
    remove-highlighter hlflags_lint_flags
    remove-hooks window lint-diagnostics
}

def lint-next -docstring "Jump to the next line that contains an error" %{ %sh{
    printf '%s\n' "$kak_opt_lint_errors" | {
        while read -r line
        do
            # get line,column pair
            coords=$(printf %s "$line" | cut -d, -f1,2)
            candidate="${coords%,*}"
            if [ "$candidate" -gt "$kak_cursor_line" ]
            then
                break
            fi
        done
        if [ "$candidate" -gt "$kak_cursor_line" ]
        then
            col="${coords#*,}"
        else
            candidate="${kak_opt_lint_errors%%,*}"
            col=$(printf '%s\n' "$kak_opt_lint_errors" | head -n1 | cut -d, -f2)
        fi
        printf '%s\n' "select $candidate.$col,$candidate.$col"
    }
}}

def lint-prev -docstring "Jump to the previous line that contains an error" %{ %sh{
    printf '%s\n' "$kak_opt_lint_errors" | sort -t, -k1,1 -rn | {
        while read -r line
        do
            coords=$(printf %s "$line" | cut -d, -f1,2)
            candidate="${coords%,*}"
            if [ "$candidate" -lt "$kak_cursor_line" ]
            then
                break
            fi
        done
        if [ "$candidate" -lt "$kak_cursor_line" ]
        then
            col="${coords#*,}"
        else
            last=$(printf '%s\n' "$kak_opt_lint_errors" | tail -n1)
            candidate=$(printf '%s\n' "$last" | cut -d, -f1)
            col=$(printf '%s\n' "$last" | cut -d, -f2)
        fi
        printf '%s\n' "select $candidate.$col,$candidate.$col"
    }
}}