From 2af05f698c583bb71f318352e1da3b9ae2d1ae31 Mon Sep 17 00:00:00 2001 From: William Carroll Date: Sat, 8 Feb 2020 16:00:31 +0000 Subject: 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. --- emacs/.emacs.d/wpc/keybindings.el | 25 +++++++- emacs/.emacs.d/wpc/vterm-mgt.el | 120 +++++++++++++++++++++++++++++++++++ emacs/.emacs.d/wpc/window-manager.el | 4 +- emacs/default.nix | 2 +- 4 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 emacs/.emacs.d/wpc/vterm-mgt.el (limited to 'emacs') 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 "" #'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 + "" #'vterm-mgt-next + "" #'vterm-mgt-prev + "" #'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 + +;;; 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`? -- cgit 1.4.1