]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
added rfc-mode
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Wed, 19 Nov 2025 09:10:25 +0000 (10:10 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Wed, 19 Nov 2025 09:10:25 +0000 (10:10 +0100)
Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
elpa/rfc-mode-20231013.1353/rfc-mode-autoloads.el [new file with mode: 0644]
elpa/rfc-mode-20231013.1353/rfc-mode-pkg.el [new file with mode: 0644]
elpa/rfc-mode-20231013.1353/rfc-mode.el [new file with mode: 0644]

diff --git a/elpa/rfc-mode-20231013.1353/rfc-mode-autoloads.el b/elpa/rfc-mode-20231013.1353/rfc-mode-autoloads.el
new file mode 100644 (file)
index 0000000..42f9d60
--- /dev/null
@@ -0,0 +1,40 @@
+;;; rfc-mode-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+\f
+;;; Generated autoloads from rfc-mode.el
+
+(autoload 'rfc-mode-read "rfc-mode" "\
+Read the RFC document NUMBER.
+Offer the number at point as default.
+
+(fn NUMBER)" t)
+(autoload 'rfc-mode-browse "rfc-mode" "\
+Browse through all RFC documents referenced in the index." t)
+(autoload 'rfc-mode "rfc-mode" "\
+Major mode to browse and read RFC documents.
+
+(fn)" t)
+(add-to-list 'auto-mode-alist '("/rfc[0-9]+\\.txt\\'" . rfc-mode))
+(register-definition-prefixes "rfc-mode" '("rfc-mode-"))
+\f
+;;; End of scraped data
+
+(provide 'rfc-mode-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; rfc-mode-autoloads.el ends here
diff --git a/elpa/rfc-mode-20231013.1353/rfc-mode-pkg.el b/elpa/rfc-mode-20231013.1353/rfc-mode-pkg.el
new file mode 100644 (file)
index 0000000..b5f1970
--- /dev/null
@@ -0,0 +1,9 @@
+;; -*- no-byte-compile: t; lexical-binding: nil -*-
+(define-package "rfc-mode" "20231013.1353"
+  "RFC document browser and viewer."
+  '((emacs "25.1"))
+  :url "https://github.com/galdor/rfc-mode"
+  :commit "ab09db78d9d1baa4da4f926930833598e1e978ce"
+  :revdesc "ab09db78d9d1"
+  :authors '(("Nicolas Martyanoff" . "nicolasNOSPAMn16f.net"))
+  :maintainers '(("Nicolas Martyanoff" . "nicolasNOSPAMn16f.net")))
diff --git a/elpa/rfc-mode-20231013.1353/rfc-mode.el b/elpa/rfc-mode-20231013.1353/rfc-mode.el
new file mode 100644 (file)
index 0000000..593db2b
--- /dev/null
@@ -0,0 +1,536 @@
+;;; rfc-mode.el --- RFC document browser and viewer -*- lexical-binding: t -*-
+
+;; Author: Nicolas Martyanoff <nicolasNOSPAMn16f.net>
+;; SPDX-License-Identifier: ISC
+;; URL: https://github.com/galdor/rfc-mode
+;; Package-Version: 20231013.1353
+;; Package-Revision: ab09db78d9d1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+
+;; This package makes it easy to browse and read RFC documents.
+
+;; It offers a Helm-based browser of the list of RFCs as well as
+;; some highlighting of hyperlinks when reading the actual RFCs.
+;; If you want to browse the list without Helm, you might prefer
+;; [rfcview](http://github.com/zeph1e/rfcview.el).
+
+;; Todo:
+;; - Use font-lock
+;; - Add hyperlinks from the `Table of Contents'
+
+;;; Code:
+
+(require 'helm nil t)
+(require 'seq)
+
+
+(declare-function helm-build-sync-source "helm-source")
+(declare-function helm-make-actions "helm-lib")
+
+;;; Configuration:
+
+(defgroup rfc-mode-group nil
+  "Tools to browse and read RFC documents."
+  :prefix "rfc-mode-"
+  :link '(url-link :tag "GitHub" "https://github.com/galdor/rfc-mode")
+  :group 'external)
+
+(defface rfc-mode-document-header-face
+  '((t :inherit font-lock-comment-face))
+  "Face used for RFC document page headers.")
+
+(defface rfc-mode-document-footer-face
+  '((t :inherit font-lock-comment-face))
+  "Face used for RFC document page footers.")
+
+(defface rfc-mode-document-section-title-face
+  '((t :inherit font-lock-keyword-face))
+  "Face used for RFC document section titles.")
+
+(defface rfc-mode-browser-ref-face
+  '((t :inherit font-lock-preprocessor-face))
+  "Face used to highlight RFC references in the RFC browser.")
+
+(defface rfc-mode-browser-title-face
+  '((t :inherit default))
+  "Face used to highlight the title of RFC documents in the RFC browser.")
+
+(defface rfc-mode-browser-title-obsolete-face
+  '((t :inherit font-lock-comment-face))
+  "Face used to highlight the title of obsolete RFC documents in the RFC browser.")
+
+(defface rfc-mode-browser-status-face
+  '((t :inherit font-lock-keyword-face))
+  "Face used to highlight RFC document statuses in the RFC browser.")
+
+(defcustom rfc-mode-directory (expand-file-name "~/rfc/")
+  "The directory where RFC documents are stored."
+  :type 'directory)
+
+(defcustom rfc-mode-document-url
+  "https://www.rfc-editor.org/rfc/rfc%s.txt"
+  "A `format'able URL for fetching arbitrary RFC documents.
+Assume RFC documents are named as e.g. rfc21.txt, rfc-index.txt."
+  :type 'string)
+
+(defcustom rfc-mode-browse-input-function
+  (if (featurep 'helm) 'helm 'completing-read)
+  "Function used by `rfc-mode-browse' to read user input.
+
+Only `read-number', `completing-read' and `helm' are explicitly
+supported.  Any other function is called with no arguments and
+must return an integer.
+
+Here `completion-read' works best if you use some completion
+mode that displays candidates \"vertically\" like `helm' does.
+`ivy-mode' is a popular choice.  `fido-mode' in combination
+with `icomplete-vertical-mode' should also work well."
+  :type '(choice (const read-number)
+                 (const completing-read)
+                 (const helm)
+                 function))
+
+(defcustom rfc-mode-use-original-buffer-names nil
+  "Whether RFC document buffers should have the name of the document file.
+If nil (the default) then use e.g. *rfc21*, otherwise use e.g. rfc21.txt."
+  :type 'boolean)
+
+(defcustom rfc-mode-browser-entry-title-width 60
+  "The width of the column containing RFC titles in the browser."
+  :type 'integer)
+
+(defcustom rfc-mode-imenu-title "RFC Contents"
+  "The title to use if `rfc-mode' adds a RFC Contents menu to the menubar."
+  :type 'string)
+
+;;; Misc variables:
+
+(defvar rfc-mode-index-entries nil
+  "The list of entries in the RFC index.")
+
+(defconst rfc-mode-title-regexp "^\\(?:[0-9]+\\.\\)+\\(?:[0-9]+\\)? .*$"
+  "Regular expression to model section titles in RFC documents.")
+
+(defvar-local rfc-mode--titles nil
+  "Buffer-local variable that keeps a list of section titles in this RFC.")
+
+(defvar rfc-mode--last-title nil
+  "Last section title that the user visited.")
+
+;;; Keys:
+
+(defvar rfc-mode-map
+  (let ((map (make-keymap)))
+    (set-keymap-parent map special-mode-map)
+    (define-key map (kbd "<tab>")     #'forward-button)
+    (define-key map (kbd "<backtab>") #'backward-button)
+    (define-key map (kbd "<prior>")   #'rfc-mode-backward-page)
+    (define-key map (kbd "<next>")    #'rfc-mode-forward-page)
+    (define-key map (kbd "g")         #'rfc-mode-goto-section)
+    (define-key map (kbd "n")         #'rfc-mode-next-section)
+    (define-key map (kbd "p")         #'rfc-mode-previous-section)
+    map)
+  "The keymap for `rfc-mode'.")
+
+;;; Main:
+
+(defun rfc-mode-init ()
+  "Initialize the current buffer for `rfc-mode'."
+  (setq-local page-delimiter "^.*?\n\f")
+  (rfc-mode-highlight)
+  (setq imenu-generic-expression (list (list nil rfc-mode-title-regexp 0)))
+  (imenu-add-to-menubar rfc-mode-imenu-title))
+
+(define-obsolete-function-alias 'rfc-mode-quit #'quit-window "rfc-mode-1.4")
+
+(defun rfc-mode-recenter ()
+  "Do the same as `recenter-top-bottom' would for the `top' position."
+  (rfc-mode-header-start)
+  (let ((recenter-positions '(top)))
+    (recenter-top-bottom)))
+
+(defun rfc-mode-backward-page ()
+  "Scroll to the previous page of the current buffer."
+  (interactive)
+  (beginning-of-line)
+  (unless (looking-at "\f")
+    (backward-page))
+  (backward-page)
+  (beginning-of-line 1)
+  (rfc-mode-recenter))
+
+(defun rfc-mode-forward-page ()
+  "Scroll to the next page of the current buffer."
+  (interactive)
+  (forward-page)
+  (beginning-of-line 1)
+  (rfc-mode-recenter))
+
+(defun rfc-mode-goto-section (section)  ;FIXME: Why not use imenu for that?
+  "Move point to SECTION."
+  (interactive
+   (let* ((default (if (member rfc-mode--last-title rfc-mode--titles)
+                       rfc-mode--last-title
+                     (car rfc-mode--titles)))
+          (completion-ignore-case t)
+          (prompt (concat "Go to section (default " default "): "))
+          (chosen (completing-read prompt rfc-mode--titles
+                                   nil nil nil nil default)))
+     (list chosen)))
+  (setq rfc-mode--last-title section)
+  (unless (rfc-mode--goto-section section)
+    (error "Section %s not found" section)))
+
+(defun rfc-mode--goto-section (section)
+  "Move point to SECTION if it exists, otherwise don't move point.
+Returns t if section is found, nil otherwise."
+  (let ((curpos (point))
+        (case-fold-search nil))
+    (goto-char (point-min))
+    (if (re-search-forward (concat "^" section) (point-max) t)
+        (progn
+          (beginning-of-line)
+          (rfc-mode-recenter)
+          t)
+      (goto-char curpos)
+      nil)))
+
+(defun rfc-mode-next-section (n)
+  "Move point to Nth next section (default 1)."
+  (interactive "p")
+  (let ((case-fold-search nil)
+        (start (point)))
+    (if (looking-at rfc-mode-title-regexp)
+        (forward-line 1))
+    (if (re-search-forward rfc-mode-title-regexp (point-max) t n)
+        (progn
+          (beginning-of-line)
+          (rfc-mode-recenter))
+      (goto-char (point-max))
+      ;; The last line doesn't belong to any section.
+      (forward-line -1))
+    ;; Ensure we never move back from the starting point.
+    (if (< (point) start) (goto-char start))))
+
+(defun rfc-mode-previous-section (n)
+  "Move point to Nth previous section (default 1)."
+  (interactive "p")
+  (let ((case-fold-search nil))
+    (if (looking-at rfc-mode-title-regexp)
+        (forward-line -1))
+    (if (re-search-backward rfc-mode-title-regexp (point-min) t n)
+        (progn
+          (beginning-of-line)
+          (rfc-mode-recenter))
+      (goto-char (point-min)))))
+
+;;;###autoload
+(defun rfc-mode-read (number)
+  "Read the RFC document NUMBER.
+Offer the number at point as default."
+  (interactive
+   (if (and current-prefix-arg (not (consp current-prefix-arg)))
+       (list (prefix-numeric-value current-prefix-arg))
+     (list (read-number "RFC number: " (rfc-mode--integer-at-point)))))
+  (display-buffer (rfc-mode--document-buffer number)))
+
+(defun rfc-mode-reload-index ()
+  "Reload the RFC document index from its original file."
+  (interactive)
+  (setq rfc-mode-index-entries nil))
+
+(defun rfc-mode--index-entries ()
+  (or rfc-mode-index-entries
+      (let ((file (rfc-mode--document-file "-index")))
+        (setq rfc-mode-index-entries
+              (rfc-mode-read-index-file file)))))
+
+;;;###autoload
+(defun rfc-mode-browse ()
+  "Browse through all RFC documents referenced in the index."
+  (interactive)
+  (pcase rfc-mode-browse-input-function
+    ('read-number
+     (display-buffer (rfc-mode--document-buffer
+                      (read-number "View RFC document: "
+                                   (rfc-mode--integer-at-point)))))
+    ('helm
+     (if (fboundp 'helm)
+        (helm :buffer "*helm rfc browser*"
+              :sources (rfc-mode-browser-helm-sources
+                        (rfc-mode--index-entries)))
+       (user-error "Helm has to be installed explicitly")))
+    ('completing-read
+     (let* ((default (rfc-mode--integer-at-point))
+            (cands (mapcar (lambda (entry)
+                             (let ((cand
+                                    (rfc-mode-browser-format-candidate entry)))
+                               (and (numberp default)
+                                    (= (plist-get entry :number) default)
+                                    (setq default (car cand)))
+                               cand))
+                           (rfc-mode--index-entries)))
+            (choice (completing-read "View RFC document: "
+                                     cands nil nil nil nil default))
+            (number (or (and (string-match "\\`RFC\\([0-9]+\\)" choice)
+                             (string-to-number (match-string 1 choice)))
+                        (ignore-errors (string-to-number choice)))))
+       (unless number
+         (user-error
+          "%s doesn't match a completion candidate and is not a number"
+          choice))
+       (display-buffer (rfc-mode--document-buffer number))))
+    (_ (display-buffer (rfc-mode--document-buffer
+                        (funcall rfc-mode-browse-input-function))))))
+
+;;;###autoload
+(define-derived-mode rfc-mode special-mode "rfc-mode"
+  "Major mode to browse and read RFC documents."
+  (rfc-mode-init))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("/rfc[0-9]+\\.txt\\'" . rfc-mode))
+(add-to-list 'auto-mode-alist '("\\*rfc[0-9]+\\*\\'" . rfc-mode))
+
+;;; Syntax utils:
+
+(defun rfc-mode-highlight ()
+  "Highlight the current buffer."
+  (setq rfc-mode--titles nil)
+  ;; FIXME: Use font-lock!
+  (with-silent-modifications
+    (let ((inhibit-read-only t))
+      ;; Headers and footers
+      (save-excursion
+        (goto-char (point-min))
+        (while (search-forward "\f" nil t)
+          (beginning-of-line)
+          (let ((form-feed (point)))
+            (let* ((footer-end (rfc-mode-previous-footer-start))
+                   (footer-start (point)))
+              (put-text-property
+               footer-start footer-end
+               'face 'rfc-mode-document-footer-face))
+            (goto-char form-feed)
+            (let* ((header-end (rfc-mode-header-start))
+                   (header-start (point)))
+              (put-text-property
+               header-start header-end
+               'face 'rfc-mode-document-header-face)
+              (goto-char header-end)))))
+      ;; Section titles
+      (save-excursion
+        (goto-char (point-min))
+        (while (search-forward-regexp rfc-mode-title-regexp nil t)
+          (let ((start (match-beginning 0))
+                (end (match-end 0)))
+            (put-text-property start end
+                               'face 'rfc-mode-document-section-title-face)
+            (push (match-string 0) rfc-mode--titles)
+            (goto-char end))))
+      ;; Keep titles in expected top to bottom order.
+      (setq rfc-mode--titles (nreverse rfc-mode--titles))
+      ;; RFC references
+      (save-excursion
+        (goto-char (point-min))
+        (while (search-forward-regexp "RFC *\\([0-9]+\\)" nil t)
+          (let ((start (match-beginning 0))
+                (end (match-end 0))
+                (number (string-to-number (match-string 1))))
+            (unless (= start (line-beginning-position))
+              (make-text-button start end
+                                'action (lambda (_button)
+                                          (rfc-mode-read number))
+                                'help-echo (format "Read RFC %d" number)
+                                'follow-link t))
+            (goto-char end)))))))
+
+(defun rfc-mode-header-start ()
+  "Move to the start of the current header.
+
+When the point is on a form feed character, move it to the start
+of the current page header and return the position of the end of
+the header."
+  (when (looking-at "\f")
+    (forward-line 1)
+    (move-end-of-line 1)
+    (prog1 (point)
+      (move-beginning-of-line 1))))
+
+(defun rfc-mode-previous-footer-start ()
+  "Move to the start of the previous footer.
+
+When the point is on a form feed character, move it to the start
+of the previous page footer and return the position of the end of
+the footer."
+  (when (looking-at "\f")
+    (forward-line -1)
+    (move-end-of-line 1)
+    (prog1 (point)
+      (move-beginning-of-line 1))))
+
+;;; Browser utils:
+
+(defun rfc-mode-browser-helm-sources (entries)
+  "Create a Helm source for ENTRIES.
+
+ENTRIES is a list of RFC index entries in the browser."
+  (helm-build-sync-source "RFC documents"
+    :candidates (mapcar #'rfc-mode-browser-format-candidate entries)
+    :action (helm-make-actions
+             "Read" #'rfc-mode-browser-helm-entry-read)))
+
+(defun rfc-mode-browser-format-candidate (entry)
+  "Create a Helm candidate for ENTRY.
+
+ENTRY is a RFC index entry in the browser."
+  (let* ((ref (rfc-mode--pad-string
+               (format "RFC%d" (plist-get entry :number)) 7))
+         (title (rfc-mode--pad-string
+                 (plist-get entry :title)
+                 rfc-mode-browser-entry-title-width))
+         (status (or (plist-get entry :status) ""))
+         (obsoleted-by (plist-get entry :obsoleted-by))
+         (obsoletep (> (length obsoleted-by) 0))
+         (string (format "%s  %s  %s"
+                         (rfc-mode--highlight-string
+                          ref 'rfc-mode-browser-ref-face)
+                         (rfc-mode--highlight-string
+                          title (if obsoletep
+                                    'rfc-mode-browser-title-obsolete-face
+                                  'rfc-mode-browser-title-face))
+                         (rfc-mode--highlight-string
+                          status 'rfc-mode-browser-status-face))))
+    (cons string entry)))
+
+(defun rfc-mode-browser-helm-entry-read (entry)
+  "The read action the Helm candidate ENTRY in the browser."
+  (let ((number (plist-get entry :number)))
+    (rfc-mode-read number)))
+
+;;; Index utils:
+
+(defun rfc-mode-read-index-file (filename)
+  "Read an RFC index file at FILENAME and return a list of entries."
+  (with-temp-buffer
+    (insert-file-contents filename)
+    (rfc-mode-read-index (current-buffer))))
+
+(defun rfc-mode-read-index (buffer)
+  "Read an RFC index file from BUFFER and return a list of entries."
+  (with-current-buffer buffer
+    (goto-char (point-min))
+    (let ((entries nil))
+      (while (search-forward-regexp "^[0-9]+ " nil t)
+        (let ((start (match-beginning 0)))
+          (search-forward-regexp " $")
+          (let* ((end (match-beginning 0))
+                 (lines (buffer-substring start end))
+                 (entry-string (replace-regexp-in-string "[ \n]+" " " lines))
+                 (entry (rfc-mode-parse-index-entry entry-string)))
+            (unless (string= (plist-get entry :title) "Not Issued")
+              (push entry entries)))))
+      (nreverse entries))))
+
+(defun rfc-mode-parse-index-entry (string)
+  "Parse the RFC document index entry STRING and return it as a plist."
+  (unless (string-match "\\(^[0-9]+\\) *\\(.*?\\)\\.\\(?: \\|$\\)" string)
+    (error "Invalid index entry format: %S" string))
+  (let* ((number-string (match-string 1 string))
+         (number (string-to-number number-string))
+         (title (match-string 2 string)))
+    (when (zerop number)
+      (error "Invalid index entry number: %S" number-string))
+    (let ((entry (list :number number :title title)))
+      (when (string-match "(Status: \\([^)]+\\))" string)
+        (plist-put entry :status (downcase (match-string 1 string))))
+      (when (string-match "(Obsoletes \\([^)]+\\))" string)
+        (plist-put entry :obsoletes
+                   (rfc-mode--parse-rfc-refs (match-string 1 string))))
+      (when (string-match "(Obsoleted by \\([^)]+\\))" string)
+        (plist-put entry :obsoleted-by
+                   (rfc-mode--parse-rfc-refs (match-string 1 string))))
+      (when (string-match "(Updates \\([^)]+\\))" string)
+        (plist-put entry :updates
+                   (rfc-mode--parse-rfc-refs (match-string 1 string))))
+      (when (string-match "(Updated by \\([^)]+\\))" string)
+        (plist-put entry :updated-by
+                   (rfc-mode--parse-rfc-refs (match-string 1 string))))
+      entry)))
+
+;;; Document utils:
+
+(defun rfc-mode--document-buffer-name (number)
+  "Return the buffer name for the RFC document NUMBER."
+  (concat "*rfc" (number-to-string number) "*"))
+
+(defun rfc-mode--document-file (number)
+  "Return the absolute file name of the RFC document NUMBER."
+  (let ((file
+         (expand-file-name (format "rfc%s.txt" number) rfc-mode-directory)))
+    (rfc-mode--ensure-directory-exists)
+    (unless (file-exists-p file)
+      (url-copy-file (format rfc-mode-document-url number) file))
+    file))
+
+(defun rfc-mode--document-buffer (number)
+  "Return a buffer visiting the RFC document NUMBER.
+
+The buffer is created if it does not exist."
+  (let* ((buffer-name (rfc-mode--document-buffer-name number))
+         (document-path (rfc-mode--document-file number)))
+    (with-current-buffer (find-file-noselect document-path)
+      (unless rfc-mode-use-original-buffer-names
+        (rename-buffer buffer-name))
+      (rfc-mode)
+      (current-buffer))))
+
+;;; Misc utils:
+
+(defun rfc-mode--integer-at-point ()
+  ;; Note that we don't use `number-at-point' as it will match
+  ;; number formats that make no sense as RFC numbers (floating
+  ;; point, hexadecimal, etc.).
+  (save-excursion
+    (skip-chars-backward "0-9")
+    (and (looking-at "[0-9]")
+         (string-to-number
+          (buffer-substring-no-properties
+           (point)
+           (progn (skip-chars-forward "0-9")
+                  (point)))))))
+
+(defun rfc-mode--ensure-directory-exists ()
+  "Check that `rfc-mode-directory' exists, creating it if it does not."
+  (when (and (not (file-exists-p rfc-mode-directory))
+             (y-or-n-p (format "Create directory %s? " rfc-mode-directory)))
+    (make-directory rfc-mode-directory t)))
+
+(defun rfc-mode--parse-rfc-ref (string)
+  "Parse a reference to a RFC document from STRING.
+
+For example: \"RFC 2822\"."
+  (when (string-match "^RFC *\\([0-9]+\\)" string)
+    (string-to-number (match-string 1 string))))
+
+(defun rfc-mode--parse-rfc-refs (string)
+  "Parse a list of references to RFC documents from STRING.
+
+For example: \"RFC3401, RFC3402 ,RFC 3403\"."
+  (seq-remove #'null (mapcar #'rfc-mode--parse-rfc-ref
+                             (split-string string "," t " +"))))
+
+(defun rfc-mode--pad-string (string width)
+  "Pad STRING with spaces to WIDTH characters."
+  (truncate-string-to-width string width 0 ?\s))
+
+(defun rfc-mode--highlight-string (string face)
+  "Highlight STRING using FACE."
+  (put-text-property 0 (length string) 'face face string)
+  string)
+
+(provide 'rfc-mode)
+
+;;; rfc-mode.el ends here