Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions .emacs.d/init.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
;; Save any custom set variable in exordium-custom-file rather than at the end of init.el:
(setq custom-file (locate-user-emacs-file "custom.el"))

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(setq package-user-dir
(locate-user-emacs-file (concat "elpa-" emacs-version)))

(when (fboundp 'native-comp-available-p)
(setq package-native-compile (native-comp-available-p)))
(package-initialize)

;; Load the packages we need if they are not installed already
(let ((package-pinned-packages (append
'((use-package . "melpa")
(diminish . "melpa")
(bind-key . "melpa"))))
(has-refreshed nil))

(defun update-package (p has-refreshed)
(unless (package-installed-p p)
(unless has-refreshed
(message "Refreshing package database...")
(package-refresh-contents)
(setq has-refreshed t)
(message "Done."))
(package-install p)))

(dolist (pkg package-pinned-packages)
(let ((p (car pkg)))
(update-package p has-refreshed))))

;; This is only needed once, near the top of the file
(eval-when-compile
;; Following line is not needed if use-package.el is in ~/.emacs.d
(require 'use-package))

(require 'use-package-ensure)
(setq use-package-always-ensure t)
(setq use-package-compute-statistics t)
;;; remove a package from the builtin list so it can be upgraded
(defun wg21org-ignore-builtin (pkg)
(assq-delete-all pkg package--builtins)
(assq-delete-all pkg package--builtin-versions))


;;; Org mode

(use-package org
:commands (org-mode)
:mode (("\\.org\\'" . org-mode))
:after (flycheck)
:bind
(:map org-mode-map
([remap org-toggle-comment] . iedit-mode))
:custom
(org-confirm-babel-evaluate t)
(org-fontify-quote-and-verse-blocks t)
(org-fontify-whole-heading-line t)
(org-log-into-drawer t)
(org-src-fontify-natively t)
(org-src-preserve-indentation t)
(org-startup-folded nil)
(org-startup-indented t)
(org-startup-truncated nil)
(org-startup-with-inline-images t)
(org-support-shift-select :always)
(org-use-sub-superscripts "{}")

;; Edit settings
(org-auto-align-tags nil)
(org-tags-column 0)
(org-catch-invisible-edits 'show-and-error)
(org-special-ctrl-a/e t)
(org-insert-heading-respect-content t)

;; Org styling, hide markup etc.
(org-hide-emphasis-markers nil)
(org-pretty-entities t)

;; ;;;; code blocks
(org-confirm-babel-evaluate nil)
(org-src-window-setup 'reorganize-frame)
(org-edit-src-persistent-message t)
(org-src-fontify-natively t)
(org-src-preserve-indentation t)
(org-src-tab-acts-natively t)
(org-edit-src-content-indentation 0)

;; ;;;; export
(org-export-with-toc t)
(org-export-headline-levels 8)
(org-export-dispatch-use-expert-ui nil)
(org-html-htmlize-output-type 'css)
(org-html-head-include-default-style t)
(org-html-head-include-scripts t)

;;; visual line mode
(visual-line-mode 1)

:hook ((org-mode . variable-pitch-mode))

:init
(add-hook 'org-src-mode-hool #'(lambda ()
(add-to-list 'flycheck-disabled-checkers 'emacs-lisp-checkdoc)))

:config
;; Enable org-babel for perl, ruby, sh, python, emacs-lisp, C, C++, etc
;; TODO: add extra languages configurable by user
(org-babel-do-load-languages
'org-babel-load-languages
`((perl . t)
(ruby . t)
(shell . t)
(python . t)
(emacs-lisp . t)
(C . t)
(dot . t)
(sql . t))))

(use-package htmlize
:ensure t)


(use-package graphviz-dot-mode
:config
(setq graphviz-dot-indent-width 4))

(org-babel-do-load-languages
'org-babel-load-languages
(append org-babel-load-languages
'((dot . t))))


(setq plantuml-jar-path "/usr/share/plantuml/plantuml.jar")
(setq plantuml-default-exec-mode 'jar)

(setq org-plantuml-jar-path (expand-file-name "/usr/share/plantuml/plantuml.jar"))
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
(org-babel-do-load-languages
'org-babel-load-languages
(append org-babel-load-languages
'((plantuml . t))))

(org-babel-do-load-languages
'org-babel-load-languages
(append org-babel-load-languages
'((ditaa . t))))

(setq org-ditaa-jar-path "/usr/share/ditaa/ditaa.jar")

(setq org-support-shift-select 'always)


;; Reveal.js + Org mode
(use-package org-re-reveal
:config
(setq org-re-reveal-root "file:////home/sdowney/bld/reveal.js"))


(use-package org-transclusion
:after org
:bind (:map
org-mode-map
("C-c C-x T" . org-transclusion-mode))
:config
(org-transclusion-mode 1)
)


(use-package ox-gfm
:after org)

(use-package with-editor)

(use-package citeproc :ensure t :after org)

;; Export orgit links as GitHub source browser URLs
(defvar orgit-base-url "https://github.com/steve-downey/expected/blob/main/"
"Base URL for exporting orgit links.")

(org-link-set-parameters
"orgit"
:export (lambda (path desc backend)
(let* ((parts (split-string path "::"))
(filepath (if (> (length parts) 1) (cadr parts) path))
(url (concat orgit-base-url filepath)))
(cond
((or (eq 'md backend) (eq 'gfm backend))
(format "[`%s`](%s)" (or desc filepath) url))
((eq 'html backend)
(format "<a href=\"%s\"><code>%s</code></a>" url (or desc filepath)))
(t url)))))









;; Allow org-transclusion to resolve orgit links to the local file system
(defun org-transclusion-add-orgit (link plist)
"Resolve orgit links into file links in-place."
(when (string= "orgit" (org-element-property :type link))
(let* ((full-path (org-element-property :path link))
(parts (split-string full-path "::"))
(repo-dir (car parts))
(inner-file (cadr parts))
(search-uuid (if (> (length parts) 2) (caddr parts) nil))
(actual-file (expand-file-name inner-file repo-dir))
(raw-link (concat "file:" actual-file)))

(when search-uuid
(setq raw-link (concat raw-link "::" search-uuid)))

;; CRITICAL: Mutate the original link in-place so downstream plugins
;; (like org-transclusion-src-lines) see the correct file path!
(org-element-put-property link :type "file")
(org-element-put-property link :path actual-file)
(org-element-put-property link :raw-link raw-link)
(if search-uuid
(org-element-put-property link :search-option search-uuid)
(org-element-put-property link :search-option nil))))
;; ALWAYS return nil so the NEXT functions (like org-transclusion-add-src-lines) can handle it!
nil)

(require 'org-transclusion)
(add-hook 'org-transclusion-add-functions 'org-transclusion-add-orgit)

;; Pinned transclusion for the epistolary blog posts.
;;
;; Two document categories, two policies (docs/epistolary-pinning-plan.md):
;; - living docs (docs/compiler_architecture.org) transclude via `file:' /
;; `orgit:' and roll forward with the worktree, which is the point of them;
;; - blog posts transclude via `orgit-file:' pinned to a `blog/phase-NN' tag,
;; so a later refactor cannot rewrite the code inside a published entry.
;;
;; Both adapters live on `org-transclusion-add-functions' and coexist; the link
;; type selects the policy. See docs/blog/pins.md for the post-to-tag mapping.
(add-to-list 'load-path
(expand-file-name
"lisp" (or (and load-file-name
(file-name-directory load-file-name))
user-emacs-directory)))
(require 'orgit-file-transclusion)
146 changes: 146 additions & 0 deletions .emacs.d/lisp/orgit-file-transclusion.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
;;; orgit-file-transclusion.el --- transclude UUID-anchored regions at a git rev -*- lexical-binding: t; -*-
;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

;; Provenance: docs/epistolary-pinning-plan.md (section 3), shared across
;; compile-time-scheme sibling repositories by copy, not by load-path coupling.
;;
;; Link form: [[orgit-file:REPO::REV::PATH::UUID]]
;; Used with: :lines 2- :src LANG :end "UUID end" (unchanged conventions)
;;
;; Where the sibling `orgit:' adapter in init.el resolves PATH against the
;; worktree -- showing the file as of now -- this one resolves it against REV,
;; so an epistolary post keeps showing the code its prose was written about.
;; REV is normally an annotated `blog/phase-NN' tag; see docs/blog/pins.md.
;;
;; No magit dependency: the blob comes from a `git show' subprocess, so batch
;; export stays light. The interactive `orgit-file' package (gggion/orgit-file)
;; uses the same link syntax, so installing it makes these links followable in
;; a live Emacs, but nothing here requires it.

;;; Commentary:
;;
;; Implementation note -- why this extracts a blob to a file instead of
;; slicing the region itself:
;;
;; `org-transclusion-add-src-lines' already implements the exact semantics the
;; posts depend on, and they are fiddlier than they look. `:end' is EXCLUSIVE
;; of the line holding the end marker; `:lines 2-' counts forward from the
;; anchor line found by the search option, not from the top of the file; and
;; `:src cpp' wraps the result in a fenced block. Reimplementing that slicing
;; would duplicate three behaviours that must not drift.
;;
;; So this adapter mirrors `org-transclusion-add-orgit': it materialises the
;; pinned blob, rewrites the link in place into a plain `file:' link with the
;; same `::UUID' search option, and returns nil so the standard src-lines
;; handler does the work. Post attributes keep meaning exactly what they
;; meant under worktree resolution -- by construction, not by imitation.
;;
;; The blob is written under a path that preserves the original repo-relative
;; path, so `find-file-noselect' picks the same major mode it would have for
;; the real file. `org-link-search', which resolves the `::UUID' anchor, is
;; mode-sensitive, so this is what keeps anchor resolution identical.

;;; Code:

(require 'org-transclusion)
(require 'subr-x)

(defgroup orgit-file-transclusion nil
"Transclude UUID-anchored source regions pinned to a git revision."
:group 'org-transclusion)

(defcustom orgit-file-transclusion-cache-dir
(expand-file-name "orgit-file-transclusion" temporary-file-directory)
"Directory holding blobs extracted from git.
Contents are disposable: each blob is cached under its resolved commit
SHA, so a stale entry cannot be served for a different revision."
:type 'directory
:group 'orgit-file-transclusion)

(defcustom orgit-file-transclusion-base-url
"https://github.com/steve-downey/expected/blob/"
"Base URL for exporting `orgit-file' links, with a trailing slash.
The pinned REV is appended, so an exported link is a permalink to the
revision the post was written against rather than to a moving branch."
:type 'string
:group 'orgit-file-transclusion)

(defun orgit-file-transclusion--parse (raw)
"Split RAW into (REPO REV PATH UUID); signal on any other shape."
(let ((parts (split-string raw "::")))
(unless (= 4 (length parts))
(error "orgit-file link needs REPO::REV::PATH::UUID, got: %s" raw))
parts))

(defun orgit-file-transclusion--rev-parse (repo rev)
"Resolve REV to a full commit SHA in REPO."
(with-temp-buffer
(unless (zerop (call-process "git" nil t nil
"-C" (expand-file-name repo)
"rev-parse" (concat rev "^{commit}")))
(error "orgit-file: cannot resolve rev %s in %s (fetch tags?): %s"
rev repo (string-trim (buffer-string))))
(string-trim (buffer-string))))

(defun orgit-file-transclusion--blob-file (repo rev path)
"Materialise PATH at REV in REPO as a local file; return its name.
The returned path ends in PATH, so the buffer gets the major mode it
would have had for the real file."
(let* ((sha (orgit-file-transclusion--rev-parse repo rev))
(cached (expand-file-name
path (expand-file-name sha orgit-file-transclusion-cache-dir))))
(unless (file-exists-p cached)
(make-directory (file-name-directory cached) t)
;; Write via a temp name and rename, so a failed `git show' can never
;; leave a truncated blob behind for a later run to trust.
(let ((tmp (concat cached ".partial")))
(with-temp-buffer
(unless (zerop (call-process "git" nil t nil
"-C" (expand-file-name repo)
"show" (format "%s:%s" sha path)))
(error "orgit-file: git show %s:%s failed in %s: %s"
rev path repo (string-trim (buffer-string))))
(let ((coding-system-for-write 'no-conversion))
(write-region (point-min) (point-max) tmp nil 'quiet)))
(rename-file tmp cached t)))
cached))

(defun orgit-file-transclusion-add (link _plist)
"Resolve an `orgit-file' LINK into a `file' link on the pinned blob.
Mutates LINK in place and returns nil, so `org-transclusion-add-src-lines'
applies the post's :lines/:src/:end attributes exactly as it does for a
worktree-resolved link."
(when (string= "orgit-file" (org-element-property :type link))
(pcase-let* ((`(,repo ,rev ,path ,uuid)
(orgit-file-transclusion--parse
(org-element-property :path link)))
(blob (orgit-file-transclusion--blob-file repo rev path)))
;; Mutate in place so downstream handlers see the resolved file path.
(org-element-put-property link :type "file")
(org-element-put-property link :path blob)
(org-element-put-property link :raw-link (concat "file:" blob "::" uuid))
(org-element-put-property link :search-option uuid)))
;; Always nil: let the next add-function build the payload.
nil)

(add-hook 'org-transclusion-add-functions #'orgit-file-transclusion-add)

;; Export `orgit-file' links as forge permalinks at the pinned revision.
;; Posts currently carry these links only on `#+transclude:' lines, which
;; org-transclusion consumes before export, so this is for future inline use.
(org-link-set-parameters
"orgit-file"
:export
(lambda (path desc backend)
(let* ((parts (split-string path "::"))
(rev (nth 1 parts))
(filepath (or (nth 2 parts) path))
(url (concat orgit-file-transclusion-base-url rev "/" filepath)))
(cond
((memq backend '(md gfm)) (format "[`%s`](%s)" (or desc filepath) url))
((eq backend 'html)
(format "<a href=\"%s\"><code>%s</code></a>" url (or desc filepath)))
(t url)))))

(provide 'orgit-file-transclusion)
;;; orgit-file-transclusion.el ends here
Loading
Loading