=== modified file 'lisp/textmodes/rst.el' --- lisp/textmodes/rst.el 2010-10-03 02:26:35 +0000 +++ lisp/textmodes/rst.el 2010-11-17 22:11:18 +0000 @@ -36,6 +36,8 @@ ;; from it; ;; - Functions to insert and automatically update a TOC in your source ;; document; +;; - Function to insert list, processing item bullets and enumerations +;; automatically; ;; - Font-lock highlighting of notable reStructuredText structures; ;; - Some other convenience functions. ;; @@ -166,11 +168,6 @@ ;; - numbering: automatically detect if we have a section-numbering directive in ;; the corresponding section, to render the toc. ;; -;; bulleted and enumerated list items -;; ---------------------------------- -;; - We need to provide way to rebullet bulleted lists, and that would include -;; automatic enumeration as well. -;; ;; Other ;; ----- ;; - It would be nice to differentiate between text files using @@ -260,6 +257,8 @@ ;; ;; Operating on Blocks of Text. ;; + ;; Inserts bullet list or enumeration list. + (define-key map [?\M-\r] 'rst-insert-list) ;; Makes paragraphs in region as a bullet list. (define-key map [(control c) (control b)] 'rst-bullet-list-region) ;; Makes paragraphs in region as a enumeration. @@ -1506,6 +1505,55 @@ ) ))) +;================================================= +; Borrowed from a2r.el (version 1.3), by Lawrence Mitchell +; I needed to make some tiny changes to the functions, so I put it here. +; -- Wei-Wei Guo + +(defconst rst-arabic-to-roman + '((1000 . "M") (900 . "CM") (500 . "D") (400 . "CD") + (100 . "C") (90 . "XC") (50 . "L") (40 . "XL") + (10 . "X") (9 . "IX") (5 . "V") (4 . "IV") + (1 . "I")) + "List of maps between Arabic numbers and their Roman numeral equivalents.") + +(defun rst-arabic-to-roman (num &optional arg) + "Convert Arabic number NUM to its Roman numeral representation. + +Obviously, NUM must be greater than zero. Don't blame me, blame the +Romans, I mean \"what have the Romans ever _done_ for /us/?\" (with +apologies to Monty Python). +If optional prefix ARG is non-nil, insert in current buffer." + (let ((map rst-arabic-to-roman) + res) + (while (and map (> num 0)) + (if (or (= num (caar map)) + (> num (caar map))) + (setq res (concat res (cdar map)) + num (- num (caar map))) + (setq map (cdr map)))) + res)) + +(defun rst-roman-to-arabic (string &optional arg) + "Convert STRING of Roman numerals to an Arabic number. + +If STRING contains a letter which isn't a valid Roman numeral, the rest +of the string from that point onwards is ignored. + +Hence: +MMD == 2500 +and +MMDFLXXVI == 2500. +If optional ARG is non-nil, insert in current buffer." + (let ((res 0) + (map rst-arabic-to-roman)) + (while map + (if (string-match (concat "^" (cdar map)) string) + (setq res (+ res (caar map)) + string (replace-match "" nil t string)) + (setq map (cdr map)))) + res)) +;================================================= (defun rst-find-pfx-in-region (beg end pfx-re) "Find all the positions of prefixes in region between BEG and END. @@ -1531,24 +1579,203 @@ (forward-line 1)) ) (nreverse pfx))) +(defun rst-insert-list-pos (newitem) + "Arrange relative position of a newly inserted list item. + +Adding a new list might consider three situations: + + (a) Current line is a blank line. + (b) Previous line is a blank line. + (c) Following line is a blank line. + +When (a) and (b), just add the new list at current line. + +when (a) and not (b), a blank line is added before adding the new list. + +When not (a), first forward point to the end of the line, and add two +blank lines, then add the new list. + +Other situations are just ignored and left to users themselves." + (if (save-excursion + (beginning-of-line) + (looking-at "^[ \t]*$")) + (if (save-excursion + (forward-line -1) + (looking-at "^[ \t]*$")) + (insert newitem " ") + (insert "\n" newitem " ")) + (progn + (end-of-line) + (insert (concat "\n\n" newitem " "))))) + +(defvar rst-initial-enums + (let ((vals ())) + (dolist (fmt '("%s." "(%s)" "%s)")) + (dolist (c '("1" "a" "A" "I" "i")) + (push (format fmt c) vals))) + (cons "#." (nreverse vals))) + "List of initial enumerations.") + +(defvar rst-initial-items + (append (mapcar 'char-to-string rst-bullets) rst-initial-enums) + "List of initial items. It's collection of bullets and enumerations.") + +(defun rst-insert-list-new-item () + "Insert a new list item. + +User is asked to select the item style first, for example (a), i), +. Use TAB +for completition and choices. + +If user selects bullets or #, it's just added with position arranged by +`rst-insert-list-pos'. + +If user selects enumerations, a further prompt is given. User need to input a +starting item, for example 'e' for 'A)' style. The position is also arranged by +`rst-insert-list-pos'." + (interactive) + (let ((itemstyle (completing-read "Providing perfered item (default '#.'): " + rst-initial-items nil t nil nil "#."))) + (rst-insert-list-pos + (case (aref itemstyle 0) + (?a + (let ((itemno (read-string "Providing starting (default a): " + nil nil "a"))) + (concat (downcase itemno) (substring itemstyle 1)))) + (?A + (let ((itemno (read-string "Providing starting (default A): " + nil nil "A"))) + (concat (upcase itemno) (substring itemstyle 1)))) + (?I + (let ((itemno (read-number "Providing starting (default 1): " 1))) + (concat (rst-arabic-to-roman itemno) (substring itemstyle 1)))) + (?i + (let ((itemno (read-number "Providing starting (default 1): " 1))) + (concat (downcase (rst-arabic-to-roman itemno)) + (substring itemstyle 1)))) + (?1 + (let ((itemno (read-number "Providing starting (default 1): " 1))) + (concat (number-to-string itemno) (substring itemstyle 1)))) + (t itemstyle))))) + (defvar rst-re-bullets (format "\\([%s][ \t]\\)[^ \t]" (regexp-quote (concat rst-bullets))) "Regexp for finding bullets.") -;; (defvar rst-re-enumerations -;; "\\(\\(#\\|[0-9]+\\)\\.[ \t]\\)[^ \t]" -;; "Regexp for finding bullets.") +(defconst rst-re-enumerator "\\(?:[a-zA-Z]\\|[0-9IVXLCDMivxlcdm]+\\)") + +(defvar rst-re-enumerations + (format "^[ \t]*\\(%s.\\|(?%s)\\)[ \t]" + rst-re-enumerator + rst-re-enumerator) + "Regexp for finding enumerations (# is not included).") (defvar rst-re-items - (format "\\(%s\\|%s\\)[^ \t]" - (format "[%s][ \t]" (regexp-quote (concat rst-bullets))) - "\\(#\\|[0-9]+\\)\\.[ \t]") - "Regexp for finding bullets.") + (format "^[ \t]*\\([%s]\\|\\(#\\|%s\\)\\.\\|(?%s)\\)[ \t]" + (regexp-quote (concat rst-bullets)) + rst-re-enumerator + rst-re-enumerator) + "Regexp for finding bullets and enumerations.") (defvar rst-preferred-bullets '(?- ?* ?+) "List of favourite bullets to set for straightening bullets.") +(defun rst-list-match-string (reg) + "Match a regex in a line and return the matched string. +If nothing matched, an empty string is returned." + (save-excursion + (end-of-line) + (if (re-search-backward reg (line-beginning-position) t) + (match-string 0) + ""))) + +(defun rst-insert-list-continue () + "Insert a list item with current list style and indentation level. + +The function works for all style of bullet lists and enumeration lists. +Only one thing needs to be noticed: + +List style alphabetical list, such as 'a.', and roman numerical list, such as +'i.', have some overlapping items, for example 'v.' The function can deal with +the problem elegantly in most situations. But when those overlapped lists +are preceded by a blank line, it is hard to determine which type to use +automatically. The function uses roman numerical list by default. If you want +alphabetical list, just use a prefix (\\[universal-argument]). " + (interactive) + (let* ((curitem (rst-list-match-string rst-re-items)) + tmpitem) + (insert + "\n" + (cond ((string-match (format "#.\\|[%s]" + (regexp-quote (concat rst-bullets))) + curitem) + curitem) + ((string-match "[0-9]+" curitem) + (let ((itemno (1+ (string-to-number + (match-string 0 curitem))))) + (replace-match (number-to-string itemno) nil nil curitem))) + ((and (string-match "[IVXLCDMivxlcdm]+" curitem) + (progn + (setq tmpitem (match-string 0 curitem)) + (or (> (length tmpitem) 1) + (and (= (length tmpitem) 1) + (null current-prefix-arg) + (let ((previtem + (save-excursion + (forward-line -1) + (rst-list-match-string + rst-re-enumerations)))) + (when (string-match "[a-zA-Z]+" previtem) + (setq previtem (match-string 0 previtem))) + (or (> (length previtem) 1) + (= (length previtem) 0))))))) + (let ((itemno (1+ (rst-roman-to-arabic tmpitem)))) + (string-match "[IVXLCDMivxlcdm]+" curitem) + (if (isearch-no-upper-case-p tmpitem nil) + (replace-match + (downcase (rst-arabic-to-roman itemno)) + nil nil curitem) + (replace-match + (rst-arabic-to-roman itemno) + nil nil curitem)))) + ((string-match "[a-zA-Z]" curitem) + (let ((itemno (1+ (string-to-char + (match-string 0 curitem))))) + (replace-match (char-to-string itemno) nil nil curitem))))))) + + +(defun rst-insert-list () + "Insert a list item at the current point. + +The command can insert a new list or a continuing list. When it is called at a +non-list line, it will promote to insert new list. When it is called at a list +line, it will insert a list with the same list style. + +1. When inserting a new list: + +User is asked to select the item style first, for example (a), i), +. Use TAB +for completition and choices. + + (a) If user selects bullets or #, it's just added. + (b) If user selects enumerations, a further prompt is given. User needs to + input a starting item, for example 'e' for 'A)' style. + +The position of the new list is arranged according to whether or not the +current line and the previous line are blank lines. + +2. When continuing a list, one thing need to be noticed: + +List style alphabetical list, such as 'a.', and roman numerical list, such as +'i.', have some overlapping items, for example 'v.' The function can deal with +the problem elegantly in most situations. But when those overlapped list are +preceded by a blank line, it is hard to determine which type to use +automatically. The function uses roman numerical list by default. If you want +alphabetical list, just use a prefix (\\[universal-argument])." + (interactive) + (if (equal (rst-list-match-string rst-re-items) "") + (rst-insert-list-new-item) + (rst-insert-list-continue))) + (defun rst-straighten-bullets-region (beg end) "Make all the bulleted list items in the region consistent. The region is specified between BEG and END. You can use this @@ -2549,7 +2776,7 @@ (dolist (x items) (goto-char (car x)) (looking-at rst-re-items) - (replace-match (format "%d. " count) nil nil nil 1) + (replace-match (format "%d." count) nil nil nil 1) (incf count) )) ))