What 8 Hours Of Notes On A Single Problem Looks Like

STARTING OUT

This is a travel log of sorts. It's the notes from my journey to fix a bug in my Grimoire (aka notes app). I use a setup called Org-Mode. It lets me run code from directly inside my notes. Kinda like Jupyter Notesbooks if you're familiar with those, but for plain-text files.

I can put javascript, python, shell scripts, and whatever else in the same file and run them all. It's amazing. But, there was a problem with python. Because so much can be going on in one file empty lines sometimes get added in a way that python's white-space sensitive interpreter can't deal with.

It's not awful. It doesn't happen all the time. But, it happens enough. And when it does it totally kills any state of flow.

Getting into a state of flow is precious to me. It's something I actively defend. So, I decided to work on a fix.

THE RESULT

To start at the end. Here's my solution. It triggers a function before trying to execute code that removes spaces in front of newlines. That means any lines with nothing but spaces get turned into just a newline which makes python happy.


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (replace-regexp-in-region
     "\s+\n"
     "\n"
     (org-element-property :begin
                           (org-element-at-point))
     (org-element-property :end
                           (org-element-at-point)))
    nil)))

(That's lisp code, btw, if you're not already familiar. I semi-mostly-kinda understand it)

Below are my notes. The way I work on projects like this is to just keep copying and pasting snippets so I can keep track of what worked and what didn't in the same file. I'll add a note or two to each one as a reminder.

I'm not expecting folks to read everything. I just though it would be interesting to kinda squint at it to see an example of one style of work. And for the reference, this was eight hours straight from start to finish.

THE ITERATIONS

This is an attempt to fix issues with org mode source blocks complaining about invalid syntex becase of white space

Given a code block like this with an empty line at the end the auto formatting will put an indent on it that causes an invalid syntax error

if __name__ == "__main__":
    print('herasdfe')
    

  File "<stdin>", line 3
    
SyntaxError: invalid syntax

This is the first thing I got from here: https://www.mail-archive.com/emacs-orgmode@gnu.org/msg123020.html

I tried to change it to remove the -final but left the t in there and the couldn't run anything after running it the first time.


(add-hook 'org-ctrl-c-ctrl-c-hook
          (function (lambda ()
                      (message "org-ctrl-c-ctrl-c-final-hook called from %s"
                               (org-element-type (org-element-context)))
                      t)))


org-ctrl-c-ctrl-c-final-hook called from src-block

This is the basic way to setup a hook for it that returns nil so the function keep working


(add-hook 'org-ctrl-c-ctrl-c-hook
          (function (lambda ()
                      (message "here")
                      nil)))



here

This prints out src-block if you're in one`


(princ (org-element-type (org-element-context)))
(terpri)
(princ (org-element-context))

src-block
(src-block (:language elisp :switches nil :parameters :eval :results output :wrap example :begin 3643 :end 3798 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
(princ (org-element-type (org-element-context)))
(terpri)
(princ (org-element-context))

 :post-blank 1 :post-affiliated 3643 :parent nil))

This gets the language


(princ (org-element-property :language (org-element-context)))

elisp

This gets the contents of the block in :value property.

It's the text of the block itself


(princ (org-element-property :value (org-element-context)))


(princ (org-element-property :value (org-element-context)))

This is the full contents of the element


(princ (org-element-at-point))

(src-block (:language elisp :switches nil :parameters :results output :wrap example :begin 4449 :end 4541 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
(princ (org-element-at-point))

 :post-blank 1 :post-affiliated 4449 :parent nil))

This moves the contents of the source block into a variable. Not sure if you'll need that or not.


(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)


(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)

Pinging into a temporary buffer (check the messages buffer)


(with-temp-buffer (message "AAAAAAAAAAAAAAAAAA in tmp"))
(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)


(with-temp-buffer (message "AAAAAAAAAAAAAAAAAAin tmp"))
(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)

This lets you execute multiple commands inside the tmp buffer


(with-temp-buffer
  (progn
    (message "AAAAAAAAAAAAAAAAAA in tmp")
    (message "BBBBBBBBBBBBBBBBBB")
    )
  )
(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)


(with-temp-buffer
  (progn
    (message "AAAAAAAAAAAAAAAAAAin tmp")
    (message "BBBBBBBBBBBBBBBBBB")
    )
  )
(setq contents (org-element-property :value (org-element-at-point)))
(princ contents)

This didn't crash so I think it's inserting the variable into the buffer


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (setq contents
          (org-element-property
           :value
           (org-element-at-point)))
    (with-temp-buffer
      (progn
        (message "AAAAAAAAAAAAAAAAAAin tmp"))
      (message "BBBBBBBBBBBBBBBBBB")
      (message "CCCCCCCCCCCCCCCC")
      (insert (format "%s" contents))
      )
    (message "--x--%s" contents)
    nil)))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point)))                       | (with-temp-buffer (progn (message AAAAAAAAAAAAAAAAAAin tmp)) (message BBBBBBBBBBBBBBBBBB) (message CCCCCCCCCCCCCCCC) (insert (format %s contents))) | (message --x--%s contents) | nil |
| lambda | nil | (setq contents (org-element-property :value (org-element-at-point)))                       | (with-temp-buffer (progn (message AAAAAAAAAAAAAAAAAAin tmp)) (message BBBBBBBBBBBBBBBBBB) (message CCCCCCCCCCCCCCCC))                               | (message --x--%s contents) | nil |
| lambda | nil | (setq contents (org-element-property :value (org-element-at-point)))                       | (with-temp-buffer (progn (message AAAAAAAAAAAAAAAAAAin tmp)) (message BBBBBBBBBBBBBBBBBB))                                                          | (message --x--%s contents) | nil |
| lambda | nil | (with-temp-buffer (progn (message AAAAAAAAAAAAAAAAAAin tmp)) (message BBBBBBBBBBBBBBBBBB)) | (setq contents (org-element-property :value (org-element-at-point)))                                                                                | (message --x--%s contents) | nil |

This didn't crash so hopefully it's doing the replace


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (setq contents
          (org-element-property :value (org-element-at-point)))
    (with-temp-buffer
      (progn
        (message "WWWWWWWWWWWWWWWWWWWWWWWWWWWWW"))
      (insert (format "%s" contents))
      (goto-char (point-min))
      (while (search-forward-regexp "\n\s+\n" nil "noerror")
        (replace-match "\n"))
      )
    nil)))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point))) | (with-temp-buffer (progn (message WWWWWWWWWWWWWWWWWWWWWWWWWWWWW)) (insert (format %s contents)) (goto-char (point-min)) (while (search-forward-regexp |

This is doing the replacement in the buffer. Now to figure out how to get it back in


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (setq contents
          (org-element-property :value (org-element-at-point)))
    (message "%s"
             (with-temp-buffer
               (progn
                                 
                 (message "LLLLLLLLLLLLLLLLLLLLLL"))
                                        
               (insert (format "%s" contents))
               (goto-char (point-min))
               
               (while (re-search-forward "^\s*\n" nil t)
                 (replace-match ""))
               (buffer-string)
               )
             )
    nil)))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point))) | (message %s (with-temp-buffer (progn (message LLLLLLLLLLLLLLLLLLLLLL)) (insert (format %s contents)) (goto-char (point-min)) (while (re-search-forward ^ * |

This doesn't work, but it doesn't break. The message comes across, but it's not fixing it in the file. Could be the regex isn't right....

In fact, I don't think it is....


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function (lambda ()
             (setq contents (org-element-property :value (org-element-at-point)))
             (org-element-put-property
              (org-element-at-point) :value
              (with-temp-buffer
                (progn
                                  
                  (message "HHHHHHHHHHHHHHHHHHHHHHHHHH"))
                
                
                (insert (format "%s" contents))
                (goto-char (point-min))
                
                (while (re-search-forward "^\s*\n" nil t)
                  (replace-match ""))
                (buffer-string)
                ))
             ;;nil
             (org-element-at-point)
             )))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point))) | (org-element-put-property (org-element-at-point) :value (with-temp-buffer (progn (message HHHHHHHHHHHHHHHHHHHHHHHHHH)) (insert (format %s contents)) (goto-char (point-min)) (while (re-search-forward ^ * |

This crashes when you try to run int on the python


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (setq contents (org-element-property :value (org-element-at-point)))
    (org-element-put-property
     (org-element-at-point) :value
     (with-temp-buffer
       (progn
         (message "DDDDDDDDDDDDDDDDDDDD"))
       (insert (format "%s" contents))
       (goto-char (point-min))
       (while (re-search-forward "^\s*\n" nil t)
         (replace-match ""))
       (goto-char (point-min))
       (while (re-search-forward "^\s*$" nil t)
         (replace-match ""))
       (buffer-string)
       ))
    (message "%s" 
             (org-element-property :value (org-element-at-point))
             )
    ;;nil
    (org-element-at-point)
    )))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point))) | (org-element-put-property (org-element-at-point) :value (with-temp-buffer (progn (message DDDDDDDDDDDDDDDDDDDD)) (insert (format %s contents)) (goto-char (point-min)) (while (re-search-forward ^ * |

The progn was closed to quickly, but even with that bug fixed this still crashes


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (setq contents
          (org-element-property :value (org-element-at-point)))
    (org-element-put-property
     (org-element-at-point) :value
     (with-temp-buffer
       (progn
         (message "TTTTTTTTTTTTTTTTTTTTTT")
         (insert (format "%s" contents))
         (progn 
           (goto-char (point-min))
           (while (re-search-forward "^\s*\n" nil t)
             (replace-match ""))
           ) 
         (progn 
           (goto-char (point-min))
           (while (re-search-forward "^\s*$" nil t)
             (replace-match ""))
           )
         (buffer-string)
         )))
    (org-element-at-point)
    )))

| lambda | nil | (setq contents (org-element-property :value (org-element-at-point))) | (org-element-put-property (org-element-at-point) :value (with-temp-buffer (progn (message TTTTTTTTTTTTTTTTTTTTTT) (insert (format %s contents)) (progn (goto-char (point-min)) (while (re-search-forward ^ * |

This is just trying to pass the value straight through to see if i'm doing that right.


(add-hook 'org-ctrl-c-ctrl-c-hook
          (function (lambda ()
                      (org-element-at-point)
                      )))

| lambda | nil | (org-element-at-point) |

That didn't work, couldn't run anything after running it. Trying to pull without going through the hook now which, in retrospect is something that would have been beneficial earlier since I wouldn't have had to restart the app between each attempt.


(message "%s" (org-element-at-point))

;; test


(src-block (:language elisp :switches nil :parameters :wrap example :begin 15937 :end 16091 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
(message "%s" (org-element-at-point))
(message "%s" (org-element-context (org-element-at-point)))

;; test


 :post-blank 1 :post-affiliated 15937 :parent nil))

Seeing what the function returns


(message "%s" (lambda () (org-element-at-point) ))

(lambda nil (org-element-at-point))

I'm not sure how this works, but it's getting the lambda?


(princ (org-element-at-point) )


| src-block | (:language elisp :switches nil :parameters :wrap example :begin 16668 :end 16761 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value |

These next few are just just send stuff straight thru. Lambdas don't appear to work the same way they seem to return themselves from what I'm seeing in the docs


(message "%s" (progn (+ 100 100)))

200

(princ (progn (+ 100 100)))

200

I think I need to send through thru, gonna try to do it as a straight passthru


(princ (org-element-at-point))


  (src-block (:language elisp :switches nil :parameters :results output :wrap example :post padder(data=*this*) :begin 17275 :end 17397 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
  (princ (org-element-at-point))
  
   :post-blank 5 :post-affiliated 17275 :parent nil))


(add-hook 'org-ctrl-c-ctrl-c-hook
          (org-element-at-point))
          

`Invalid function: (src-block (:language "elisp" :switches nil :parameters ":results output :wrap example :post padder(data=*this*)" :begin 18051 :end 18220 ...))`


Something clicked. I need to send a function in. That's what I was doing, but I didn't really understand it. It was just copied code.

This one passes stuff through which looks likes what I want.


(defun process-element ()
  (org-element-at-point)
  )
(princ (process-element))

| src-block | (:language elisp :switches nil :parameters :wrap example :begin 18775 :end 18891 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value |

This one is using an argument to pass thru, the values are different. I'm guessing because it's coming from a different point (e.g. in the other one it's directly inside process-element and in this one it's in `princ (process-element)


(defun process-element (el)
  el
  )

(princ (process-element (org-element-at-point)))

  (src-block (:language elisp :switches nil :parameters :results output :wrap example :post padder(data=*this*) :begin 19207 :end 19386 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
  (defun process-element (el)
    el
    )
  
  (princ (process-element (org-element-at-point)))
   :post-blank 7 :post-affiliated 19207 :parent nil))

This busted again. Restarting


(defun process-element (el)
  el
)

(add-hook 'org-ctrl-c-ctrl-c-hook
          (process-element (org-element-at-point)))
          

Trying with ' didn't work either


(defun process-element (el)
  el
)

(add-hook 'org-ctrl-c-ctrl-c-hook
          '(process-element (org-element-at-point)))
          

Invalid function: (process-element (org-element-at-point))

Next


(defun process-element (el)
  el
)

(add-hook 'org-ctrl-c-ctrl-c-hook
          'process-element (org-element-at-point))
          

run-hook-with-args-until-success: Wrong number of arguments: (lambda (el) el), 0

Backing up.


(princ (lambda () (+ 10 10) ))

(lambda nil (+ 10 10))

This didn't work either (added ' in front of (org-element-at-point))


(add-hook 'org-ctrl-c-ctrl-c-hook
          (function (lambda ()
                      '(org-element-at-point)
                      )))

Straight thru test


(princ (org-element-at-point))


  (src-block (:language elisp :switches nil :parameters :results output :wrap example :post padder(data=*this*) :begin 21965 :end 22096 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
  
  (princ (org-element-at-point))
  
  
  
   :post-blank 11 :post-affiliated 21965 :parent nil))

This is working to pull all the spaces out from the start of line.


(setq contents
      (org-element-property :value
                            (org-element-at-point)))

(princ
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn
       (goto-char (point-min))
       (while (re-search-forward "^\s" nil t)
         (replace-match ""))
       )
     (buffer-string)
     )))           



  
  (setq contents
  (org-element-property :value
  (org-element-at-point)))
  
  (princ
  (with-temp-buffer
  (progn
  (insert (format "%s" contents))
  (progn
  (goto-char (point-min))
  (while (re-search-forward "^\s" nil t)
  (replace-match ""))
  )
  (buffer-string)
  )))           
  
  
  

This is working to replace any spaces that proceed a newline which will remove them from empty lines which I think is the fix if I can get it in place before the processing occurs


(setq contents
      (org-element-property :value
                            (org-element-at-point)))
(princ
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn
       
       (goto-char (point-min))
       (while (re-search-forward "\s+\n" nil t)
         (replace-match "\n"))
       )
     (buffer-string)
     )))           

                            


(setq contents
      (org-element-property :value
                            (org-element-at-point)))
(princ
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn

       (goto-char (point-min))
       (while (re-search-forward "\s+\n" nil t)
         (replace-match "\n"))
       )
     (buffer-string)
     )))



(NOTE: I almost quit here, but was writing it up and had the idea to do a more direct edit which is what finally solved it)

Starting to look at editing directly. find and replace of the block itself doesn't seem like something that's done directly with newlines. So, looking to blow away the region and replace it in it


(princ (org-element-at-point))

(push-mark (org-element-property :end
                            (org-element-at-point)))

;; ToDO: deactivate the mark

(push-mark (org-element-property :begin
                            (org-element-at-point)))

(setq contents
      (org-element-property :value
                            (org-element-at-point)))
(setq updated-contents
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn
       
       (goto-char (point-min))
       (while (re-search-forward "\s+\n" nil t)
         (replace-match "\n"))
       )
     (buffer-string)
     )))           

;; (goto-char 
;;       (org-element-property :end
;;                             (org-element-at-point)))


(princ updated-contents)
                            
;; (goto-char (point-min))
;; (while (search-forward contents nil t)
;;   (replace-match updated-contents))


(src-block (:language elisp :switches nil :parameters :results output :wrap example :begin 24337 :end 24967 :number-lines nil :preserve-indent nil :retain-labels t :use-labels t :label-fmt nil :value 
(princ (org-element-at-point))

(setq contents
      (org-element-property :value
                            (org-element-at-point)))
(setq updated-contents
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn
       
       (goto-char (point-min))
       (while (re-search-forward "\s+\n" nil t)
         (replace-match "\n"))
       )
     (buffer-string)
     )))           


(princ updated-contents)
                            
;; (goto-char (point-min))
;; (while (search-forward contents nil t)
;;   (replace-match updated-contents))


 :post-blank 1 :post-affiliated 24337 :parent nil))
(princ (org-element-at-point))

(setq contents
      (org-element-property :value
                            (org-element-at-point)))
(setq updated-contents
 (with-temp-buffer
   (progn
     (insert (format "%s" contents))
     (progn

       (goto-char (point-min))
       (while (re-search-forward "\s+\n" nil t)
         (replace-match "\n"))
       )
     (buffer-string)
     )))


(princ updated-contents)

;; (goto-char (point-min))
;; (while (search-forward contents nil t)
;;   (replace-match updated-contents))


This does the replacement directly in the region


(replace-regexp-in-region
 "\s+\n"
 "\n"
 (org-element-property :begin
                       (org-element-at-point))
 (org-element-property :end
                       (org-element-at-point)))


I think this is it.


(add-hook
 'org-ctrl-c-ctrl-c-hook
 (function
  (lambda ()
    (replace-regexp-in-region
     "\s+\n"
     "\n"
     (org-element-property :begin
                           (org-element-at-point))
     (org-element-property :end
                           (org-element-at-point)))
    nil)))

Test:

if __name__ == "__main__":
    print('Check test 1 2 1 2')


Check test 1 2 1 2

Yep. Looks like it works.

References

  • https://www.mail-archive.com/emacs-orgmode@gnu.org/msg123020.html
  • https://www.mail-archive.com/emacs-orgmode@gnu.org/msg123021.html
  • https://orgmode.org/worg/dev/org-element-api.html
  • https://orgmode.org/worg/doc.html
  • https://emacs.stackexchange.com/questions/34205/insert-the-value-of-a-variable-into-the-buffer-im-editing
  • https://www.gnu.org/software/emacs/manual/html_node/eintr/progn.html
  • https://wilkesley.org/~ian/xah/emacs/elisp_compact_empty_lines.html
  • http://doc.endlessparentheses.com/Var/org-ctrl-c-ctrl-c-hook.html
  • http://jeremy.zawodny.com/blog/archives/008872.html
  • http://xahlee.info/emacs/emacs/elisp_find_replace_text.html
  • https://cs.calvin.edu/courses/cs/214/adams/labs/10/e-lisp/
  • https://emacs.stackexchange.com/questions/10679/multiline-regex-replace-within-elisp-function
  • https://emacs.stackexchange.com/questions/20346/unset-an-org-babel-property
  • https://emacs.stackexchange.com/questions/2399/code-block-specific-interpreter-in-org-babel
  • https://emacs.stackexchange.com/questions/28620/problems-with-org-ctrl-c-ctrl-c-and-org-9-0
  • https://emacs.stackexchange.com/questions/29131/get-contents-of-a-named-source-block
  • https://emacs.stackexchange.com/questions/31011/how-to-modify-content-of-org-mode-document-using-org-element-api
  • https://emacs.stackexchange.com/questions/46068/how-to-apply-org-ctrl-c-ctrl-c-hook-from-command-line
  • https://emacs.stackexchange.com/questions/46296/how-can-i-get-elements-parent-at-point
  • https://emacs.stackexchange.com/questions/62868/how-to-use-c-header-files-in-org-mode-source-code-blocks
  • https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=1f1574c9a2a3562299d40ceaaceb804ab1ab4831
  • https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/org.el
  • https://github.com/bzg/org-mode/blob/main/doc/org-manual.org
  • https://github.com/emacs-mirror/emacs/blob/master/lisp/simple.el
  • https://github.com/yyr/org-mode/blob/master/lisp/org-element.el
  • https://jherrlin.github.io/posts/emacs-orgmode-source-code-blocks2/
  • https://kb.iu.edu/d/aiuf#:~:text=Click%20the%20mouse's%20left%20button,you%20selected%20should%20be%20highlighted.
  • https://kitchingroup.cheme.cmu.edu/blog/2016/05/29/Expanding-orgmode-py-to-get-better-org-python-integration/
  • https://lists.gnu.org/archive/html/emacs-orgmode/2010-01/msg00703.html
  • https://lists.gnu.org/archive/html/emacs-orgmode/2011-03/msg00857.html
  • https://orgmode.org/manual/Environment-of-a-Code-Block.html
  • https://orgmode.org/worg/dev/org-element-api.html
  • https://orgmode.org/worg/dev/org-syntax.html
  • https://orgmode.org/worg/doc.html
  • https://orgmode.org/worg/exporters/org-element-docstrings.html
  • https://orgmode.org/worg/org-contrib/babel/intro.html
  • https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-elisp.html
  • https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-lisp.html
  • https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-python.html
  • https://orgmode.org/worg/org-contrib/org-exp-blocks.html
  • https://orgmode.org/worg/users/bzg.html
  • https://quarto.org/docs/reference/formats/org.html
  • https://sachachua.com/dotemacs/index.html
  • https://stackoverflow.com/questions/12411770/emacs-interactive-search-search-forward-regexp-difference
  • https://stackoverflow.com/questions/17208842/how-to-parse-org-mode-by-org-element-elegantly
  • https://stackoverflow.com/questions/22852075/replace-character-at-point-in-emacs
  • https://stackoverflow.com/questions/2627289/how-to-replace-a-region-in-emacs-with-yank-buffer-contents
  • https://stackoverflow.com/questions/2627289/how-to-replace-a-region-in-emacs-with-yank-buffer-contents
  • https://stackoverflow.com/questions/26289607/is-there-anything-similar-to-return-statement-of-c-in-lisp
  • https://stackoverflow.com/questions/26478594/how-to-delete-empty-lines-in-a-file-using-emacs
  • https://stackoverflow.com/questions/27032218/indentation-not-working-properly-in-emacs-for-python
  • https://stackoverflow.com/questions/68537645/how-to-use-regular-expression-in-common-lisp-to-get-everything-in-a-string-until
  • https://stackoverflow.com/questions/6886024/how-to-replace-a-search-with-multiple-lines-in-emacs
  • https://stackoverflow.com/questions/9310738/is-it-possible-to-use-return-from-inside-a-lambda-function
  • https://wilkesley.org/~ian/xah/emacs/elisp_compact_empty_lines.html
  • https://www.gnu.org/software/emacs/manual/html_node/eintr/defun.html
  • https://www.gnu.org/software/emacs/manual/html_node/eintr/lambda.html
  • https://www.gnu.org/software/emacs/manual/html_node/eintr/progn.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Anonymous-Functions.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Char-Classes.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Current-Buffer.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Excursions.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Lambda-Expressions.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Marker-Insertion-Types.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Markers.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Functions.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Search.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Replacing-Match.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Search-and-Replace.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Setting-Variables.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Temporary-Displays.html
  • https://www.gnu.org/software/emacs/manual/html_node/elisp/The-Mark.html
  • https://www.gnu.org/software/emacs/manual/html_node/emacs/Mark-Ring.html
  • https://www.gnu.org/software/emacs/manual/html_node/emacs/Replace.html
  • https://www.masteringemacs.org/article/removing-blank-lines-buffer
  • https://www.math.utah.edu/docs/info/emacs-lisp-intro_13.html
  • https://www.oreilly.com/library/view/learning-gnu-emacs/1565921526/ch04s02.html
  • https://www.reddit.com/r/emacs/comments/9szvx7/multiline_search_and_replace_over_multiple_files/
  • https://www.tutorialspoint.com/lisp/lisp_returning_values_functions.htm