Emacs IETF
Prologue
Some years ago I discovered ietf-cli in OpenBSD ports. It creates a local mirror of IETF drafts, RFCs and other files and makes them accessible from the command line. I have it configured to use Emacs for the pager1.
I was never too happy about the cli interface though. While it is open
source I never got around to hack on it or to send feature
requests. These are the things I do not like. Most of them are a
question of work flow more than limitations of the ietf
tool:
- I never figured out how to configure tab-completion in my shell for it.
- I am not yet running a shell inside of Emacs, so it breaks my work flow when reading a draft and I want to quickly open an RFC for reference.
- The tool is pretty opinionated. You need to call just so for it do
what you ask. To open an RFC you need to type
ietf rfc 1925
. It will not acceptietf RFC1925
orietf rfc1925
. That means that copy & paste does not work most of the time.
More recently I discovered the vertico completion extension combined with orderless completion. Those two extensions provide the kind of completion I want for RFCs and even more so for drafts:
[Orderless] divides the pattern into space-separated components, and matches candidates that match all of the components in any order.
Emacs-lisp
The ietf
tool provides the mirror
command that rsyncs the IETF
documents to a configurable folder. Reading an RFC or draft then
comes down to opening the correct file. So we can copy the code of
find-file
, change default-directory
and we are good to go:
;; were ietf mirror stores the local mirror (setq ietf-mirror "~/ietf-mirror/") ;; look for drafts in the 'short-id/' sub-directory and make sure the ;; buffer is a read-only text buffer. (defun ietf-draft (filename &optional wildcards) (interactive (let ((default-directory (concat ietf-mirror "short-id/"))) (find-file-read-args "Find file: " (confirm-nonexistent-file-or-buffer)))) (let ((value (find-file-noselect filename nil nil wildcards))) (if (listp value) (mapcar 'pop-to-buffer-same-window (nreverse value)) (pop-to-buffer-same-window value)) (text-mode) (read-only-mode))) ;; RFCs are stored in 'in-notes/' (defun ietf-rfc (filename &optional wildcards) (interactive (let ((default-directory (concat ietf-mirror "in-notes/")) (completion-ignored-extensions '("/" ".html" ".json" ".pdf" ".ps" ".tar" ".xml" ".xsd"))) (find-file-read-args "Find file: " (confirm-nonexistent-file-or-buffer)))) (let ((value (find-file-noselect filename nil nil wildcards))) (if (listp value) (mapcar 'pop-to-buffer-same-window (nreverse value)) (pop-to-buffer-same-window value)) (text-mode) (read-only-mode)))
Update 2024-03-25: Flipping the order of (text-mode)
and
(read-only-mode)
so that (read-only-mode)
gets activated last
makes it work correctly with view-mode
if that mode is automatically
activated by (setq view-read-only t)
on read-only buffers.
Setting a global key binding lets us open RFCs and drafts from anywhere within Emacs:
(global-set-key (kbd "C-c i d") 'ietf-draft) (global-set-key (kbd "C-c i r") 'ietf-rfc)
Epilogue
The ietf
tool has many more options that I have not explored or used
yet. rfc
and draft
are the two main commands I am using and on
occasion I am using the diff tool.
I will keep the tool around to keep the mirror up2date and for the
occasional use of diff
. Having moved the functionality I use most
often into Emacs and making it behave like I want it is a huge
improvement.
Footnotes:
Like normal people… I uses emacsclient for performance reasons.