bling / fzf.el

A front-end for fzf
GNU General Public License v3.0
360 stars 50 forks source link

fzf sometimes fails with extra characters at the end of returned file name #84

Closed pierre-rouleau closed 1 year ago

pierre-rouleau commented 1 year ago

Running on Emacs 26.3 with fzf 0.35.1.

I noticed that under some circumstances fzf or fzf-directory fail opening the file found and selected.

The error reported error in process sentinel: user-error: followed with a file name that either had trailing whitespaces or had extra characters like a '$' or other characters that seemed to be part of the file name.

Upon investigation I found that the target variable in fzf/after-term-handle-exit had either trailing whitespace or extra characters. I'm not sure if the problem is a fzf problem or an Emacs 26.3 problem getting the returned string properly.

In either case I solved the problem by adding protection code against these types of errors which do not impact performances:

I will submit the bug fix once my previous PR (https://github.com/bling/fzf.el/pull/83) is integrated.

pierre-rouleau commented 1 year ago

My current solution to this is to use the following code:

(defun fzf/after-term-handle-exit (directory action)                                                                                                                                                                                                                                                                                                                        
  "Create and return lambda that handles the result of fzf.                                                                                                                                                                                                                                                                                                                 

The lambda must conform to `term-handle-exit':  i.e. accept 2 arguments:                                                                                                                                                                                                                                                                                                    
1) a process name, 2) an output msg.                                                                                                                                                                                                                                                                                                                                        

The lambda will call ACTION on the result of fzf if fzf exited successfully.                                                                                                                                                                                                                                                                                                
DIRECTORY, if non-nil, is prepended to the result of fzf."                                                                                                                                                                                                                                                                                                                  
  (lambda (process-name msg)                                                                                                                                                                                                                                                                                                                                                
    (let* ((exit-code (fzf/exit-code-from-event msg))                                                                                                                                                                                                                                                                                                                       
           (text (buffer-substring-no-properties (point-min) (point-max)))                                                                                                                                                                                                                                                                                                  
           (lines (split-string text "\n" t "\s*>\s+"))                                                                                                                                                                                                                                                                                                                     
           (target (string-trim                                                                                                                                                                                                                                                                                                                                             
                    (concat                                                                                                                                                                                                                                                                                                                                                 
                     (when directory                                                                                                                                                                                                                                                                                                                                        
                       (file-name-as-directory directory))                                                                                                                                                                                                                                                                                                                  
                     (car (last (butlast lines))))))                                                                                                                                                                                                                                                                                                                        
           (orig-target target))                                                                                                                                                                                                                                                                                                                                            
      ;; Sometimes the string returned by fzf has extraneous characters at the                                                                                                                                                                                                                                                                                              
      ;; end of the real/correct file name. Attempt to extract the correct                                                                                                                                                                                                                                                                                                  
      ;; file name by stripping 1 character at a time from the end.                                                                                                                                                                                                                                                                                                         
      (unless (file-exists-p target)                                                                                                                                                                                                                                                                                                                                        
        (while (and  (not (string= "" target))                                                                                                                                                                                                                                                                                                                              
                     (not (file-exists-p target)))                                                                                                                                                                                                                                                                                                                          
          (setq target (substring target 0 -1))))                                                                                                                                                                                                                                                                                                                           
      ;; report any remaining error by message instead of exception since                                                                                                                                                                                                                                                                                                   
      ;; we're in a handler we can't interrupt and provides a better trace.                                                                                                                                                                                                                                                                                                 
      (when (or (string= "" target)                                                                                                                                                                                                                                                                                                                                         
                (not (file-exists-p target)))                                                                                                                                                                                                                                                                                                                               
        (message "TERMINATING: process:[%s], msg:[%s]" process-name msg )                                                                                                                                                                                                                                                                                                   
        (message "FZF PROBLEM: non existing file identified [%s]" orig-target)                                                                                                                                                                                                                                                                                              
        (message "FZF returned text: [%s]" text))                                                                                                                                                                                                                                                                                                                           
      ;; Kill the fzf buffer and restore the previous window configuration.                                                                                                                                                                                                                                                                                                 
      (kill-buffer fzf/buffer-name)                                                                                                                                                                                                                                                                                                                                         
      (jump-to-register fzf/window-register)                                                                                                                                                                                                                                                                                                                                
      (message (format "FZF exited with code %s" exit-code))                                                                                                                                                                                                                                                                                                                
      ;; Only do something with the result if fzf was successful.                                                                                                                                                                                                                                                                                                           
      (when (string= "0" exit-code) (funcall action target)))                                                                                                                                                                                                                                                                                                               
    ;; Remove this advice so as to not interfere with other usages of `term`.                                                                                                                                                                                                                                                                                               
    ;; This gets added back in `fzf/start`                                                                                                                                                                                                                                                                                                                                  
    (advice-remove 'term-handle-exit                                                                                                                                                                                                                                                                                                                                        
                   (fzf/after-term-handle-exit directory action))))