How to disable zsh tab completion for NFS dirs?

Solution 1:

To disable the completion completely if you are in those directories, you can use this code:

function restricted-expand-or-complete() {
        if [[ ! $PWD = /mnt/* ]]; then    
                zle expand-or-complete
        else    
                echo -en "\007"
        fi
}
zle -N restricted-expand-or-complete
bindkey "^I" restricted-expand-or-complete

This defines a function which checks if your working directory starts with /mnt/. If not, the default completion function is called via zle expand-or-complete otherwise a beep is produced. The function is declared as a widget (zle -N) and bound to TAB (bindkey).

However, this is only a start, because when you do somethine like that

/foo/bar$ cp hello.c /mnt/[TAB]

you are lost again. So you also had to exclude the /mnt/ tree from completion system. According to the doc that should do the trick:

zstyle ':completion:*:*files' ignored-patterns '/mnt/*'
zstyle ':completion:*:*directories' ignored-patterns '/mnt/*'

But I'm not sure if that'll prevent any stat function call to /mnt/ or only removes the matches afterwards. Please try and if there's still a noticable delay, extend the if clause in the restricted-expand-or-complete function to

if [[ ! $PWD = /mnt/* && ! ${${(z)LBUFFER}[-1]} = */mnt/* ]]

[EDIT]

I reworked this hack to be more flexible, but it still has issues and I'm still positive directly modifying the completion functions (perhaps _path_files) would be much cleaner. However...

This works (a summary in examples):

  • ls <TAB> is blocked inside a slow dir (/mnt)
  • ls /mnt/<TAB> is blocked
  • ls /home/user/symlink_to_mnt/<TAB> is blocked
  • cd /; ls mnt/<TAB> is blocked
  • tar --exclude-from=/mnt/<TAB> is blocked (an also the other variants, symlink, relative path)

This doesn't work:

  • in-path completion still completes /m/s/p to /mnt/some/path
  • completion of command options is blocked, if the option does not start with -, e.g. apt-get inst<TAB> won't work inside /mnt
  • in / strange behavior occurs under Cygwin, with Linux everything is fine

And here's the code:

function restricted-expand-or-complete() {

   # split into shell words also at "=", if IFS is unset use the default (blank, \t, \n, \0)
   local IFS="${IFS:- \n\t\0}="

   # this word is completed
   local complt

   # if the cursor is following a blank, you are completing in CWD
   # the condition would be much nicer, if it's based on IFS
   if [[ $LBUFFER[-1] = " " || $LBUFFER[-1] = "=" ]]; then
      complt="$PWD"
   else
      # otherwise take the last word of LBUFFER
      complt=${${=LBUFFER}[-1]}
   fi

   # determine the physical path, if $complt is not an option (i.e. beginning with "-")
   [[ $complt[1] = "-" ]] || complt=${complt:A}/

   # activate completion only if the file is on a local filesystem, otherwise produce a beep
   if [[ ! $complt = /mnt/* && ! $complt = /another/nfs-mount/* ]]; then    
      zle expand-or-complete
   else    
      echo -en "\007"
   fi
}
zle -N restricted-expand-or-complete
bindkey "^I" restricted-expand-or-complete