Hi everyone, I find the following list in TODO list in rst.el. ;; bulleted and enumerated list items ;; ---------------------------------- ;; - We need to provide way to rebullet bulleted lists, and that would include ;; automatic enumeration as well. I write some functions implemented the functionality. Hope it is helpful. The following is the usage and diff. The attached file is a rst.el after I modified. I also fixed some tiny bugs, such as roman numerals missed the "D". Best wishes, Wei-Wei USAGE ===== There is only one key binding needed which is M-RET. 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: Users are asked to select the item style first, for example (a), i), +. Use TAB for completion and choices. (a) If user selects bullets or #, it's just added. (b) If user selects enumerates, a further prompt is given. User need to input a starting item, for example 'e' for 'A)' style. The position of the new list is arranged according 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 proceeded by a blank line, it is hard to determine which type to use automatically. The function uses roman numerical list as default. If you want alphabetical list, just use a prefix (C-u). DIFF ==== --- rst.el.old 2008-12-12 18:23:23.000000000 +0800 +++ rst.el 2008-12-12 18:16:17.000000000 +0800 @@ -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 [(meta return)] '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. @@ -1502,6 +1501,62 @@ ) ))) +;================================================= +; Borrowed from a2r.el (version 1.3), writen by Lawrence Mitchell +; I need to make some tiny changes on the functions, so I put it here. +; -- Wei-Wei Guo + +(defconst 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 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 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)) + +(defconst roman-to-arabic + '(("M" . 1000) ("CM" . 900) ("D" . 500) ("CD" . 400) + ("C" . 100) ("XC" . 90) ("L" . 50) ("XL" . 40) + ("X" . 10) ("IX" . 9) ("V" . 5) ("IV" . 4) + ("I" . 1)) + "List of maps between Roman numerals and their Arabic equivalents.") + +(defun 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 roman-to-arabic)) + (while map + (if (string-match (concat "^" (caar map)) string) + (setq res (+ res (cdar 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. @@ -1527,23 +1582,231 @@ (forward-line 1)) ) (nreverse pfx))) +(defun rst-indent-return () + "Add a new line at blank line with indent keeped." + (interactive) + (when (save-excursion + (beginning-of-line) + (looking-at "^[ \t]*$")) + (beginning-of-line) + (insert "\n") + (end-of-line))) + +(defun rst-insert-list-pos (newitem) + "Arrage 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 (concat newitem " ")) + (insert (concat "\n" newitem " "))) + (progn + (end-of-line) + (insert (concat "\n\n" newitem " "))))) + +(defvar rst-initial-enums + '("#." "1." "a." "A." "I." "i." "(1)" "(a)" "(A)" "(I)" "(i)" "1)" "a)" "A)" "I)" "i)") + "List of initial enumerates.") + +(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-new-pos'. + +If user selects enumerates, 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-new-pos'. +" + (interactive) + (let (itemstyle itemno itemfirst) + (setq itemstyle (completing-read "Providing perfered item (default '#.'): " + rst-initial-items nil t nil nil "#.")) + (when (string-match "[aA1Ii]" itemstyle) + (setq itemfirst (match-string 0 itemstyle)) + (cond ((equal itemfirst "a") + (progn + (setq itemno (read-string "Providing starting (default a): " nil nil "a")) + (setq itemstyle (replace-match + (downcase itemno) + nil nil itemstyle)))) + ((equal itemfirst "A") + (progn + (setq itemno (read-string "Providing starting (default A): " nil nil "A")) + (setq itemstyle (replace-match + (upcase itemno) + nil nil itemstyle)))) + ((equal itemfirst "I") + (progn + (setq itemno (read-number "Providing starting (default 1): " 1)) + (string-match "[aA1Ii]" itemstyle) + (setq itemstyle (replace-match + (arabic-to-roman itemno) + nil nil itemstyle)))) + ((equal itemfirst "i") + (progn + (setq itemno (read-number "Providing starting (default 1): " 1)) + (string-match "[aA1Ii]" itemstyle) + (setq itemstyle (replace-match + (downcase (arabic-to-roman itemno)) + nil nil itemstyle)))) + ((equal itemfirst "1") + (progn + (setq itemno (read-number "Providing starting (default 1): " 1)) + (string-match "[aA1Ii]" itemstyle) + (setq itemstyle (replace-match + (number-to-string itemno) + nil nil itemstyle)))) + )) + (rst-insert-list-pos 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.") +(defvar rst-re-enumerates + (format "^[ \t]*\\(%s\\|%s\\)[ \t]" + "\\([0-9]+\\|[a-zA-Z]\\|[IVXLCDMivxlcdm]+\\)\\." + "(?\\([0-9]+\\|[a-zA-Z]\\|[IVXLCDMivxlcdm]+\\))") + "Regexp for finding enumerates (# 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]" + (format "[%s]" (regexp-quote (concat rst-bullets))) + "\\(#\\|[a-z]\\|[0-9]+\\|[A-Z]\\|[IVXLCDM]+\\|[ivxlcdm]+\\)\\." + "(?\\([a-z]\\|[0-9]+\\|[A-Z]\\|[IVXLCDM]+\\|[ivxlcdm]+\\))") + "Regexp for finding bullets and enumerates.") -(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 by match-string. + +If nothing matched, a empty string is returned." + (let (matched) + (save-excursion + (end-of-line) + (if (re-search-backward reg (line-beginning-position) t) + (setq matched (match-string 0)) + (setq matched ""))) + matched)) + +(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 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 proceeded +by a blank line, it is hard to determine which type to use automatically. The +function uses roman numerical list defaultly. If you want alphabetical list, just +use a prefix (\\[universal-argument]). +" + (interactive) + (let (curitem newitem itemno previtem tmpitem) + (setq curitem (rst-list-match-string rst-re-items)) + (cond ((string-match (format "#.\\|[%s]" + (regexp-quote (concat rst-bullets))) curitem) + (setq newitem curitem)) + ((string-match "[0-9]+" curitem) + (progn + (setq itemno (1+ + (string-to-number + (match-string 0 curitem)))) + (setq newitem (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) + (progn + (save-excursion + (forward-line -1) + (setq previtem (rst-list-match-string rst-re-enumerates)) + (when (string-match "[a-zA-Z]+" previtem) + (setq previtem (match-string 0 previtem)))) + (or (> (length previtem) 1) + (= (length previtem) 0))))))) + (progn + (setq itemno (1+ (roman-to-arabic tmpitem))) + (string-match "[IVXLCDMivxlcdm]+" curitem) + (if (isearch-no-upper-case-p tmpitem nil) + (setq newitem (replace-match + (downcase (arabic-to-roman itemno)) + nil nil curitem)) + (setq newitem (replace-match + (arabic-to-roman itemno) + nil nil curitem))))) + ((string-match "[a-zA-Z]" curitem) + (progn + (setq itemno (1+ + (string-to-char + (match-string 0 curitem)))) + (setq newitem (replace-match + (char-to-string itemno) + nil nil curitem))))) + (insert (concat "\n" newitem)))) + + +(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 enumerates, a further prompt is given. User need to input a +starting item, for example 'e' for 'A)' style. + +The position of the new list is arranged according 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 proceeded +by a blank line, it is hard to determine which type to use automatically. The +function uses roman numerical list defaultly. 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. @@ -1551,9 +1814,9 @@ after you have merged multiple bulleted lists to make them use the same/correct/consistent bullet characters. -See variable `rst-preferred-bullets' for the list of bullets to -adjust. If bullets are found on levels beyond the -`rst-preferred-bullets' list, they are not modified." +See variable `rst-bullets' for the list of bullets to adjust. +If bullets are found on levels beyond the `rst-bullets' list, +they are not modified." (interactive "r") (let ((bullets (rst-find-pfx-in-region beg end @@ -1572,7 +1835,7 @@ (let ((poslist ())) ; List of (indent . positions). (maphash (lambda (x y) (push (cons x y) poslist)) levtable) - (let ((bullets rst-preferred-bullets)) + (let ((bullets rst-bullets)) (dolist (x (sort poslist 'car-less-than-car)) (when bullets ;; Apply the characters.