It's definitely powerful, but I'll push back a little bit on i3/Sway being really underpowered. There are things I can do in Emacs I can't do in i3. And the reverse is not true, Emacs is definitely capable of a superset of features. However, it's really only the very power-level stuff, the kinds of features you're listing above are all in the category of stuff that I already do in i3.

And what I found is that stuff like "toggling every Firefox window" or "listing all of my open windows and letting me fuzzy-search them" turns out to be about as power-level as I get in practice. So if losing the concept of buffers is the only thing holding you back, I still use "buffers" in i3, I use the same language to think about everything that I used with EXWM. Off the top of my head, some of the stuff I have set up:

- Multiple-key keybindings like I had in EXWM. For example, `alt-spc w d` (command window delete) hides a "buffer" instead of closing it, just like it does in my Emacs config. `alt-spc w /` splits a window right. Modes in general in i3 are abusable in some really interesting ways.

- Buffer fuzzy-search, toggles for application groups, re-grouping applications into different workspaces, etc...

- Window swapping, even between workspaces

- Window splitting/reorganizing using the same keybindings that I used in Emacs. This is a situation where i3 actually has an extremely slight edge because EXWM can't have multiple window views into the same application buffer, so you can't have two windows pointing to the same Firefox buffer. And this problem doesn't vanish outside of Emacs, but working with pure X11 gives you slightly more potential to do weird things around that.

I'm definitely not going to tell someone to switch, if you use EXWM and like it, great, stick with it, I see no problem with that at all. Just that I don't want to scare someone off of trying i3/Sway if they assume they can't script it.

----

The big difference I've found in how I build my config now is that i3 kind of takes the opposite approach to Emacs on interaction with other programs, where instead of having everything bundled inside one config file and pulling state out of those programs into Emacs, in i3 you call out of the config file into normal scripts and those scripts can control i3 with normal messaging or shell commands. So you can still build these really composable setups that are doing very complicated things, it just forces you to be a bit more Unix-y about it.

My personal experience (others may vary):

- The upside to that approach is that i3 is a bit less cumbersome to integrate with other programs, not because Emacs can't, but purely because the syntax is a little bit nicer and I don't need to worry about stuff like threading as much. Big upside in that I can have i3 triggers and commands that originate from other programs. In Emacs I can't really do anything unless Emacs started it or unless I set up a messaging system. It's kind of cool that any extension or script that's running in another program can also call into my i3-config for me if I decide to give it shell access.

- The big downside is stuff like state management, which becomes more annoying because you can't just have a list of pointers to windows that you care about stored in memory. You can do it (session variables, files, etc), but it's the reverse of the problem above, it's just more annoying than it would be in a Lisp repl where you could just make a list. I still haven't really gotten around to building an undo command just because I haven't gotten around to thinking about where I want to store the undo tree.

- And the other (slight) downside is not having nice display prompts built in by default. It's understated in Emacs how nice it is to have one generally agreed-on way to display a list of options on the screen, and Linux in general is missing that kind of universal one-off toolkit for building quick text interfaces/prompts for things. There are a lot of projects that try to fill in these gaps, I haven't found any that I love yet for quick scripting. But that could just be me.

Some of the stuff I miss from EXWM also comes from Emacs being kind of bad at playing with other programs. There's not a lot of good ways for me to add Emacs text buffers to my i3 search commands because Emacs doesn't have any good way I've ever been able find to run a command externally and then pipe the contents of the output buffer back. That means that any functionality I have in Emacs is just kind of locked in there. I can have a keyboard shortcut that opens up an Emacs frame to my TODO list and toggles that on/off, but it has to open up a frame. This has always been weird to me because I'm running Emacs in headless mode, it seems like I should be able to run commands and query text from a buffer without opening a frame.

But all that to say, if all you're talking about is toggling groups of windows or organizing views by project, then that's fine; you can do stuff like that no problem. i3 will give you access to its entire layout tree, so you can traverse it, you can get a list of buffers, and you can do things like edit it, save it to file, reload it. It's not perfect (stuff like merging trees is kind of annoying), but it's pretty usable for most stuff I've tried to do with it. It mostly just comes down to how complicated you want to get with remembering layout and managing state and stuff while you're toggling things.

----

My buffer swap script is:

#!/bin/bash
selected_id=$(~/.config/i3/scripts/helpers/buffer-list.sh)
i3-msg swap container with con_id "$selected_id"
i3-msg [con_id="$selected_id"] focus

Yes, I know the selected_id line is potentially vulnerable to malicious buffer names.

To split a buffer right and prompt for a new program (same setup I had in EXWM) I have <alt-spc w /> mapped to:

#!/bin/bash
selected_id=$(~/.config/i3/scripts/helpers/buffer-list.sh)
i3-msg mark __current_focused_buffer
i3-msg split h
i3-msg [con_id="$selected_id"] move container to mark __current_focused_buffer
i3-msg unmark __current_focused_buffer
i3-msg [con_id="$selected_id"] focus

Querying buffers as a helper script: (note the hurdles getting a buffer list from Emacs, and calling into multiple languages to do other parsing just because my Bash-foo is pretty bad):

#!/bin/bash
# Get emacs buffers
# echo "-eval (print-buffer-list)" | nc -U /run/user/$(id -u)/emacs/server 2>/dev/null
# TODO finish this and do something with it.

# Get i3 buffers -- I'm using node scripts for some parsing here because it's
# easier and because I can. Try to stop me.
i3_tree=$(i3-msg -t get_tree)
i3_buffers=$(echo $i3_tree | node ~/.config/i3/scripts/helpers/parse-i3-windows.js)

selected_buffer=$(echo "$i3_buffers" | dmenu -i -b -l 10 -fn 'pango:monospace 18' -p 'Choose a buffer:')
selected_id=$(echo $selected_buffer | awk '//{print $NF}')
echo $selected_id

This also can do things like filter out buffers based on titles or their position in the workspace, so basically all the same stuff you would expect from EXWM on that front.

I mentioned in the previous comment, but the other thing I've discovered switching to i3 is that being able to get low-level with some of this stuff and control i3 directly from anywhere turns out to be pretty nice for this kind of setup even if it does mean I'm forced to be a bit more verbose in my code, because my config is pretty set-it-and-forget-it; I add new capabilities only when I need them and otherwise I basically leave it alone. So I'm not really optimizing for being able to re-type this by memory constantly; I rarely edit my config once I have it the way I want it, and then I just have these "operations" like splitting/swapping as helper scripts and if I ever need to use them as part of another command, I call into them.