Skip to content

Add ghostel-project-dwim - #620

Closed
mrcnski wants to merge 1 commit into
dakra:mainfrom
mrcnski:project-dwim
Closed

mrcnski wants to merge 1 commit into
dakra:mainfrom
mrcnski:project-dwim

Conversation

@mrcnski

@mrcnski mrcnski commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Problem

Working with more than one terminal per project is awkward, because no single command is safe to bind as "take me to a project terminal":

  • ghostel-project keys its reuse check on the unnumbered project identity. Once only numbered or renamed instances remain (e.g. open two project terminals, kill the first), it creates yet another terminal instead of switching to the surviving one.
  • Reaching a specific one of several project terminals means ghostel-project-next cycling or ghostel-project-list-buffers, and both signal an error when the project has no terminal yet.

(Related: #133 asked for support for multiple terminals per project. The cycle and list commands have since been added to ghostel, but without a tidy way to make everything work together in a single command.)

Fix

A new command, ghostel-project-dwim, that does the expected thing at every amount of project terminals:

  • with no project terminal it creates one via ghostel-project
  • with exactly one it switches to it, whatever it is named
  • with several it prompts via read-buffer, defaulting to the forward-cycle buffer like ghostel-project-list-buffers does. Membership uses the existing ghostel-project-buffer-scope machinery.
  • A prefix argument passes through to ghostel-project, so C-u still forces a new terminal and numeric session arguments keep their meaning.

For what it's worth, I surveyed how comparable packages (project.el's project-shell/project-eshell, eat, vterm, multi-vterm, mistty, vterm-toggle, shell-pop) handle several-terminals-per-project. All of them provide numeric prefix indices or blind cycling, consistent with ghostel, but none supports a picker. As far as I could tell, this would be a novel feature unique to ghostel. 🚀

Since ghostel already ships a read-buffer picker, extending it into a dwim entry point seemed like the natural ghostel-native answer.

Other info

Four new tests in test/ghostel-buffers-test.el cover the three branches and the prefix pass-through.

README command table and CHANGELOG entries included.

🤖 Generated with Claude Code

@dakra

dakra commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR.

I'm in the process of overhauling the whole public API anyway.
I'll have to sleep a night or two over your PR.
At first glance it's not necessarily what I would assume a -dwim command does,
it's basically just ghostel-project but when there are more than 1 buffer in the project it's ghostel-project-list-buffers.

@mrcnski

mrcnski commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Yep, it's basically a convenience wrapper over those two commands, picking the right one situationally. No worries if this isn't accepted, I'm just tinkering and contributing in case it's useful to others. :) Out of curiosity, what was your expectation for a dwim command?

In case it helps for the public API, here's my research into what's provided by other packages.

Package Named always-create? Force-new mechanism Several-terminals answer
project.el no C-u — (reuses first match)
eat no C-u / numeric index numeric index
vterm no C-u / numeric / string name numeric index
multi-vterm base command always new n/a cycle (-next/-prev)
mistty yes (mistty-create) prefix, but overloaded cycle (base command)
vterm-toggle no delegates to vterm cycle (-forward/-backward)
shell-pop no numeric index numeric index
  • "Named always-create" means whether there's a dedicated function to always create a new project terminal. For ghostel the answer would be no, which follows the dominant convention (call the project command with C-u to force a new project terminal).
  • For the final column ("Several-terminals"), I believe that ghostel combines prior art by accepting both numeric indices and cycling, which I think is sensible.

Create the project terminal when the project has none, switch to it
when there is exactly one, and pick one via read-buffer when there
are several (membership per ghostel-project-buffer-scope).  A prefix
argument passes through to ghostel-project, so C-u still forces a
new terminal.

Co-Authored-By: Claude Fable 5 <[email protected]>
@mrcnski

mrcnski commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

I've updated my personal dwim command to build on the recently-added consult-ghostel functionality:

  (defun ghostel-project-dwim (&optional arg)
    "Create, switch to, or consult-pick this project's ghostel terminal.
With prefix ARG, force a new terminal via `ghostel-project'."
    (interactive "P")
    (let ((bufs (and (not arg) (project-current nil)
                     (ghostel-project-buffer-list))))
      (cond ((or arg (null bufs)) (ghostel-project arg))
            ((null (cdr bufs))
             (pop-to-buffer (car bufs)
                            (append display-buffer--same-window-action
                                    '((category . comint)))))
            (t (consult-ghostel-project)))))

The result? Why it looks splendid (and works intuitively!).

CleanShot 2026-08-30 at 14 41 01@2x

@mrcnski mrcnski closed this Sep 18, 2026
@dakra

dakra commented Sep 18, 2026

Copy link
Copy Markdown
Owner

@mrcnski I'm thinking of changing the default behaviour of consult-ghostel(-project) to yours.
Or at least when there is no Terminal to just create one.
I don't think for consult-xxx commands there is a precedence like for the other terminal commands so it's not confusing other users (e.g. M-x ghostel has to behave like it does because M-x term, M-x vterm etc all behave the same).
What do you think?

@mrcnski

mrcnski commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for considering this and sharing your thoughts.

Regarding ghostel keeping precedent with term, vterm etc.: completely agreed here. In case it wasn't clear, my proposal was not about affecting the behavior of existing commands but about adding a new command, named something like ghostel-dwim.

About the consult commands getting some (or all) of the dwim behavior: here I think we should look at what existing consult commands do, and opt for consistency. I had Fable look at them and it found that "no built-in [consult command] bypasses the minibuffer when there is exactly one candidate" and that "there is [...] a general consult precedent, and the current consult-ghostel already fits it".

So here I think the more ergonomic functionality should live in a wrapper around the default consult ghostel commands - maybe documented somewhere like the README if it's too opinionated to bundle with the package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants