Search code examples
windowsvimwindows-subsystem-for-linux

How to Open Windows Paths in Vim on WSL by Dynamically Modifying isfname?


I'm working in Vim on WSL, editing files that often contain Windows file paths, such as:

<Command>C:\Python\trunk\windows\scripts\RunBackup.bat</Command>

When I place my cursor on a Windows path like the one above, I'd like to open the corresponding file using Vim. In Linux paths, I can use gf, but for Windows paths, this doesn't work because of backslashes () and other characters like colons (:) in the path, which are not considered part of a "file name" by default.

I tried modifying Vim's isfname setting to include characters like :, , and spaces dynamically within a function. My approach temporarily adds these characters to isfname, uses to get the path, and then restores isfname to its original value after extracting the path. Here's the function I wrote:

function! OpenWindowsPath()
  " Save the original 'isfname' value
  let l:original_isfname = &isfname

  " Define the characters to ensure are in 'isfname'
  let l:required_chars = [':', '\\', ' ']
  let l:added_chars = []

  " Loop through required characters and add if missing
  for char in l:required_chars
    if index(split(&isfname, ','), char) == -1
      let &isfname .= ',' . char
      call add(l:added_chars, char)  " Track added characters
    endif
  endfor

  " Get the file path under the cursor using <cfile>
  let l:win_path = expand('<cfile>')

  " Restore the original 'isfname' value
  for char in l:added_chars
    let &isfname = substitute(&isfname, ',' . char, '', '')
  endfor

  " Convert Windows path to WSL path
  let l:linux_path = system('wslpath -u ' . shellescape(l:win_path))
  " Trim any trailing newline from the result
  let l:linux_path = substitute(l:linux_path, '\n', '', 'g')

  " Open the file in Vim
  if filereadable(l:linux_path)
    execute 'edit ' . l:linux_path
  else
    echoerr 'File not found: ' . l:linux_path
  endif
endfunction

" Map a keybinding (e.g., <Leader>o) to call the function
nnoremap <Leader>o :call OpenWindowsPath()<CR>

Issue:

Even after modifying isfname, doesn't always return the full Windows path under the cursor. For example:

  • Cursor on C: may only return C:.
  • Cursor on RunBackup.bat may only return RunBackup.bat.
  • Paths with spaces or special characters (e.g., C:\Program Files\Test.bat) are problematic. Even when space is added to isfname, the behavior is inconsistent.

Is there a reliable way to extract the full Windows path under the cursor, regardless of cursor position, with support for Windows path that contains spaces?


Solution

  • so it turned out my modification to the isfname wasn't working because I was adding characters, a useful hint came from the vim wiki for adding a space using its ascii number :set isfname+=32.

    So the correct manipulation of the isfname look like below::

      " Define the ASCII codes for required characters
      let l:required_chars_ascii = [58, 92, 32]  " 58 = ':', 92 = '\', 32 = space
      " Loop through required characters and add if missing
      for char_ascii in l:required_chars_ascii
        if index(split(&isfname, ','), nr2char(char_ascii)) == -1
          execute 'set isfname+=' . char_ascii
        endif
      endfor
    

    The full function looks like below::

    function! OpenWindowsPath()
      " Save the original 'isfname' value
      let l:original_isfname = &isfname
    
      " Define the ASCII codes for required characters
      let l:required_chars_ascii = [58, 92, 32]  " 58 = ':', 92 = '\', 32 = space
    
      " Loop through required characters and add if missing
      for char_ascii in l:required_chars_ascii
        if index(split(&isfname, ','), nr2char(char_ascii)) == -1
          execute 'set isfname+=' . char_ascii
        endif
      endfor
    
      " Get the file path under the cursor using <cfile>
      let l:win_path = expand('<cfile>')
    
      " Restore the original 'isfname' value
      let &isfname = l:original_isfname
    
      " Convert Windows path to WSL path
      let l:linux_path = system('wslpath -u ' . shellescape(l:win_path))
      " Trim any trailing newline from the result
      let l:linux_path = substitute(l:linux_path, '\n', '', 'g')
    
      " Open the file in Vim
      if filereadable(l:linux_path)
        execute 'edit ' . l:linux_path
      else
        echoerr 'File not found: ' . l:linux_path
      endif
    endfunction
    
    " Map a keybinding (e.g., <Leader>o) to call the function
    nnoremap <Leader>o :call OpenWindowsPath()<CR>