简体   繁体   English

emacs shell:用ido更改目录

[英]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. 我越来越多地使用emacs shell-mode,并且我希望可以改进一些东西:更改目录时的完成。 I'd love to use ido or projectile-find-dir for that. 我喜欢用ido或者projectile-find-dir

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,…). 截至今天我尽我所能的Emacs的外壳之外 ,尽可能多使用emacs的力量尽可能(访问文件与IDO,查找文件的项目, projectile ,探索里面dired,...树)。

I don't cd that often. 我不经常这样做。 When I work in a different project I open up another shell buffer. 当我在一个不同的项目中工作时,我打开另一个shell缓冲区。 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 ). 但是当我不得不这样做的时候,我真的很想念ido或fasd shell实用程序(它的工作原理,但没有它的完成界面,这对zsh来说很棒,而且没有像使用ido那样强大,可能是https:// github。 com / clvv / fasd )。

How to wire that in elisp ? 如何在elisp中连接?

I know we can give a list to ido-completing-read ; 我知道我们可以列出ido-completing-read ;

In the shell, typing cd ../<TAB> opens up a new *Completions* buffer. 在shell中,键入cd ../<TAB>打开一个新的* Completions *缓冲区。 It uses comint-dynamic-completion , but how to get that list in an elisp list, not in a buffer ? 它使用comint-dynamic-completion ,但是如何在elisp列表中获取该列表,而不是在缓冲区中?

  • is it possible to wire that completions list into ido ? 是否可以将完成列表连接到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 编辑 :这是另一种很好的方式来cd到最近访问过的目录,使用fasd实用程序和ido完成: https ://gitlab.com/emacs-stuff/fasd-shell/blob/master/README.org

See another SO question . 另一个SO问题

ps: eshell doesn't work well with some shell scripts, I'd like to stay in shell-mode. ps:eshell与某些shell脚本不兼容,我想保持shell模式。

Try this, it is a quick and dirty hack and may fail in some cases but should work generally. 试试这个,这是一个快速和肮脏的黑客,在某些情况下可能会失败,但应该通常工作。 Also pardon my elisp 还原谅我的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 如果当前输入的命令是cd ,则在shell中按<tab>将提示使用ido可用的目录,否则它将回退到默认完成,退出按C-RET

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM