Overdr0ne/shelldon

Emacs Lisp

117

7 commits

updated May 16, 2025

See the code

README

#+TITLE: Shelldon: an enhanced shell interface

[[https://melpa.org/#/shelldon][file:https://melpa.org/packages/shelldon-badge.svg]]

[[./shell.jpg]]

* Introduction
This is what I use for my main shell/terminal. It is largely just an enhancement of async-shell-command that provides a more minibuffer-centric workflow, allowing you to store and navigate command outputs in separate buffers among other things.

[[./example.gif]]
(I have lots of windowing and minibuffer enhancements from other packages, so yours probably won’t look exactly like this. See usage below for ideas...)

* Installation
I just use straight+use-package like so:
#+begin_src elisp
    (use-package shelldon
      :straight (shelldon :type git
                          :host github
                          :repo "Overdr0ne/shelldon"
                          :branch "master"
                          :files ("shelldon.el"))
        ; below is the configuration I use, take what you want...
        ; tell bash this shell is interactive
        (setf shell-command-switch "-ic")
        ; recursive minibuffers for nested autocompletion from minibuffer commands,
        ; to e.g. interactively select from the kill-ring
        (setf enable-recursive-minibuffers t)
        ; comint output may contain SGR control sequences that may be translated into
        ; text properties if emacs has something equivalent. This requires special
        ; processing.
        (add-hook 'shelldon-mode-hook 'ansi-color-for-comint-mode-on)
        (add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
        (autoload 'ansi-color-for-comint-mode-on "ansi-color" nil t)
        ; Tell Emacs how to display shelldon’s output buffers
        (add-to-list 'display-buffer-alist
                     '("*shelldon:"
                       (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window display-buffer-pop-up-window)
                       (side . right)
                       (slot . 0)
                       (window-width . 80)))
  )
#+end_src
* Usage
In a standard terminal, all command inputs and outputs are tangled together in one buffer. In Shelldon, each command is entered into the minibuffer individually with the *shelldon* command, much like an internal M-x command, and output is sent to a buffer derived from each command name. Here are the core commands.

| command              | description                           |
|----------------------+---------------------------------------|
| shelldon             | enter shell command in the minibuffer |
| shelldon-loop        | enter a sequence of shell commands    |
| shelldon-output-hist | go too shelldon output buffer         |

Since Shelldon is largely just an enhancement of built-in shell interaction facilities, much of this is already documented within Emacs, but I've tried to present a unified workflow here.

** minibuffer
- You may of course search the minibuffer history for *shelldon* commands, as with any comint command, with *comint-history-isearch-backward* (M-r) and the like.

- If you’d like to then use shelldon like an actual REPL, well, we’re just missing the loop part, so I’ve also provided a *shelldon-loop* command.

- Customize the prompt by modifying the *shelldon-prompt-str* variable. You may also toggle the display of the current working directory with the shell-command-prompt-show-cwd, just like other shell commands.

- By popular demand, you may also change the workdir without leaving shelldon-loop with the *shelldon-cd* command, bound to "C-x C-f" in *shelldon-minibuffer-local-command-map* by default. This requires:
  #+begin_src elisp
  (setf enable-recursive-minibuffers t)
  #+end_src
  Personally, I just use dired when I want to navigate.
- You may of course add any other local minibuffer keybindings you like to *shelldon-minibuffer-local-command-map*.

** shelldon-mode
This is analogous to, and derived from Shell-mode -- basically the mode for displaying shelldon output.

- To search the output history, use the *shelldon-output-hist* command, and select the command whose output you’d like to open.

- Output buffer names begin with "*shelldon" such that you may configure display-buffer-alist to change how output windows are displayed. I’m a big fan of side windows, so I have configured things to display output buffers in the right side window by default. You can change this of course, by simply modifying the display-buffer-alist. For example, you could display the output buffer in the bottom side window like so:

  #+begin_src elisp
    (setf (alist-get "*\\(shelldon.*\\)" display-buffer-alist)
          `((display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window)
            (side . bottom)
            (slot . 0)
            (reusable-frames . visible)))
  #+end_src

  The buffer names also contain the executed command, so that may be used as well to configure windows on a per-command basis.

- You may need to emulate terminal keypresses in some situations, like for interactive menus. I use the *term-send-* set of functions for this purpose. So I map <up> to *term-send-up* and <down> to *term-send-down*.

** sh-mode (shell script mode)
- You may also send commands to Shelldon from a bash script buffer with the *shelldon-send-region* and *shelldon-send-current-line* commands. This is actually my preferred way of using shelldon- it is particularly useful when writing full-fledged shell scripts, allowing you to test as you go. Have a look:
  
[[./mode-demo.gif]]

** autocompletion
- I would also highly recommend installing the [[https://github.com/szermatt/emacs-bash-completion][bash-completion]] package, and probably some sort of completion enhancement package like [[https://github.com/raxod502/selectrum][selectrum]].

- If you do use some kind of autocompletion extension, you may wish to disable sorting. There’s no universal way to do that unfortunately, but here’s what I do for selectrum, to give you an idea:
  #+begin_src elisp
    (advice-add #'shelldon-output-history :around
		(lambda (old-fn)
		    (let ((selectrum-should-sort nil))
		    (funcall old-fn))))
  #+end_src

** miscellaneous
- If you would like shelldon to inherit your startup shell configurations, just pass the "-ic" flags to the shell(credit: http://www.nextpoint.se/?p=864):
  #+begin_src elisp
    (setq shell-command-switch “-ic”)
  #+end_src

- To enable ANSI colors, these must be set:
  #+begin_src elisp
    (add-hook 'shelldon-mode-hook 'ansi-color-for-comint-mode-on)
    (add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
    (autoload 'ansi-color-for-comint-mode-on "ansi-color" nil t)
  #+end_src

And that’s about it. Enjoy...

* Todos
** It might be possible to generalize this for other shells like the python shell.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

Overdr0ne

4 commits

b3n

1 commits

flocks

1 commits

syohex

1 commits

Overdr0ne/shelldon

Emacs Lisp

117

7 commits

updated May 16, 2025

See the code

README

#+TITLE: Shelldon: an enhanced shell interface

[[https://melpa.org/#/shelldon][file:https://melpa.org/packages/shelldon-badge.svg]]

[[./shell.jpg]]

* Introduction
This is what I use for my main shell/terminal. It is largely just an enhancement of async-shell-command that provides a more minibuffer-centric workflow, allowing you to store and navigate command outputs in separate buffers among other things.

[[./example.gif]]
(I have lots of windowing and minibuffer enhancements from other packages, so yours probably won’t look exactly like this. See usage below for ideas...)

* Installation
I just use straight+use-package like so:
#+begin_src elisp
    (use-package shelldon
      :straight (shelldon :type git
                          :host github
                          :repo "Overdr0ne/shelldon"
                          :branch "master"
                          :files ("shelldon.el"))
        ; below is the configuration I use, take what you want...
        ; tell bash this shell is interactive
        (setf shell-command-switch "-ic")
        ; recursive minibuffers for nested autocompletion from minibuffer commands,
        ; to e.g. interactively select from the kill-ring
        (setf enable-recursive-minibuffers t)
        ; comint output may contain SGR control sequences that may be translated into
        ; text properties if emacs has something equivalent. This requires special
        ; processing.
        (add-hook 'shelldon-mode-hook 'ansi-color-for-comint-mode-on)
        (add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
        (autoload 'ansi-color-for-comint-mode-on "ansi-color" nil t)
        ; Tell Emacs how to display shelldon’s output buffers
        (add-to-list 'display-buffer-alist
                     '("*shelldon:"
                       (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window display-buffer-pop-up-window)
                       (side . right)
                       (slot . 0)
                       (window-width . 80)))
  )
#+end_src
* Usage
In a standard terminal, all command inputs and outputs are tangled together in one buffer. In Shelldon, each command is entered into the minibuffer individually with the *shelldon* command, much like an internal M-x command, and output is sent to a buffer derived from each command name. Here are the core commands.

| command              | description                           |
|----------------------+---------------------------------------|
| shelldon             | enter shell command in the minibuffer |
| shelldon-loop        | enter a sequence of shell commands    |
| shelldon-output-hist | go too shelldon output buffer         |

Since Shelldon is largely just an enhancement of built-in shell interaction facilities, much of this is already documented within Emacs, but I've tried to present a unified workflow here.

** minibuffer
- You may of course search the minibuffer history for *shelldon* commands, as with any comint command, with *comint-history-isearch-backward* (M-r) and the like.

- If you’d like to then use shelldon like an actual REPL, well, we’re just missing the loop part, so I’ve also provided a *shelldon-loop* command.

- Customize the prompt by modifying the *shelldon-prompt-str* variable. You may also toggle the display of the current working directory with the shell-command-prompt-show-cwd, just like other shell commands.

- By popular demand, you may also change the workdir without leaving shelldon-loop with the *shelldon-cd* command, bound to "C-x C-f" in *shelldon-minibuffer-local-command-map* by default. This requires:
  #+begin_src elisp
  (setf enable-recursive-minibuffers t)
  #+end_src
  Personally, I just use dired when I want to navigate.
- You may of course add any other local minibuffer keybindings you like to *shelldon-minibuffer-local-command-map*.

** shelldon-mode
This is analogous to, and derived from Shell-mode -- basically the mode for displaying shelldon output.

- To search the output history, use the *shelldon-output-hist* command, and select the command whose output you’d like to open.

- Output buffer names begin with "*shelldon" such that you may configure display-buffer-alist to change how output windows are displayed. I’m a big fan of side windows, so I have configured things to display output buffers in the right side window by default. You can change this of course, by simply modifying the display-buffer-alist. For example, you could display the output buffer in the bottom side window like so:

  #+begin_src elisp
    (setf (alist-get "*\\(shelldon.*\\)" display-buffer-alist)
          `((display-buffer-reuse-window display-buffer-in-previous-window display-buffer-in-side-window)
            (side . bottom)
            (slot . 0)
            (reusable-frames . visible)))
  #+end_src

  The buffer names also contain the executed command, so that may be used as well to configure windows on a per-command basis.

- You may need to emulate terminal keypresses in some situations, like for interactive menus. I use the *term-send-* set of functions for this purpose. So I map <up> to *term-send-up* and <down> to *term-send-down*.

** sh-mode (shell script mode)
- You may also send commands to Shelldon from a bash script buffer with the *shelldon-send-region* and *shelldon-send-current-line* commands. This is actually my preferred way of using shelldon- it is particularly useful when writing full-fledged shell scripts, allowing you to test as you go. Have a look:
  
[[./mode-demo.gif]]

** autocompletion
- I would also highly recommend installing the [[https://github.com/szermatt/emacs-bash-completion][bash-completion]] package, and probably some sort of completion enhancement package like [[https://github.com/raxod502/selectrum][selectrum]].

- If you do use some kind of autocompletion extension, you may wish to disable sorting. There’s no universal way to do that unfortunately, but here’s what I do for selectrum, to give you an idea:
  #+begin_src elisp
    (advice-add #'shelldon-output-history :around
		(lambda (old-fn)
		    (let ((selectrum-should-sort nil))
		    (funcall old-fn))))
  #+end_src

** miscellaneous
- If you would like shelldon to inherit your startup shell configurations, just pass the "-ic" flags to the shell(credit: http://www.nextpoint.se/?p=864):
  #+begin_src elisp
    (setq shell-command-switch “-ic”)
  #+end_src

- To enable ANSI colors, these must be set:
  #+begin_src elisp
    (add-hook 'shelldon-mode-hook 'ansi-color-for-comint-mode-on)
    (add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
    (autoload 'ansi-color-for-comint-mode-on "ansi-color" nil t)
  #+end_src

And that’s about it. Enjoy...

* Todos
** It might be possible to generalize this for other shells like the python shell.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

Overdr0ne

4 commits

b3n

1 commits

flocks

1 commits

syohex

1 commits

Languages

Emacs Lisp

100.0%