Fricitionless depth-first search with :grep and friends

When I’m walking the results of :grep, :lsp-references and friends, I often want to do another recursive search.
Running :grep again destroys the old search buffer and with it the context of my original search, which is annoying.
We can rename the *grep* buffer but that’s a manual step that I might forget to do. Also, it becomes tedious to pick names after multiple levels of recursion.

To make this easier I have each :grep command push its resulting buffer onto a stack.
When I’m done with the current buffer, I can pop from the stack and return to the state before the latest search.

This allows to do a depth-first search across a code base, without having to keep any context in my head.

Here is the code with some example mappings.
I want to package it (as plugin or in the stdlib) but it currently generates a lot of garbage buffers, I’d like to solve this first. Maybe we can have hidden buffers…

# example mappings to traverse search results:
# temporary dependency on kak-lsp for brevity
map global normal <c-n> %{:lsp-next-location %opt{locations_stack_top}<ret>}
map global normal <c-p> %{:lsp-previous-location %opt{locations_stack_top}<ret>}
map global normal <c-r> %{:locations-pop<ret>}

declare-option -hidden str-list locations_stack

declare-option -hidden str locations_stack_top
hook global GlobalSetOption locations_stack=.* %{
    set-option global locations_stack_top %sh{
        eval set -- $kak_quoted_opt_locations_stack
        for top; do :; done
        printf %s "$top"
    }
}

hook global WinDisplay \*(?:callees|callers|diagnostics|goto|find|grep|implementations|lint-output|references|symbols)\*(?:-\d+)? %{
    locations-push
}

define-command locations-push -docstring "push a new locations buffer onto the stack" %{
    evaluate-commands %sh{
        eval set -- $kak_quoted_opt_locations_stack
        if printf '%s\n' "$@" | grep -Fxq -- "$kak_bufname"; then
            exit # already in the stack
        fi
        # rename to avoid conflict with *grep* etc.
        newname=$kak_bufname-$#
        echo "try %{ delete-buffer! $newname }"
        echo "rename-buffer $newname"
    }
    set-option -add global locations_stack %val{bufname}
}

define-command locations-pop -docstring "pop a locations buffer from the stack and return to previous location" %{
    evaluate-commands %sh{
        eval set -- $kak_quoted_opt_locations_stack
        if [ $# -lt 2 ]; then
            echo "fail locations-pop: no locations buffer to pop"
        fi
    }
    delete-buffer %opt{locations_stack_top}
    set-option -remove global locations_stack %opt{locations_stack_top}
    try %{
        evaluate-commands -try-client %opt{jumpclient} %{
            buffer %opt{locations_stack_top}
            grep-jump
        }
    }
}

define-command locations-clear -docstring "delete locations buffers" %{
    evaluate-commands %sh{
        eval set --  $kak_quoted_opt_locations_stack
        printf 'try %%{ delete-buffer %s }\n' "$@"
    }
    set-option global locations_stack
}
2 Likes

Thats just lovely.