Slatectx: Adding some context to the desktop

Date: — Topic: — Lang: — by Slatian

Most workflows today are app-centric, this is my attempt at trying something different

Why I created slatectx

To put my usecase and workflow in a few sentences:

What I'm mostly doing is editing textfiles, writing code, configuration, notes and so on. I'm doing a lot of thing in the shell (bash) and my text editor (kakoune), usually working with more than one window open using i3 or sway as a window manager. Leading to …

I want a new terminal and I want it in the same pwd as the last terminal I interacted with on that workspace!

The workspace constraint is because I sometimes switch between a reference or otherwise related project to the main thing I'm working on and I don't want the pwd on one to influence the pwd on the other and usually I already separate those by putting them onto a different workspace.

Identifying Contexts

To be able to do anything that is context aware, one needs a way to identify a certain context and a way of finding out which context currently applies. Since this journey started with a simple POC and then stayed pretty simple slatectx contexts are named by opaque (no assumptions based on the name) strings containing alphanumeric characters, dashes - and underscores _ with slashes \ being forbidden because these have to work in filenames.

Generating Context Names

With the requirements being that I don't want an additional daemon for doing housekeeping and be able to debug where the names are coming from I settled on the following rough naming scheme:

<session_type><sesion_id_or_display>-<workspace>

For example, the workspace I'm writing this post on maps to the context y1-9, derived from me currently using a wayland session that has wayland-1 set as its WAYLAND_DISPLAY and being on workspace 1. With an ssh connection this would simply be ssh and tmux uses t-<tmux_pid>.

To generate those identifiers my approach involved finding out which session type is currently used, getting the workspace and then stitching that together in the slatectx-get-context-name which is the central interface for getting the name of the current context (A bit like xdg-open for opening files).

The slatectx-get-context-name script is supposed to be replaced when one wishes to use a different context naming logic.

Switching directories

With the problem of figuring out the current context out solved one can implement things like a mot recently used directory (the original motivation). The logic behind that has now moved to its own tool slatectx-pwd.

When used with the set command it validates that the directory exists (and is a directory), turns a relative into an absolute path and stores the result in $XDG_RUNTIME_DIR/slatectx-pwd/$(slatectx-get-context-name).

The get operation reads this file, runs the checks again (the filesystem could have been modified in between) and prints the path out, depending on the check results with an error return code.

This little helper is used in my bashrc to cd into the last used directory on startup and hooked into the PROMPT_COMMAND to record the last used directory.

A similar thing happens with a filedialog script I'm using to quickly navigate the filesystem and open files, which on startup gets passed the result of slatectx-pwd get and has a hook to set the pwd on a change-directory event.

Another thing that is set up in my shell and text-editor is a cdx alias for quickly jumping to the slatectx-pwd in an already open session.

There is also experimental configuration for integrating it with lf which I don't use as often as I'd like to, because I haven't figured out how to make use of lf in a way that makes me not run into a wall at some point where a command I expect isn't there because I haven't figured out how to configure that part of lf yet. (one of the reasons I wrote the filedialog script)

Shared text-editor

One thing I missed after switching to kakoune from gedit was that with gedit I could just have a keybind that opened another text editing window and I could just move the tabs between them (not missing the tab UI though).

With kakoune having a client-server architecture one could replicate having multiple clients share the same open buffers, if only there was a way to find out what the server is call … yes one can tell kakoune what the socket should be called and whether it should start in client or server mode. That's what the kak-slatectx and the kak-e are for.

kak-slatectx

kak-slatectx replaces the kak command (using an alias) and by default tells kakoune to start with the given slatectx context as the socket name in client or server mode depending on the presence of the socket file. To make it a full replacement, this behaviour is completely disabled, should one attempt to pass the -c or -s options to kakoune.

kak-e

kak-e is the slatectx enabled fileopener for kakoune, for it to work kakoune has to remember the last window one interacted with, which is a pretty simple FocusIn hook paired with a custom current_client option. kak-e constructs an edit command for kakoune that then opens the given files in the last focused window, launching a new kakoune instance if needed.

Reusing an image viewer

Another thing I set up a wrapper for is imv (which at the time of writing is looking for a new maintainer!), an image viewer which also has a socket for remote controlling it. Since imv always uses its pid for this, an approach similar to slatectx-pwd is used to store the pid of the currently running imv instance and then using that like kak-e to open files in an already open window (Which currently is a bit glitchy).

imv-slatectx also has an option to disable launching an imv instance, which is useful for preview displays. Open an Image, look at it select the next and the image viewer automatically jumps to the selected image. Close the image viewer and the previews are gone with more screen real estate for managing files. (I've integrated it like this into my lf configuration and it is pretty useful)

Conclusion

Here I described why and how I used some relatively simple shellscript to implement some shared pieces of information that can be used to make applications work together. Something I miss with most GUI applications that maybe have a hared recently used, but completely ignore what the one using them is currently working on.

That said: Feel free to to reuse my code and ideas on this one and nag me on writing documentation and packaging if you are interested.