One session for all projects

I’m experimenting with @ulis’s workflow to live with a single session.

In this workflow, I use buffer and find-edit commands quite intensively, and one thing I missed was the ability to switch to the project root of the current buffer, so the use of find-edit is easy.

define-command find-edit -params 1 -shell-script-candidates 'fd --type file' 'edit %arg(1)'
define-command find-edit-all -params 1 -shell-script-candidates 'fd --no-ignore --type file' 'edit %arg(1)'

alias global f find-edit
alias global fa find-edit-all

To solve that, I created a change-directory command and added a hook to automatically navigate into the project root of the current buffer:

hook global WinDisplay .* %{
  try change-directory-git
1 Like

Thanks for sharing. I’ve been using similar commands in


I also improved Connect to handle this use case.

alias kak=kak-connect

I’m not using multiple sessions at all. My use-case is simply – load up Kakoune and open project with fzf-project command from my fzf.kak script. It will automatically prompt me to choose file in this project to open. Searching files is not always from root, but if I’ve changed directory I can open files from root with fzf-git. If I need to work with another project, I can switch between them through fzf-project again.

This is more heavy solution than simply using shell candidates to get a list of files though.

Using a single session works quite great over SSH. I’ve got rid of tmux ever since.

I’m using the following configuration:

And ssh <host> -t kak-connect.

Usually, I type the command once and repeat Up and Return in a new terminal.

1 Like

This seems releated: I just came up with a thing for switching projects which is one of those things that’s both dumb simple and useful:

# Project
# ‾‾‾‾‾‾‾

define-command \
    -docstring 'Switch projects' \
    -params 1 \
    -shell-script-candidates %{IFS=:; for p in $CDPATH; do [ "$p" = . ] || ls -1 "$p" 2>/dev/null; done} \
    -menu \
    project %{
    evaluate-commands %sh{
        findProjectDir() {
            local IFS=:
            for p in $CDPATH; do
                if [ "$p" = . ]; then
                if [ -d "$p/$1" ]; then
                    echo "$p/$1"
                    return 0
            return 1
        dir=$(findProjectDir "$@")
        printf 'cd "%s"\n' "$dir"

alias global p project

I have projects in a few directories, so I set CDPATH to .:~/src:~/src/dotfiles/whatever, and I get candidates and automatically cd into one of those dirs.

1 Like

What are the core benefits of this flow over 1 per project?

I’m using the same kind of function but in fzf.kak, to change current projects, and it allows me to use external tools from proper location. Kak-lsp works if it has correct root or can find a root by itself, so in order for it to work I need to change to at least my project root. Since I have several projects it’s convenient to change PWD over time when I bounce between tasks. Also it narrows file searching and grepping.

@andreyorst don’t I get that all with 1 kakoune per project started from the projects root in its own tmux pane?

Not trying to put down the idea in anyway, I just don’t grok it.

I generally use one tmux window with an identically named Kakoune session per project… but I have cases where I want to switch projects. My first window has weechat on the left side, and on the right I do “miscellaneous” work, such as Kakoune PRs, nixpkgs PRs, tweak my dotfiles.

Although I feel like all this isn’t right yet. I’ve looked into tmuxp and queued up documentation on other tmux session managers, and I haven’t forgotten about my auto-layout-of-tmux-window idea yet.

I use single tmux session all the time. It doesn’t go away when I close terminal, and when I create new terminals they automatically attach to this session. This means I have single terminal opened pretty much always. I’ve acheived this with this alias:

alias tmux="tmux new-session -d -s \>_ 2>/dev/null; tmux new-session -t \>_ \; set-option destroy-unattached"
if [ -z "$TMUX" ]; then

So I always have single terminal with multiple tmux tabs in it. If I need to do several tasks at once, I use panes or tabs. I use tabs if these tasks are not related, isolating those in different views, and panes if they are related to each other. This is pretty much the same way how I use Kakoune.

When I work with single project, I use single tmux window, with several panes created by Kakoune. If I need to work with two different projects simultaniously I create new window with new Kakoune instance in it. But that’s a rare case and most of the time I just switch projects in single Kakoune instance without closing it for whole day. The same applies to my usage of Emacs and previously Vim. In emacs, if I need to work with two different documentations I use different virtual frames (like windows in tmux).

I’m now moving to one session for all projects with a fixed working directory not in any of the projects, and figured I’d log my issues here.

The fixed working directory bit is that I keep the editor in ~/src, which is where all of my projects are. This means I’m one directory away from everything I’m working on. It lowers the effort to switch projects and enables me to work easily across projects (like making changes to some utility and verifying it works with my dotfiles).


Bad (or, as yet unsolved problems):

:git log and :git diff don’t work inside Kakoune

I’m never in a git directory, and definitely not in the project I’m thinking about, so these can’t find the git directory. Which is not too bad, since I can run git diff |buffer - and I actually prefer this method - except…

buffer script has no filetype

It should be possible to auto-detect the filetype of text captured with buffer, but I haven’t done it. We rely on file already, and it detects the things I want, but how do I detect from a partial fifo buffer?

Jumping in *grep* doesn’t work

I’m not sure why. (It’s possible I messed it up somehow, but I don’t understand it yet).

In order to get decent results, I need to supply a directory name, e.g. :grep foo projectname, and the buffer gets the right filetype, and the filenames are correctly relative to the current working directory, but <ret> doesn’t jump to them.

Jumping in *grep* doesn’t work

I’m also annoyed by the same issue. I suppose this @occivink’s PR might cause it, because once I revert this commit, there is no problem.

But hmm… I can’t find what’s wrong with this PR (so I have’t created GitHub issue until now…).

Perhaps it’s the % bracket quote, which doesn’t allow %reg{x} to be expanded until the string is evaluated. Not sure why regular quotation mark quote wasn’t used there, which would expand the %reg{x} yet keep the result quoted.

Perhaps it’s the % bracket quote, which doesn’t allow %reg{x} to be expanded until the string is evaluated. Not sure why regular quotation mark quote wasn’t used there, which would expand the %reg{x} yet keep the result quoted.

It’s so that the expansion of the %reg{} is done not by eval, but by edit and so it ensures that the argument splitting is correct.
However, you’re right that it currently doesn’t seem to work if you use the “jumpclient” behavior (which I don’t, so I did not notice). Registers should be shared between clients though, so this might be a proper bug?

edit: this PR should fix it
edit #2: some “special” registers (%, ., #, integer ones) are client specific so maybe this is correct