1541 lines
58 KiB
EmacsLisp
1541 lines
58 KiB
EmacsLisp
;;; irfc.el --- Interface for IETF RFC document.
|
||
|
||
;; Filename: irfc.el
|
||
;; Description: Interface for IETF RFC document.
|
||
;; Author: Andy Stewart <lazycat.manatee@gmail.com>
|
||
;; Juanma Barranquero <lekktu@gmail.com>
|
||
;; Maintainer: Niels Widger <niels.widger@gmail.com>
|
||
;; Copyright (C) 2009, Andy Stewart, all rights reserved.
|
||
;; Copyright (C) 2009, Juanma Barranquero, all rights reserved.
|
||
;; Copyright (C) 2010, Niels Widger, all rights reserved.
|
||
;; Created: 2009-01-14 08:13:15
|
||
;; Version: 0.5.6
|
||
;; Last-Updated: Fri Aug 17 19:42:29 2012 (-0400)
|
||
;; By: Samuel Bronson
|
||
;; URL: https://www.emacswiki.org/emacs/download/irfc.el
|
||
;; Keywords: RFC, IETF
|
||
;; Compatibility: GNU Emacs 22 ~ 23
|
||
;;
|
||
;; Features that might be required by this library:
|
||
;;
|
||
;; `cl' `url-vars' `thingatpt'
|
||
;;
|
||
|
||
;;; This file is NOT part of GNU Emacs
|
||
|
||
;;; License
|
||
;;
|
||
;; This program is free software; you can redistribute it and/or modify
|
||
;; it under the terms of the GNU General Public License as published by
|
||
;; the Free Software Foundation; either version 3, or (at your option)
|
||
;; any later version.
|
||
|
||
;; This program is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
;; GNU General Public License for more details.
|
||
|
||
;; You should have received a copy of the GNU General Public License
|
||
;; along with this program; see the file COPYING. If not, write to
|
||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
||
;; Floor, Boston, MA 02110-1301, USA.
|
||
|
||
;; Introduction:
|
||
;;
|
||
;; For historical reasons, IETF Internet RFCs are required to be in a plain
|
||
;; ASCII text format that's best-suited for sending directly to a 6-lpi
|
||
;; US-letter-size printer. This makes them suboptimal for viewing on-screen,
|
||
;; as you will be doing for countless hours if you're ever doing network
|
||
;; programming to one of them. Fortunately, the ASCII format is usually
|
||
;; close to what you, the Emacs zealot, *truly* want -- which is a format
|
||
;; suited to more pleasurably viewing the RFC in Emacs.
|
||
;;
|
||
;; The `irfc' package uses Emacs overlays to add some fortification and
|
||
;; hide the page headers and footers (which it replaces with one-line page
|
||
;; number references that look like "(p.1)", right-justified). The file is
|
||
;; never modified, and you can see the raw ASCII text by pressing `T'.
|
||
;;
|
||
|
||
;;; Commentary:
|
||
;;
|
||
;; Interface for IETF RFC document.
|
||
;;
|
||
;; This package use some code from `rfcview.el'.
|
||
;; Thanks "Neil W. Van Dyke"!
|
||
;;
|
||
;; The features this package provide:
|
||
;;
|
||
;; * Format RFC document for easy reading.
|
||
;; * Single keystroke for fast view.
|
||
;; * Render status switch.
|
||
;; * Smart table and content switch.
|
||
;; * Visit RFC link around point.
|
||
;; * Jump to RFC reference around point.
|
||
;; * Download RFC document *asynchronous*.
|
||
;;
|
||
;; Below are commands you can use:
|
||
;;
|
||
;; `irfc-render-toggle' Toggle render status with RFC buffer.
|
||
;; `irfc-quit' Quit RFC buffer.
|
||
;; `irfc-visit' Ask for RFC number and visit document.
|
||
;; `irfc-reference-goto' Ask for RFC reference and jump to it.
|
||
;; `irfc-head-goto' Ask for heading name and jump to it.
|
||
;; `irfc-head-number-goto' Ask for heading number and jump to it.
|
||
;; `irfc-follow' Visit RFC document around point.
|
||
;; `irfc-table-jump' Switch between table and content.
|
||
;; `irfc-page-goto' Goto page.
|
||
;; `irfc-page-next' Jump next page.
|
||
;; `irfc-page-prev' Jump previous page.
|
||
;; `irfc-page-first' Jump first page.
|
||
;; `irfc-page-last' Jump last page.
|
||
;; `irfc-page-table' Jump table page.
|
||
;; `irfc-head-next' Jump next heading.
|
||
;; `irfc-head-prev' Jump previous heading.
|
||
;; `irfc-rfc-link-next' Jump next RFC link.
|
||
;; `irfc-rfc-link-prev' Jump previous RFC link.
|
||
;; `irfc-scroll-up-one-line' Scroll up one line.
|
||
;; `irfc-scroll-down-one-line' Scroll down one line.
|
||
;;
|
||
;; Tips:
|
||
;;
|
||
;; You can use command `irfc-render-toggle' to toggle render status.
|
||
;;
|
||
;; Command `irfc-table-jump' can switch between table and content,
|
||
;; example you stay cursor at *table*, and type "G" will jump corresponding
|
||
;; content in buffer, alike, you can stay at any content and type "G"
|
||
;; will jump corresponding table item.
|
||
;;
|
||
;; Command `irfc-follow' will visit RFC document around point,
|
||
;; example you stay cursor at "[RFC3986]", and type "o" will
|
||
;; open rfc3986.txt in storage directory. If have not found
|
||
;; this file in directory, will download from `https://www.ietf.org/rfc/'
|
||
;; and open it when download complete.
|
||
;;
|
||
;; And command ‘irfc-follow’ can also use at title of RFC document.
|
||
;; Example rfc3986.txt contain “Obsoletes: 2732, 2396, 1808” at title,
|
||
;; you can move cursor to “2732” and type “o” will visit RFC 2732 document.
|
||
;; ‘irfc-follow’ support below keywords in title:
|
||
;;
|
||
;; “Request for Comments:”
|
||
;; “Updates:”
|
||
;; “Obsoletes:”
|
||
;;
|
||
;; You can use command `irfc-rfc-link-next' or `irfc-rfc-link-prev'
|
||
;; to jump next or previous RFC link in document.
|
||
;;
|
||
;; Command `irfc-visit' will ask the user for a RFC number and will
|
||
;; visit that document, either from `irfc-directory', if exists, or by
|
||
;; downloading it. This command can serve as entry point for Irfc,
|
||
;; to go to a RFC without having to visit the file or remember
|
||
;; whether it is already in `irfc-directory'.
|
||
;; And if you visit same document with your previous type, so just
|
||
;; hit RET, and don't need type RFC document number.
|
||
;;
|
||
;; Command `irfc-reference-goto' will ask the user for a reference
|
||
;; number and will jump to that citation in the Normative
|
||
;; References/Informative References heading.
|
||
;;
|
||
;; Command `irfc-head-goto' will ask the user for a heading name and
|
||
;; will jump to that heading. Completion list in minibuffer is
|
||
;; available.
|
||
;;
|
||
;; Command `irfc-head-number-goto' will ask the user for a heading
|
||
;; number and will jump to that heading. Completion list in minibuffer
|
||
;; is available.
|
||
;;
|
||
|
||
|
||
;;; Installation:
|
||
;;
|
||
;; Put irfc.el to your load-path.
|
||
;; The load-path is usually ~/elisp/.
|
||
;; It's set in your ~/.emacs like this:
|
||
;; (add-to-list 'load-path (expand-file-name "~/elisp"))
|
||
;;
|
||
;; And the following to your ~/.emacs startup file.
|
||
;;
|
||
;; (require 'irfc)
|
||
;;
|
||
;; Setup your storage directory for RFC documents.
|
||
;;
|
||
;; (setq irfc-directory "YourStorageDirectory")
|
||
;;
|
||
;; If you want make RFC document load `irfc-mode' automatically,
|
||
;; setup like below:
|
||
;;
|
||
;; (setq irfc-assoc-mode t)
|
||
;;
|
||
|
||
;;; Customize:
|
||
;;
|
||
;; `irfc-assoc-mode' whether assoc RFC document with `irfc-mode'.
|
||
;; `irfc-directory' the storage directory for RFC document.
|
||
;; `irfc-download-base-url' the base url for download RFC document.
|
||
;; `irfc-buffer-name-includes-title' whether buffer name should
|
||
;; include the RFC document's title
|
||
;; `irfc-highlight-requirement-keywords' whether RFC requirement
|
||
;; keywords specified in `irfc-requirement-keywords' list
|
||
;; should be highlighted using the face specified by
|
||
;; `irfc-requirement-keyword-face'.
|
||
;; `irfc-requirement-keywords' list of RFC requirement keywords to
|
||
;; highlight if `irfc-highlight-requirement-keywords' is t.
|
||
;; `irfc-highlight-references' whether RFC references should be
|
||
;; highlighted using the face specified by `irfc-reference-face'.
|
||
;;
|
||
;; All of the above can customize by:
|
||
;; M-x customize-group RET irfc RET
|
||
;;
|
||
|
||
;;; Change log:
|
||
;; 2012/08/17
|
||
;; * Samuel Bronson:
|
||
;; * Added `autoload' cookies in key locations.
|
||
;; * Modified `irfc-open' to create the `irfc-directory' if it's
|
||
;; missing (after verifying that the user is okay with this).
|
||
;; * Now, the user can install using e.g. `el-get' and never have
|
||
;; to touch init!
|
||
;;
|
||
;; 2011/07/12
|
||
;; * Juanma Barranquero:
|
||
;; * Fixed let-binding in `irfc-render-buffer'.
|
||
;; * Use `string-to-number' instead of `string-to-int'.
|
||
;; * Use `when' whenever appropriate.
|
||
;; * Lowercase arg of `irfc-current-head'.
|
||
;; * Remove unused local variable in `irfc-rfc-link-prev'.
|
||
;; * Add leading underscore to unused parameters in `irfc-fill-tables'
|
||
;; (that removes bytecompiler warnings if compiled under lexical
|
||
;; binding in Emacs 24+).
|
||
;; * Remove `function' from lambda expressions in
|
||
;; `irfc-overlay-put-alist' and `irfc-overlay-remove-all', and use
|
||
;; `mapc' instead of `mapcar'.
|
||
;; * Use `nconc' in `irfc-overlay-remove-all' to avoid a bit of
|
||
;; unnecessary consing when dealing with temporary lists.
|
||
;; * Remove unused local variable in `irfc-get-buffer' and simplify
|
||
;; format call (no need to pass as argument a constant string).
|
||
;; * Several fixes in docstrings and error messages.
|
||
;;
|
||
;; 2010/09/28
|
||
;; * Niels Widger:
|
||
;; * Added new mappings: "f" to `irfc-head-goto' and "F"
|
||
;; to `irfc-head-number-goto'.
|
||
;; * Added new variables: `irfc-heading-names-table',
|
||
;; `irfc-heading-names-list', `irfc-heading-numbers-table'
|
||
;; and `irfc-heading-numbers-list'.
|
||
;; * Added new functions: `irfc-read-heading-name',
|
||
;; `irfc-read-heading-number', `irfc-heading-number-at-point',
|
||
;; `irfc-head-goto', `irfc-head-number-goto' and
|
||
;; `irfc-fill-tables'.
|
||
;; * `irfc-render-buffer' makes new call to `irfc-fill-tables'.
|
||
;;
|
||
;; 2010/09/24
|
||
;; * Niels Widger:
|
||
;; * Added new function `irfc-reference-goto' that prompts a user
|
||
;; for a reference number and jumps to that citation.
|
||
;; * Added mapping from "r" to `irfc-reference-goto' in keymap.
|
||
;; * Added several helper functions used by `irfc-reference-goto':
|
||
;; `irfc-read-reference', `irfc-reference-at-point' and
|
||
;; `irfc-current-head'.
|
||
;; * Added requirement for `thingatpt'.
|
||
;;
|
||
;; 2010/09/23
|
||
;; * Niels Widger:
|
||
;; * Added new RFC requirement keyword overlay and RFC reference
|
||
;; overlay.
|
||
;; * Several new variables: `irfc-highlight-requirement-keywords',
|
||
;; `irfc-highlight-references' and `irfc-requirement-keywords'.
|
||
;; * New faces: `irfc-requirement-keyword-face' and
|
||
;; `irfc-reference-face'.
|
||
;; * New overlays: `irfc-requirement-keyword-overlay' and
|
||
;; `irfc-reference-overlay'.
|
||
;; * Modified `irfc-render-buffer' to call
|
||
;; `irfc-render-buffer-overlay-requirement-keyword' and
|
||
;; `irfc-render-buffer-overlay-reference'.
|
||
;;
|
||
;; 2010/09/20
|
||
;; * Niels Widger:
|
||
;; * New variable `irfc-buffer-name-includes-title'.
|
||
;; * Add new function `irfc-rename-buffer' to include RFC document
|
||
;; title in buffer name if `irfc-buffer-name-includes-title' is t.
|
||
;; * Modify `irfc-render-buffer' to call `irfc-rename-buffer'
|
||
;; function.
|
||
;;
|
||
;; 2009/02/13
|
||
;; * Andy Stewart:
|
||
;; * New variable `irfc-table-regex'.
|
||
;; * Fix bug of `irfc-table-jump'.
|
||
;; * Fix doc.
|
||
;;
|
||
;; 2009/01/29
|
||
;; * Andy Stewart:
|
||
;; * Fix overlay RFC link (as format [RFC-number]) in RFC1034.txt.
|
||
;; * Fix RFC link jump bug.
|
||
;;
|
||
;; 2009/01/22
|
||
;; * Juanma Barranquero
|
||
;; * Add require information to avoid compile warning.
|
||
;; * Add new function `irfc-unload-function' to cleanup
|
||
;; when execute command `unload-feature'.
|
||
;;
|
||
;; 2009/01/21
|
||
;; * Juanma Barranquero
|
||
;; * Add new command `irfc-visit' for fast open or download RFC
|
||
;; document.
|
||
;; * Fix doc.
|
||
;; * Andy Stewart:
|
||
;; * Applied Juanma's patch (with slightly modified). Thanks!
|
||
;; * Add variable `irfc-last-visit-number' to record last input
|
||
;; RFC document number, save type if have to visit same document
|
||
;; with previous times.
|
||
;; * Fix bug of function `irfc-download-callback'.
|
||
;; Display error information when download RFC document failed.
|
||
;;
|
||
;; 2009/01/18
|
||
;; * Andy Stewart:
|
||
;; * Make `irfc-follow' can open RFC link at title.
|
||
;; Now support below keyword in title:
|
||
;; "Request for Comments:"
|
||
;; "Updates:"
|
||
;; "Obsoletes:"
|
||
;; * Add new commands: `irfc-rfc-link-next' and `irfc-rfc-link-prev'.
|
||
;; * Fix doc.
|
||
;;
|
||
;; * Juanma Barranquero:
|
||
;; * Fix defface error, and improve document.
|
||
;;
|
||
;; 2009/01/17
|
||
;; * Andy Stewart:
|
||
;; * Fix doc.
|
||
;; * Remove function `irfc-render-buffer-hide-cr'.
|
||
;; * Thanks "Juanma Barranquero" improve document and advices. :)
|
||
;;
|
||
;; 2009/01/16
|
||
;; * Andy Stewart:
|
||
;; * Modified code for 22 compatibility.
|
||
;; * Fix doc.
|
||
;;
|
||
;; 2009/01/14
|
||
;; * Andy Stewart:
|
||
;; * First released.
|
||
;;
|
||
|
||
;;; Acknowledgements:
|
||
;;
|
||
;; Neil W. Van Dyke <neil@neilvandyke.org>
|
||
;; For create rfcview.el
|
||
;; Juanma Barranquero <lekktu@gmail.com>
|
||
;; Thanks Juanma Barranquero send many patches.
|
||
;; Juanma, thank you very much! :)
|
||
;;
|
||
|
||
;;; TODO
|
||
;;
|
||
;;
|
||
;;
|
||
|
||
;;; Require
|
||
(require 'url-vars)
|
||
(require 'thingatpt)
|
||
|
||
;;; Code:
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Customize ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;;###autoload
|
||
(defgroup irfc nil
|
||
"Interface for IETF RFC documents."
|
||
:group 'edit)
|
||
|
||
;; This is autoloaded so that it will take effect without users having
|
||
;; to `load'/`require' this package in their init file.
|
||
;;;###autoload
|
||
(defcustom irfc-assoc-mode nil
|
||
"If non-nil, RFC documents are associated with `irfc-mode'.
|
||
Default is nil."
|
||
:type 'boolean
|
||
:set (lambda (symbol value)
|
||
(set symbol value)
|
||
(if value
|
||
(add-to-list 'auto-mode-alist
|
||
'("/rfc[0-9]+\\.txt\\'" . irfc-mode))
|
||
(remove-hook 'auto-mode-alist
|
||
'("/rfc[0-9]+\\.txt\\'" . irfc-mode))))
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-directory "~/.emacs.d/RFC/"
|
||
"The storage directory for RFC document download and search."
|
||
:type 'string
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-download-base-url "https://www.ietf.org/rfc/"
|
||
"The base URL for downloading RFC documents."
|
||
:type 'string
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-buffer-name-includes-title t
|
||
"If non-nil, buffer names for RFC documents will include the RFC title.
|
||
The format for the buffer name will be 'RFCTITLE (RFCNUM.TXT)'.
|
||
If nil, the buffer name is just RFCNUM.TXT. Default is t."
|
||
:type 'boolean
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-highlight-requirement-keywords t
|
||
"If non-nil, requirement keywords specified by
|
||
`irfc-requirement-keywords' list will be highlighted using the
|
||
face specified by `irfc-requirement-keyword-face'.
|
||
Default is t."
|
||
:type 'boolean
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-highlight-references t
|
||
"If non-nil, RFC document references specified by the
|
||
`irfc-reference-regex' regular expression will be highlighted
|
||
using the face specified by `irfc-reference-face'. Default is
|
||
t."
|
||
:type 'boolean
|
||
:group 'irfc)
|
||
|
||
(defcustom irfc-requirement-keywords '("MUST" "MUST NOT"
|
||
"REQUIRED"
|
||
"SHALL" "SHALL NOT"
|
||
"SHOULD" "SHOULD NOT"
|
||
"RECOMMENDED" "NOT RECOMMENDED"
|
||
"MAY" "OPTIONAL" "NOT")
|
||
"List of requirement keyword strings to be highlighted if
|
||
`irfc-highlight-requirement-keywords' is t."
|
||
:type '(repeat (string))
|
||
:group 'irfc)
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Faces ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
(defface irfc-title-face
|
||
'((t (:foreground "Gold" :bold t)))
|
||
"Face used for titles."
|
||
:group 'irfc)
|
||
(defvar irfc-title-overlay nil
|
||
"Overlay for `irfc-title-face'.")
|
||
|
||
(defface irfc-head-name-face
|
||
'((t (:foreground "DarkRed" :bold t :underline t)))
|
||
"Face used for heading names."
|
||
:group 'irfc)
|
||
(defvar irfc-head-name-overlay nil
|
||
"Overlay for `irfc-head-name-face'.")
|
||
|
||
(defface irfc-head-number-face
|
||
'((t (:foreground "DarkRed" :bold t)))
|
||
"Face used for heading numbers."
|
||
:group 'irfc)
|
||
(defvar irfc-head-number-overlay nil
|
||
"Overlay for `irfc-head-number-face'.")
|
||
|
||
(defface irfc-rfc-number-face
|
||
'((t (:foreground "Green3" :bold t)))
|
||
"Face used for RFC number in the header."
|
||
:group 'irfc)
|
||
(defvar irfc-rfc-number-overlay nil
|
||
"Overlay for `irfc-rfc-number-face'.")
|
||
|
||
(defface irfc-std-number-face
|
||
'((t (:foreground "Grey" :bold t)))
|
||
"Face used for STD number in the header."
|
||
:group 'irfc)
|
||
(defvar irfc-std-number-overlay nil
|
||
"Overlay for `irfc-std-number-face'.")
|
||
|
||
(defface irfc-rfc-link-face
|
||
'((t (:foreground "Grey30" :bold t)))
|
||
"Face used for RFC link in the header."
|
||
:group 'irfc)
|
||
(defvar irfc-rfc-link-overlay nil
|
||
"Overlay for `irfc-rfc-link-face'.")
|
||
|
||
(defface irfc-table-item-face
|
||
'((t (:foreground "LawnGreen")))
|
||
"Face used for Table item."
|
||
:group 'irfc)
|
||
(defvar irfc-table-item-overlay nil
|
||
"Overlay for `irfc-table-item-face'.")
|
||
|
||
(defface irfc-requirement-keyword-face
|
||
'((t (:foreground "red1" :bold t)))
|
||
"Face used for requirement keywords."
|
||
:group 'irfc)
|
||
(defvar irfc-requirement-keyword-overlay nil
|
||
"Overlay for `irfc-requirement-keyword-face'.")
|
||
|
||
(defface irfc-reference-face
|
||
'((t (:foreground "blue1" :bold t)))
|
||
"Face used for RFC document references."
|
||
:group 'irfc)
|
||
(defvar irfc-reference-overlay nil
|
||
"Overlay for `irfc-reference-face'.")
|
||
|
||
(defvar irfc-hide-overlay nil
|
||
"Overlay for hiding whitespace or blank lines.")
|
||
|
||
(defvar irfc-page-number-overlay nil
|
||
"Overlay for page number.")
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
(defvar irfc-mode-map
|
||
(let ((map (make-sparse-keymap)))
|
||
(define-key map (kbd "j") 'next-line)
|
||
(define-key map (kbd "k") 'previous-line)
|
||
(define-key map (kbd "h") 'backward-char)
|
||
(define-key map (kbd "l") 'forward-char)
|
||
(define-key map (kbd "e") 'scroll-down)
|
||
(define-key map (kbd "SPC") 'scroll-up)
|
||
(define-key map (kbd "J") 'irfc-scroll-up-one-line)
|
||
(define-key map (kbd "K") 'irfc-scroll-down-one-line)
|
||
(define-key map (kbd ",") 'end-of-buffer)
|
||
(define-key map (kbd ".") 'beginning-of-buffer)
|
||
(define-key map (kbd "T") 'irfc-render-toggle)
|
||
(define-key map (kbd "q") 'irfc-quit)
|
||
(define-key map (kbd "o") 'irfc-follow)
|
||
(define-key map (kbd "v") 'irfc-visit)
|
||
(define-key map (kbd "r") 'irfc-reference-goto)
|
||
(define-key map (kbd "f") 'irfc-head-goto)
|
||
(define-key map (kbd "F") 'irfc-head-number-goto)
|
||
(define-key map (kbd "g") 'irfc-page-goto)
|
||
(define-key map (kbd "N") 'irfc-page-next)
|
||
(define-key map (kbd "P") 'irfc-page-prev)
|
||
(define-key map (kbd ">") 'irfc-page-last)
|
||
(define-key map (kbd "<") 'irfc-page-first)
|
||
(define-key map (kbd "b") 'irfc-page-table)
|
||
(define-key map (kbd "H") 'irfc-head-next)
|
||
(define-key map (kbd "L") 'irfc-head-prev)
|
||
(define-key map (kbd "G") 'irfc-table-jump)
|
||
(define-key map (kbd "<tab>") 'irfc-rfc-link-next)
|
||
(define-key map (kbd "<backtab>") 'irfc-rfc-link-prev)
|
||
map)
|
||
"Keymap used by `irfc-mode'.")
|
||
|
||
(defvar irfc-stock-section-names
|
||
'("abstract"
|
||
"acknowledgement"
|
||
"acknowledgements"
|
||
"acknowledgment"
|
||
"acknowledgments"
|
||
"appendices"
|
||
"author's address"
|
||
"authors' addresses"
|
||
"bibliography"
|
||
"chair's address"
|
||
"copyright notice"
|
||
"copyright statement"
|
||
"editor's address"
|
||
"editors' addresses"
|
||
"full copyright notice"
|
||
"full copyright statement"
|
||
"iesg note"
|
||
"index"
|
||
"introduction"
|
||
"references and bibliography"
|
||
"references"
|
||
"security considerations"
|
||
"status of this memo"
|
||
"table of contents")
|
||
"The stock name for overlay heading.")
|
||
|
||
(defvar irfc-download-buffer nil
|
||
"Download buffer used by `url-retrieve'.
|
||
This variable is always buffer-local.")
|
||
(make-variable-buffer-local 'irfc-download-buffer)
|
||
|
||
(defvar irfc-download-url nil
|
||
"URL from which to download files.
|
||
This variable is always buffer-local.")
|
||
(make-variable-buffer-local 'irfc-download-url)
|
||
|
||
(defvar irfc-render-p t
|
||
"Render status for RFC buffer.
|
||
This variable is always buffer-local.")
|
||
(make-variable-buffer-local 'irfc-render-p)
|
||
|
||
(defvar irfc-total-pages 0
|
||
"Total number of pages in RFC buffer.
|
||
This variable is always buffer-local.")
|
||
(make-variable-buffer-local 'irfc-total-pages)
|
||
|
||
(defvar irfc-heading-names-list nil
|
||
"List of heading names in RFC buffer.
|
||
This variable is buffer-local in buffers where `irfc-mode' has
|
||
been called.")
|
||
|
||
(defvar irfc-heading-numbers-list nil
|
||
"List of heading numbers in RFC buffer.
|
||
This variable is buffer-local in buffers where `irfc-mode' has
|
||
been called.")
|
||
|
||
(defvar irfc-heading-names-table nil
|
||
"Table mapping heading names to position in RFC buffer.
|
||
This variable is buffer-local in buffers where `irfc-mode' has
|
||
been called.")
|
||
|
||
(defvar irfc-heading-numbers-table nil
|
||
"Table mapping heading numbers to position in RFC buffer.
|
||
This variable is buffer-local in buffers where `irfc-mode' has
|
||
been called.")
|
||
|
||
(defvar irfc-last-visit-number nil
|
||
"Number of the last RFC document visited.")
|
||
|
||
(defvar irfc-table-regex "^[ ]+\\([A-Z]?[0-9\\.]*\\)[ ]+\\([^\\.\n]+\\)[\\. ]+\\([0-9]+\\)$"
|
||
"The regular-expression that match table item.")
|
||
|
||
(defvar irfc-reference-regex "\\[[0-9]+]"
|
||
"The regular-expression that matches normative/informative
|
||
references.")
|
||
|
||
(defvar irfc-reference-format-regex "\\[%d]"
|
||
"The format string for use with `format' function for creating
|
||
regular-expressions that match a normative/informative
|
||
reference.")
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;;###autoload
|
||
(define-derived-mode irfc-mode text-mode "Irfc"
|
||
"Major mode for IETF RFC documents."
|
||
;; Setup.
|
||
(use-local-map irfc-mode-map)
|
||
(setq buffer-read-only t)
|
||
(setq font-lock-defaults nil)
|
||
(auto-save-mode 0)
|
||
(make-local-variable 'irfc-heading-names-list)
|
||
(make-local-variable 'irfc-heading-numbers-list)
|
||
(make-local-variable 'irfc-heading-names-table)
|
||
(make-local-variable 'irfc-heading-numbers-table)
|
||
(setq irfc-heading-names-table (make-hash-table :test 'equal))
|
||
(setq irfc-heading-numbers-table (make-hash-table :test 'equal))
|
||
(setq irfc-heading-names-list nil)
|
||
(setq irfc-heading-numbers-list nil)
|
||
;; Render.
|
||
(irfc-render-buffer))
|
||
|
||
(defun irfc-render-buffer ()
|
||
"Render RFC buffer."
|
||
(interactive)
|
||
(save-excursion
|
||
(let ((case-fold-search nil)
|
||
(top-point (point-min))
|
||
(title-line-point nil)
|
||
temp-point)
|
||
;; Clean up overlays.
|
||
(irfc-overlay-remove-all)
|
||
;; Hide whitespace at start of file.
|
||
(setq temp-point (irfc-render-buffer-hide-whitespace-at-start))
|
||
(when temp-point (setq top-point temp-point))
|
||
;; Hide any extraneous blank lines.
|
||
(setq title-line-point (irfc-render-buffer-hide-blank-line top-point))
|
||
;; Add overlays for page headers and footers.
|
||
(irfc-render-buffer-overlay-page-number)
|
||
;; Add overlay for the RFC number.
|
||
(irfc-render-buffer-overlay-rfc-number top-point title-line-point)
|
||
;; Add overlay for the STD number.
|
||
(irfc-render-buffer-overlay-std-number top-point title-line-point)
|
||
;; Add overlay for the table item.
|
||
(irfc-render-buffer-overlay-table-item top-point)
|
||
;; Add overlay for the RFC link.
|
||
(irfc-render-buffer-overlay-rfc-link top-point)
|
||
;; Add overlay for the title.
|
||
(irfc-render-buffer-overlay-title title-line-point)
|
||
;; Add overlay for the heading.
|
||
(irfc-render-buffer-overlay-head title-line-point)
|
||
;; Add overlay for requirement keywords.
|
||
(when irfc-highlight-requirement-keywords
|
||
(irfc-render-buffer-overlay-requirement-keyword top-point))
|
||
;; Add overlay for references.
|
||
(when irfc-highlight-references
|
||
(irfc-render-buffer-overlay-reference top-point))
|
||
;; Rename buffer
|
||
(irfc-rename-buffer title-line-point)
|
||
;; Fill heading names/numbers tables
|
||
(irfc-fill-tables title-line-point))))
|
||
|
||
(defun irfc-render-toggle ()
|
||
"Toggle RFC buffer render status."
|
||
(interactive)
|
||
(if irfc-render-p
|
||
(irfc-render-turn-off)
|
||
(irfc-render-turn-on)))
|
||
|
||
(defun irfc-render-turn-on ()
|
||
"Turn on RFC buffer render status."
|
||
(irfc-render-buffer)
|
||
(setq irfc-render-p t))
|
||
|
||
(defun irfc-render-turn-off ()
|
||
"Turn off RFC buffer render status."
|
||
(irfc-overlay-remove-all)
|
||
(setq irfc-render-p nil))
|
||
|
||
(defun irfc-quit ()
|
||
"Quit RFC buffer."
|
||
(interactive)
|
||
(kill-buffer (current-buffer)))
|
||
|
||
(defun irfc-table-jump ()
|
||
"Jump between table and content.
|
||
You can jump to the corresponding table item when you are at content.
|
||
You can jump to the corresponding content when you are at table."
|
||
(interactive)
|
||
(if (irfc-have-table-p)
|
||
(let ((original-position (point))
|
||
head-name
|
||
page-number
|
||
match-list)
|
||
(cond ((irfc-in-table-p)
|
||
;; When in table.
|
||
(beginning-of-line)
|
||
(if (search-forward-regexp irfc-table-regex (line-end-position) t)
|
||
;; Jump content when find valid table item.
|
||
(progn
|
||
;; Get head name and page number.
|
||
(setq head-name (match-string 0))
|
||
(setq head-name (replace-regexp-in-string "[\\. ]+\\([0-9]+\\)$" "" head-name))
|
||
(setq head-name (replace-regexp-in-string "^[ ]+" "" head-name))
|
||
(setq page-number (string-to-number (match-string 3)))
|
||
;; Jump page.
|
||
(irfc-page-goto page-number)
|
||
;; Search head.
|
||
(re-search-forward head-name nil t)
|
||
;; Indent.
|
||
(back-to-indentation))
|
||
;; Restore original position and output message
|
||
;; when at invalid table item.
|
||
(message "Invalid table item.")
|
||
(goto-char original-position)))
|
||
;; Do nothing when at front of table.
|
||
((irfc-front-table-p)
|
||
(message "In front of table."))
|
||
;; Jump corresponding table item from content.
|
||
(t
|
||
;; Get head name and page number.
|
||
(end-of-line)
|
||
(setq match-list (irfc-head-move t))
|
||
(setq head-name (buffer-substring-no-properties (nth 2 match-list)
|
||
(nth 3 match-list)))
|
||
(setq page-number (irfc-current-page))
|
||
;; Jump table.
|
||
(irfc-page-table)
|
||
;; Search head.
|
||
(re-search-forward (concat (regexp-quote head-name) "[\\. ]+"
|
||
(regexp-quote (number-to-string page-number))))
|
||
;; Indent.
|
||
(back-to-indentation))))
|
||
;; Do nothing when haven't table in this RFC document.
|
||
(message "This RFC document contains no Table of Contents.")))
|
||
|
||
(defun irfc-reference-goto (&optional number)
|
||
"Goto reference NUMBER."
|
||
(interactive (list (irfc-read-reference)))
|
||
(let ((original-position (point)) (done nil) (found nil) (beg) (end))
|
||
(goto-char (point-min))
|
||
(while (not done)
|
||
(if (not (re-search-forward
|
||
(concat "^[ \t]*"
|
||
(format irfc-reference-format-regex number))
|
||
(point-max) t))
|
||
(setq done t)
|
||
(setq beg (match-beginning 0))
|
||
(setq end (match-end 0))
|
||
(let ((name (irfc-current-head)))
|
||
(if (not (or (string= name "References")
|
||
(string= name "Normative References")
|
||
(string= name "Informative References")))
|
||
(goto-char end)
|
||
(goto-char beg)
|
||
(setq found t)
|
||
(setq done t)))))
|
||
(when (not found)
|
||
(goto-char original-position)
|
||
(message "Cannot find reference %d" number))))
|
||
|
||
(defun irfc-read-reference ()
|
||
"Read reference as a number using a reference found at point as
|
||
default."
|
||
(let ((default (irfc-reference-at-point)))
|
||
(if (eq default nil)
|
||
(read-number "Reference number: ")
|
||
(read-number "Reference number: " default))))
|
||
|
||
(defun irfc-reference-at-point ()
|
||
"Returns reference at point as a number or nil if one is not
|
||
found."
|
||
(if (not (thing-at-point-looking-at irfc-reference-regex))
|
||
nil
|
||
(let* ((match (buffer-substring (match-beginning 0) (match-end 0)))
|
||
(len (length match)))
|
||
(string-to-number (substring match 1 (1- len))))))
|
||
|
||
(defun irfc-read-heading-name ()
|
||
"Read heading name as a string."
|
||
(completing-read "Heading name: " irfc-heading-names-list nil t))
|
||
|
||
(defun irfc-read-heading-number ()
|
||
"Read heading number as a string using a heading number found
|
||
at point as default."
|
||
(let ((default (irfc-heading-number-at-point)))
|
||
(completing-read
|
||
(concat "Heading number" (if (eq default nil) "" (format " (default %s)" default)) ": ")
|
||
irfc-heading-numbers-list nil t nil nil default nil)))
|
||
|
||
(defun irfc-heading-number-at-point ()
|
||
"Returns heading number at point as a string or nil if one is
|
||
not found."
|
||
(if (not (thing-at-point-looking-at "\\([0-9]+\\.\\)*[0-9]+"))
|
||
nil
|
||
(let ((match (match-string 0)))
|
||
(cond ((member match irfc-heading-numbers-list) match)
|
||
((member (concat match ".") irfc-heading-numbers-list) (concat match "."))))))
|
||
|
||
(defun irfc-page-goto (number)
|
||
"Goto page NUMBER."
|
||
(interactive "nPage number: ")
|
||
(cond ((<= number 1)
|
||
;; Move beginning of buffer when page number
|
||
;; is equal or below 1.
|
||
(call-interactively 'beginning-of-buffer)
|
||
(when (< number 1)
|
||
(message "Top page reached.")))
|
||
(t
|
||
;; Move special page.
|
||
(let ((original-position (point))
|
||
(original-render-status irfc-render-p)
|
||
reach-bottom-p)
|
||
;; Set max page number when
|
||
;; query page is above max limit.
|
||
(when (> number irfc-total-pages)
|
||
(setq number irfc-total-pages)
|
||
(setq reach-bottom-p t))
|
||
;; Turn off render.
|
||
(irfc-render-turn-off)
|
||
;; Search page number.
|
||
(goto-char (point-min))
|
||
(if (re-search-forward (concat "\\[Page "
|
||
(regexp-quote (number-to-string (1- number)))
|
||
"\\]$")
|
||
nil t)
|
||
;; Move special page when search successful.
|
||
(progn
|
||
;; Adjust cursor position.
|
||
(forward-line +3)
|
||
(re-search-forward "^.+$" nil t)
|
||
(back-to-indentation)
|
||
;; Recenter when reach bottom page.
|
||
(when reach-bottom-p
|
||
(recenter 0)
|
||
(message "Bottom page reached.")))
|
||
;; Restore original position when search failed.
|
||
(goto-char original-position))
|
||
;; Revert render status.
|
||
(unless (equal original-render-status irfc-render-p)
|
||
(irfc-render-toggle))))))
|
||
|
||
(defun irfc-page-next (arg)
|
||
"Move to next ARGth page.
|
||
ARG defaults to 1."
|
||
(interactive "P")
|
||
(irfc-page-goto (+ (irfc-current-page) (or arg 1))))
|
||
|
||
(defun irfc-page-prev (arg)
|
||
"Move to previous ARGth page.
|
||
ARG defaults to 1."
|
||
(interactive "P")
|
||
(irfc-page-goto (- (irfc-current-page) (or arg 1))))
|
||
|
||
(defun irfc-page-first ()
|
||
"Move to first page."
|
||
(interactive)
|
||
(irfc-page-goto 1))
|
||
|
||
(defun irfc-page-last ()
|
||
"Move to last page."
|
||
(interactive)
|
||
(irfc-page-goto irfc-total-pages))
|
||
|
||
(defun irfc-page-table ()
|
||
"Move to Table of Contents."
|
||
(interactive)
|
||
(if (irfc-have-table-p)
|
||
(progn
|
||
(goto-char (point-min))
|
||
(re-search-forward "^Table of Contents$" nil t)
|
||
(back-to-indentation))
|
||
(message "This RFC document has no Table of Contents.")))
|
||
|
||
;;;###autoload
|
||
(defun irfc-follow ()
|
||
"Open RFC document around point.
|
||
Download and open RFC document if it
|
||
does not exist in `irfc-directory'."
|
||
(interactive)
|
||
(let ((rfc-file-name (irfc-get-rfc-filename)))
|
||
(if rfc-file-name
|
||
;; Open RFC document.
|
||
(irfc-open rfc-file-name)
|
||
(message "No valid RFC link found at cursor."))))
|
||
|
||
;;;###autoload
|
||
(defun irfc-visit (&optional rfc-number)
|
||
"Open RFC document RFC-NUMBER.
|
||
Download and open RFC document if it
|
||
does not exist in `irfc-directory'."
|
||
(interactive)
|
||
(or rfc-number
|
||
(setq rfc-number (read-number
|
||
"RFC document to visit: "
|
||
irfc-last-visit-number)))
|
||
(setq irfc-last-visit-number rfc-number)
|
||
(irfc-open (format "rfc%s.txt" rfc-number)))
|
||
|
||
(defun irfc-head-goto (NAME)
|
||
"Goto heading NAME."
|
||
(interactive (list (irfc-read-heading-name)))
|
||
(let ((new-point (gethash NAME irfc-heading-names-table)))
|
||
(if (eq new-point nil)
|
||
(message "Cannot find heading \"%s\"" NAME)
|
||
(goto-char new-point)
|
||
(back-to-indentation))))
|
||
|
||
(defun irfc-head-next ()
|
||
"Move to next heading."
|
||
(interactive)
|
||
(let ((original-position (point)))
|
||
(end-of-line)
|
||
(if (irfc-head-move)
|
||
;; Move to next heading,
|
||
;; when search successful.
|
||
(beginning-of-line)
|
||
;; Restore original position
|
||
;; when search failed.
|
||
(goto-char original-position)
|
||
(message "No next heading."))))
|
||
|
||
(defun irfc-head-prev ()
|
||
"Move to previous heading."
|
||
(interactive)
|
||
(let ((original-position (point)))
|
||
(beginning-of-line)
|
||
(unless (irfc-head-move t)
|
||
;; Restore original position
|
||
;; when search failed.
|
||
(goto-char original-position)
|
||
(message "No previous heading."))))
|
||
|
||
(defun irfc-current-head (&optional print)
|
||
"Returns name of the current heading.
|
||
If optional argument PRINT is non-nil, print the name before returning it."
|
||
(interactive)
|
||
(save-excursion
|
||
(irfc-head-prev)
|
||
(re-search-forward "^\\([0-9]+\\.\?\\)+[ \t]+" (line-end-position) t)
|
||
(let ((name (buffer-substring (point) (line-end-position))))
|
||
(when print
|
||
(message "%s" name))
|
||
name)))
|
||
|
||
(defun irfc-head-number-goto (NAME)
|
||
"Goto heading number NAME."
|
||
(interactive (list (irfc-read-heading-number)))
|
||
(let ((new-point (gethash NAME irfc-heading-numbers-table)))
|
||
(if (eq new-point nil)
|
||
(irfc-head-number-goto (concat NAME "."))
|
||
(goto-char new-point)
|
||
(back-to-indentation))))
|
||
|
||
(defun irfc-scroll-up-one-line ()
|
||
"Scroll up one line."
|
||
(interactive)
|
||
(scroll-up 1))
|
||
|
||
(defun irfc-scroll-down-one-line ()
|
||
"Scroll down one line."
|
||
(interactive)
|
||
(scroll-down 1))
|
||
|
||
(defun irfc-rfc-link-next ()
|
||
"Move the point to the next RFC link."
|
||
(interactive)
|
||
(let ((original-point (point)))
|
||
(when (re-search-forward "\\(\\B\\[RFC-?[0-9]+\\]\\B\\|[ \t]+[0-9]+\\)" nil t)
|
||
(catch 'match
|
||
(while (and (not (string-match "\\[\\(RFC-?[0-9]+\\)\\]" (irfc-get-symbol-non-blank)))
|
||
(or (not (irfc-title-rfc-link-p)) ;not valid RFC link number
|
||
(eolp))) ;number at end of line is invalid RFC number
|
||
(unless (re-search-forward "\\(\\B\\[RFC-?[0-9]+\\]\\B\\|[ \t]+[0-9]+\\)" nil t)
|
||
(goto-char original-point)
|
||
(message "No next RFC link.")
|
||
(throw 'match "Match last one.")))))))
|
||
|
||
(defun irfc-rfc-link-prev ()
|
||
"Move the point to the previous RFC link."
|
||
(interactive)
|
||
(when (re-search-backward "\\(\\B\\[RFC-?[0-9]+\\]\\B\\|[ \t]+[0-9]+\\)" nil t)
|
||
(catch 'match
|
||
(while
|
||
;; Not match [RFCnnn] format.
|
||
(not (string-match "\\[\\(RFC-?[0-9]+\\)\\]" (irfc-get-symbol-non-blank)))
|
||
(skip-chars-forward " ")
|
||
(if (and (irfc-title-rfc-link-p) ;is valid RFC link number
|
||
(save-excursion ;skip number at end of line.
|
||
(search-forward-regexp "[0-9]+" nil t)
|
||
(not (eolp))))
|
||
(progn
|
||
(when (string-match "^request for comments:[ \t]+$"
|
||
(buffer-substring-no-properties (line-beginning-position)
|
||
(point)))
|
||
(message "No previous RFC link."))
|
||
(throw 'match "Match title RFC link."))
|
||
(re-search-backward "\\(\\B\\[RFC-?[0-9]+\\]\\B\\|[ \t]+[0-9]+\\)" nil t))))))
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utilities functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
(defun irfc-open (rfc-file-name)
|
||
"Open RFC document with RFC-FILE-NAME."
|
||
|
||
;; Make sure `irfc-directory' exists.
|
||
(unless (file-directory-p irfc-directory)
|
||
(if (y-or-n-p (format "Create directory %s to hold RFCs? "
|
||
irfc-directory))
|
||
(make-directory irfc-directory t)
|
||
(error "Customize `irfc-directory', then!")))
|
||
|
||
(let (filepath)
|
||
(if (string-equal rfc-file-name (buffer-name))
|
||
;; Notify user if current buffer is search RFC document.
|
||
(message "Current RFC document.")
|
||
;; Get full file path.
|
||
(setq filepath (expand-file-name rfc-file-name irfc-directory))
|
||
(cond ((file-exists-p filepath)
|
||
;; Open corresponding RFC document.
|
||
(if (get-buffer rfc-file-name)
|
||
;; Switch corresponding buffer when it open.
|
||
(switch-to-buffer rfc-file-name)
|
||
;; Or open that file.
|
||
(find-file filepath)))
|
||
(t
|
||
;; Otherwise download and open corresponding RFC document.
|
||
(irfc-download (concat irfc-download-base-url rfc-file-name)))))))
|
||
|
||
(defun irfc-render-buffer-hide-whitespace-at-start ()
|
||
"Hide whitespace at start of file.
|
||
Return adjusted point."
|
||
(goto-char (point-min))
|
||
(if (re-search-forward "\\`\\([ \t\f]*\r?\n\\)+" nil t)
|
||
(progn
|
||
(irfc-overlay-hide-region (match-beginning 0) (match-end 0))
|
||
(point))
|
||
nil))
|
||
|
||
(defun irfc-render-buffer-overlay-page-number ()
|
||
"Add overlays for page headers and footers."
|
||
(let ((headerfooter-re (concat "^[ \t]*"
|
||
"\\(\r?\n\\)" ; #1
|
||
"\\([ \t]*\r?\n\\)*" ; #2
|
||
"[^ \t\f].*\\[Page "
|
||
"\\([0-9iIvVxX]+\\)" ; #3
|
||
"\\][ ]*\r?\n?"
|
||
"\\(" ; <#4
|
||
"\f"
|
||
"\\([ \t]*\r?\n\\)?" ; #5
|
||
"\\(" ; <#6
|
||
"\\(" ; <#7
|
||
"RFC [0-9]+"
|
||
"\\|" ; |#7
|
||
"Internet-Draft[ \t]"
|
||
"\\)" ; >#7
|
||
".*\r?\n"
|
||
"\\([ \t]*\r?\n\\)*" ; #8
|
||
"\\)?" ; >#6
|
||
"\\)?" ; >#4
|
||
)))
|
||
(while (re-search-forward headerfooter-re nil t)
|
||
;; Hide old page information for clear reading.
|
||
(irfc-overlay-hide-region (match-end 1) (match-end 0))
|
||
(when (match-beginning 6)
|
||
(let* ((overlay (irfc-overlay-add (match-beginning 1)
|
||
(match-end 1)
|
||
'irfc-page-number-overlay))
|
||
;; Get page number.
|
||
(page-num (1+ (string-to-number (match-string 3))))
|
||
;; Generate page string.
|
||
(page-str (format "(p.%s)" (number-to-string page-num)))
|
||
;; Generate new page string.
|
||
(new-str (concat (make-string (max (- 79
|
||
(- (match-beginning 1)
|
||
(match-beginning 0))
|
||
(length page-str))
|
||
0)
|
||
32)
|
||
page-str)))
|
||
;; Record total pages number.
|
||
(setq irfc-total-pages page-num)
|
||
;; Overlay new page string instead old one.
|
||
(overlay-put overlay
|
||
'before-string
|
||
new-str))))))
|
||
|
||
(defun irfc-render-buffer-hide-blank-line (top-point)
|
||
"Hide any extraneous blank lines between top header and before title.
|
||
Argument TOP-POINT is the top point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(unless (re-search-forward (concat "^[ \t]*\r?\n"
|
||
"\\(\\([ \t]*\r?\n\\)+\\)?")
|
||
nil t)
|
||
(error "This doesn't seem to be an RFC - no blank line before title"))
|
||
(when (match-beginning 1)
|
||
;; Hide blanks lines between top header and before title.
|
||
(irfc-overlay-hide-region (match-beginning 1) (match-end 1)))
|
||
(point))
|
||
|
||
(defun irfc-render-buffer-overlay-rfc-number (top-point title-line-point)
|
||
"Overlay RFC number.
|
||
Argument TOP-POINT is the top point of RFC buffer after render.
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(while (let ((case-fold-search t))
|
||
(re-search-forward "^\\(request for comments\\|updates\\|obsoletes\\):\\( RFCs\\)?[ \t]+\\(\\([0-9X]+\\)\\(,[ \t]+[0-9]+\\)*\\)"
|
||
title-line-point t))
|
||
;; Overlay RFC number.
|
||
(irfc-overlay-add (match-beginning 3)
|
||
(match-end 3)
|
||
'irfc-rfc-number-overlay)))
|
||
|
||
(defun irfc-render-buffer-overlay-std-number (top-point title-line-point)
|
||
"Overlay STD number.
|
||
Argument TOP-POINT is the top point of RFC buffer after render.
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(when (let ((case-fold-search nil))
|
||
(re-search-forward "^STD:[ \t]+[0-9]+"
|
||
title-line-point t))
|
||
;; Overlay STD number.
|
||
(irfc-overlay-add (match-beginning 0)
|
||
(match-end 0)
|
||
'irfc-std-number-overlay)))
|
||
|
||
(defun irfc-render-buffer-overlay-table-item (top-point)
|
||
"Overlay valid item in table for jump.
|
||
Argument TOP-POINT is the top point of RFC buffer after render."
|
||
(when (irfc-have-table-p) ;whether have table in current buffer
|
||
(goto-char top-point)
|
||
(let* ((case-fold-search t)
|
||
(start-position (re-search-forward "^Table of Contents$" nil t))
|
||
(end-position (re-search-forward "^[0-9\\.]+" nil t)))
|
||
(goto-char start-position)
|
||
(while (re-search-forward irfc-table-regex end-position t)
|
||
;; Overlay valid table item.
|
||
(irfc-overlay-add (match-beginning 2)
|
||
(match-end 2)
|
||
'irfc-table-item-overlay)))))
|
||
|
||
(defun irfc-render-buffer-overlay-rfc-link (top-point)
|
||
"Overlay valid RFC link.
|
||
Argument TOP-POINT is the top point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(while (let ((case-fold-search nil))
|
||
(re-search-forward "\\[RFC-?[0-9]+\\]"
|
||
nil t))
|
||
;; Overlay valid RFC link.
|
||
(irfc-overlay-add (match-beginning 0)
|
||
(match-end 0)
|
||
'irfc-rfc-link-overlay)))
|
||
|
||
(defun irfc-render-buffer-overlay-title (title-line-point)
|
||
"Add overlays to the title line(s).
|
||
Note that we currently assume no blank lines in the title; otherwise
|
||
we have to do a perfect job of identifying the first non-title line
|
||
\(usually a section heading, which some some RFCs make difficult to
|
||
always identify).
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(goto-char title-line-point)
|
||
(when (re-search-forward (concat
|
||
"\\([^ \t\f\r\n].*[^ \t\f\r\n]\\)"
|
||
"\\(\r?\n[ \t]*[^ \t\f\r\n].*[^ \t\f\r\n]\\)*"))
|
||
;; Overlay title.
|
||
(irfc-overlay-add (match-beginning 0)
|
||
(match-end 0)
|
||
'irfc-title-overlay)))
|
||
|
||
(defun irfc-render-buffer-overlay-head (title-line-point)
|
||
"Overlay heading.
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(goto-char title-line-point)
|
||
(let (match-list)
|
||
(while (setq match-list (irfc-head-move))
|
||
(when (and (nth 0 match-list) (nth 1 match-list))
|
||
;; Overlay heading number.
|
||
(irfc-overlay-add (nth 0 match-list)
|
||
(nth 1 match-list)
|
||
'irfc-head-number-overlay))
|
||
;; Overlay heading name.
|
||
(irfc-overlay-add (nth 2 match-list)
|
||
(nth 3 match-list)
|
||
'irfc-head-name-overlay))))
|
||
|
||
(defun irfc-render-buffer-overlay-requirement-keyword (top-point)
|
||
"Overlay RFC specification requirements.
|
||
Argument TOP-POINT is the top point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(while (let ((case-fold-search nil))
|
||
(re-search-forward (regexp-opt irfc-requirement-keywords 'words)
|
||
nil t))
|
||
;; Overlay RFC requirement keyword.
|
||
(irfc-overlay-add (match-beginning 0)
|
||
(match-end 0)
|
||
'irfc-requirement-keyword-overlay)))
|
||
|
||
|
||
(defun irfc-render-buffer-overlay-reference (top-point)
|
||
"Overlay RFC references.
|
||
Argument TOP-POINT is the top point of RFC buffer after render."
|
||
(goto-char top-point)
|
||
(while (let ((case-fold-search nil))
|
||
(re-search-forward irfc-reference-regex
|
||
nil t))
|
||
;; Overlay RFC reference.
|
||
(irfc-overlay-add (match-beginning 0)
|
||
(match-end 0)
|
||
'irfc-reference-overlay)))
|
||
|
||
(defun irfc-rename-buffer (title-line-point)
|
||
"Rename buffer to include RFC title.
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(goto-char title-line-point)
|
||
(let ((rfc-txt "")
|
||
(rfc-title ""))
|
||
;; Set RFC title
|
||
(when (re-search-forward (concat
|
||
"\\([^ \t\f\r\n].*[^ \t\f\r\n]\\)"
|
||
"\\(\r?\n[ \t]*[^ \t\f\r\n].*[^ \t\f\r\n]\\)*"))
|
||
(setq rfc-title (match-string 0))
|
||
(setq rfc-title (replace-regexp-in-string "[\r\n\t\f ]+" " " rfc-title)))
|
||
;; Set RFC txt
|
||
(when (string-match "\\(rfc[0-9]+\.txt\\)" (buffer-name))
|
||
(setq rfc-txt (match-string 1 (buffer-name))))
|
||
;; Set buffer name
|
||
(if irfc-buffer-name-includes-title
|
||
(rename-buffer (concat rfc-title " (" rfc-txt ")"))
|
||
(rename-buffer rfc-txt))))
|
||
|
||
(defun irfc-fill-tables (title-line-point)
|
||
"Fill heading names/numbers tables and lists.
|
||
Argument TITLE-LINE-POINT is the title line point of RFC buffer after render."
|
||
(clrhash irfc-heading-numbers-table)
|
||
(clrhash irfc-heading-names-table)
|
||
(setq irfc-heading-numbers-list nil)
|
||
(setq irfc-heading-names-list nil)
|
||
(goto-char title-line-point)
|
||
(let (match-list)
|
||
;; Populate irfc-heading-numbers-table and irfc-heading-names-table
|
||
(while (setq match-list (irfc-head-move))
|
||
(when (and (nth 0 match-list) (nth 1 match-list))
|
||
(puthash (buffer-substring (nth 0 match-list) (nth 1 match-list))
|
||
(nth 0 match-list) irfc-heading-numbers-table))
|
||
(puthash (buffer-substring (nth 2 match-list) (nth 3 match-list))
|
||
(nth 2 match-list) irfc-heading-names-table))
|
||
;; Generate irfc-heading-numbers-list
|
||
(maphash (lambda (number _point)
|
||
(setq irfc-heading-numbers-list (cons number irfc-heading-numbers-list)))
|
||
irfc-heading-numbers-table)
|
||
;; Generate irfc-heading-names-list
|
||
(maphash (lambda (name _point)
|
||
(setq irfc-heading-names-list (cons name irfc-heading-names-list)))
|
||
irfc-heading-names-table)))
|
||
|
||
(defun irfc-head-move (&optional reverse)
|
||
"Move to special heading.
|
||
Return heading list for overlay.
|
||
Default is to move to next heading;
|
||
move to previous heading if REVERSE is non-nil."
|
||
(let ((case-fold-search t)
|
||
;; Note: We can't just look for lines that begin in column 0, since
|
||
;; some RFCs put source code, ASCII-art, description list headings,
|
||
;; body text, and other stuff in column 0.
|
||
;; So we look for stock headings and ones that appear to
|
||
;; begin with section numbers.
|
||
(heading-re (concat
|
||
"^"
|
||
"\\(" ; <#1
|
||
"\\(" ; <#2 = numbered section
|
||
"\\(" ; <#3 = number
|
||
"\\([0-9]+\\.?\\|[A-Z]\\.\\)" ; #4
|
||
"\\([0-9]+\\.?\\)*" ; #5
|
||
"\\)" ; >#3 = number
|
||
"[ \t]+"
|
||
"\\([^\r\n]+\\)" ; #6 = name
|
||
"\\)" ; >#2 = numbered section
|
||
"\\|" ; |#1
|
||
"\\(" ; <#7 = stock section
|
||
"\\(" ; <#8
|
||
(mapconcat 'identity irfc-stock-section-names "\\|")
|
||
"\\)" ; >#8
|
||
":?[ \t]*$"
|
||
"\\)" ; >#7 = stock section
|
||
"\\|" ; |#1
|
||
"\\(" ; <#9 = lit-appendix
|
||
|
||
"appendix[ \t]+"
|
||
"\\([A-Z]\\)" ; #10 = number
|
||
|
||
"\\(\\.\\|:\\|[ \t]+-\\)" ; #11
|
||
"[ \t]+"
|
||
"\\([^\r\n]+\\)" ; #12 = name
|
||
|
||
"\\)" ; >#9 = lit-appendix
|
||
"\\)" ; >#1
|
||
)))
|
||
(if (if reverse
|
||
;; Search backward.
|
||
(re-search-backward heading-re nil t)
|
||
;; Search forward.
|
||
(re-search-forward heading-re nil t))
|
||
(let ((num-match nil)
|
||
(num-highlight-begin nil)
|
||
(num-highlight-end nil)
|
||
(name-match nil))
|
||
;; Get the match data numbers.
|
||
(cond ((match-beginning 3) (setq num-match 3
|
||
name-match 6))
|
||
((match-beginning 8) (setq num-match nil
|
||
name-match 8))
|
||
((match-beginning 9) (setq num-match 10
|
||
name-match 12)
|
||
(setq num-highlight-begin (match-beginning 9)
|
||
num-highlight-end (match-end 11)))
|
||
(t (error " should never happen")))
|
||
;; Return heading list for overlay.
|
||
(list
|
||
(if num-match
|
||
(or num-highlight-begin
|
||
(match-beginning num-match))
|
||
nil)
|
||
(if num-match
|
||
(or num-highlight-end
|
||
(match-end num-match))
|
||
nil)
|
||
(match-beginning name-match)
|
||
(match-end name-match)))
|
||
nil)))
|
||
|
||
(defun irfc-overlay-put-alist (symbol alist)
|
||
"Put special overlay prop with value.
|
||
SYMBOL is overlay variable.
|
||
ALIST contain special properties for overlay."
|
||
(mapc (lambda (cell)
|
||
(put symbol (nth 0 cell) (cdr cell)))
|
||
alist))
|
||
|
||
(defun irfc-overlay-remove-all ()
|
||
"Remove all overlays from current buffer."
|
||
(mapc (lambda (lst)
|
||
(when lst
|
||
(delete-overlay lst)
|
||
))
|
||
(let ((lists (overlay-lists)))
|
||
(nconc (car lists) (cdr lists)))))
|
||
|
||
(defun irfc-overlay-add (begin end category)
|
||
"Add overlay.
|
||
BEGIN is start position to overlay.
|
||
END is end position to overlay.
|
||
CATEGORY is special overlay variable."
|
||
(or category (error "Irfc-overlay-add nil category"))
|
||
(let ((overlay (make-overlay begin end)))
|
||
(overlay-put overlay 'category category)
|
||
overlay))
|
||
|
||
(defun irfc-overlay-hide-region (start end)
|
||
"Use overlay to hide region.
|
||
START is start position to hide.
|
||
END is end position to hide."
|
||
(irfc-overlay-add start end 'irfc-hide-overlay))
|
||
|
||
(defun irfc-have-table-p ()
|
||
"Return non-nil if the RFC contain a Table of Contents."
|
||
(save-excursion
|
||
(let ((case-fold-search t))
|
||
(goto-char (point-min))
|
||
(re-search-forward "^Table of Contents$" nil t))))
|
||
|
||
(defun irfc-front-table-p ()
|
||
"Return t when point is before the Table of Contents."
|
||
(let ((case-fold-search t)
|
||
(original-position (point))
|
||
table-start-position)
|
||
(save-excursion
|
||
(goto-char (point-min))
|
||
(setq table-start-position (re-search-forward "^Table of Contents$" nil t))
|
||
(< original-position table-start-position))))
|
||
|
||
(defun irfc-in-table-p ()
|
||
"Return t when point is in the Table of Contents."
|
||
(let ((case-fold-search t)
|
||
(original-position (point))
|
||
table-start-position
|
||
table-end-position)
|
||
(save-excursion
|
||
;; Get start and end position of table.
|
||
(goto-char (point-min))
|
||
(re-search-forward "^Table of Contents$" nil t)
|
||
(beginning-of-line)
|
||
(setq table-start-position (point))
|
||
(re-search-forward "^[0-9\\.]+" nil t)
|
||
(beginning-of-line)
|
||
(forward-char -1)
|
||
;; Compare current cursor with table scope.
|
||
(setq table-end-position (point))
|
||
(and (>= original-position table-start-position)
|
||
(<= original-position table-end-position)))))
|
||
|
||
(defun irfc-current-page ()
|
||
"Return current page number at point."
|
||
(let ((original-render-status irfc-render-p)
|
||
current-page)
|
||
(save-excursion
|
||
;; Turn off render.
|
||
(irfc-render-turn-off)
|
||
(if (re-search-forward "\\[Page \\([0-9]+\\)\\]$" nil t)
|
||
;; Set current page number when search successful.
|
||
(setq current-page (string-to-number (match-string 1)))
|
||
;; Set max page number when search failed.
|
||
(setq current-page irfc-total-pages))
|
||
;; Revert render status.
|
||
(unless (equal original-render-status irfc-render-p)
|
||
(irfc-render-toggle)))
|
||
current-page))
|
||
|
||
(defun irfc-download (url)
|
||
"Download RFC document URL.
|
||
URL is download URL that base on `irfc-download-base-url'."
|
||
(let* ((url-request-method "GET")
|
||
(url-request-extra-headers nil)
|
||
(url-mime-accept-string "*/*")
|
||
(parsed-url (url-generic-parse-url url))
|
||
download-buffer
|
||
download-buffer-name)
|
||
;; Get unique buffer for handle download information.
|
||
(setq download-buffer (irfc-get-buffer))
|
||
(setq download-buffer-name (buffer-name download-buffer))
|
||
(with-current-buffer (get-buffer download-buffer-name)
|
||
;; Bind download url with local buffer.
|
||
(setq irfc-download-url url)
|
||
(setq irfc-download-buffer (url-retrieve url
|
||
'irfc-download-callback
|
||
(list download-buffer-name))))))
|
||
|
||
(defun irfc-download-callback (&optional redirect download-buffer-name)
|
||
"Callback for `irfc-download'.
|
||
With `irfc-download', this downloads RFC files asynchronously.
|
||
REDIRECT is default return argument for `url-retrieve'.
|
||
DOWNLOAD-BUFFER-NAME is the buffer name for handling download content."
|
||
(if (eq (car redirect) ':error)
|
||
;; Output error information if download RFC document failed.
|
||
(with-current-buffer (get-buffer download-buffer-name)
|
||
(message "Not found %s." irfc-download-url)
|
||
(kill-buffer download-buffer-name))
|
||
;; Decode retrieve information.
|
||
(irfc-retrieve-decode download-buffer-name 'utf-8)
|
||
(with-current-buffer (get-buffer download-buffer-name)
|
||
;; Write file.
|
||
(write-file (expand-file-name (url-file-nondirectory irfc-download-url) irfc-directory))
|
||
;; Switch buffer.
|
||
(switch-to-buffer (current-buffer)))))
|
||
|
||
(defun irfc-retrieve-decode (retrieve-buffer-name coding)
|
||
"Decode the retrieve buffer RETRIEVE-BUFFER-NAME with coding CODING."
|
||
(declare (special url-http-end-of-headers))
|
||
(with-current-buffer (get-buffer retrieve-buffer-name)
|
||
(insert
|
||
(with-current-buffer irfc-download-buffer
|
||
(set-buffer-multibyte t)
|
||
(goto-char (1+ url-http-end-of-headers))
|
||
(decode-coding-region
|
||
(point) (point-max)
|
||
(coding-system-change-eol-conversion coding 'dos))
|
||
(buffer-substring (point) (point-max))))
|
||
(goto-char (point-min))))
|
||
|
||
(defun irfc-get-buffer ()
|
||
"Get a buffer for temporary storage of downloaded content.
|
||
Uses `current-time' to make buffer name unique."
|
||
(let ((time-now (current-time)))
|
||
(get-buffer-create (format "*irfc<%s-%s-%s>*"
|
||
(nth 0 time-now) (nth 1 time-now) (nth 2 time-now)))))
|
||
|
||
(defun irfc-get-rfc-filename ()
|
||
"Return filename for RFC file.
|
||
Look at point and extract an RFC number: either a string `[RFCnnn]',
|
||
or a RFC number in a standard header field (`Updates:', etc.).
|
||
In that case, return `rfcnnn.txt'; otherwise return nil."
|
||
(let (case-fold-search
|
||
(symbol (irfc-get-symbol-non-blank)))
|
||
(cond ((string-match "\\[\\(RFC-?[0-9]+\\)\\]" symbol)
|
||
(format "%s.txt" (replace-regexp-in-string "-" "" (downcase (match-string 1 symbol)))))
|
||
((and (string-match "^\\([0-9]+\\),*$" symbol)
|
||
(irfc-title-rfc-link-p))
|
||
(string-match "^\\([0-9]+\\),*$" symbol)
|
||
(format "rfc%s.txt" (match-string 1 symbol)))
|
||
(t
|
||
nil))))
|
||
|
||
(defun irfc-title-rfc-link-p ()
|
||
"Return t if current point is at title RFC link.
|
||
Otherwise return nil."
|
||
(save-excursion
|
||
(let ((case-fold-search t))
|
||
(search-forward-regexp " \\|$" nil t)
|
||
(skip-chars-backward " ")
|
||
(if (string-match "^\\(request for comments\\|updates\\|obsoletes\\):\\( RFCs\\)?[ \t]+\\(\\([0-9X]+\\)\\(,[ \t]+[0-9]+\\)*\\)\\b"
|
||
(buffer-substring-no-properties (line-beginning-position) (point)))
|
||
t
|
||
nil))))
|
||
|
||
(defun irfc-get-symbol-non-blank ()
|
||
"Return symbol between `blank'."
|
||
(save-excursion
|
||
(let (start end)
|
||
(search-backward-regexp " \\|^" nil t)
|
||
(skip-chars-forward " ")
|
||
(setq start (point))
|
||
(search-forward-regexp " \\|$" nil t)
|
||
(skip-chars-backward " ")
|
||
(setq end (point))
|
||
(and start
|
||
end
|
||
(>= end start)
|
||
(buffer-substring-no-properties start end)))))
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; Overlay setup.
|
||
(irfc-overlay-put-alist 'irfc-title-overlay '((face . irfc-title-face)))
|
||
(irfc-overlay-put-alist 'irfc-head-name-overlay '((face . irfc-head-name-face)))
|
||
(irfc-overlay-put-alist 'irfc-head-number-overlay '((face . irfc-head-number-face)))
|
||
(irfc-overlay-put-alist 'irfc-rfc-number-overlay '((face . irfc-rfc-number-face)))
|
||
(irfc-overlay-put-alist 'irfc-std-number-overlay '((face . irfc-std-number-face)))
|
||
(irfc-overlay-put-alist 'irfc-rfc-link-overlay '((face . irfc-rfc-link-face)))
|
||
(irfc-overlay-put-alist 'irfc-table-item-overlay '((face . irfc-table-item-face)))
|
||
(irfc-overlay-put-alist 'irfc-requirement-keyword-overlay '((face . irfc-requirement-keyword-face)))
|
||
(irfc-overlay-put-alist 'irfc-reference-overlay '((face . irfc-reference-face)))
|
||
(irfc-overlay-put-alist 'irfc-hide-overlay
|
||
'((face . default)
|
||
(intangible . t)
|
||
(invisible . t)))
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Cleanup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
(defun irfc-unload-function ()
|
||
"Unload the Irfc library."
|
||
(dolist (buffer (buffer-list))
|
||
(set-buffer buffer)
|
||
(when (eq major-mode 'irfc-mode)
|
||
(irfc-render-turn-off)
|
||
(text-mode)))
|
||
;; `nil' means continue standard unloading.
|
||
nil)
|
||
|
||
(provide 'irfc)
|
||
|
||
;;; irfc.el ends here
|
||
|
||
;;; LocalWords: irfc IETF lpi rfcview Dyke txt YourStorageDirectory DarkRed cr
|
||
;;; LocalWords: LawnGreen iesg nPage filepath headerfooter iIvVxX num str SPC
|
||
;;; LocalWords: lst eol Juanma Barranquero ARGth RFCnnn backtab rfcnnn regex
|
||
;;; LocalWords: Juanma's
|