emacs shell: change directory with ido

I use the emacs shell-mode more and more, and there's something that I wish could be improved: the completion when changing directory. I'd love to use ido or projectile-find-dir for that.

My workflow

As of today I do all I can outside of emacs' shell, to use the power of emacs as much as possible (visiting files with ido, finding files in project with projectile , exploring the tree inside dired,…).

I don't cd that often. When I work in a different project I open up another shell buffer. But when I have to, I really miss ido or the fasd shell utility (which works, but without its completion interface which is great with zsh, and which isn't as powerfull as the use of ido could be https://github.com/clvv/fasd).

How to wire that in elisp ?

I know we can give a list to ido-completing-read ;

In the shell, typing cd ../<TAB> opens up a new *Completions* buffer. It uses comint-dynamic-completion , but how to get that list in an elisp list, not in a buffer ?

  • is it possible to wire that completions list into ido ? (or projectile or helm or whatever)
  • I would appreciate too if you link me to accurate documentation (there's a lot, it's difficult to know what's useful for me)
  • or does a solution exist yet ?
  • Thanks !

    edit : here is another nice way to cd to recently visited directories, with the fasd utility and ido completion: https://gitlab.com/emacs-stuff/fasd-shell/blob/master/README.org

    See another SO question.

    ps: eshell doesn't work well with some shell scripts, I'd like to stay in shell-mode.


    Try this, it is a quick and dirty hack and may fail in some cases but should work generally. Also pardon my elisp

    (require 'ido)
    (require 'cl-lib)
    (require 'shell)
    
    (defvar my-dir-selected nil "Flag to indicate that user has selected the directory")
    
    (defun my-filter-cd-input (current-input)
      "Takes current user input for `cd' the a list
        whose car is the 'maximum possible directory path'
        and cdr is remaining string.
    
        Examples:
        '~/.emacs.d/in => ('~./emacs.d/' 'in')
        '/home/gue' => ('/home/' 'gue')
        '~/../' => ('~/../' '')"
      (let* ((unquoted-input (shell-unquote-argument current-input))
         (components (split-string unquoted-input "/"))
             (directory-parts (butlast components))
             (possible-prefix (car (last components))))
        (list (if (string= possible-prefix "")
                  unquoted-input
                (concat (mapconcat 'identity directory-parts "/")
                        (when directory-parts "/")))
              possible-prefix)))
    
    (defun my-complete-directory-name (directory current-input)
      "Prompts user for directories in `directory', `current-input'
        is the string entered by the user till now"
      (let* ((filtered-input (my-filter-cd-input current-input))
             (directory-path (car filtered-input))
             (partial-input (cadr filtered-input))
             (directory-choices (mapcar 'file-name-nondirectory
                                        (condition-case nil
                                            (cl-remove-if-not 'file-directory-p
                                                              (directory-files (concat directory directory-path) t))
                                          ('file-error (list)))))
             (selected-name (ido-completing-read "Directory: "
                                                 directory-choices
                                                 nil nil partial-input)))
        (comint-delete-input)
        (insert (concat "cd " 
                (shell-quote-argument (concat directory-path selected-name "/"))))))
    
    (defun my-prompt-for-dir-or-fallback ()
      "If current shell command is `cd' prompt for directory
        using ido otherwise fallback to normal completion"
      (interactive)
      (let* ((user-input (buffer-substring-no-properties (comint-line-beginning-position)
                                                         (point-max))))
        (if (and (>= (length user-input) 3)
                 (string= (substring user-input 0 3) "cd "))
            (progn 
              (setq my-dir-selected nil)
              (while (not my-dir-selected)
                (my-complete-directory-name default-directory 
                        (buffer-substring-no-properties (+ (comint-line-beginning-position) 3) 
                                        (point-max))))
              (comint-send-input))
          (call-interactively 'completion-at-point))))
    
    (define-key shell-mode-map (kbd "<tab>") 'my-prompt-for-dir-or-fallback)
    
    (add-hook 'ido-setup-hook 'ido-my-keys)
    
    (defun ido-my-keys ()
      "Add my keybindings for ido."
      (define-key ido-completion-map (kbd "<C-return>") (lambda ()
                                                            (interactive)
                                                            (setq my-dir-selected t)
                                                            (ido-exit-minibuffer))))
    

    Hitting <tab> in shell will prompt for directories available using ido if the currently entered command is cd , otherwise it will fallback to default completion, to exit press C-RET

    链接地址: http://www.djcxy.com/p/18294.html

    上一篇: 在Entity Framework中将复杂类型映射到结果形式SqlQuery

    下一篇: emacs shell:用ido更改目录