Fix some bugs based in eshell and vterm
After making my presentation on eshell, I encountered some bugs that needed addressing.
This commit is contained in:
parent
4df8279e20
commit
6a74607b85
2 changed files with 98 additions and 57 deletions
|
@ -413,35 +413,35 @@ I’m calling the ability to get a buffer contents, /flow/ (Fetch contents as Li
|
||||||
- /as a string/ :: no conversion
|
- /as a string/ :: no conversion
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defun eshell/flow (&rest args)
|
(defun eshell/flow (&rest args)
|
||||||
"Output the contents of one or more buffers as a string.
|
"Output the contents of one or more buffers as a string.
|
||||||
Usage: flow [OPTION] [BUFFER ...]
|
Usage: flow [OPTION] [BUFFER ...]
|
||||||
-h, --help show this usage screen
|
-h, --help show this usage screen
|
||||||
-l, --lines output contents as a list of lines
|
-l, --lines output contents as a list of lines
|
||||||
-w, --words output contents as a list of space-separated elements "
|
-w, --words output contents as a list of space-separated elements "
|
||||||
(let* ((options (eshell-getopts '((:name words :short "w" :long "words")
|
(let* ((options (eshell-getopts '((:name words :short "w" :long "words")
|
||||||
(:name lines :short "l" :long "lines")
|
(:name lines :short "l" :long "lines")
|
||||||
(:name string :short "s" :long "string")
|
(:name string :short "s" :long "string")
|
||||||
(:name help :short "h" :long "help"
|
(:name help :short "h" :long "help"
|
||||||
:help eshell/flow))
|
:help eshell/flow))
|
||||||
args))
|
args))
|
||||||
(buffers (gethash 'parameters options))
|
(buffers (gethash 'parameters options))
|
||||||
(content (thread-last parameters
|
(content (thread-last buffers
|
||||||
(-map 'eshell-flow-buffer-contents)
|
(-map 'eshell-flow-buffer-contents)
|
||||||
(s-join "\n"))))
|
(s-join "\n"))))
|
||||||
(if (gethash 'help options)
|
(if (gethash 'help options)
|
||||||
(error (documentation 'eshell/flow))
|
(error (documentation 'eshell/flow))
|
||||||
|
|
||||||
;; No buffer specified? Use the default buffer's contents:
|
;; No buffer specified? Use the default buffer's contents:
|
||||||
(unless buffers
|
(unless buffers
|
||||||
(setq content
|
(setq content
|
||||||
(eshell-flow-buffer-contents ha-eshell-ebbflow-buffername)))
|
(eshell-flow-buffer-contents ha-eshell-ebbflow-buffername)))
|
||||||
|
|
||||||
;; Do we need to convert the output to lines or split on words?
|
;; Do we need to convert the output to lines or split on words?
|
||||||
(cond
|
(cond
|
||||||
((gethash 'words options) (split-string content))
|
((gethash 'words options) (split-string content))
|
||||||
((gethash 'lines options) (split-string content "\n"))
|
((gethash 'lines options) (split-string content "\n"))
|
||||||
(t content)))))
|
(t content)))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
Straight-forward to acquire the contents of a buffer :
|
Straight-forward to acquire the contents of a buffer :
|
||||||
|
@ -699,6 +699,7 @@ The [[https://github.com/joddie/pcre2el][pcre2el]] project can convert from a Li
|
||||||
(regexp "[0-9]\\{1,2\\}")))
|
(regexp "[0-9]\\{1,2\\}")))
|
||||||
(ipaddr (seq b256 "." b256 "." b256 "." b256))
|
(ipaddr (seq b256 "." b256 "." b256 "." b256))
|
||||||
(time (seq digit (optional digit) ":" (= 2 digit) (optional ":" (= 2 digit))))
|
(time (seq digit (optional digit) ":" (= 2 digit) (optional ":" (= 2 digit))))
|
||||||
|
(email (seq (1+ (regexp "[^,< ]")) "@" (1+ (seq (1+ (any alnum "-"))) ".") (1+ alnum)))
|
||||||
(date (seq (= 2 digit) (or "/" "-") (= 2 digit) (or "/" "-") (= 4 digit)))
|
(date (seq (= 2 digit) (or "/" "-") (= 2 digit) (or "/" "-") (= 4 digit)))
|
||||||
(ymd (seq (= 4 digit) (or "/" "-") (= 2 digit) (or "/" "-") (= 2 digit)))
|
(ymd (seq (= 4 digit) (or "/" "-") (= 2 digit) (or "/" "-") (= 2 digit)))
|
||||||
(uuid (seq (= 8 hex) "-" (= 3 (seq (= 4 hex) "-")) (= 12 hex)))
|
(uuid (seq (= 8 hex) "-" (= 3 (seq (= 4 hex) "-")) (= 12 hex)))
|
||||||
|
@ -1028,8 +1029,8 @@ This requires capture templates that don’t do any formatting. I will reused =c
|
||||||
(defun ha-eshell-engineering-capture (capture-template comment cmd out)
|
(defun ha-eshell-engineering-capture (capture-template comment cmd out)
|
||||||
"Capture formatted string in CAPTURE-TEMPLATE.
|
"Capture formatted string in CAPTURE-TEMPLATE.
|
||||||
Base the string created on COMMENT, CMD, and OUT. Return OUTPUT."
|
Base the string created on COMMENT, CMD, and OUT. Return OUTPUT."
|
||||||
(let* ((command (s-trim cmd))
|
(let* ((command (when cmd (s-trim cmd)))
|
||||||
(output (s-trim out))
|
(output (when out (s-trim out)))
|
||||||
(results (concat
|
(results (concat
|
||||||
(when comment (format "%s\n\n" comment))
|
(when comment (format "%s\n\n" comment))
|
||||||
(when command (format "#+begin_src shell\n %s\n#+end_src\n\n" command))
|
(when command (format "#+begin_src shell\n %s\n#+end_src\n\n" command))
|
||||||
|
@ -1061,6 +1062,21 @@ This function simply calls [[help-org-capture][org-capture]] with [[info:org#Tem
|
||||||
"Call `org-capture' with the `ee' template to enter text into the engineering notebook."
|
"Call `org-capture' with the `ee' template to enter text into the engineering notebook."
|
||||||
(org-capture nil "ee"))
|
(org-capture nil "ee"))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defun ha-eshell-target-engineering-notebook (output)
|
||||||
|
"Write OUTPUT into the engineering notebook via `org-capture'."
|
||||||
|
(ha-eshell-engineering-capture "ef" nil nil output))
|
||||||
|
|
||||||
|
(defun ha-eshell-target-clocked-in-task (output)
|
||||||
|
"Write OUTPUT into the current clocked in task via `org-capture'."
|
||||||
|
(ha-eshell-engineering-capture "cc" nil nil output))
|
||||||
|
#+end_src
|
||||||
|
And finally, add our new functions to [[elisp(describe-variable 'eshell-virtual-targets)][eshell-virtual-targets]]:
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(add-to-list 'eshell-virtual-targets '("/dev/e" ha-eshell-target-engineering-notebook nil))
|
||||||
|
(add-to-list 'eshell-virtual-targets '("/dev/c" ha-eshell-target-engineering-notebook nil))
|
||||||
|
#+end_src
|
||||||
* Special Prompt
|
* Special Prompt
|
||||||
Following [[http://blog.liangzan.net/blog/2012/12/12/customizing-your-emacs-eshell-prompt/][these instructions]], we build a better prompt with the Git branch in it (Of course, it matches my Bash prompt). First, we need a function that returns a string with the Git branch in it, e.g. ":master"
|
Following [[http://blog.liangzan.net/blog/2012/12/12/customizing-your-emacs-eshell-prompt/][these instructions]], we build a better prompt with the Git branch in it (Of course, it matches my Bash prompt). First, we need a function that returns a string with the Git branch in it, e.g. ":master"
|
||||||
#+begin_src emacs-lisp :tangle no
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
|
|
@ -241,19 +241,16 @@ This is used in calls to =interactive= to select a host."
|
||||||
Simply calling =vterm= fails to load my full environment, so this allows me to start the terminal in a particular directory (defaulting to the root of the current project):
|
Simply calling =vterm= fails to load my full environment, so this allows me to start the terminal in a particular directory (defaulting to the root of the current project):
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defun ha-shell (&optional directory)
|
(defun ha-shell (&optional directory)
|
||||||
"Creates and tidies up a =vterm= terminal shell in side window."
|
"Creates and tidies up a =vterm= terminal shell in side window."
|
||||||
(interactive (list (read-directory-name "Starting Directory: " (projectile-project-root))))
|
(interactive (list (read-directory-name "Starting Directory: " (projectile-project-root))))
|
||||||
(let* ((win-name "Terminal")
|
(let* ((win-name (ha--terminal-name-from-dir directory))
|
||||||
(buf-name (format "*%s*" win-name))
|
(buf-name (format "*%s*" win-name))
|
||||||
(default-directory (or directory default-directory)))
|
(default-directory (or directory default-directory)))
|
||||||
(setq ha-latest-ssh-window-name buf-name)
|
(setq ha-latest-ssh-window-name buf-name)
|
||||||
(if (not (fboundp 'vterm))
|
(if (not (fboundp 'vterm))
|
||||||
(make-term win-name ha-ssh-shell)
|
(make-term win-name ha-ssh-shell)
|
||||||
(vterm buf-name)
|
(vterm buf-name))))
|
||||||
;; (ha-ssh-send "source ~/.bash_profile" buf-name)
|
|
||||||
;; (ha-ssh-send "clear" buf-name)
|
|
||||||
)))
|
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
Before we leave this section, I realize that I would like a way to /add/ to my list of hosts:
|
Before we leave this section, I realize that I would like a way to /add/ to my list of hosts:
|
||||||
|
@ -264,26 +261,54 @@ 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)))
|
(add-to-list 'ha-ssh-favorite-hostnames (cons hostname ip-address)))
|
||||||
#+end_src
|
#+end_src
|
||||||
** Programmatic Interface
|
** Programmatic Interface
|
||||||
|
Let’s send stuff to it:
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defun ha-shell-send (command &optional directory)
|
||||||
|
"Send COMMAND to existing shell based on DIRECTORY.
|
||||||
|
If the shell doesn't already exist, start on up by calling
|
||||||
|
the `ha-shell' function."
|
||||||
|
(let* ((win-name (ha--terminal-name-from-dir directory))
|
||||||
|
(win-rx (rx "*" (literal win-name) "*"))
|
||||||
|
(bufs (seq-filter (lambda (b) (when (string-match win-rx (buffer-name b)) b))
|
||||||
|
(buffer-list)))
|
||||||
|
(buf (first bufs)))
|
||||||
|
(unless buf
|
||||||
|
(setq buf (ha-shell directory)))
|
||||||
|
(ha-ssh-send command buf)))
|
||||||
|
|
||||||
|
(defun ha--terminal-name-from-dir (&optional directory)
|
||||||
|
"Return an appropriate title for a terminal based on DIRECTORY.
|
||||||
|
If DIRECTORY is nil, use the `projectile-project-name'."
|
||||||
|
(unless directory
|
||||||
|
(setq directory (projectile-project-name)))
|
||||||
|
(format "Terminal: %s" (file-name-base (directory-file-name directory))))
|
||||||
|
|
||||||
|
(ert-deftest ha--terminal-name-from-dir-test ()
|
||||||
|
(should
|
||||||
|
(string= (ha--terminal-name-from-dir "~/other/hamacs/") "Terminal: hamacs"))
|
||||||
|
(should
|
||||||
|
(string= (ha--terminal-name-from-dir) "Terminal: hamacs")))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defun ha-ssh-send (phrase &optional window-name)
|
(defun ha-ssh-send (phrase &optional window-name)
|
||||||
"Send command PHRASE to the currently running SSH instance.
|
"Send command PHRASE to the currently running SSH instance.
|
||||||
If you want to refer to another session, specify the correct WINDOW-NAME.
|
If you want to refer to another session, specify the correct WINDOW-NAME.
|
||||||
This is really useful for scripts and demonstrations."
|
This is really useful for scripts and demonstrations."
|
||||||
(unless window-name
|
(unless window-name
|
||||||
(setq window-name ha-latest-ssh-window-name))
|
(setq window-name ha-latest-ssh-window-name))
|
||||||
|
(save-window-excursion
|
||||||
|
(pop-to-buffer window-name)
|
||||||
|
|
||||||
(pop-to-buffer window-name)
|
(if (fboundp 'vterm)
|
||||||
|
(progn
|
||||||
(if (fboundp 'vterm)
|
(vterm-send-string phrase)
|
||||||
(progn
|
(vterm-send-return))
|
||||||
(vterm-send-string phrase)
|
(progn
|
||||||
(vterm-send-return))
|
(term-send-raw-string phrase)
|
||||||
(progn
|
(term-send-input)))))
|
||||||
(term-send-raw-string phrase)
|
|
||||||
(term-send-input))))
|
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
On the rare occasion that I write a shell script, or at least, need to execute some one-line shell commands from some document, I have a function that combines a /read line from buffer/ and then send it to the currently running terminal:
|
On the rare occasion that I write a shell script, or at least, need to execute some one-line shell commands from some document, I have a function that combines a /read line from buffer/ and then send it to the currently running terminal:
|
||||||
|
|
Loading…
Reference in a new issue