about summary refs log tree commit diff
path: root/emacs
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2020-02-08T16·00+0000
committerWilliam Carroll <wpcarro@gmail.com>2020-02-10T10·06+0000
commit2af05f698c583bb71f318352e1da3b9ae2d1ae31 (patch)
tree8db766d3c0cc78496ec1113e912fc537c8add74a /emacs
parent8584059e7cd72656b3e90785c25d2fbb5b350229 (diff)
Support vterm-mgt.el
I enjoyed using term-switcher so much that I ended up adopting vterm as my
primary terminal. After reaching for vterm as often as I did, I realized that I
would enjoy supporting cycling through instances, creating new instances,
deleting existing instances, renaming instances. Thus spawned vterm-mgt.el.

I'm particularly excited about the KBD to toggle between vterm instances and
source code buffers.
Diffstat (limited to 'emacs')
-rw-r--r--emacs/.emacs.d/wpc/keybindings.el25
-rw-r--r--emacs/.emacs.d/wpc/vterm-mgt.el120
-rw-r--r--emacs/.emacs.d/wpc/window-manager.el4
-rw-r--r--emacs/default.nix2
4 files changed, 146 insertions, 5 deletions
diff --git a/emacs/.emacs.d/wpc/keybindings.el b/emacs/.emacs.d/wpc/keybindings.el
index 53ea9683c8ec..5cebf34b8f0a 100644
--- a/emacs/.emacs.d/wpc/keybindings.el
+++ b/emacs/.emacs.d/wpc/keybindings.el
@@ -15,9 +15,10 @@
 (require 'chrome)
 (require 'scrot)
 (require 'ivy-clipmenu)
-(require 'term-switcher)
 (require 'general)
 (require 'window-manager)
+(require 'vterm-mgt)
+(require 'buffer)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configuration
@@ -43,7 +44,27 @@
 
 (keybinding/exwm "<C-M-tab>" #'exwm/switch-to-exwm-buffer)
 
-(general-define-key (kbd/raw 'x11 "t") #'ts/switch-to-terminal)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Vterm
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Show or hide a vterm buffer.  I'm intentionally not defining this in
+;; vterm-mgt.el because it consumes `buffer/show-previous', and I'd like to
+;; avoid bloating vterm-mgt.el with dependencies that others may not want.
+(general-define-key (kbd/raw 'x11 "t")
+                    (lambda ()
+                      (interactive)
+                      (if (vterm-mgt--instance? (current-buffer))
+                          (switch-to-buffer (first (buffer/source-code-buffers)))
+                        (call-interactively #'vterm-mgt-find-or-create))))
+
+(general-define-key
+ :keymaps '(vterm-mode-map)
+ "C-S-n" #'vterm-mgt-instantiate
+ "C-S-w" #'vterm-mgt-kill
+ "<C-tab>" #'vterm-mgt-next
+ "<C-S-iso-lefttab>" #'vterm-mgt-prev
+ "<s-backspace>" #'vterm-mgt-rename-buffer)
 
 (provide 'keybindings)
 ;;; keybindings.el ends here
diff --git a/emacs/.emacs.d/wpc/vterm-mgt.el b/emacs/.emacs.d/wpc/vterm-mgt.el
new file mode 100644
index 000000000000..95ad3f4592b6
--- /dev/null
+++ b/emacs/.emacs.d/wpc/vterm-mgt.el
@@ -0,0 +1,120 @@
+;;; vterm-mgt.el --- Help me manage my vterm instances -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Supporting functions to instantiate vterm buffers, kill existing vterm
+;; buffers, rename vterm buffers, cycle forwards and backwards through vterm
+;; buffers.
+;;
+;; Many of the functions defined herein are intended to be bound to
+;; `vterm-mode-map'.  Some assertions are made to guard against calling
+;; functions that are intended to be called from outside of a vterm buffer.
+;; These assertions shouldn't error when the functions are bound to
+;; `vterm-mode-map'.  If for some reason, you'd like to bind these functions to
+;; a separate keymap, caveat emptor.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'cycle)
+(require 'vterm)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst vterm-mgt--instances (cycle/new)
+  "A cycle tracking all of my vterm instances.")
+
+(defcustom vterm-mgt-scroll-on-focus nil
+  "When t, call `end-of-buffer' after focusing a vterm instance.")
+
+(defun vterm-mgt--instance? (b)
+  "Return t if the buffer B is a vterm instance."
+  (equal 'vterm-mode (buffer-local-value 'major-mode b)))
+
+(defmacro vterm-mgt--assert-vterm-buffer ()
+  "Error when the `current-buffer' is not a vterm buffer."
+  '(prelude/assert (vterm-mgt--instance? (current-buffer))))
+
+(defun vterm-mgt-next ()
+  "Replace the current buffer with the next item in `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (cycle/focus-item (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle/next vterm-mgt--instances))
+  (when vterm-mgt-scroll-on-focus (end-of-buffer)))
+
+(defun vterm-mgt-prev ()
+  "Replace the current buffer with the previous item in `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (cycle/focus-item (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle/prev vterm-mgt--instances))
+  (when vterm-mgt-scroll-on-focus (end-of-buffer)))
+
+(defun vterm-mgt-instantiate ()
+  "Create a new vterm instance.
+
+Prefer calling this function instead of `vterm'.  This function ensures that the
+  newly created instance is added to `vterm-mgt--instances'.
+
+If however you must call `vterm', if you'd like to cycle through vterm
+  instances, make sure you call `vterm-mgt-populate-cycle' to allow vterm-mgt to
+  collect any untracked vterm instances."
+  (interactive)
+  (let ((buffer (vterm)))
+    (cycle/append buffer vterm-mgt--instances)
+    (cycle/focus-item buffer vterm-mgt--instances)))
+
+(defun vterm-mgt-kill ()
+  "Kill the current buffer and remove it from `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (let ((buffer (current-buffer)))
+    (cycle/remove buffer vterm-mgt--instances)
+    (kill-buffer buffer)))
+
+(defun vterm-mgt-find-or-create ()
+  "Call `switch-to-buffer' on a focused vterm instance if there is one.
+
+When `cycle/focused?' returns nil, focus the first item in the cycle.  When
+there are no items in the cycle, call `vterm-mgt-instantiate' to create a vterm
+instance."
+  (interactive)
+  (if (cycle/empty? vterm-mgt--instances)
+      (vterm-mgt-instantiate)
+    (if (cycle/focused? vterm-mgt--instances)
+        (switch-to-buffer (cycle/current vterm-mgt--instances))
+      (progn
+        (cycle/jump 0 vterm-mgt--instances)
+        (switch-to-buffer (cycle/current vterm-mgt--instances))))))
+
+(defun vterm-mgt-rename-buffer (name)
+  "Rename the current buffer ensuring that its NAME is wrapped in *vterm*<...>.
+This function should be called from a buffer running vterm."
+  (interactive "SRename vterm buffer: ")
+  (vterm-mgt--assert-vterm-buffer)
+  (rename-buffer (format "vterm<%s>" name)))
+
+(defun vterm-mgt-repopulate-cycle ()
+  "Fill `vterm-mgt--instances' with the existing vterm buffers.
+
+If for whatever reason, the state of `vterm-mgt--instances' is corrupted and
+  misaligns with the state of vterm buffers in Emacs, use this function to
+  attempt to restore the state."
+  (interactive)
+  (setq vterm-mgt--instances
+        (->> (buffer-list)
+             (-filter #'vterm-mgt--instance?)
+             cycle/from-list)))
+
+(provide 'vterm-mgt)
+;;; vterm-mgt.el ends here
diff --git a/emacs/.emacs.d/wpc/window-manager.el b/emacs/.emacs.d/wpc/window-manager.el
index 51e4d49d6e7c..8dad4963c285 100644
--- a/emacs/.emacs.d/wpc/window-manager.el
+++ b/emacs/.emacs.d/wpc/window-manager.el
@@ -26,7 +26,7 @@
 (require 'display)
 (require 'dotfiles)
 (require 'org-helpers)
-(require 'vterm)
+(require 'vterm-mgt)
 (require 'dash)
 (require 'evil)
 
@@ -521,7 +521,7 @@ This function asssumes that BUFFER passes the `exwm/exwm-buffer?' predicate."
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      (progn
        (exwm/switch "Terminal")
-       (vterm))
+       (vterm-mgt-instantiate))
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;; Todos
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/emacs/default.nix b/emacs/default.nix
index 1635c0286f90..affc9f605ce7 100644
--- a/emacs/default.nix
+++ b/emacs/default.nix
@@ -37,6 +37,7 @@ let
     ]) ++
 
     (with epkgs.melpaPackages; [
+      vterm
       base16-theme
       ivy-pass
       clipmon # TODO: Prefer an Emacs client for clipmenud.
@@ -122,7 +123,6 @@ let
 
     (with depot.tools.emacs-pkgs; [
       dottime
-      term-switcher
     ]));
 
 # TODO: Do I need `pkgs.lib.fix`?