diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/emacs-pkgs/niri/default.nix | 7 | ||||
-rw-r--r-- | tools/emacs-pkgs/niri/niri.el | 89 |
2 files changed, 96 insertions, 0 deletions
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 <tazjin@tvl.su> +;; 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"))))))) |