Skip to content

Annotated examples of "Kakoune script"

Taupiqueur edited this page Oct 21, 2020 · 1 revision

From https://github.com/occivink/kakoune-find

# Apply changes only to open buffers, it takes 4 parameters
def -hidden find-apply-impl -params 4 %{
    # Evaluate the following block of Kakoune commands in the buffer indicated by %arg{1}
    # (which holds the filename that was selected in the find buffer)
    eval -buffer %arg{1} %{
        try %{ # If something fails in this block, it will not stop the execution outside of this try block
            # go to the target line and select it (except for \n)

            # arg 2 holds the line number
            # %arg{2}g   : Jump to the line indicated by %arg{2}
            # <a-x>      : Select the current line
            # H          : Move the end of the selection 1 to the left, this deselects the newline character
            exec "%arg{2}g<a-x>H"
            # check for noop, and abort if it's one
            reg / %arg{3} # Set the search register "/" to the contents of %arg{3}, which is a regex that matches if the current selection is exactly the changed content
            # <a-K>     : Keep all lines that DONT match the regex in the "/" register.
            # This regex is the changed content, so if it matches, we don't need to do anything.
            # <a-K> throws an error if no selections are left, which happens if the current selection contains the changed content
            exec <a-K><ret>
            # replace
            reg '"' %arg{4}  # Set the paste register (") to the contents of %arg{4} which contains the changed content
            exec R           # Replace the current selection with the contents of the (") register
            reg s "%reg{s}o" # Append an "o" to the "s" register since we successfully applied a change
        } catch %{
            # If the try block failed execute this
            # This happens if the <a-K> threw an error because the content was already the new content, so we could ignore
            reg i "%reg{i}o"  # Append an "o" to the "i" register since a change was ignored
        }
    }
}
def -hidden find-apply-force-impl -params 4 %{
    try %{
        # First try to run the normal apply, this will fail if it cannot open a buffer with the given filename.
        find-apply-impl %arg{@}
    } catch %{
        # the buffer wasn't open: try editing it
        # if this fails there is nothing we can do
        # -no-hooks : don't execute any hooks triggreed by these commands
        # -draft    : execute while preserving the selections of the user
        # -existing : Fail if the file does not exist, instead of creating the file
        eval -no-hooks -draft "edit -existing %arg{1}"        # Open the file with the given filename (%arg{1})
        find-apply-impl %arg{@}                               # Run the normal apply, this should succeed because we just opened a buffer for this file
        # -buffer   : Execute the given commands in the context of the given buffer (%arg{1})
        eval -no-hooks -buffer %arg{1} "write; delete-buffer" # Write the buffer to disk and delete it
    }
}

# This function takes 0 or 1 parameters
def find-apply-changes -params ..1 -docstring "
find-apply-changes [-force]: apply changes specified in the current buffer to their respective file
If -force is specified, changes will also be applied to files that do not currently have a buffer
" %{
    # Execute the block of code without running any Kakoune hooks
    # Save the registers c,s,i and f because they will be changed in this code
    eval -no-hooks -save-regs 'csif' %{
        # Set the values of s, i and f
        reg s "" # keep track of the amount of changes applied
        reg i "" # keep track of the amount of changes ignored
        reg f "" # keep track of the amount of changes failed
        # The following block is run in a %sh{} block, so it is shell code
        reg c %sh{                              # store the implementation to execute in the register "c"
            [ "$1" = "-force" ] &&              # If the first argument of find-apply-changes is the string "-force"
            printf find-apply-force-impl ||     # Use the force implementation
            printf find-apply-impl              # Else use the normal implementation
        }
        # Execute the block of code and save the / (find) and " (yank) registers because they will be changed
        eval -save-regs '/"' -draft %{
            # select all lines that match the *find* pattern
            #
            # This regex is quite hairy, I'll split it out
            # %       : select whole file
            # 3s      : create a selection for each match of the given regex and select the 3rd capture, this opens a prompt to enter the regex
            # ^       : Beginning of line

            # The following is capture group 1 which holds the filename
            # (       : Enter first capture group
            # [^\n]+? : Match any character except newline for 1 or more times, make the match as short as possible (?)
            # )       : Close first capture group

            # :       : Match a ":"

            # The following is capture group 2 which holds the line number
            # (       : Enter second capture group
            # \d+     : Match 1 or more numbers
            # )       : Close second capture group

            # The following is a non-capturing group "(?:   )" it does not capture its content. It matches an optional column number
            # (?:     : Open non-capturing group
            # :       : Match a ":"
            # \d+     : Match 1 or more numbers
            # )       : Close non-capturing group
            # ?       : Make matching this group optional

            # :       : Match a ":", this is the ":" before the line content

            # The following is capture group 3 which holds the (changed) line content
            # (       : Enter third capture group
            # [^\n]*  : Match 0 or more characters that are not a newline
            # )       : Close third capture group
            # $       : Match end of line

            # The <ret> key stands for pressing "Enter" or "Return", since `exec` executes keys, we need to hit enter to close the prompt that was opened by the `s` key.
            exec '%3s^([^\n]+?):(\d+)(?::\d+)?:([^\n]*)$<ret>'
            # Now the three capture groups are saved into the registres 1, 2 and 3.
            # 1 holds the filename
            # 2 holds the line numbers
            # 3 holds the (changed) line content

            # Evaluate the folowing for every selection (-itersel)
            eval -itersel %{
                try %{ # If something fails in this block, it will not stop the execution outside of this try block
                    # Set the search pattern ("/" register) to the selected content (which is the third capture group containing the line content)
                    exec -save-regs '' <a-*>
                    # Execute the command stored in register c with the following arguments:
                    # filename (reg 1)
                    # line number (reg 2)
                    # A regex that matches the line content (reg "/"). The \A and \z match the beginning and end of a selection respectively.
                    # The (changed) line content (reg 3)
                    %reg{c} %reg{1} %reg{2} "\A%reg{/}\z" %reg{3}
                } catch %{
                    # If the executed function failed for the given selection, add an "o" to the f register
                    reg f "%reg{f}o"
                }
            }
        }
        # print something in the prompt line while interpreting markup
        echo -markup %sh{                              # Open a shell
            printf "{Information}"                     # Apply the {Information} face, see :doc faces
            s=${#kak_main_reg_s}                       # Set a local variable "s" to the length of the Kakoune "s" register which holds an "o" for every successful application
            [ $s -ne 1 ] && p=s                        # If there are 0 or more than 1 changes applied, we need to say "changes" instead of "change", so set $p to "s"
            printf "%i change%s applied" "$s" "$p"     # Print the number of changes applied
            i=${#kak_main_reg_i}                       # Set a local variable "i" to the length of the Kakoune "i" register which holds an "o" for every successful application
            [ $i -gt 0 ] && printf ", %i ignored" "$i" # If there are ignored selections, print how many it were
            f=${#kak_main_reg_f}                       # Set a local variable "f" to the length of the Kakoune "f" register which holds an "o" for every successful application
            [ $f -gt 0 ] && printf ", %i failed" "$f"  # If there are failed selections, print how many it were
        }
    }
}
Clone this wiki locally