Engineering Notebook and Eshell
This is a big feature allows me to easily capture both commands and output from eshell into my "General Notes". The idea is that I could edit/massage that content to keep that file small.
This commit is contained in:
		
							parent
							
								
									a5179c12d2
								
							
						
					
					
						commit
						ac71278fdd
					
				
					 1 changed files with 229 additions and 94 deletions
				
			
		
							
								
								
									
										273
									
								
								ha-eshell.org
									
									
									
									
									
								
							
							
						
						
									
										273
									
								
								ha-eshell.org
									
									
									
									
									
								
							| 
						 | 
					@ -177,71 +177,25 @@ Calling Emacs functions that take a single argument from =eshell= that could acc
 | 
				
			||||||
      ;; probably isn't helpful to display in the `eshell' window.
 | 
					      ;; probably isn't helpful to display in the `eshell' window.
 | 
				
			||||||
      ""))
 | 
					      ""))
 | 
				
			||||||
#+end_src
 | 
					#+end_src
 | 
				
			||||||
** Less and More
 | 
					The =eshell-command= is supposed to be an interactive command for prompting for a shell command in the mini-buffer. However, I have some functions that run a command and gather the output. For that, we call =eshell-command= but a =t= for the second argument:
 | 
				
			||||||
While I can type =find-file=, I often use =e= as an alias for =emacsclient= in Terminals, so let’s do something similar for =eshell=:
 | 
					 | 
				
			||||||
Also note that we can take advantage of the =eshell-fn-on-files= function to expand the [[help:find-file][find-file]] (which takes one argument), to open more than one file at one time.
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
  (defun eshell/e (&rest files)
 | 
					  (defun eshell-command-to-string (command)
 | 
				
			||||||
    "Essentially an alias to the `find-file' function."
 | 
					    "Return results of executing COMMAND in an eshell environtment.
 | 
				
			||||||
    (eshell-fn-on-files 'find-file 'find-file-other-window files))
 | 
					  The COMMAND can either be a string or a list."
 | 
				
			||||||
 | 
					    (when (listp command)
 | 
				
			||||||
  (defun eshell/ee (&rest files)
 | 
					      ;; Since `eshell-command' accepts a string (and we want all its
 | 
				
			||||||
    "Edit one or more files in another window."
 | 
					      ;; other goodies), we synthesize a string, but since `command'
 | 
				
			||||||
    (eshell-fn-on-files 'find-file-other-window 'find-file-other-window files))
 | 
					      ;; could be a parsed list, we quote all of the arguments.
 | 
				
			||||||
 | 
					      ;;
 | 
				
			||||||
 | 
					      ;; Hacky. Until I figure out a better way to call eshell,
 | 
				
			||||||
 | 
					      ;; as `eshell-named-command' doesn't work reliably:
 | 
				
			||||||
 | 
					      (setq command (s-join " " (cons (first command)
 | 
				
			||||||
 | 
					                                      (--map (format "\"%s\"" it) (rest command))))))
 | 
				
			||||||
 | 
					    (with-temp-buffer
 | 
				
			||||||
 | 
					      (eshell-command command t)
 | 
				
			||||||
 | 
					      (buffer-string)))
 | 
				
			||||||
#+end_src
 | 
					#+end_src
 | 
				
			||||||
No way would I accidentally type any of the following commands:
 | 
					*** Getopts
 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (defalias 'eshell/emacs 'eshell/e)
 | 
					 | 
				
			||||||
  (defalias 'eshell/vi 'eshell/e)
 | 
					 | 
				
			||||||
  (defalias 'eshell/vim 'eshell/e)
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Both =less= and =more= are the same to me. as I want to scroll through a file. Sure the [[https://github.com/sharkdp/bat][bat]] program is cool, but from eshell, we could call [[help:view-file][view-file]], and hit ~q~ to quit and return to the shell.
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (defun eshell/less (&rest files)
 | 
					 | 
				
			||||||
    "Essentially an alias to the `view-file' function."
 | 
					 | 
				
			||||||
    (eshell-fn-on-files 'view-file 'view-file-other-window files))
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
Do I type =more= any more than =less=?
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (defalias 'eshell/more 'eshell/less)
 | 
					 | 
				
			||||||
  (defalias 'eshell/view 'eshell/less)
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
** Ebb and Flow output to Emacs Buffers
 | 
					 | 
				
			||||||
This is an interesting experiment.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Typing a command, but the output isn’t right. So you punch the up arrow, and re-run the command, but this time pass the output through executables like =tr=, =grep=, and even =awk=. Still not right? Rinse and repeat.  Tedious. Since using Emacs to edit text is what we do best, what if we took the output of a command from Eshell, edit that output in a buffer, and then use that edited output in further commands?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
I call this workflow of sending command output back and forth into an Emacs buffer, an /ebb/ and /flow/ approach, where the =ebb= function (for Edit a Bumped Buffer … or something like that), takes some command output, and opens it in a buffer (with an =ebbflow= minor mode), allowing us to edit or alter the data. Pull that data back to the Eshell session with the [[help:emacs/flow][flow]] function (for Fetch buffer data by Lines or Words … naming is hard).
 | 
					 | 
				
			||||||
*** The ebbflow Buffer
 | 
					 | 
				
			||||||
If I don’t specify a specific buffer name, we use this default value:
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (defvar ha-eshell-ebbflow-buffername "*eshell-edit*"
 | 
					 | 
				
			||||||
    "The name of the buffer that eshell can use to store temporary input/output.")
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This buffer has a minor-mode that binds ~C-c C-q~ to close the window and return to the Eshell that spawned it:
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (defun ha-eshell-ebbflow-return ()
 | 
					 | 
				
			||||||
    "Close the ebb-flow window and return to Eshell session."
 | 
					 | 
				
			||||||
    (interactive)
 | 
					 | 
				
			||||||
    (when (boundp 'ha-eshell-ebbflow-close-window)
 | 
					 | 
				
			||||||
      (bury-buffer))
 | 
					 | 
				
			||||||
    (when (boundp 'ha-eshell-ebbflow-return-buffer)
 | 
					 | 
				
			||||||
      (pop-to-buffer ha-eshell-ebbflow-return-buffer)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  (define-minor-mode ebbflow-mode
 | 
					 | 
				
			||||||
    "Get your foos in the right places."
 | 
					 | 
				
			||||||
    :lighter " ebb"
 | 
					 | 
				
			||||||
    :keymap (let ((map (make-sparse-keymap)))
 | 
					 | 
				
			||||||
              (define-key map (kbd "C-c C-q") 'ha-eshell-ebbflow-return)
 | 
					 | 
				
			||||||
              map))
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
Since I use Evil, I also add ~Q~ to call this function:
 | 
					 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					 | 
				
			||||||
  (evil-define-key 'normal ebbflow-mode-map (kbd "Q") 'ha-eshell-ebbflow-return)
 | 
					 | 
				
			||||||
#+end_src
 | 
					 | 
				
			||||||
*** Supporting Functions
 | 
					 | 
				
			||||||
I need a function to analyze command line options. I’ve tried to use [[help:eshell-eval-using-options][eshell-eval-using-options]], but it lacks the ability to have both dashed parameter arguments /and/ non-parameter arguments. For instance, I want to type:
 | 
					I need a function to analyze command line options. I’ve tried to use [[help:eshell-eval-using-options][eshell-eval-using-options]], but it lacks the ability to have both dashed parameter arguments /and/ non-parameter arguments. For instance, I want to type:
 | 
				
			||||||
#+begin_src sh
 | 
					#+begin_src sh
 | 
				
			||||||
  flow --lines some-buffer another-buffer
 | 
					  flow --lines some-buffer another-buffer
 | 
				
			||||||
| 
						 | 
					@ -314,6 +268,9 @@ This wee beastie takes a list of arguments given to the function, along with a /
 | 
				
			||||||
                         ((null defarg)
 | 
					                         ((null defarg)
 | 
				
			||||||
                          (puthash 'parameters (cons arg rest) retmap))
 | 
					                          (puthash 'parameters (cons arg rest) retmap))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                         ((plist-get defarg :help)
 | 
				
			||||||
 | 
					                          (error (documentation (plist-get defarg :help))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                         ;; If argument definition has a integer parameter,
 | 
					                         ;; If argument definition has a integer parameter,
 | 
				
			||||||
                         ;; convert next entry as a number and process rest:
 | 
					                         ;; convert next entry as a number and process rest:
 | 
				
			||||||
                         ((eq (plist-get defarg :parameter) 'integer)
 | 
					                         ((eq (plist-get defarg :parameter) 'integer)
 | 
				
			||||||
| 
						 | 
					@ -379,6 +336,70 @@ Let’s make some test examples:
 | 
				
			||||||
        (should (stringp (first parms)))
 | 
					        (should (stringp (first parms)))
 | 
				
			||||||
        (should (bufferp (second parms))))))
 | 
					        (should (bufferp (second parms))))))
 | 
				
			||||||
#+end_src
 | 
					#+end_src
 | 
				
			||||||
 | 
					** Less and More
 | 
				
			||||||
 | 
					While I can type =find-file=, I often use =e= as an alias for =emacsclient= in Terminals, so let’s do something similar for =eshell=:
 | 
				
			||||||
 | 
					Also note that we can take advantage of the =eshell-fn-on-files= function to expand the [[help:find-file][find-file]] (which takes one argument), to open more than one file at one time.
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun eshell/e (&rest files)
 | 
				
			||||||
 | 
					    "Essentially an alias to the `find-file' function."
 | 
				
			||||||
 | 
					    (eshell-fn-on-files 'find-file 'find-file-other-window files))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defun eshell/ee (&rest files)
 | 
				
			||||||
 | 
					    "Edit one or more files in another window."
 | 
				
			||||||
 | 
					    (eshell-fn-on-files 'find-file-other-window 'find-file-other-window files))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					No way would I accidentally type any of the following commands:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defalias 'eshell/emacs 'eshell/e)
 | 
				
			||||||
 | 
					  (defalias 'eshell/vi 'eshell/e)
 | 
				
			||||||
 | 
					  (defalias 'eshell/vim 'eshell/e)
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Both =less= and =more= are the same to me. as I want to scroll through a file. Sure the [[https://github.com/sharkdp/bat][bat]] program is cool, but from eshell, we could call [[help:view-file][view-file]], and hit ~q~ to quit and return to the shell.
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun eshell/less (&rest files)
 | 
				
			||||||
 | 
					    "Essentially an alias to the `view-file' function."
 | 
				
			||||||
 | 
					    (eshell-fn-on-files 'view-file 'view-file-other-window files))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					Do I type =more= any more than =less=?
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defalias 'eshell/more 'eshell/less)
 | 
				
			||||||
 | 
					  (defalias 'eshell/view 'eshell/less)
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					** Ebb and Flow output to Emacs Buffers
 | 
				
			||||||
 | 
					This is an interesting experiment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Typing a command, but the output isn’t right. So you punch the up arrow, and re-run the command, but this time pass the output through executables like =tr=, =grep=, and even =awk=. Still not right? Rinse and repeat.  Tedious. Since using Emacs to edit text is what we do best, what if we took the output of a command from Eshell, edit that output in a buffer, and then use that edited output in further commands?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I call this workflow of sending command output back and forth into an Emacs buffer, an /ebb/ and /flow/ approach, where the =ebb= function (for Edit a Bumped Buffer … or something like that), takes some command output, and opens it in a buffer (with an =ebbflow= minor mode), allowing us to edit or alter the data. Pull that data back to the Eshell session with the [[help:emacs/flow][flow]] function (for Fetch buffer data by Lines or Words … naming is hard).
 | 
				
			||||||
 | 
					*** The ebbflow Buffer
 | 
				
			||||||
 | 
					If I don’t specify a specific buffer name, we use this default value:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defvar ha-eshell-ebbflow-buffername "*eshell-edit*"
 | 
				
			||||||
 | 
					    "The name of the buffer that eshell can use to store temporary input/output.")
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This buffer has a minor-mode that binds ~C-c C-q~ to close the window and return to the Eshell that spawned it:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun ha-eshell-ebbflow-return ()
 | 
				
			||||||
 | 
					    "Close the ebb-flow window and return to Eshell session."
 | 
				
			||||||
 | 
					    (interactive)
 | 
				
			||||||
 | 
					    (when (boundp 'ha-eshell-ebbflow-close-window)
 | 
				
			||||||
 | 
					      (bury-buffer))
 | 
				
			||||||
 | 
					    (when (boundp 'ha-eshell-ebbflow-return-buffer)
 | 
				
			||||||
 | 
					      (pop-to-buffer ha-eshell-ebbflow-return-buffer)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (define-minor-mode ebbflow-mode
 | 
				
			||||||
 | 
					    "Get your foos in the right places."
 | 
				
			||||||
 | 
					    :lighter " ebb"
 | 
				
			||||||
 | 
					    :keymap (let ((map (make-sparse-keymap)))
 | 
				
			||||||
 | 
					              (define-key map (kbd "C-c C-q") 'ha-eshell-ebbflow-return)
 | 
				
			||||||
 | 
					              map))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					Since I use Evil, I also add ~Q~ to call this function:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (evil-define-key 'normal ebbflow-mode-map (kbd "Q") 'ha-eshell-ebbflow-return)
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
*** flow (or Buffer Cat)
 | 
					*** flow (or Buffer Cat)
 | 
				
			||||||
Eshell can send the output of a command sequence to a buffer:
 | 
					Eshell can send the output of a command sequence to a buffer:
 | 
				
			||||||
#+begin_src sh
 | 
					#+begin_src sh
 | 
				
			||||||
| 
						 | 
					@ -401,7 +422,8 @@ I’m calling the ability to get a buffer contents, /flow/ (Fetch contents as Li
 | 
				
			||||||
      (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))
 | 
				
			||||||
                                      args))
 | 
					                                      args))
 | 
				
			||||||
             (buffers (gethash 'parameters options))
 | 
					             (buffers (gethash 'parameters options))
 | 
				
			||||||
             (content (thread-last parameters
 | 
					             (content (thread-last parameters
 | 
				
			||||||
| 
						 | 
					@ -469,7 +491,8 @@ We have three separate use-cases:
 | 
				
			||||||
    (let* ((options  (eshell-getopts '((:name insert  :short "i" :long "insert")
 | 
					    (let* ((options  (eshell-getopts '((:name insert  :short "i" :long "insert")
 | 
				
			||||||
                                       (:name append  :short "a" :long "append")
 | 
					                                       (:name append  :short "a" :long "append")
 | 
				
			||||||
                                       (:name prepend :short "p" :long "prepend")
 | 
					                                       (:name prepend :short "p" :long "prepend")
 | 
				
			||||||
                                       (:name help    :short "h" :long "help"))
 | 
					                                       (:name help    :short "h" :long "help"
 | 
				
			||||||
 | 
					                                              :help eshell/ebb))
 | 
				
			||||||
                                     args))
 | 
					                                     args))
 | 
				
			||||||
           (location (cond
 | 
					           (location (cond
 | 
				
			||||||
                      ((gethash 'insert  options) :insert)
 | 
					                      ((gethash 'insert  options) :insert)
 | 
				
			||||||
| 
						 | 
					@ -704,21 +727,21 @@ Pretty ugly, but what about using =::= as a separator of the /lambda/ from the /
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here is my initial function. After separating the arguments into two groups (split on the =::= string), we iterate over the file elements, creating a /form/ that includes the filename.
 | 
					Here is my initial function. After separating the arguments into two groups (split on the =::= string), we iterate over the file elements, creating a /form/ that includes the filename.
 | 
				
			||||||
#+begin_src emacs-lisp
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
  (defun eshell/map (&rest args)
 | 
					  (defun eshell/do (&rest args)
 | 
				
			||||||
    "Execute a command sequence over a collection of file elements.
 | 
					    "Execute a command sequence over a collection of file elements.
 | 
				
			||||||
  Separate the sequence and the elements with a `::' string.
 | 
					  Separate the sequence and the elements with a `::' string.
 | 
				
			||||||
  For instance:
 | 
					  For instance:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      map chown _ angela :: *.org(u'oscar')
 | 
					      do chown _ angela :: *.org(u'oscar')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  The function substitutes the `_' sequence to a single filename
 | 
					  The function substitutes the `_' sequence to a single filename
 | 
				
			||||||
  element, and if not specified, it appends the file name to the
 | 
					  element, and if not specified, it appends the file name to the
 | 
				
			||||||
  command. So the following works as expected:
 | 
					  command. So the following works as expected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      map chmod a+x :: *.org"
 | 
					      do chmod a+x :: *.org"
 | 
				
			||||||
    (seq-let (forms elements) (-split-on "::" args)
 | 
					    (seq-let (forms elements) (-split-on "::" args)
 | 
				
			||||||
      (dolist (element (-flatten (-concat elements)))
 | 
					      (dolist (element (-flatten (-concat elements)))
 | 
				
			||||||
        ;; Replace the _ or append the filename:
 | 
					        (message "Working on %s ... %s" element forms)
 | 
				
			||||||
        (let* ((form (if (-contains? forms "_")
 | 
					        (let* ((form (if (-contains? forms "_")
 | 
				
			||||||
                         (-replace "_" element forms)
 | 
					                         (-replace "_" element forms)
 | 
				
			||||||
                       (-snoc forms element)))
 | 
					                       (-snoc forms element)))
 | 
				
			||||||
| 
						 | 
					@ -926,6 +949,118 @@ b.txt
 | 
				
			||||||
a.org
 | 
					a.org
 | 
				
			||||||
8:Nam vestibulum accumsan nisl.
 | 
					8:Nam vestibulum accumsan nisl.
 | 
				
			||||||
#+end_example
 | 
					#+end_example
 | 
				
			||||||
 | 
					** Engineering Notebook
 | 
				
			||||||
 | 
					I want both the command and the output (as well as comments) to be able to go into an org-mode file, I call my /engineering notebook/. Where in that file? If I use =en= that goes in a “General Notes” section, and =ec= goes into the currently clocked in task in that file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I use =ex= to refer to both =en= / =ec=. Use cases:
 | 
				
			||||||
 | 
					  - =ex <command>= :: run the command given and send the output to the notebook
 | 
				
			||||||
 | 
					  - =ex [-n #]= :: grab the output from a previously executed command (defaults to last one)
 | 
				
			||||||
 | 
					  - =ex -c "<comment>" <command>= :: run command and write the comment to the current date in the notebook
 | 
				
			||||||
 | 
					  - =ex <command> :: <comment>= :: run command and write comment to the notebook
 | 
				
			||||||
 | 
					  - =<command> > ex= :: write output from /command/ to the notebook. This won’t add the command that generated the output.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The =-c= option can be combined with the /command/, but I don’t want it to grab the last output, as I think I would just like to send text to the notebook as after thoughts. If the option to =-c= is blank, perhaps it just calls the capture template to allow me to enter voluminous content.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This requires capture templates that don’t do any formatting. I will reused =c c= from [[file:ha-capturing-notes.org::*General Notes][capturing-notes]] code, and create other templates under =e= prefix:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  ;; (setq org-capture-templates nil)
 | 
				
			||||||
 | 
					  (add-to-list 'org-capture-templates
 | 
				
			||||||
 | 
					               '("e" "Engineering Notebook"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (add-to-list 'org-capture-templates
 | 
				
			||||||
 | 
					               '("ee" "Notes and Commentary" plain
 | 
				
			||||||
 | 
					                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
				
			||||||
 | 
					                 "%i" :empty-lines 1 :tree-type month :unnarrowed t))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (add-to-list 'org-capture-templates
 | 
				
			||||||
 | 
					               '("ef" "Piped-in Contents" plain
 | 
				
			||||||
 | 
					                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
				
			||||||
 | 
					                 "%i" :immediate-finish t :empty-lines 1 :tree-type month))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun ha-eshell-engineering-notebook (capture-template args)
 | 
				
			||||||
 | 
					    "Capture commands and output from Eshell into an Engineering Notebook.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Usage:   ex [ options ] [ command string ] [ :: prefixed comments ]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  A _command string_ is an eshell-compatible shell comman to run,
 | 
				
			||||||
 | 
					  and if not given, uses previous commands in the Eshell history.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Options:
 | 
				
			||||||
 | 
					    -c, --comment   A comment string displayed before the command
 | 
				
			||||||
 | 
					    -n, --history   The historical command to use, where `0' is the
 | 
				
			||||||
 | 
					                    previous command, and `1' is the command before that.
 | 
				
			||||||
 | 
					    -t, --template  The `keys' string to specify the capture template"
 | 
				
			||||||
 | 
					    (let* (output
 | 
				
			||||||
 | 
					           (options  (eshell-getopts
 | 
				
			||||||
 | 
					                      '((:name comment :short "c" :long "comment" :parameter string)
 | 
				
			||||||
 | 
					                        (:name history :short "n" :long "history" :parameter integer)
 | 
				
			||||||
 | 
					                        (:name captemp :short "t" :long "template" :parameter string)
 | 
				
			||||||
 | 
					                        (:name interact :short "i" :long "interactive")
 | 
				
			||||||
 | 
					                        (:name help    :short "h" :long "help"
 | 
				
			||||||
 | 
					                               :help ha-eshell-engineering-notebook))
 | 
				
			||||||
 | 
					                      args))
 | 
				
			||||||
 | 
					           (sh-call  (gethash 'parameters options))
 | 
				
			||||||
 | 
					           (sh-parts (-split-on "::" sh-call))
 | 
				
			||||||
 | 
					           (command  (s-join " " (first sh-parts)))
 | 
				
			||||||
 | 
					           ;; Combine the -c parameter with text following ::
 | 
				
			||||||
 | 
					           (comment  (s-join " " (cons (gethash 'comment options)
 | 
				
			||||||
 | 
					                                       (second sh-parts))))
 | 
				
			||||||
 | 
					           (history  (or (gethash 'history options) 0)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ;; Given a -t option? Override the default:
 | 
				
			||||||
 | 
					      (when (gethash 'captemp options)
 | 
				
			||||||
 | 
					        (setq capture-template (gethash 'captemp options)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (when (gethash 'interact options)
 | 
				
			||||||
 | 
					        (setq capture-template "ee"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (cond
 | 
				
			||||||
 | 
					       (sh-call   ; Gotta a command, run it!
 | 
				
			||||||
 | 
					        (ha-eshell-engineering-capture capture-template comment command
 | 
				
			||||||
 | 
					                                       (eshell-command-to-string (first sh-parts))))
 | 
				
			||||||
 | 
					       (t         ; Otherwise, get the history
 | 
				
			||||||
 | 
					        (ha-eshell-engineering-capture capture-template comment
 | 
				
			||||||
 | 
					                                       (ring-ref eshell-history-ring (1+ history))
 | 
				
			||||||
 | 
					                                       (eshell/output history))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defun ha-eshell-engineering-capture (capture-template comment cmd out)
 | 
				
			||||||
 | 
					    "Capture formatted string in CAPTURE-TEMPLATE.
 | 
				
			||||||
 | 
					  Base the string created on COMMENT, CMD, and OUT. Return OUTPUT."
 | 
				
			||||||
 | 
					    (let* ((command (s-trim cmd))
 | 
				
			||||||
 | 
					           (output  (s-trim out))
 | 
				
			||||||
 | 
					           (results (concat
 | 
				
			||||||
 | 
					                     (when comment (format "%s\n\n" comment))
 | 
				
			||||||
 | 
					                     (when command (format "#+begin_src shell\n  %s\n#+end_src\n\n" command))
 | 
				
			||||||
 | 
					                     (when (and command output) "#+results:\n")
 | 
				
			||||||
 | 
					                     (when output  (format "#+begin_example\n%s\n#+end_example\n" output)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (message results)
 | 
				
			||||||
 | 
					      (org-capture-string results capture-template)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ;; Return output from the command, or nothing if there wasn't anything:
 | 
				
			||||||
 | 
					      (or output "")))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					And now we have a =en= and a =ec= version:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun eshell/en (&rest args)
 | 
				
			||||||
 | 
					    "Call `ha-eshell-engineering-notebook' to \"General Notes\"."
 | 
				
			||||||
 | 
					    (interactive)
 | 
				
			||||||
 | 
					    (ha-eshell-engineering-notebook "ef" args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defun eshell/ec (&rest args)
 | 
				
			||||||
 | 
					    "Call `ha-eshell-engineering-notebook' to current clocked-in task."
 | 
				
			||||||
 | 
					    (interactive)
 | 
				
			||||||
 | 
					    (ha-eshell-engineering-notebook "cc" args))
 | 
				
			||||||
 | 
					#+end_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This function simply calls [[help-org-capture][org-capture]] with [[info:org#Template elements][a template]]:
 | 
				
			||||||
 | 
					#+begin_src emacs-lisp
 | 
				
			||||||
 | 
					  (defun eshell/cap (&rest args)
 | 
				
			||||||
 | 
					    "Call `org-capture' with the `ee' template to enter text into the engineering notebook."
 | 
				
			||||||
 | 
					    (org-capture nil "ee"))
 | 
				
			||||||
 | 
					#+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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue