"exec -with-hooks" for only specific hook/group

I messing around with writing a command to fix the indentation of text using the indent hooks but I only want to use those hooks, not trigger every other, is there a way I could do that?

define-command fix-indent -docstring "Fix indentation of selection" %{
  # Does not work correctly when selections are at the end of the document
  exec x
  eval -save-regs 'c' %sh{
    echo "exec <a-d>"
    IFS=$'\n'
    keys=$(
      echo -e "$(echo "$kak_selection" | sed -E 's/\\/\\\\/g;s/^[ \t]+//g')\n"  |
      sed 's/</<lt>/g'                                                          |
      sed 's/;/<semicolon>/g'                                                   |
      sed 's/$/<ret>/g;'                                                        |
      sed 's/ /<space>/g'                                                       |
      sed 's/%/%%/g'                                                            |
      sed 's/\t/<tab>/g'                                                        |
      sed 's/"/<dquote>/g'                                                      |
      sed "s/'/<quote>/g"                                                       |
      sed ':a;N;$!ba;s/\n//g'                                                   |
      sed 's/<ret>$//'
    )
    echo "exec -with-hooks 'O' \""$keys"\" '<esc>x<a-d>:select $kak_selection_desc<ret>x<a-:><a-semicolon>'"

    # echo "exec <dquote>cyuu<dquote>cR" # Needed to clear BufWrite hooks changes
  }
}

If you’re asking about exec -with-hooks, then no, it’s not possible to include only certain types of hooks. Although the indentation hooks shipped in different Kakoune filetype plugins are often copied from one plugin to the next, so there’s at least a minimum of consistency, they don’t have to be. In addition, a lot of people have the indentation hooks specifically disabled, so you can’t rely on them in general.

If your want to change the way indentation works, your best bet is to disable the existing hooks and replace them with your own, perhaps copy/pasted and edited from the originals, rather than trying to integrate with them as-is.

If you’re asking about exec -with-hooks

Seeing as there is no way to use -with-hooks with specific hooks then that’s that on that matter but to come back to the next point

If your want to change the way indentation works, your best bet is to disable the existing hooks and replace them with your own, perhaps copy/pasted and edited from the originals, rather than trying to integrate with them as-is.

Even if I use my own custom indent hook, I don’t see any way to insert text using the exec command with that hook, which is the whole point of what I am trying to do here. Thank you for the response though

A hook is just a hook, you can execute any command you like, including execute-keys.

Different hooks execute in different situations; indent hooks are usually triggered by an InsertChar \n hook, which means they run in insert mode, which means execute-keys should insert whatever keys you want to press. For example:

kak -n -e 'hook global InsertChar \n %{ execute-keys world }'

If I type ā€œhelloā€ and hit Enter, Kakoune types ā€œworldā€ immediately after it.

I think you’re misunderstanding the intention of the command I’m trying to create, the command takes your selection and reinserts it using the indent hooks to ā€œfixā€ the indentation of the selection. I don’t currently see how that is possible with what it is you are describing

How would I copy the current text, delete the selection then reinsert them with my own custom insert hook. I’m not retyping the code manually

Ah, you’re correct, I didn’t understand what you were trying to do - I thought you were trying to take text that had been indented by the existing hooks, and then retype it slightly differently to get a different result.

If the goal is to use Kakoune’s indenting hooks as an auto-formatting tool, then what you have is I think the best you can hope for.

I figured, thanks for responding. I have decided to use a way simpler approach that isn’t as nice as full indenting but at least will align some of the code

define-command align-to-prev-indent -docstring "Align selection lines to previous line's indent" %{
  eval -save-regs 'l' %{
    exec x<dquote>lZ<a-:><a-semicolon>K<a-s>)<a-&><dquote>lzx
  }
}
map global normal <s-tab> ":align-to-prev-indent<ret>"
1 Like

for that it’s worth, i’ve been using something like this for reindenting blocks of code

define-command indent-and-append -override %{
    execute-keys -with-hooks %sh{
        if [ "$kak_cursor_char_value" = "10" ] && [ "$kak_cursor_column" = "1" ] && [ "$kak_cursor_line" != "1" ]; then
            if [ "$kak_cursor_line" = "$kak_buf_line_count" ]; then
                printf "<a-d>o"
            else
                printf "<a-d>ko"
            fi
        else
            printf "<s-a>"
        fi
    }
}

define-command reindent %{
    evaluate-commands -save-regs '"^/wicp' %{
        set-register p '' # for safety
        try %{ disable-auto-pairs } # if auto-pairs is installed

        set-register w %val{selections_desc} # add whole selection to register w. otherwise removing comments will break the mark
        # save selection to register i
        execute-keys '<a-:>xH"iZ'

        # save and delete comments (c for contents, p for position)
        try %{
            set-register slash "(^[ \t]*%opt{comment_line}[^\n]*\n)"
            execute-keys "s<ret>"
            set-register c %val{selections}
            execute-keys "<a-d>"
            set-register p %val{selections_desc}
        }
        execute-keys '"iz'

        # unindent
        try %{
            execute-keys '"_s^[ \t]+<ret><a-d>'
        }
        execute-keys '"iz'

        # escape backslashes
        try %{
            execute-keys 's\\<ret>i\<esc>'
        }
        execute-keys '"iz'

        # retype everything with indentation hooks enables
        execute-keys -with-hooks "<a-d>:indent-and-append<ret>%sh{ echo ""$kak_selection"" | sed -e 's/</<lt>/g' }<esc>:select %val{selection_desc}<ret>"

        # restore comments
        try %{
            select %reg{p}
            execute-keys '"cP'
        }
        select %reg{w}

        try %{ enable-auto-pairs }
    }
}
map global normal '=' ":reindent<ret>"

it is a bit overly complicated, but works most of the time. from what i’ve found, it completely breaks on css, doesn’t de-indent closing ā€˜)’ in go’s multiline import, and doesn’t handle extra semicolon that is automatically inserted at the end of c-family enum, union and struct declarations.

adding support for hook groups does sound like it could make things easier for what we are trying to do here, but not really. as already mentioned, not all filetype plugins are created equal - even highlighting is all over the place (c-family doesn’t highlight operators at all, go does, while python treats = as builtin and == as operator).
it would take great effort to do the overhaul and enforce one particular indenting standard across all of them. and this in turn would break somebody else’s custom tooling built on top of currently existing behavior

1 Like

This seems like what I was trying to implement but it seemed really hacky and I just wanted a somewhat consistent solution. I’ll give your own a try for a while, thanks

I noticed that this would add extra backslashes to the text, adding

try %{
  execute-keys "s\\\\<ret>a<backspace><esc>"
}

after

execute-keys -with-hooks ā€œ:indent-and-append%sh{ echo ā€œā€$kak_selectionā€" | sed -e ā€˜s/<//g’ }:select %val{selection_desc}"

Fixes it for me.
Have you experienced that?

it does not for me, even with empty kakrc, with the exception of lines that start with \. my build of kakoune is from commit 03019caf, things may have changed between the versions.

on a side note, having looked at this command again, the :select %val{selection_desc} part is completely redundant, a line comment above selected block (if the block is not at the top of buffer) would make the whole selection commented, and an empty line above it would keep the first line unindented. this is rough

I’m messing around with the command to make it more consistent, I’ll send whatever I land on later

What I have ended up with so far that mostly works how I want

define-command indent-and-append -override %{
  execute-keys -with-hooks %sh{
    if [ "$kak_cursor_char_value" = "10" ] && [ "$kak_cursor_column" = "1" ] && [ "$kak_cursor_line" != "1" ]; then
      if [ "$kak_cursor_line" = "$kak_buf_line_count" ]; then
        printf "<a-d>o"
      else
        printf "<a-d>ko"
      fi
    else
      printf "<s-a>"
    fi
  }
}

declare-option -hidden int _reindent_undo_count 0
define-command reindent %{
  evaluate-commands -save-regs '"^/wicp' %{
    execute-keys '"iZx'
    evaluate-commands %sh{
      if [ ${#kak_selection} -eq 1 ] && [ $kak_cursor_char_value -eq 10 ]; then
        printf "fail"
      fi
    }
    execute-keys '"iz'

    set-register p '' # for safety
    try %{ disable-auto-pairs } # if auto-pairs is installed

    set-register w %val{selections_desc} # add whole selection to register w. otherwise removing comments will break the mark
    # save selection to register i
    execute-keys '<a-:>xH"iZ'

    # save and delete comments (c for contents, p for position)
    try %{
      set-register slash "(^\h* %opt{comment_line} [^\n]*\n)"
      execute-keys "xs<ret>_"
      set-register c %val{selections}
      set-register p %val{selections_desc}
    }
    execute-keys '"iz'

    # unindent
    try %{
      execute-keys 's^\h+<ret><a-d>'
      set-option -add global _reindent_undo_count 1
    }
    execute-keys '"iz'

    # escape backslashes
    try %{
      execute-keys 's\\<ret>i\<esc>'
      set-option -add global _reindent_undo_count 1
    }
    execute-keys '"iz'

    # retype everything with indentation hooks enables
    execute-keys -with-hooks "<a-d>:indent-and-append<ret>%sh{ echo ""$kak_selection"" | sed -E 's/</<lt>/g' }<esc>:select %val{selection_desc}<ret>"
    set-option -add global _reindent_undo_count 1

    # restore comments
    try %{
      select %reg{p}
      execute-keys 'x_"cR'
    }
    select %reg{w}

    try %{
      execute-keys "xs\\\\<ret>a<backspace><esc>"
      set-option -add global _reindent_undo_count 1
    }
    
    try %{
      execute-keys "xy%opt{_reindent_undo_count}uxR"
    }
    execute-keys "x"
    set-option global _reindent_undo_count 0

    try %{ enable-auto-pairs }

    echo ''
  }
}