From 87e2d4e292adb8722568b50447572c9318ba8bde Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Thu, 6 Jan 2022 15:36:39 -0800 Subject: [PATCH] Move stuff around Needed to move the Terminal stuff over to remoting, as I don't include the aux-apps in my Work-specific Emacs system. While I was at it, I noticed some key conflicts with org, so fixed that too. --- bootstrap.org | 16 +++-- ha-aux-apps.org | 120 +--------------------------------- ha-config.org | 14 ++++ ha-feed-reader.org | 2 +- ha-org.org | 12 ++-- ha-remoting.org | 157 ++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 174 insertions(+), 147 deletions(-) diff --git a/bootstrap.org b/bootstrap.org index 3380222..0d94f5f 100644 --- a/bootstrap.org +++ b/bootstrap.org @@ -124,16 +124,20 @@ And I need to install the [[https://gpgtools.org/][GPG Suite]]. While very nice, ** Basic Libraries The following packages come with Emacs, but seems like they still need loading: #+BEGIN_SRC emacs-lisp -(require 'subr-x) -#+END_SRC -Ugh. Why am I getting a missing =first= function error? Let's just define it: -#+BEGIN_SRC emacs-lisp - (defun first (elt) (car elt)) + (use-package cl-lib + :straight (:type built-in) + :init (defun first (elt) (car elt)) + :commands (first)) + + (require 'subr-x) #+END_SRC +Ugh. Why am I getting a missing =first= function error? I define a simple implementation, that the CL library will overwrite ... at some point. + While most libraries will take care of their dependencies, I want to install /my dependent libraries/. Especially, [[https://github.com/magnars/.emacs.d/][Magnar Sveen]]'s Clojure-inspired [[https://github.com/magnars/dash.el][dash.el]] project: #+BEGIN_SRC emacs-lisp (use-package dash) #+END_SRC +Sure this package is essentially syntactic sugar, and to help /share/ my configuration, I attempt to use =thread-last= instead of =->>=, but, I still like it. The [[https://github.com/magnars/s.el][s.el]] project is a simpler string manipulation library that I (and other projects) use: #+BEGIN_SRC emacs-lisp @@ -189,13 +193,13 @@ The following loads the rest of my org-mode literate files. I add them as they a "ha-org-clipboard.org" "ha-capturing-notes.org" "ha-agendas.org" - "ha-irc.org" "ha-passwords.org" "ha-remoting.org" "ha-programming.org" "ha-programming-python.org" ,(unless (ha-emacs-for-work?) '("ha-org-journaling.org" + "ha-irc.org" "ha-org-publishing.org" "ha-email.org" "ha-aux-apps.org" diff --git a/ha-aux-apps.org b/ha-aux-apps.org index 14d8a1d..c47ef88 100644 --- a/ha-aux-apps.org +++ b/ha-aux-apps.org @@ -24,113 +24,6 @@ A literate programming file for helper apps in Emacs. #+END_SRC * Introduction The following applications are not really needed. I alternate between trying to /stay in Emacs/ taking advantage of the consistent interface, and simply using a stand-alone app on my Workday computer. -* Terminal -The following section configures my Terminal experience, both inside and outside Emacs. -** Eshell -I used to use [[http://www.howardism.org/Technical/Emacs/eshell.html][Eshell all the time]], but now I've migrated most of /work/ directly into Emacs (rewriting all those shell scripts a Emacs Lisp code). However, a shell is pretty good for my brain at organizing files (old habits, maybe). - -#+BEGIN_SRC emacs-lisp - (use-package eshell - :config (ha-leader "a e" '("eshell" . eshell-here))) -#+END_SRC - -I usually want a new window running Eshell, that is smaller than the current buffer: - -#+BEGIN_SRC emacs-lisp - (defun eshell-here () - "Opens up a new shell in the directory associated with the - current buffer's file. Rename the eshell buffer name to match - that directory to make multiple eshell windows easier." - (interactive) - (let* ((parent (if (buffer-file-name) - (file-name-directory (buffer-file-name)) - default-directory)) - (height (/ (window-total-height) 3)) - (name (car (last (split-string parent "/" t))))) - (split-window-vertically (- height)) - (eshell "new") - (rename-buffer (concat "*eshell: " name "*")) - - (insert (concat "ls")) - (eshell-send-input))) -#+END_SRC - -And since Emacs supplies Eshell, we can just define these helper functions: - -#+BEGIN_SRC emacs-lisp - (defun eshell/e (file) - (find-file file)) - - (defun eshell/ee (file) - (find-file-other-window file)) - - (defun eshell/x () - (insert "exit") - (eshell-send-input) - (delete-window)) -#+END_SRC - -Add my org-specific predicates, see this [[http://www.howardism.org/Technical/Emacs/eshell-fun.html][this essay]] for the details: -#+BEGIN_SRC emacs-lisp - (defun eshell-org-file-tags () - "Helps the eshell parse the text the point is currently on, - looking for parameters surrounded in single quotes. Returns a - function that takes a FILE and returns nil if the file given to - it doesn't contain the org-mode #+FILETAGS: entry specified." - - ;; Step 1. Parse the eshell buffer for our tag between quotes - ;; Make sure to move point to the end of the match: - (if (looking-at "'\\([^)']+\\)'") - (let* ((tag (match-string 1)) - (reg (rx bol "#+FILETAGS: " - (zero-or-more any) - word-start - (literal tag) - word-end - (zero-or-more any) - eol))) - (goto-char (match-end 0)) - - ;; Step 2. Return the predicate function: - ;; Careful when accessing the `reg' variable. - `(lambda (file) - (with-temp-buffer - (insert-file-contents file) - (re-search-forward ,reg nil t 1)))) - (error "The `T' predicate takes an org-mode tag value in single quotes."))) - - (defvar eshell-predicate-alist nil - "A list of predicates than can be applied to a globbing pattern.") - (add-to-list 'eshell-predicate-alist '(?T . (eshell-org-file-tags))) -#+END_SRC -** VTerm - -I'm not giving up on Eshell, but I am playing around with [[https://github.com/akermu/emacs-libvterm][vterm]], and it is pretty good, but I use it primarily as a more reliable approach to [[file:ha-remoting.org][a remote shell]]. - -VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previous word, and yeah, I want to make sure that both keystrokes do the same thing. - -#+BEGIN_SRC emacs-lisp :tangle no -(use-package vterm - :init - (setq vterm-shell "/usr/local/bin/fish") - ;; Granted, I seldom pop out to the shell except during code demonstrations, - ;; but I like how C-p/C-n jumps up to each prompt entry using this setting - ;; that works with my prompt: - (setq vterm-use-vterm-prompt-detection-method nil - term-prompt-regexp "^.* $ ") - :config - (dolist (k '("" "")) - (define-key vterm-mode-map (kbd k) - (lambda () (interactive) (vterm-send-key (kbd "C-w"))))) - - (advice-add 'vterm-copy-mode :after 'evil-normal-state)) -#+END_SRC - -The advantage of running terminals in Emacs is the ability to copy text without a mouse. For that, hit ~C-c C-t~ to enter a special copy-mode. If I go into this mode, I might as well also go into normal mode to move the cursor. - -*Note:* To exit the copy-mode (and copy the selected text to the clipboard), hit ~Return~. - -Hrm. Seems that I might want a function to copy the output of the last command to a register, or even an org-capture... * Twitter The venerable [[https://github.com/hayamiz/twittering-mode/tree/master][twittering-mode]] allows me to follow all the twits. #+BEGIN_SRC emacs-lisp @@ -152,18 +45,7 @@ I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be For some reason, you need [[https://github.com/Fanael/rainbow-identifiers][rainbow-identifiers]] to work, oh, I guess the docs state this. *Note:* Turning this off as it needs version 1.7.7 of =tdlib=, and that isn't easily available on my Mac. Maybe I may enable this on my Linux system. -* Demo It -Making demonstrations /within/ Emacs with [[https://github.com/howardabrams/demo-it][demo-it]]. -#+BEGIN_SRC emacs-lisp - (use-package demo-it - :straight (:type git :protocol ssh :host github :repo "howardabrams/demo-it") - :commands (demo-it-create demo-it-start)) -#+END_SRC -Perhaps I should change the reference to this for more local development: -#+begin_example - :straight (:local-repo "~/other/demo-it") -#+end_example -** RPG DM +* RPG DM Been working on a project for getting Emacs helping as a /Dungeon Master's Assistant/, and I must say, it is coming along nicely. In case you are reading this, let me know, and I'll start to share it. #+BEGIN_SRC emacs-lisp diff --git a/ha-config.org b/ha-config.org index 518e92c..12dbc7a 100644 --- a/ha-config.org +++ b/ha-config.org @@ -650,6 +650,9 @@ Ways to search for information goes under the ~s~ key. This primarily depends on #+BEGIN_SRC emacs-lisp (use-package rg + :init ; I sometimes call `grep`: + ; (grep-apply-setting 'grep-command "rg -n -H --no-heading -e ") + :config (ha-leader "s" '(:ignore t :which-key "search") @@ -1215,6 +1218,17 @@ I primarily use [[https://github.com/jaypei/emacs-neotree][Neotree]] when I am s (evil-define-key 'normal neotree-mode-map (kbd "g") 'neotree-refresh) (evil-define-key 'normal neotree-mode-map (kbd "H") 'neotree-hidden-file-toggle)) #+END_SRC +** Demo It +Making demonstrations /within/ Emacs with [[https://github.com/howardabrams/demo-it][demo-it]]. +#+BEGIN_SRC emacs-lisp + (use-package demo-it + :straight (:type git :protocol ssh :host github :repo "howardabrams/demo-it") + :commands (demo-it-create demo-it-start)) +#+END_SRC +Perhaps I should change the reference to this for more local development: +#+begin_example + :straight (:local-repo "~/other/demo-it") +#+end_example * Technical Artifacts :noexport: Let's provide a name so that the file can be required: diff --git a/ha-feed-reader.org b/ha-feed-reader.org index 91a1935..361ee9a 100644 --- a/ha-feed-reader.org +++ b/ha-feed-reader.org @@ -68,7 +68,7 @@ According to Ben Maughan and [[http://pragmaticemacs.com/emacs/to-eww-or-not-to- #+END_SRC Finally, we can add it to the =apps= menu: #+BEGIN_SRC emacs-lisp -(ha-leader "a e" 'elfeed) +(ha-leader "a f" 'elfeed) #+END_SRC * The Feeds :elfeed: ** Personal :personal: diff --git a/ha-org.org b/ha-org.org index ea3b4e7..49a6f67 100644 --- a/ha-org.org +++ b/ha-org.org @@ -319,10 +319,10 @@ To make the snippets more context aware, this predicate Keybindings available to all file buffers: #+NAME: global-keybindings #+BEGIN_SRC emacs-lisp :tangle no -(ha-leader - "o l" '("store link" . org-store-link) - "o x" '("org capture" . org-capture) - "o c" '("clock out" . org-clock-out)) + (ha-leader + "o l" '("store link" . org-store-link) + "o x" '("org capture" . org-capture) + "o c" '("clock out" . org-clock-out)) #+END_SRC Bindings specific to org files: @@ -332,9 +332,9 @@ Bindings specific to org files: "e" '("exports" . org-export-dispatch) "I" '("insert id" . org-id-get-create) "l" '("insert link" . org-insert-link) - "n" '("store link" . org-store-link) + "N" '("store link" . org-store-link) "o" '("goto link" . ace-link-org) - "p" '("set property" . org-set-property) + "P" '("set property" . org-set-property) "q" '("set tags" . org-set-tags-command) "t" '("todo" . org-todo) "T" '("list todos" . org-todo-list) diff --git a/ha-remoting.org b/ha-remoting.org index d929a7c..155cccc 100644 --- a/ha-remoting.org +++ b/ha-remoting.org @@ -22,7 +22,103 @@ A literate configuration for accessing remote systems. ;; ;;; Code: #+END_SRC -* Introduction +* Local Terminals +The following section configures my Terminal experience, both inside and outside Emacs. +** Eshell +I used to use [[http://www.howardism.org/Technical/Emacs/eshell.html][Eshell all the time]], but now I've migrated most of /work/ directly into Emacs (rewriting all those shell scripts a Emacs Lisp code). However, a shell is pretty good for my brain at organizing files (old habits, maybe). + +First, my /aliases/ follow me around, and the following creates the alias file, =~/.emacs.d/eshell/alias=: +#+BEGIN_SRC shell :tangle (identity eshell-aliases-file) :mkdirp yes + alias ll ls -l $* + alias clear recenter 0 + alias emacs 'find-file $1' + alias e 'find-file $1' + alias ee 'find-file-other-window $1' + alias grep 'rg -n -H --no-heading -e "$*"' + alias find 'echo Please use fd instead.' # :-) +#+END_SRC + +Since that file already exists (probably), the following command may not be necessary: +#+BEGIN_SRC emacs-lisp + (use-package eshell + :custom + (eshell-kill-on-exit t) + (eshell-destroy-buffer-when-process-dies t) + + :hook + (eshell-exit . (lambda () (delete-window)))) +#+END_SRC + +I usually want a new window running Eshell, that is smaller than the current buffer: + +#+BEGIN_SRC emacs-lisp + (defun eshell-there (parent) + "Open an eshell session in a PARENT directory + in a smaller window named after this directory." + (let ((name (thread-first parent + (split-string "/" t) + (last) + (car))) + (height (/ (window-total-height) 3))) + (split-window-vertically (- height)) + (eshell "new") + (rename-buffer (concat "*eshell: " name "*")) + + (insert (concat "ls")) + (eshell-send-input))) +#+END_SRC + +We either want to start the shell in the same parent as the current buffer, or at the root of the project: +#+BEGIN_SRC emacs-lisp + (defun eshell-here () + "Opens up a new shell in the directory associated with the + current buffer's file. Rename the eshell buffer name to match + that directory to make multiple eshell windows easier." + (interactive) + (eshell-there (if (buffer-file-name) + (file-name-directory (buffer-file-name)) + default-directory))) + + (defun eshell-project () + "Open a new shell in the project root directory, in a smaller window." + (interactive) + (eshell-there (projectile-project-root))) +#+END_SRC + +Add my org-specific predicates, see this [[http://www.howardism.org/Technical/Emacs/eshell-fun.html][this essay]] for the details: +#+BEGIN_SRC emacs-lisp + (defun eshell-org-file-tags () + "Helps the eshell parse the text the point is currently on, + looking for parameters surrounded in single quotes. Returns a + function that takes a FILE and returns nil if the file given to + it doesn't contain the org-mode #+FILETAGS: entry specified." + + ;; Step 1. Parse the eshell buffer for our tag between quotes + ;; Make sure to move point to the end of the match: + (if (looking-at "'\\([^)']+\\)'") + (let* ((tag (match-string 1)) + (reg (rx bol "#+FILETAGS: " + (zero-or-more any) + word-start + (literal tag) + word-end + (zero-or-more any) + eol))) + (goto-char (match-end 0)) + + ;; Step 2. Return the predicate function: + ;; Careful when accessing the `reg' variable. + `(lambda (file) + (with-temp-buffer + (insert-file-contents file) + (re-search-forward ,reg nil t 1)))) + (error "The `T' predicate takes an org-mode tag value in single quotes."))) + + (defvar eshell-predicate-alist nil + "A list of predicates than can be applied to a globbing pattern.") + (add-to-list 'eshell-predicate-alist '(?T . (eshell-org-file-tags))) +#+END_SRC +* Remote Terminals Sure =iTerm= is nice for connecting and running commands on remote systems, however, it lacks a command line option that allows you to select and manipulate the displayed text without a mouse. This is where Emacs can shine. *Feature One:* @@ -58,8 +154,36 @@ Working with remote shell connections programmatically, for instance: Actually the =win-name= in this case is optional, as it will use a good default. -* Variables +** VTerm + +I'm not giving up on Eshell, but I am playing around with [[https://github.com/akermu/emacs-libvterm][vterm]], and it is pretty good, but I use it primarily as a more reliable approach for remote terminal sessions. + +VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previous word, and yeah, I want to make sure that both keystrokes do the same thing. + +#+BEGIN_SRC emacs-lisp :tangle no +(use-package vterm + :init + (setq vterm-shell "/usr/local/bin/fish") + ;; Granted, I seldom pop out to the shell except during code demonstrations, + ;; but I like how C-p/C-n jumps up to each prompt entry using this setting + ;; that works with my prompt: + (setq vterm-use-vterm-prompt-detection-method nil + term-prompt-regexp "^.* $ ") + :config + (dolist (k '("" "")) + (define-key vterm-mode-map (kbd k) + (lambda () (interactive) (vterm-send-key (kbd "C-w"))))) + + (advice-add 'vterm-copy-mode :after 'evil-normal-state)) +#+END_SRC + +The advantage of running terminals in Emacs is the ability to copy text without a mouse. For that, hit ~C-c C-t~ to enter a special copy-mode. If I go into this mode, I might as well also go into normal mode to move the cursor. + +*Note:* To exit the copy-mode (and copy the selected text to the clipboard), hit ~Return~. + +Hrm. Seems that I might want a function to copy the output of the last command to a register, or even an org-capture... +** Variables Let's begin by defining some variables used for communication between the functions. #+BEGIN_SRC emacs-lisp @@ -79,7 +203,7 @@ Also, let's make it easy for me to change my default shell: "The executable to the shell I want to use locally.") #+END_SRC -* Interactive Interface to Remote Systems +** Interactive Interface to Remote Systems The function, =ha-ssh= pops up a list of /favorite hosts/ and then uses the =vterm= functions to automatically SSH into the chosen host: @@ -149,7 +273,7 @@ Before we leave this section, I realize that I would like a way to /add/ to my l (add-to-list 'ha-ssh-favorite-hostnames (cons hostname ip-address))) #+END_SRC -* Programmatic Interface +** Programmatic Interface The previous functions (as well as my own end of sprint demonstrations) often need to issue some commands to a running terminal session, which is a simple wrapper around a /send text/ and /send return/ sequence: @@ -207,7 +331,7 @@ Let's have a quick way to bugger out of the terminal: (delete-window)) #+END_SRC -* Editing Remote Files +** Editing Remote Files TRAMP, when it works, is amazing that we can give it a reference to a remote directory, and have =find-file= magically autocomplete. @@ -238,7 +362,7 @@ We can even edit it as root: (ha-ssh--find-file tramp-ssh-ref other-window))) #+END_SRC -* OpenStack Interface +** OpenStack Interface Instead of making sure I have a list of remote systems already in the favorite hosts cache, I can pre-populate it with a call to OpenStack (my current VM system I'm using). These calls to the =openstack= CLI assume that the environment is already filled with the credentials. Hey, it is my local laptop ... @@ -324,15 +448,18 @@ Emacs), hit =C-c C-k=." This file, so far, as been good-enough for a Vanilla Emacs installation, but to hook into Doom's leader for some sequence binding, this code isn't: #+BEGIN_SRC emacs-lisp -(ha-leader - "a s" '(:ignore t :which-key "ssh") - "a s v" '("vterm" . vterm) - "a s o" '("overcloud" . ha-ssh-overcloud) - "a s l" '("local shell" . ha-shell) - "a s s" '("remote shell" . ha-ssh) - "a s q" '("quit shell" . ha-ssh-exit) - "a s f" '("find-file" . ha-ssh-find-file) - "a s r" '("find-root" . ha-ssh-find-root)) + (ha-leader + "a e" '("eshell" . eshell-here) + "a E" '("top eshell" . eshell-project) + + "a s" '(:ignore t :which-key "ssh") + "a s v" '("vterm" . vterm) + "a s o" '("overcloud" . ha-ssh-overcloud) + "a s l" '("local shell" . ha-shell) + "a s s" '("remote shell" . ha-ssh) + "a s q" '("quit shell" . ha-ssh-exit) + "a s f" '("find-file" . ha-ssh-find-file) + "a s r" '("find-root" . ha-ssh-find-root)) #+END_SRC * Technical Artifacts :noexport: