Skip to content

Instantly share code, notes, and snippets.

@bkaestner
Created February 19, 2022 13:16
Show Gist options
  • Save bkaestner/885ea15c067aaab6093158112f4339b5 to your computer and use it in GitHub Desktop.
Save bkaestner/885ea15c067aaab6093158112f4339b5 to your computer and use it in GitHub Desktop.
Detangling ideas
;;; detangle.el --- tangle code into Org files -*- lexical-binding: t; -*-
;; Copyright (C) 2022
;; SPDX-License-Identifier: MIT
;; Author: Benjamin Kästner
;; Keywords: convenience
;;; Commentary:
;; WIP. Not tested enough yet. Handle with care. The usual stuff.
;;; Code:
(defun detangle-file (filename)
"Detangle the given Emacs Lisp file into an `org-mode' file."
(interactive "f")
(copy-file filename (concat filename ".org"))
(with-current-buffer (find-file-noselect (concat filename ".org"))
(goto-char (point-min))
;; This function uses a very simple internal state machine with
;; two states, `text' and `code'. State transition from `code' to
;; `text' is triggered by pure comment lines, i.e. lines that only
;; contain a comment. State transition from `text' to `code' is
;; triggered on the first non-empty non-comment line.
(let ((state 'text) ; initial state is text
(clevel 1)) ; initial current org-mode level is 1
(while (not (eobp))
(cond
;; Warning: This pattern uses [ \t] after the semicolons by
;; choice, as [[:space:]] also contains whitespace and would
;; thus join multiple, otherwise empty comment lines
;; together.
((looking-at "^\\([[:space:]]*\\(;+\\)[ \t]*\\)")
(if (eq state 'code)
;; End of code transition
(progn
(insert "#+END_SRC")
(newline)
;; We need go go backward after the newline, otherwise
;; we jump behind the comment at the end of the loop.
(forward-line -1)
(setq state 'text))
(let ((level (- (match-end 2) (match-beginning 2) 2)))
(replace-match "")
(when (> level 0)
;; TODO: Consider `org-current-level' instead?
(beginning-of-line)
(org-insert-heading)
(while (> level clevel)
(org-demote)
(cl-incf clevel))
(while (< level clevel)
(org-promote)
(cl-decf clevel))))))
;; Keep and skip empty lines
((looking-at-p "^[[:space:]]*$") nil)
(t
;; We hit a non-empty, non-comment line and transition into
;; the `code' state.
(when (eq state 'text)
(insert "#+BEGIN_SRC emacs-lisp")
(newline)
(setq state 'code))))
(forward-line))
(when (eq state 'code)
(insert "#+END_SRC")))))
(provide 'detangle)
;;; detangle.el ends here

detangle.el — tangle code into Org files -*- lexical-binding: t; -*-

Copyright (C) 2022 SPDX-License-Identifier: MIT Author: Benjamin Kästner Keywords: convenience

Commentary:

WIP. Not tested enough yet. Handle with care. The usual stuff.

Code:

(defun detangle-file (filename)
  "Detangle the given Emacs Lisp file into an `org-mode' file."
  (interactive "f")
  (copy-file filename (concat filename ".org"))
  (with-current-buffer (find-file-noselect (concat filename ".org"))
    (goto-char (point-min))

This function uses a very simple internal state machine with two states, `text’ and `code’. State transition from `code’ to `text’ is triggered by pure comment lines, i.e. lines that only contain a comment. State transition from `text’ to `code’ is triggered on the first non-empty non-comment line.

(let ((state 'text)          ; initial state is text
      (clevel 1))            ; initial current org-mode level is 1
  (while (not (eobp))
    (cond

Warning: This pattern uses [ \t] after the semicolons by choice, as :space: also contains whitespace and would thus join multiple, otherwise empty comment lines together.

((looking-at "^\\([[:space:]]*\\(;+\\)[ \t]*\\)")
 (if (eq state 'code)

End of code transition

(progn 
  (insert "#+END_SRC")
  (newline)

We need go go backward after the newline, otherwise we jump behind the comment at the end of the loop.

    (forward-line -1)
    (setq state 'text))
(let ((level (- (match-end 2) (match-beginning 2) 2)))
  (replace-match "")
  (when (> level 0)

TODO: Consider `org-current-level’ instead?

(beginning-of-line)
(org-insert-heading)
(while (> level clevel)
  (org-demote)
  (cl-incf clevel))
(while (< level clevel)
  (org-promote)
  (cl-decf clevel))))))

Keep and skip empty lines

((looking-at-p "^[[:space:]]*$") nil)
(t

We hit a non-empty, non-comment line and transition into the `code’ state.

          (when (eq state 'text)
            (insert "#+BEGIN_SRC emacs-lisp")
            (newline)
            (setq state 'code))))
        (forward-line))
      (when (eq state 'code)
        (insert "#+END_SRC")))))

(provide 'detangle)

detangle.el ends here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment