Finding file path in current line

Vim had a great feature gf which automatically selected file path in current line and jumped to the file.

I have these three bits in my config to acheive the same thing:
smart-select-file, search-file, and gf mapping.

I’m pretty satisfied with latter two bits. find-file works really well for me, and the mapping is fine.

But the smart-select-file function is not ideal:

try %{
    execute-keys "<a-k>..<ret>"
} catch %{
    # guess we have nonblank character under cursor
    execute-keys "<a-k>\w<ret>"
    execute-keys "<a-i><a-w>"
} catch %{
    # try selecting inside first occurrence of <...> string
    execute-keys "<a-x>s<<ret>)<space>"
    execute-keys "<a-i>a"
} catch %{
    # try selecting inside first occurrence of "..."
    execute-keys '<a-x>s"<ret>)<space>'
    execute-keys "<a-i>Q"
} catch %{
    # try selecting inside first  occurrence of '...'
    execute-keys "<a-x>s'<ret>)<space>"
    execute-keys "<a-i>q"
} catch %{
    # try select from cursor to the end of the line
    execute-keys "<a-l><a-k>\w<ret>"
} catch %{
    # try select from beginning to the end of the line
    execute-keys "Gi<a-l><a-k>\w<ret>"
} catch %{
    fail "no file can be selected"
}
try %{
    execute-keys "s/?\w[\S]+(?!/)<ret>)<space>"
} catch %{
    fail "failed to select file"
}

It continuously tries to select file in current line by trying different patterns, and it works, but this isn’t really searching for a path. Vim had really good guessing code, that could extract path from quotes, if it is quoted, and other interesting things.

I’ve tried to avoid shell expansions here, to make it more robust, and used only execute-keys and try catch to do conditional testing. Any recommendation on how I could improve the smart-select-file function intelligence?

} catch %{
    # try selecting inside first occurrence of "..."
    execute-keys '<a-x>s"<ret>)<space>'
    execute-keys "<a-i>Q"
} catch %{
    # try selecting inside first  occurrence of '...'
    execute-keys "<a-x>s'<ret>)<space>"
    execute-keys "<a-i>q"
}

This part reminds me of an old experiment I tried once.
In JavaScript, there’s 3 ways to make strings: 'single-quote strings', "double-quote strings" and `back-quote strings`.

But most of the time while editing, you just want to select a string, regardless of the type of quotes. (I wish this behavior was built-into Kakoune)

This is the command I ended up with:

define-command select-quote %{
  # save original
  execute-keys '"oZ'

  # save simple and back to original
  try %{
    execute-keys '<a-i>q"sZ'
  }
  execute-keys '"oz'

  # save double and back to original
  try %{
    execute-keys '<a-i>Q"dZ'
  }
  execute-keys '"oz'

  # save grave and back to original
  try %{
    execute-keys '<a-i>g"gZ'
  }
  execute-keys '"oz'

  try %{
    # sdg
    execute-keys '"sz'
    execute-keys '"d<a-z>-'
    execute-keys '"g<a-z>-'
  } catch %{
    # sd
    execute-keys '"sz'
    execute-keys '"d<a-z>-'
  } catch %{
    # sg
    execute-keys '"sz'
    execute-keys '"g<a-z>-'
  } catch %{
    # dg
    execute-keys '"dz'
    execute-keys '"g<a-z>-'
  } catch %{
    # s
    execute-keys '"sz'
  } catch %{
    # d
    execute-keys '"dz'
  } catch %{
    # g
    execute-keys '"gz'
  } catch %{
    # reset
    execute-keys '"oz'
  }
  echo
}

Unfortunately this snippet does not really help to simplify your code, but I just wanted to share with you that I’m also often trapped in the try/catch dance.

2 Likes

As an exercise I tried to implement the same functionality without catch statements (may not be identical in all circumstances).

define-command -docstring "all-string-select: selects any quote type."\
all-string-select %{
    # save original position for future selections.
    execute-keys '"oZ'
    # save original position to our 'final' register. Acts as default.
    execute-keys '"fZ'
    # save the whole buffer to the comparison register.
    execute-keys '%"sZ'
    try %{
        # try and select small quote block. If successful is saved to comparison & final.
        execute-keys '"oz<a-i>q"sZ"fZ'
    }
    try %{
        # try and select double quote block.
        execute-keys '"oz<a-i>Q'
        # compare to previous largest selection and save smallest to comparison & final.
        # EDIT - "s<a-z>-"sZ can be replaced with "s<a-Z>-
        execute-keys '"s<a-z>-"sZ"fZ'
    }
    try %{
        # repeat for grave marker
        execute-keys '"oz<a-i>g'
        execute-keys '"s<a-z>-"fZ'
    }
    # get the smallest quote object (or original cursor)
    execute-keys '"fz'
    echo
}

It has the benefit of scaling better if you wanted to add more checks because you perform them as you go rather than at the end, meaning you don’t have to try every option.

The approach might be applicable to andrey’s problem by doing something like

Select the smallest of
    x
    <a-i><a-w>
    <a-i>q
    <a-i>Q
    <a-i>a
    <a-i>g

But you end up with a function that takes get the object under the cursor rather than in the current line since
<a-i>q !== "<a-x>s'<ret>)<space><a-i>q"

1 Like