From 867b28bda197c27b6219bc2535aae3a2dd9a28e6 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 27 Sep 2024 23:53:49 +0300 Subject: feat(emacs-pkgs/niri): functions for seamless niri/emacs switching Introduces a new buffer switching function which is also capable of switching to existing Emacs frames that already display the target buffer, or to other windows displayed in the same Niri session. Not all behaviour is done yet, and there's an explanatory comment in the package with more details. Change-Id: I5a548931a681ba32fdb352ecec66845a75268c19 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12535 Reviewed-by: tazjin Tested-by: BuildkiteCI --- tools/emacs-pkgs/niri/default.nix | 7 +++ tools/emacs-pkgs/niri/niri.el | 89 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 tools/emacs-pkgs/niri/default.nix create mode 100644 tools/emacs-pkgs/niri/niri.el (limited to 'tools') diff --git a/tools/emacs-pkgs/niri/default.nix b/tools/emacs-pkgs/niri/default.nix new file mode 100644 index 000000000000..995b1b6208ad --- /dev/null +++ b/tools/emacs-pkgs/niri/default.nix @@ -0,0 +1,7 @@ +{ depot, ... }: + +depot.tools.emacs-pkgs.buildEmacsPackage rec { + pname = "niri"; + version = "1.0"; + src = ./niri.el; +} diff --git a/tools/emacs-pkgs/niri/niri.el b/tools/emacs-pkgs/niri/niri.el new file mode 100644 index 000000000000..a677d093755d --- /dev/null +++ b/tools/emacs-pkgs/niri/niri.el @@ -0,0 +1,89 @@ +;;; niri.el --- seamless niri/emacs integration. -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2024 The TVL Contributors +;; +;; Author: Vincent Ambo +;; Version: 1.0 +;; Package-Requires: ((emacs "27.1")) +;; +;;; Commentary: +;; +;; After having used EXWM for many years (7 or so?) it's become second nature +;; that there is no difference between windows and Emacs buffers. This means +;; that from any Emacs buffer (or, in the case of EXWM, from any X window) it's +;; possible to switch to any of the others. +;; +;; This implements similar logic for Emacs running in Niri, consisting of two +;; sides of the integration: +;; +;; # In Emacs +;; +;; Inside of Emacs, when switching buffers, populate the buffer-switching menu +;; additionally with all open Niri windows. Selecting a Niri window moves the +;; screen to that window. +;; +;; # Outside of Emacs +;; +;; Provides an interface for the same core functionality that can be used from +;; shell scripts, and bound to selectors like dmenu or rofi. +;; +;; # Switching to Emacs buffers +;; +;; Some special logic exists for handling the case of switching to an Emacs +;; buffer. There are several conditions that we can be in, that each have a +;; predictable result: +;; +;; In a non-Emacs window, selecting an Emacs buffer will either switch to an +;; Emacs frame already displaying this buffer, or launch a new frame for it. +;; +;; Inside of Emacs, if *another* frame is already displaying the buffer, switch +;; to it. Otherwise the behaviour is the same as standard buffer switching. + +(require 'seq) +(require 'map) + +(defun niri-list-windows () + "List all currently open Niri windows." + (json-parse-string + (shell-command-to-string "niri msg -j windows"))) + +(defun niri--list-selectables () + "Lists all currently selectable things in a format that can work +with completing-read. Selectable means all open Niri windows and +all Emacs buffers." + (let* (;; all niri windows except for emacs frames + (windows (seq-filter (lambda (w) (not (equal (map-elt w "app_id") "emacs"))) + (niri-list-windows))) + + ;; all non-hidden buffers + (buffers (seq-filter (lambda (b) (not (string-prefix-p " " (buffer-name b)))) + (buffer-list))) + (selectables (make-hash-table :test 'equal :size (+ (length windows) + (length buffers))))) + (seq-do (lambda (window) + (map-put! selectables (map-elt window "title") + (cons :niri window))) + windows) + + (seq-do (lambda (buf) + (map-put! selectables (buffer-name buf) + (cons :emacs buf))) + buffers) + + selectables)) + +(defun niri-go-anywhere () + "Interactively select and switch to an open Niri window, or an + Emacs buffer." + (interactive) + (let* ((selectables (niri--list-selectables)) + (target-key (completing-read "Switch to: " (map-keys selectables))) + (target (map-elt selectables target-key))) + + (pcase (car target) + (:emacs (pop-to-buffer (cdr target) '((display-buffer-reuse-window + display-buffer-same-window) + (reusable-frames . 0)))) + (:niri + (shell-command (format "niri msg action focus-window --id %d" + (map-elt (cdr target) "id"))))))) -- cgit 1.4.1