A simple project switcher for Kakoune
Table of contents
I always appreciate a project-switching interface in my editor.
I didn't expect Kakoune, a barebones terminal editor, to have a built-in project switcher. But, after executing cd $project_dir; kak for the nth time, I started looking for a solution.
How other editors implement a project switcher
VS Code provides a project switcher through Open Recent action, bound to Ctrl+R on Linux. In my experience, any file or folder opened with VS Code's Open File or Open Folder dialogs show up here. So its a bit of a stretch to call it a project switcher if you frequently open files directly.
The following video snippet demonstrates VS Code's switcher:
JetBrains IDEs offer an unbounded Open Recent Project action, which opens the switcher at the tip of the cursor. though I use a FrameSwitcher plugin which performs the job better.
The following video snippet demonstrates WebStorm's switcher:
Elements of a project switcher
I identify two essential elements of a project switcher interface:
- Display a list of projects already registered with the interface, through which one can fuzzy find and switch to a project. Once switched, the selected project's directory becomes the current directory within the editor's context, so that editor actions happen in the selected project directory's context. So, for instance, a file finder would now look for a file within the selected project's directory. And so on.
- Add a new project to the interface. Usually this is automatic. When user opens a new project in JetBrains IDEs or VS Code, the editor automatically register the project to the switcher interface.
How I manifested the elements in Kakoune
Kakoune's scripting primitives, combined with its philosophy to use shell scripting to extend itself, were adequate tools to build a simple project switcher.
I needed a place to persistently register opened project directories in Kakoune. I chose a plaintext projects file that sits besides kakoune's config file ~/.config/kak/kakrc. Each line in projects store a path to a project dir.
Registering a new project with the interface
I wrote a Kakoune command :project-add that:
- lets user choose a directory from their filesystem, using Kakoune's path completion interface
-
appends the path of the chosen directory in
projectsfile - uses Kakoune's :change-directory primitive to set the chosen directory as Kakoune's working directory
Here is the implementation:
define-command project-add \
-docstring "Add a project" \
-params 1 %{
nop %sh{ printf "%s\n" "$1" >> ~/.config/kak/projects }
change-directory %arg{1}
exec ":file-picker "
}
complete-command project-add file
Switching to a registered project
I implemented a Kakoune command :project-pick that:
-
lists the paths stored in
projectsfile using Kakoune's completion interface - sets the working directory of Kakoune to the selected path using its :change-directory primitive
Here is the implementation:
define-command project-pick \
-docstring "Pick a project" \
-params 1 %{
change-directory %arg{1}
exec ":file-picker "
}
complete-command project-pick \
shell-script-candidates \
%{ cat ~/.config/kak/projects }
Bonus - file picker
Both :project-add and :project-pick commands lands the user on a file picker interface, which again builds on top of Kakoune's completion interface, so that they can start working right away on their project.
define-command file-picker \
-docstring "Pick a file from current directory" \
-params 1 %{ edit %arg{1} }
complete-command file-picker \
shell-script-candidates \
%{ git ls-files -c -o --exclude-standard }
Bonus - keybindings
Both the commands received their own keybindings for quick execution. Kakoune offers custom user modes which can act as namespaces to neatly tuck away related keybindings. I created a project user mode to do just that for my project switcher.
declare-user-mode project
map global user p ":enter-user-mode project
" \
-docstring "Project commands"
map global project p ":project-pick " \
-docstring "Pick a project"
map global project a ":project-add " \
-docstring "Add a project"
In closing
I could implement guardrails like checking for existence of the projects file, or add a :project-delete command, but I'm satisfied with the current state. Any comments are welcome.
Written by Jayesh Bhoot