Show me your gf (goto-file)

A gripe I have with the default goto-file command is that it doesn’t understand syntax like file:line:column which is often outputted by debuggers (including kakoune’s !).

I’ve looked around and found some promising plugins, which I might link if I find again. They were focused on deciphering line and column numbers, and my current need is to quickly navigate rust’s cargo ouput.

So I made the following script file, which does 2 things:

  • provide a way to quickly navigate from filename to filename (candidates) (mapped to gF)
  • decipher one very simple format of file:line:column (overriding gf)
## better gf

map -docstring "file[:line[:column]]" global goto f "<esc>: goto-file<ret>"
define-command -docstring "go to the file corresponding to the current selection" goto-file %{
  # TODO add something to the effect of -itersel
  evaluate-commands edit -existing %sh{ echo "$kak_selection" | sed 's/:/ /g' }
}
alias global gf goto-file

# TODO use @val{count} if provided
define-command -docstring "go to the next filename candidate" -hidden goto-filename-regex %{
  evaluate-commands -draft %{
    try %{
      edit -existing *gf*
    } catch %{
      edit -scratch *gf*
      # turning the result of ls into a regex
      execute-keys '! ls -aF<ret><a-s>i|\Q<a-;><a-;>\E<del><esc>'
      # inserting absolute paths (root and home)
      execute-keys ',I((?<lt>=\s)~?/[\w\.]<esc>'
      # allowing suffixe
      execute-keys 'A)[^\s]*<esc>'
    }
    execute-keys -save-regs '' 'ggx_"/y'
  }
}

define-command -docstring "go to the next filename candidate" -hidden goto-filename %{
  evaluate-commands -save-regs '/' %{
    goto-filename-regex
    execute-keys '/<ret>'
  }
}
alias global gF goto-filename

define-command -docstring "go to the previous filename candidate. Unstable, might get replaced by count goto-filename" -hidden goto-filename-back %{
  evaluate-commands -save-regs '/' %{
    goto-filename-regex
    execute-keys '<a-/><ret>'
  }
}

map -docstring "goto filename" global goto F "<esc>: goto-filename<ret>g"
map -docstring "goto filename" global goto <a-F> "<esc>: goto-filename-back<ret>g"

I’m very curious how other people tackle that problem. I’m not entirely satisfied with what I have now, but for me, it’s a huge improvement over selecting filenames manually.

1 Like

I use this, it’s very helpful:

define-command -override my-goto-file -docstring "jump to file:line:col at cursor" %{
	evaluate-commands -save-regs ^ %{
		try %{
			execute-keys -save-regs '' Z
			execute-keys %{<semicolon><a-a><a-w>1s(?:cargo:warning=)?([^\s]+)(?::(\d+))(?::(\d+)\b(?![.]))<ret>)<space><esc>,<esc>}
		} catch %{
			execute-keys -save-regs '' z
			execute-keys %{<semicolon><a-a><a-w>1s(?:cargo:warning=)?([^:\s]+)(?::(\d+))?(?::(\d+)\b(?![.]))?<ret>)<space><esc>,<esc>}
		}
	}
	execute-keys -with-hooks "gf%reg{2}g<a-h>%reg{3}lh"
}

my current need is to quickly navigate rust’s cargo ouput.

cargo-next-error from the kakoune-cargo plugin should work.
We should try to add that to the stdlib, potentially merging into make.kak

2 Likes

I know this is an old thread but just decided to add the one I came up with today

define-command goto-file %{
  eval -save-regs 'gc' %{
    set-register c %val{cursor_char_column}
    exec '<dquote>gZx<dquote>gz'
    try %{
      exec '<a-a><a-w><a-semicolon>s(~|\$[\w-]+|\.\w*)?/?[\w.\-/]+(:[0-9]+)?(:[0-9]+)?<ret>'
    } catch %{
      exec '<dquote>gz'
      fail 'Cursor not over file path'
    }
    eval %sh{
      if [ $kak_reg_c -lt $kak_cursor_column ]; then
        echo "exec '<dquote>gz'"
        echo "fail 'Cursor not over file path'"
      fi
      file="$(echo "$kak_selection" | sed 's/^$HOME/~/;s/:/ /g')"
      echo "exec '<dquote>gz'"
      echo "try %{edit -existing $file} catch %{ fail 'Unable to find file \`$file\`' }"
    }
  }
}
map global goto f "<esc>:goto-file<ret>" -docstring "Go to matched file under the cursor"

I was trying to emulate how the behavior works in helix

I’ll join the thread bumping party (and this forum… Hello!).

I recently made one that tries not to shell out. I haven’t battle tested this enough to determine how solid it is, but it’s worked well enough for me so far in the use cases I come across. I found that with-hooks is required for filetype rcs to be loaded. And the Z register is required for resetting the selection before trying the next text object.

define-command smart-goto-file -docstring "a (hopefully) smarter goto file command" %{
    evaluate-commands -save-regs 'Z' %{
        try %{
            execute-keys -save-regs '' -with-hooks Zgf
        } catch %{
            execute-keys -with-hooks z<a-i><a-w>gf
        } catch %{
            execute-keys -with-hooks z<a-i><dquote>gf
        } catch %{
            execute-keys -with-hooks z<a-i><quote>gf
        } catch %{
            execute-keys -with-hooks z<a-i>bgf
        } catch %{
            execute-keys -with-hooks z<a-i>rgf
        } catch %{
            execute-keys -with-hooks z<a-i>Bgf
        } catch %{
            execute-keys -with-hooks z<a-i><lt>gf
        }
    }
}
map global goto f "<esc>: smart-goto-file<ret>" -docstring "smart goto file"

I suppose my goto file isn’t in the spirit of going to a line and column number. I just like not having to perfectly select the file’s full string each time. Just put your cursor somewhere in the string and hit gf.

3 Likes

This is cool, the reason I even went to shell was for the situation you have : and if the text isn’t surrounded by a pair character (I spent an unnecessary amount of time trying not to use shell)

Just updated this cause I forgot to also make a check that the cursor was not outside the length of the selected file

define-command goto-file %{
  eval -save-regs 'gce' %{
    set-register c %val{cursor_char_column}
    exec '<dquote>gZx<dquote>gz'
    try %{
      exec '<a-a><a-w><a-semicolon>s(~|\$[\w-]+|\.\w*)?/?[\w.\-/]+(:[0-9]+)?(:[0-9]+)?<ret>'
    } catch %{
      exec '<dquote>gz'
      fail 'Cursor not over file path'
    }
    eval %sh{
      if [ $kak_reg_c -lt $kak_cursor_column ]; then
        echo "exec '<dquote>gz'"
        echo "fail 'Cursor not over file path'"
      fi
    }
    exec "<a-semicolon>"
    set-register e %val{cursor_char_column}
    eval %sh{
      if [ $kak_reg_c -gt $kak_reg_e ]; then
        echo "exec '<dquote>gz'"
        echo "fail 'Cursor not over file path'"
      fi
      file="$(echo "$kak_selection" | sed "s|^\$HOME|~|;s|^\./|$(dirname "$kak_buffile")/|;s|:|  |g")"
      echo "exec '<dquote>gz'"
      echo "try %{edit -existing $file} catch %{ fail 'Unable to find file \`$kak_selection\`' }"
    }
  }
}
map global goto f "<esc>:goto-file<ret>" -docstring "Go to matched file under the cursor"