Skip to content

Instantly share code, notes, and snippets.

@vp777
Last active February 17, 2024 08:42
Show Gist options
  • Save vp777/bcb6962fc36bab46bf25b023a9d17e7b to your computer and use it in GitHub Desktop.
Save vp777/bcb6962fc36bab46bf25b023a9d17e7b to your computer and use it in GitHub Desktop.

originally inspired by https://gist.github.com/irsdl/5fc80ebad0d6fa211d3efdec288250a6

#examples:
#cdw C:\Use[tab]
#cdw C:\Program\ [double tab]
#cdw "C:\Program Files\[double tab]
#
#lsw should also work, eg lsw -lh C:\Use[tab]
#
#experimental support for cd to accept windows path as well
#eg it can handle both cd /home and cd C:\ 
#i havent tested it very much
#
#should be easy to add support for other commands
#make use of CDPATH somehow, eg simple CDPATH=/mnt/c/Users/$USER we can then just do cd Desktop without all the below
#we can also add some additional paths that currently are not handled by wslpath, eg ~

COMP_WORDBREAKS=${COMP_WORDBREAKS//:} #removes : from word breaks

#converts from /mnt/xxx to C:\xxx, custom implementation since wslpath always returns the target path if we are dealing with a symlink
function wslpath_to_win() {
    local path_parts win_path
    
    IFS=/ read -r -a path_parts <<< "$1"
    win_path=$(IFS=\\; echo "${path_parts[2]}:\\${path_parts[*]:3}" )
    echo "${win_path}"
}

function wslify_args_and_run() {
    local arg f fmeta process_only_paths=false new_args=()
    f="$1";shift
    fmeta=("$f")
    [[ $f = builtin\ * ]] && fmeta=(builtin "${f#builtin }")
    
    for arg in "$@"; do
      [[ $process_only_paths == true || $arg != -* ]] && arg="$(wslpath "${arg}")"
      [[ $arg == -- ]] && process_only_paths=true
      new_args+=("$arg")
    done
    
    "${fmeta[@]}" "${new_args[@]}"
}

function cdw() {
    wslify_args_and_run "builtin cd" "$@"
}

function lsw() {
    wslify_args_and_run ls "$@"
}

function _wslcomp_ls() {
  local custom_compgen_args=("-f")
  _wslcomp "$@"
}

function _wslcomp() {
  local cur mod_cur trcur current_comp  wpath path_parts debug=0
  local compgen_args=("-d")
  
  [[ -n $custom_compgen_args ]] && compgen_args=("${custom_compgen_args[@]}")
  
  function p() {
      local of=/dev/fd/1
      [[ $debug -eq 0 ]] && of=/dev/null
      echo "$@" >$of
  }
  
  cur=$(_get_cword)
  if [[ $cur != ?:* && $cur != ??:* ]]; then #test if we are looking to windows paths acceptable by wslpath, eg only c:\xxx, not very important limits possibility for issues with _wcd
    p "not supported windows path in arg: $cur"
    return 0
  fi
  
  #manually fix some escaped characters, not sure if this the right way, likely there is an alternative to _get_cword that does this job?
  #maybe look into how _cd does this (run `type _cd`)
  #in any case, we remove leading/trailing double quotes and replace "\ " with " "
  mod_cur="$cur"
  mod_cur="${mod_cur#\"}"
  mod_cur="${mod_cur%\"}"
  mod_cur="${mod_cur//\\ / }"
  
  
  #[[ $mod_cur = \~* ]] && {
  #  mod_cur="${mod_cur#?}"
  #  mod_cur="C:\\Users\\${USER}${mod_cur}"
  #  p "Expanded tilde to: $mod_cur" 
  #  we need a good way to replace cu
  #}
  
  trcur=$(wslpath "$mod_cur") #converts from C:\xxx to /mnt/xxx
  p "cur=$cur mod_cur=$mod_cur trcur=$trcur"
  
  while IFS= read -r -d '' current_comp; do
    #wpath=$(wslpath -w "$current_comp") #we avoid this since symlinks get expanded which causes issues
    wpath=$(wslpath_to_win "$current_comp")
    p "@@@@@ current_comp=${current_comp} @@@@@@@@@ wpath=${wpath} @@@@@@@@@@"
    COMPREPLY+=("\"${wpath}\"")
  done < <(compgen "${compgen_args[@]}" -- "$trcur"|tr '\n' '\0')
}

complete -o nospace -F _wslcomp cdw
complete -o nospace -F _wslcomp_ls lsw

alias lsw='lsw --color=auto'

#remove everything hereafter and just use cdw if overwriting cd causes issues, one sideeffect is the suppression of stderr for the current cmd
function _wcd() {
    _wslcomp "$@"
    [[ ${#COMPREPLY[@]} -eq 0 ]] && "${_prev_cd_comp}" "$@"
}

#untested
function is_win_path() {
    return $(wslpath "$1" >/dev/null)
}

#kind of clumsy implementation, it makes the assumption that if the built cd fails then we are likely dealing with a windows path
#and that the failed builtin cd command doesnt have any sideeffects. maybe another implementation is to first iterate the cd args
#to see if we can find a windows dir, eg using something like the is_win_path
function cd() {
    2>/dev/null builtin cd "$@" || cdw "$@" 
}

_prev_cd_comp=$(complete -p cd|grep -oe '-F [^ ]*'|awk '{print $2}')
complete -o nospace -F _wcd cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment