bb-tasks-and-more
Table of Contents
Design is about pulling things apart.
Rich Hickey
With bash-completion we get a really nice boost for making shell commands from Emacs.
Babashka tasks completions are for free, together with make, same as dynamic git, ssh and whatever.
Here is the screencast on youtube.
Below are sort of show notes.
1. Make sure you have completions in your shell (bash for me)
shell-file-name ; (setf shell-file-name "/bin/bash")
1.a for bb:
https://book.babashka.org/#_terminal_tab_completion
.bashrc
_bb_complete() {
BB_TASKS=$(bb tasks|bb -io '(->> *input* (drop 2) (map #(-> % (str/split #" ") first)))')
BB_HELP=$(bb help|bb -io '(->> *input* (map #(->> % (re-find #"^ ([-a-z]+)") second)) (filter some?))')
COMPREPLY=($(compgen -W "$BB_TASKS $BB_HELP" -- "${COMP_WORDS[$COMP_CWORD]}"))
}
complete -f -F _bb_complete bb
2. Setup bash-completion
https://github.com/szermatt/emacs-bash-completion
(use-package bash-completion :config (bash-completion-setup) (setf bash-completion-use-separate-processes t) (defun bash-completion-capf-1 (bol) (bash-completion-dynamic-complete-nocomint (funcall bol) (point) t)) (defun bash-completion-capf () (bash-completion-capf-1 #'point-at-bol)) (add-hook 'sh-mode-hook (defun mm/add-bash-completion () (add-hook 'completion-at-point-functions #'bash-completion-capf nil t))))
read-shell-command
Afterwards, this should work for you:
(I always try with git check...).
(read-shell-command "your command: ")
read-shell-command sets up a special keymap where tab now means completion-at-point.
read-from-minibuffer, 3thr arg
(read-from-minibuffer "foo: " nil (let ((m (make-sparse-keymap))) (define-key m "f" (defun my-hi () (interactive) (insert "lol"))) m))
Commands that use read-shell-command
- async-shell-command, shell-command
- dired-do-async-shell-command, dired-do-shell-command
- compile
I use project.el to run async shell commands and compile in the project root.
Run in any dir?
Design is about splitting things apart.
- Select a current dir, up to you. My main paths would be
consult-dir,project-switch-project,- consult-buffer + bookmarks
- Run
async-shell-commandor a friend of it.
More config
(setq async-shell-command-buffer 'new-buffer) (defun path-slug (dir) "Returns the initials of `dir`s path, with the last part appended fully Example: (path-slug \"/foo/bar/hello\") => \"f/b/hello\" " (let* ((path (replace-regexp-in-string "\\." "" dir)) (path (split-string path "/" t)) (path-s (mapconcat (lambda (it) (cl-subseq it 0 1)) (nbutlast (copy-sequence path) 1) "/")) (path-s (concat path-s "/" (car (last path))))) path-s)) (defun mm/put-command-in-async-buff-name (f &rest args) (let* ((path-s (if default-directory (path-slug default-directory) "")) (command (car args)) (buffname (concat path-s " " command)) (shell-command-buffer-name-async (format "*async-shell-command %s*" (string-trim (substring buffname 0 (min (length buffname) 50)))))) (apply f args))) (advice-add 'shell-command :around #'mm/put-command-in-async-buff-name) (add-hook 'comint-mode-hook (defun mm/do-hack-dir-locals (&rest _) (hack-dir-local-variables-non-file-buffer))) ;; it logs a warning when you hack a local ;; Making process-environment buffer-local while locally let-bound! ;; It is sort of want I want though (advice-add #'start-process-shell-command :before #'mm/do-hack-dir-locals) (advice-add 'compile :filter-args (defun mm/always-use-comint-for-compile (args) `(,(car args) t)))
shell
(defun mm/with-current-window-buffer (f &rest args) (with-current-buffer (window-buffer (car (window-list))) (apply f args))) (defun mm/shell-via-async-shell-command () (let ((display-buffer-alist '((".*" display-buffer-same-window)))) (async-shell-command shell-file-name))) (advice-add #'mm/shell-via-async-shell-command :around #'mm/with-current-window-buffer) (setf shell-kill-buffer-on-exit t) (add-hook 'shell-mode-hook (defun mm/shell-dont-mess-with-scroll-conservatively () (setq-local scroll-conservatively 0)))
I invoke mm/shell-via-async-shell-command with a stumpwm keybinding. This is the only reason I need mm/with-current-window-buffer irrc.
See also
- Bashing Is Better Than Extending, more in-depth on the same topic. But one of my earlier blog posts and too long.