├── README.md ├── ido-springboard.el ├── ivy-springboard.el └── springboard.el /README.md: -------------------------------------------------------------------------------- 1 | How many times have you wanted to fire off a quick command, such as M-!, but 2 | the directory you want to run the command in isn't the same as the directory 3 | of the current buffer? In those situations, you want a quick way to change 4 | the default-directory *for only the next command*. That is what Springboard 5 | aims to solve. 6 | 7 | There are three packages in this repo, any which of you may require and use: 8 | 1. springboard package, springboard.el 9 | 10 | This package works by integrating with Helm. 11 | 12 | Bind it to a convenient key, like `Control-.`, and after you press it you'll 13 | see a handy Helm buffer showing the directories of all the files you've 14 | recently visited, plus the permanent directory list from 15 | `springboard-directories` -- a good place to list your active project 16 | directories. 17 | 18 | 2. ido-springboard package, ido-springboard.el 19 | 20 | This package works by advising ido-switch-buffer 21 | 22 | 3. ivy-springboard package, ivy-springboard.el 23 | 24 | This package works by advising ivy-switch-buffer 25 | 26 | In any of the three packages above, type a few chars to narrow down to the 27 | directory of interest, then just type your command, like `M-!`, `C-x C-f`, or 28 | whatever custom bindings you may have to run PCVS, Magit, etc. The moment you 29 | type your command, Springboard disappears, and if your command needs minibuffer 30 | input, you'll now be in the minibuffer for that new command. 31 | 32 | Here is an example use-package form for using ivy-springboard package: 33 | 34 | ``` 35 | (use-package ivy-springboard 36 | :demand t 37 | :load-path "/path/to/springboard directory") 38 | ``` 39 | -------------------------------------------------------------------------------- /ido-springboard.el: -------------------------------------------------------------------------------- 1 | ;;; ido-springboard.el --- Temporarily change default-directory for one command 2 | 3 | ;; Copyright (C) 2012 John Wiegley 4 | 5 | ;; Author: John Wiegley 6 | ;; Created: 13 Jun 2012 7 | ;; Version: 1.0 8 | ;; Keywords: ido 9 | ;; X-URL: https://github.com/jwiegley/springboard 10 | 11 | ;; This program is free software; you can redistribute it and/or 12 | ;; modify it under the terms of the GNU General Public License as 13 | ;; published by the Free Software Foundation; either version 2, or (at 14 | ;; your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, but 17 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | ;; General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 24 | ;; Boston, MA 02111-1307, USA. 25 | 26 | ;;; Commentary: 27 | 28 | ;; How many times have you wanted to fire off a quick command, such as M-!, 29 | ;; but the directory you want to run the command in isn't the same as the 30 | ;; directory of the current buffer? In those situations, you want a quick way 31 | ;; to change the default-directory *for only the next command*. That is what 32 | ;; Ido-Springboard aims to solve. 33 | ;; 34 | ;; It overrides command keys in ido-mode so that they use the 35 | ;; default-directory of the current viable completion, rather than the 36 | ;; default-directory of the buffer where you started the ido command. 37 | 38 | (require 'ido) 39 | 40 | (defgroup ido-springboard nil 41 | "Change default-directory for commands invoked at `ido-switch-buffer'." 42 | :group 'ido) 43 | 44 | (defcustom ido-springboard-ignored-commands 45 | '(self-insert-command 46 | delete-backward-char 47 | abort-recursive-edit 48 | exit-minibuffer 49 | switch-to-buffer 50 | backward-char 51 | forward-char 52 | kill-line 53 | move-beginning-of-line 54 | move-end-of-line 55 | backward-kill-word 56 | forward-kill-word) 57 | "Commands that will not be trapped by Ido-Springboard." 58 | :type '(repeat command) 59 | :group 'ido-springboard) 60 | 61 | (eval-when-compile 62 | (defvar ido-springboard-trapped nil)) 63 | 64 | (defun ido-springboard-match-directory () 65 | (let ((item (or (let ((buf (get-buffer (car ido-matches)))) 66 | (and buf 67 | (with-current-buffer buf 68 | default-directory))) 69 | (and ido-use-virtual-buffers ido-virtual-buffers 70 | (cdr (assoc (car ido-matches) 71 | ido-virtual-buffers)))))) 72 | (cond ((file-directory-p item) 73 | item) 74 | ((file-exists-p item) 75 | (file-name-directory item)) 76 | (t 77 | nil)))) 78 | 79 | (defun ido-springboard-add-trap () 80 | (add-hook 'pre-command-hook 'ido-springboard-trap-command t t)) 81 | 82 | (defun ido-springboard-remove-trap () 83 | (remove-hook 'pre-command-hook 'ido-springboard-trap-command t)) 84 | 85 | (defun ido-springboard-trap-command () 86 | (unless ido-springboard-trapped 87 | (condition-case err 88 | (unless (or (memq this-command ido-springboard-ignored-commands) 89 | (string-match "\\`ido-" (symbol-name this-command))) 90 | (let ((dir (ido-springboard-match-directory))) 91 | (when dir 92 | ;; (message "Trapped command: %s" this-command) 93 | (loop for buf in (buffer-list) 94 | when (minibufferp buf) 95 | do (with-current-buffer buf 96 | (ido-springboard-remove-trap))) 97 | (setq ido-springboard-trapped t) 98 | (throw 'abort dir)))) 99 | (error 100 | (message "Error occurred: %s" err))))) 101 | 102 | ;;;###autoload 103 | (defadvice ido-switch-buffer (around ido-springboard-ido-switch-buffer activate) 104 | "Adds ability to set `default-directory' for commands at ido minibuffer." 105 | (interactive) 106 | (add-hook 'minibuffer-setup-hook 'ido-springboard-add-trap) 107 | (add-hook 'minibuffer-exit-hook 'ido-springboard-remove-trap) 108 | (unwind-protect 109 | (let* (ido-springboard-trapped 110 | ido-springboard-already-trapped 111 | (default-directory (catch 'abort (ignore ad-do-it)))) 112 | (if default-directory 113 | (call-interactively this-command))) 114 | (remove-hook 'minibuffer-setup-hook 'ido-springboard-add-trap) 115 | (remove-hook 'minibuffer-exit-hook 'ido-springboard-remove-trap))) 116 | 117 | (provide 'ido-springboard) 118 | 119 | ;;; ido-springboard.el ends here 120 | -------------------------------------------------------------------------------- /ivy-springboard.el: -------------------------------------------------------------------------------- 1 | ;;; ivy-springboard.el --- Temporarily change default-directory for one command 2 | 3 | ;; Copyright (C) 2012 John Wiegley 4 | 5 | ;; Author: John Wiegley 6 | ;; Created: 13 Jun 2012 7 | ;; Version: 1.0 8 | ;; Keywords: ivy 9 | ;; X-URL: https://github.com/jwiegley/springboard 10 | 11 | ;; This program is free software; you can redistribute it and/or 12 | ;; modify it under the terms of the GNU General Public License as 13 | ;; published by the Free Software Foundation; either version 2, or (at 14 | ;; your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, but 17 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | ;; General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 24 | ;; Boston, MA 02111-1307, USA. 25 | 26 | ;;; Commentary: 27 | 28 | ;; How many times have you wanted to fire off a quick command, such as M-!, 29 | ;; but the directory you want to run the command in isn't the same as the 30 | ;; directory of the current buffer? In those situations, you want a quick way 31 | ;; to change the default-directory *for only the next command*. That is what 32 | ;; Ivy-Springboard aims to solve. 33 | ;; 34 | ;; It overrides command keys in ivy-mode so that they use the 35 | ;; default-directory of the current viable completion, rather than the 36 | ;; default-directory of the buffer where you started the ivy command. 37 | 38 | (require 'ivy) 39 | 40 | (defgroup ivy-springboard nil 41 | "Change default-directory for commands invoked at `ivy-switch-buffer'." 42 | :group 'ivy) 43 | 44 | (defcustom ivy-springboard-ignored-commands 45 | '(self-insert-command 46 | delete-backward-char 47 | abort-recursive-edit 48 | exit-minibuffer 49 | switch-to-buffer 50 | backward-char 51 | forward-char 52 | kill-line 53 | move-beginning-of-line 54 | move-end-of-line 55 | backward-kill-word 56 | forward-kill-word) 57 | "Commands that will not be trapped by Ivy-Springboard." 58 | :type '(repeat command) 59 | :group 'ivy-springboard) 60 | 61 | (eval-when-compile 62 | (defvar ivy-springboard-trapped nil)) 63 | 64 | (defun ivy-springboard-match-directory () 65 | (let ((item (or (let ((buf (get-buffer (car ivy--all-candidates ;; ido-matches 66 | )))) 67 | (and buf 68 | (with-current-buffer buf 69 | default-directory))) 70 | (and ivy-use-virtual-buffers ivy--virtual-buffers 71 | (cdr (assoc (car ivy--all-candidates) 72 | ivy--virtual-buffers)))))) 73 | (cond ((file-directory-p item) 74 | item) 75 | ((file-exists-p item) 76 | (file-name-directory item)) 77 | (t 78 | nil)))) 79 | 80 | (defun ivy-springboard-add-trap () 81 | (add-hook 'pre-command-hook 'ivy-springboard-trap-command t t)) 82 | 83 | (defun ivy-springboard-remove-trap () 84 | (remove-hook 'pre-command-hook 'ivy-springboard-trap-command t)) 85 | 86 | (defun ivy-springboard-trap-command () 87 | (unless ivy-springboard-trapped 88 | (condition-case err 89 | (unless (or (memq this-command ivy-springboard-ignored-commands) 90 | (string-match "\\`ivy-" (symbol-name this-command))) 91 | (let ((dir (ivy-springboard-match-directory))) 92 | (when dir 93 | ;; (message "Trapped command: %s" this-command) 94 | (loop for buf in (buffer-list) 95 | when (minibufferp buf) 96 | do (with-current-buffer buf 97 | (ivy-springboard-remove-trap))) 98 | (setq ivy-springboard-trapped t) 99 | (throw 'abort dir)))) 100 | (error 101 | (message "Error occurred: %s" err))))) 102 | 103 | ;;;###autoload 104 | (defadvice ivy-switch-buffer (around ivy-springboard-ivy-switch-buffer activate) 105 | "Adds ability to set `default-directory' for commands at ivy minibuffer." 106 | (interactive) 107 | (add-hook 'minibuffer-setup-hook 'ivy-springboard-add-trap) 108 | (add-hook 'minibuffer-exit-hook 'ivy-springboard-remove-trap) 109 | (unwind-protect 110 | (let* (ivy-springboard-trapped 111 | ivy-springboard-already-trapped 112 | (default-directory (catch 'abort (ignore ad-do-it)))) 113 | (if default-directory 114 | (call-interactively this-command))) 115 | (remove-hook 'minibuffer-setup-hook 'ivy-springboard-add-trap) 116 | (remove-hook 'minibuffer-exit-hook 'ivy-springboard-remove-trap))) 117 | 118 | (provide 'ivy-springboard) 119 | 120 | ;;; ivy-springboard.el ends here 121 | -------------------------------------------------------------------------------- /springboard.el: -------------------------------------------------------------------------------- 1 | ;;; springboard.el --- Temporarily change default-directory for one command 2 | 3 | ;; Copyright (C) 2012 John Wiegley 4 | 5 | ;; Author: John Wiegley 6 | ;; Created: 13 Jun 2012 7 | ;; Version: 1.0 8 | ;; Keywords: helm 9 | ;; Package-Requires: ((helm "1.6.9")) 10 | ;; X-URL: https://github.com/jwiegley/springboard 11 | 12 | ;; This program is free software; you can redistribute it and/or 13 | ;; modify it under the terms of the GNU General Public License as 14 | ;; published by the Free Software Foundation; either version 2, or (at 15 | ;; your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, but 18 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | ;; General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 25 | ;; Boston, MA 02111-1307, USA. 26 | 27 | ;;; Commentary: 28 | 29 | ;; How many times have you wanted to fire off a quick command, such as M-!, 30 | ;; but the directory you want to run the command in isn't the same as the 31 | ;; directory of the current buffer? In those situations, you want a quick way 32 | ;; to change the default-directory *for only the next command*. That is what 33 | ;; Springboard aims to solve. 34 | ;; 35 | ;; Bind it to a convenient key, like Control-., and after you press it you'll 36 | ;; see a handy Helm buffer showing the directories of all the files you've 37 | ;; recently visited, plus the permanent directory list from 38 | ;; `springboard-directories' -- a good place to list your active project 39 | ;; directories. 40 | ;; 41 | ;; Type a few chars to narrow down to the directory of interest, then just 42 | ;; type your command, like M-!, C-x C-f, or whatever custom bindings you may 43 | ;; have to run PCVS, Magit, etc. The moment you type your command, 44 | ;; Springboard disappears, and if your command needs minibuffer input, you'll 45 | ;; now be in the minibuffer for that new command. 46 | 47 | ;;; Code: 48 | 49 | (require 'helm) 50 | (require 'recentf) 51 | (require 'dired) 52 | 53 | (defgroup springboard nil 54 | "Temporarily change default-directory for one command" 55 | :group 'helm) 56 | 57 | (defcustom springboard-directories '("/usr/local") 58 | "List of directories that are always displayed in the Springboard." 59 | :type '(repeat directory) 60 | :group 'springboard) 61 | 62 | (defvar springboard-map nil) 63 | 64 | ;;;###autoload 65 | (defun springboard-apply-passthrough-keys () 66 | (setq springboard-map (copy-keymap helm-map)) 67 | (mapc #'(lambda (key) (define-key springboard-map key nil)) 68 | springboard-passthrough-keys)) 69 | 70 | (defcustom springboard-passthrough-keys '([(control ?x)] [open]) 71 | "List of directories that are always displayed in the Springboard. 72 | If you change this variable outside of the Customization interface, 73 | you must then call `springboard-apply-passthrough-keys'." 74 | :set #'(lambda (var value) 75 | (set var value) 76 | (springboard-apply-passthrough-keys)) 77 | :type '(repeat (sexp :tag "Key Codes")) 78 | :group 'springboard) 79 | 80 | (defcustom springboard-ignored-commands '(self-insert-command 81 | delete-backward-char 82 | abort-recursive-edit) 83 | "Commands that will not be trapped by Springboard. 84 | If you enter a command in Springboard that you were expecting 85 | Helm to handle, but instead your Springboard buffer suddenly 86 | disappears, then you need to add that command to this list." 87 | :type '(repeat command) 88 | :group 'springboard) 89 | 90 | (defcustom springboard-recentf-cutoff 50 91 | "Only consider this many files from the `recentf-files' list." 92 | :type 'integer 93 | :group 'springboard) 94 | 95 | (defcustom springboard-recentf-filter-function 96 | (function (lambda (elem) (not (string-match "\\.el\\.gz" elem)))) 97 | "Only use `recentf-files' entries for which function returns non-nil." 98 | :type 'function 99 | :group 'springboard) 100 | 101 | (defvar springboard-history nil) 102 | 103 | (eval-when-compile 104 | (defvar springboard-trapped nil) 105 | (defvar springboard-already-trapped nil)) 106 | 107 | (defun springboard-add-trap () 108 | (add-hook 'pre-command-hook 'springboard-trap-command t t)) 109 | 110 | (defun springboard-remove-trap () 111 | (remove-hook 'pre-command-hook 'springboard-trap-command t)) 112 | 113 | (defun springboard-trap-command () 114 | (unless springboard-already-trapped 115 | (condition-case err 116 | (if (or (memq this-command springboard-ignored-commands) 117 | (where-is-internal this-command (list springboard-map) t)) 118 | (setq springboard-trapped nil) 119 | (message "Trapped command: %s" this-command) 120 | (setq springboard-trapped t 121 | springboard-already-trapped t) 122 | (mapc #'(lambda (buf) 123 | (if (minibufferp buf) 124 | (with-current-buffer buf 125 | (springboard-remove-trap)))) 126 | (buffer-list)) 127 | (helm-confirm-and-exit-minibuffer)) 128 | (error 129 | (message "Error occurred: %s" err))))) 130 | 131 | (defun springboard-current-history () 132 | (let ((recentf-filtered-list 133 | (delete 134 | nil 135 | (mapcar (function 136 | (lambda (elem) 137 | (and (funcall springboard-recentf-filter-function 138 | elem) 139 | elem))) 140 | recentf-list)))) 141 | (delete-dups 142 | (append (delete 143 | nil (mapcar #'(lambda (buf) 144 | (let ((path (buffer-file-name buf))) 145 | (when path 146 | (if (file-directory-p path) 147 | path 148 | (file-name-directory path))))) 149 | (buffer-list))) 150 | (mapcar #'file-name-directory 151 | (butlast recentf-filtered-list 152 | (- (length recentf-filtered-list) 153 | springboard-recentf-cutoff))))))) 154 | 155 | ;;;###autoload 156 | (defun springboard () 157 | "`helm'-based command for temporarily changing the default directory." 158 | (interactive) 159 | (add-hook 'minibuffer-setup-hook 'springboard-add-trap) 160 | (add-hook 'minibuffer-exit-hook 'springboard-remove-trap) 161 | (unwind-protect 162 | (let* (springboard-trapped 163 | springboard-already-trapped 164 | special-display-buffer-names 165 | special-display-regexps 166 | helm-persistent-action-use-special-display 167 | (default-directory 168 | (helm-comp-read 169 | "Springboard: " springboard-directories 170 | :test #'file-directory-p 171 | :buffer "*Springboard*" 172 | :history (springboard-current-history) 173 | :input-history 'springboard-history 174 | :keymap springboard-map 175 | :persistent-action #'dired))) 176 | (message "Directory: %s; Trapped: %s" default-directory 177 | springboard-trapped) 178 | (if springboard-trapped 179 | (call-interactively this-command) 180 | (dired default-directory))) 181 | (remove-hook 'minibuffer-setup-hook 'springboard-add-trap) 182 | (remove-hook 'minibuffer-exit-hook 'springboard-remove-trap))) 183 | 184 | (provide 'springboard) 185 | 186 | ;;; springboard.el ends here 187 | --------------------------------------------------------------------------------