--- rst.el.old 2010-05-10 15:49:49.000000000 +0800 +++ rst.el 2010-06-28 16:22:05.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 @@ -239,14 +236,14 @@ ;; Section Decorations. ;; ;; The adjustment function that decorates or rotates a section title. - (define-key map [(control c) (control a)] 'rst-adjust) - (define-key map [(control c) (control ?=)] 'rst-adjust) + ;(define-key map [(control c) (control a)] 'rst-adjust) + ;(define-key map [(control c) (control ?=)] 'rst-adjust) (define-key map [(control ?=)] 'rst-adjust) ;; (Does not work on the Mac OSX.) ;; Display the hierarchy of decorations implied by the current document contents. - (define-key map [(control c) (control h)] 'rst-display-decorations-hierarchy) + (define-key map [(control c) (control t)] 'rst-display-decorations-hierarchy) ;; Homogeneize the decorations in the document. (define-key map [(control c) (control s)] 'rst-straighten-decorations) -;; (define-key map [(control c) (control s)] 'rst-straighten-deco-spacing) + ;(define-key map [(control c) (control s)] 'rst-straighten-deco-spacing) ;; ;; Section Movement and Selection. @@ -254,39 +251,75 @@ ;; Mark the subsection where the cursor is. (define-key map [(control c) (control m)] 'rst-mark-section) ;; Move forward/backward between section titles. - (define-key map [(control c) (control n)] 'rst-forward-section) - (define-key map [(control c) (control p)] 'rst-backward-section) + (define-key map [(meta n)] 'rst-forward-section) + (define-key map [(meta p)] 'rst-backward-section) ;; ;; Operating on Blocks of Text. ;; + ;; Inserts bullet list or enumeration list. + (define-key map [(meta return)] 'rst-insert-list) + ;; Inserts definition list. + ;(define-key map [(control c) t] 'rst-insert-definition) + ;; Inserts field list. + ;(define-key map [(control c) f] 'rst-insert-field) ;; Makes paragraphs in region as a bullet list. - (define-key map [(control c) (control b)] 'rst-bullet-list-region) + ;(define-key map [(control c) (control b)] 'rst-bullet-list-region) ;; Makes paragraphs in region as a enumeration. - (define-key map [(control c) (control e)] 'rst-enumerate-region) + ;(define-key map [(control c) (control e)] 'rst-enumerate-region) ;; Converts bullets to an enumeration. - (define-key map [(control c) (control v)] 'rst-convert-bullets-to-enumeration) + ;(define-key map [(control c) (control v)] 'rst-convert-bullets-to-enumeration) ;; Makes region a line-block. - (define-key map [(control c) (control d)] 'rst-line-block-region) + ;(define-key map [(control c) (control d)] 'rst-line-block-region) ;; Make sure that all the bullets in the region are consistent. - (define-key map [(control c) (control w)] 'rst-straighten-bullets-region) + ;(define-key map [(control c) (control w)] 'rst-straighten-bullets-region) ;; Shift region left or right (taking into account of enumerations/bullets, etc.). (define-key map [(control c) (control l)] 'rst-shift-region-left) (define-key map [(control c) (control r)] 'rst-shift-region-right) ;; Comment/uncomment the active region. - (define-key map [(control c) (control c)] 'comment-region) + ;(define-key map [(control c) (control c)] 'comment-region) + + ;; Insert option or directive. + (define-key map [(control c) (control o)] 'rst-insert-option) + (define-key map [(control c) (control d)] 'rst-insert-directive) + ;; Insert text replace definition. + ;(define-key map [(control c) t] 'rst-insert-replace) + ;; Insert image and figures + ;(define-key map [(control c) p] 'rst-insert-image) + ;(define-key map [(control c) m] 'rst-insert-figure) + ;; Insert admonition + ;(define-key map [(control c) t] 'rst-insert-admonition) + + ;; + ;; Hypylink, Footnote, and Citation Features. + ;; + (define-key map [(control c) (control h)] 'rst-insert-link) + ;; Insert hyperlink + ;(define-key map [(control c) l] 'rst-insert-inline-link) + ;; Insert footnote + ;(define-key map [(control c) f] 'rst-insert-footnote) + ;; Insert citation + ;(define-key map [(control c) c] 'rst-insert-citation) + ;; Hyperlink jumping + (define-key map [(control c) (l)] 'rst-link-jump-to-reference) + (define-key map [(control c) (n)] 'rst-link-jump-within-targets) + (define-key map [(control c) (t)] 'rst-link-jump-to-target) + ;; Footnote and citation jumping + (define-key map [(control c) (r)] 'rst-footnote-citation-jump-to-reference) + (define-key map [(control c) (m)] 'rst-footnote-citation-jump-within-targets) + (define-key map [(control c) (u)] 'rst-footnote-citation-jump-to-target) ;; ;; Table-of-Contents Features. ;; ;; Enter a TOC buffer to view and move to a specific section. (define-key map [(control c) (control t)] 'rst-toc) - ;; Insert a TOC here. - (define-key map [(control c) (control i)] 'rst-toc-insert) + ;; Insert a TOC here. use `rst-insert-directive' instead + ;(define-key map [(control c) i] 'rst-toc-insert) ;; Update the document's TOC (without changing the cursor position). (define-key map [(control c) (control u)] 'rst-toc-update) ;; Got to the section under the cursor (cursor must be in TOC). - (define-key map [(control c) (control f)] 'rst-goto-section) + ;(define-key map [(control c) (control f)] 'rst-goto-section) ;; ;; Converting Documents from Emacs. @@ -344,8 +377,8 @@ (defcustom rst-mode-hook nil - "Hook run when Rst mode is turned on. -The hook for Text mode is run before this one." + "Hook run when Rst Mode is turned on. +The hook for Text Mode is run before this one." :group 'rst :type '(hook)) @@ -1464,8 +1497,8 @@ (lambda (deco) (cons (rst-position (cdr deco) hier) (let ((m (make-marker))) - (goto-char (point-min)) - (forward-line (1- (car deco))) + (goto-char (point-min)) + (forward-line (1- (car deco))) (set-marker m (point)) m))) alldecos)) @@ -1499,14 +1532,242 @@ ;; adjust for the changes in the document. (dolist (deco (nreverse alldecos)) ;; Go to the appropriate position. - (goto-char (point-min)) - (forward-line (1- (car deco))) + (goto-char (point-min)) + (forward-line (1- (car deco))) (insert "@\n") ;; FIXME: todo, we ) ))) +;================================================= +; list related functions. + +(defconst roman-number-list + '("I" "II" "III" "IV" "V" "VI" "VII" "VIII" "IX" "X" "XI" "XII" "XIII" "XIV" "XV" + "XVI" "XVII" "XVIII" "XIX" "XX" "XXI" "XXII" "XXIII" "XXIV" "XXV" "XXVI" "XXVII" + "XXVIII" "XXIX" "XXX" "XXXI" "XXXII" "XXXIII" "XXXIV" "XXXV" "XXXVI" "XXXVII" + "XXXVIII" "XXXIX" "XL" "XLI" "XLII" "XLIII" "XLIV" "XLV" "XLVI" "XLVII" "XLVIII" + "XLIX" "L") + "List of Roman numerals.") + +(defconst letter-list + '("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" + "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z") + "List of Latin letter.") + +(defvar rst-re-bullets + (format "\\([%s][ \t]\\)[^ \t]" (regexp-quote (concat rst-bullets))) + "Regexp for finding bullets.") + +(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") + +(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 "^[ \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.") + +(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), add a blank line before adding the new list. + +When not (a), add a blank line and a new line at current point. + +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 + (insert (concat "\n\n" newitem " "))))) + +(defun rst-insert-list-new-bullet () + "Insert a new list bullet. +" + (interactive) + (let (itemstyle) + (setq itemstyle "-") + (rst-insert-list-pos itemstyle))) + +(defun rst-insert-list-new-item (itemno) + "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 "P") + (if (not itemno) + (setq itemno 1)) + (setq itemno (1- itemno)) + (let (itemstyle 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") + (setq itemstyle (replace-match (nth itemno letter-list) + nil nil itemstyle))) + ((equal itemfirst "a") + (setq itemstyle (replace-match (downcase (nth itemno letter-list)) + nil nil itemstyle))) + ((equal itemfirst "I") + (setq itemstyle (replace-match (nth itemno roman-number-list) + nil nil itemstyle))) + ((equal itemfirst "i") + (setq itemstyle (replace-match (downcase (nth itemno roman-number-list)) + nil nil itemstyle))) + ((equal itemfirst "1") + (setq itemstyle (replace-match (number-to-string (1+ itemno)) + nil nil itemstyle))) + )) + (rst-insert-list-pos itemstyle))) + +(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) + (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 + (string-match "[IVXLCDMivxlcdm]+" curitem) + (if (isearch-no-upper-case-p tmpitem nil) + (progn + (setq itemno (car (cdr (member + (match-string 0 (upcase curitem)) + roman-number-list)))) + (setq newitem (replace-match (downcase itemno) nil nil curitem))) + (progn + (setq itemno (car (cdr (member + (match-string 0 curitem) + roman-number-list)))) + (setq newitem (replace-match itemno nil nil curitem)))))) + ((string-match "[a-yA-Y]" 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 (itemno) + "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 "P") + (if (equal (rst-list-match-string rst-re-items) "") + (if (null current-prefix-arg) + (rst-insert-list-new-bullet) + (rst-insert-list-new-item itemno)) + (rst-insert-list-continue))) + +;============== + (defun rst-find-pfx-in-region (beg end pfx-re) "Find all the positions of prefixes in region between BEG and END. This is used to find bullets and enumerated list items. PFX-RE @@ -1531,33 +1792,15 @@ (forward-line 1)) ) (nreverse pfx))) -(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-items - (format "\\(%s\\|%s\\)[^ \t]" - (format "[%s][ \t]" (regexp-quote (concat rst-bullets))) - "\\(#\\|[0-9]+\\)\\.[ \t]") - "Regexp for finding bullets.") - -(defvar rst-preferred-bullets - '(?- ?* ?+) - "List of favourite bullets to set for straightening bullets.") - (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 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 @@ -1576,7 +1819,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. @@ -1586,6 +1829,9 @@ (insert (string (car bullets)))) (setq bullets (cdr bullets)))))))) +;================================================= + + (defun rst-rstrip (str) "Strips the whitespace at the end of string STR." (string-match "[ \t\n]*\\'" str) @@ -1912,7 +2158,7 @@ (let ((p (point))) (save-excursion (when (rst-toc-insert-find-delete-contents) - (insert "\n ") + (insert "\n ") (rst-toc-insert) )) ;; Somehow save-excursion does not really work well. @@ -2823,7 +3069,7 @@ 1 rst-block-face) ;; `Enumerated Lists`_ (list - (concat re-bol "\\((?\\(#\\|[0-9]+\\|[A-Za-z]\\|[IVXLCMivxlcm]+\\)[.)]" + (concat re-bol "\\((?\\(#\\|[0-9]+\\|[A-Za-z]\\|[IVXLCDMivxlcdm]+\\)[.)]" re-blksep1 "\\)") 1 rst-block-face) ;; `Definition Lists`_ FIXME: missing @@ -3189,6 +3435,633 @@ (set-match-data mtc) t)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; directives and hyperlinks. + +;===== directive type list and command ===== + +(defvar rst-directive-type-alist + '(("definition" . rst-insert-definition) + ("field" . rst-insert-field) + ("admonition" . rst-insert-admonition) + ("image" . rst-insert-image) + ("figure" . rst-insert-figure) + ("topic" . rst-insert-topic) + ("sidebar" . rst-insert-sidebar) + ("line-block" . rst-insert-line-block) + ("parsed-literal" . rst-insert-parsed-literal) + ("rubric" . rst-insert-rubric) + ("epigraph" . rst-insert-epigraph) + ("highlights" . rst-insert-highlights) + ("pull-quote" . rst-insert-pull-quote) + ("compound" . rst-insert-compound) + ("container" . rst-insert-container) + ("table" . rst-insert-table) + ("csv-table" . rst-insert-csv-table) + ("list-table" . rst-insert-list-table) + ("contents" . rst-insert-contents) + ("sectnum" . rst-insert-sectnum) + ("replace" . rst-insert-replace) + ("unicode" . rst-insert-unicode) + ("date" . rst-insert-date) + ("include" . rst-insert-include) + ("raw" . rst-insert-raw)) + "List of directive inserting functions of directive types.") + +(defvar rst-directive-types + '("definition" "field" "admonition" + "image" "figure" + "topic" "sidebar" "line-block" "parsed-literal" "rubric" "epigraph" + "highlights" "pull-quote" "compound" "container" + "table" "csv-table" "list-table" + "contents" "sectnum" "include" "raw" + "replace" "unicode" "date" +) + "List of directive types") + +(defvar rst-directive-option-list + '(("definition" rst-option-definition t) + ("field" rst-option-field t) + ("admonition" rst-option-admonition nil) + ("image" rst-option-image nil) + ("figure" rst-option-figure t) + ("topic" nil t) + ("sidebar" rst-option-sidebar t) + ("line-block" nil t) + ("parsed-literal" nil t) + ("rubric" nil nil) + ("epigraph" nil t) + ("highlights" nil t) + ("pull-quote" nil t) + ("compound" nil t) + ("container" nil t) + ("table" nil t) + ("csv-table" rst-option-csv-table t) + ("list-table" rst-option-list-table t) + ("contents" rst-contents-option nil) + ("sectnum" rst-sectnum-option nil) + ("replace" nil nil) + ("unicode" rst-option-unicode nil) + ("date" nil nil) + ("include" rst-include-option nil) + ("raw" rst-option-raw t)) + "List of option functions of directive types.") + +(defun rst-add-directive-type (type directfunc optalist content) + "Adding new directive to directive alist and completion list. + +Use the following way to add directive type. + + (rst-add-directive-type \"definition\" + 'rst-insert-definition + 'rst-directive-options + 'content-presence-boolean) +" + (add-to-list 'rst-directive-types type) + (add-to-list 'rst-directive-type-alist (cons type directfunc)) + (add-to-list 'rst-directive-option-list (list type optalist content))) + +(defun rst-add-directives (directlist) + "Meta function of add directives. + +Elements of directives should arranged as + + (type funciton option-list content-boolean). +" + (dolist (direct directlist) + (eval (cons 'rst-add-directive-type direct)))) + +(defun rst-insert-directive () + "Meta-function of all directives." + (interactive) + (let (type optlist content optorder) + (setq type (completing-read "Providing directive type: " rst-directive-types)) + (funcall (cdr (assoc type rst-directive-type-alist))) + (setq optlist (eval (car (cdr (assoc type rst-directive-option-list))))) + (setq content (eval (cadr (cdr (assoc type rst-directive-option-list))))) + (if optlist + (progn + (if (not optorder) + (progn + (setq optorder 1) + (insert " "))) + (while (y-or-n-p "Set directive option(s)? ") + (rst-option-directive optlist)))) + (if content + (newline-and-indent) + (newline-and-indent)))) + +;==== directive and list definitions ==== + +;--- list looks like directive --- + +(defun rst-insert-definition () + "Insert a definition list" + (interactive) + (let (term classifiers classel) + (setq term (read-string "Providing the definition's term: ")) + (setq classifiers (read-string "Providing classifier(s) (if many, seperated by ', '): ")) + (if (equal classifiers "") + (insert term "\n ") + (progn + (setq classifiers (split-string classifiers ", ")) + (dolist (tmpclass classifiers) + (setq classel (concat classel " : " tmpclass))) + (insert term classel "\n "))))) + +(defun rst-insert-field () + "Insert a field list." + (interactive) + (let (field value) + (setq field (read-string "Providing field: ")) + (save-excursion + (beginning-of-line) + (insert (concat ":" field ": "))))) + +;--- function for defining directives --- + +(defun rst-insert-directive-type (type &optional argument) + "Insert the first line of directive" + (insert (concat ".. " type ":: " argument)) + (newline-and-indent)) + +(defun rst-insert-directive-option (option &optional value) + "Insert an option line of directive" + (insert (concat ":" option ": " value))) + +(defun rst-insert-option () + "Insert a directive option." + (interactive) + (let (option value) + (save-excursion + (re-search-backward "\\.\\. \\sw+::") + (setq option (read-string "Providing directive option: ")) + (setq value (read-string "Providing option value: ")) + (end-of-line) + (newline-and-indent) + (insert " ") + (rst-insert-directive-option option value)))) + +(defun rst-option-directive (optalist) + "Insert directive options in directive inserting function." + (let (optlist option type value) + (setq optlist (mapcar 'car optalist)) + (setq option (completing-read "Providing option: " optlist)) + (setq type (car (cdr (assoc option optalist)))) + (setq value + (cond + ((equal type "flag") nil) + ((equal type "option") (completing-read + "Providing optional value: " + (cadr (cdr (assoc option optalist))))) + ((equal type "number") (number-to-string + (read-number "Providing numeric value: "))) + ((equal type "string") (read-string "Providing value: ")))) + (rst-insert-directive-option option value) + (newline-and-indent))) + +;--- directives --- + +(defun rst-insert-admonition () + "Insert a admonition." + (interactive) + (let (admon argu) + (setq admon (read-string "Providing admonition type: ")) + (setq argu (read-string "Providing admonition description: ")) + (rst-insert-directive-type admon argu))) + +(defun rst-insert-image () + "Insert a image." + (interactive) + (let (arg) + (setq arg (read-string "Providing image and its path: ")) + (rst-insert-directive-type "image" arg))) + +(defvar rst-option-image + '(("align" "option" ("top" "middle" "bottom" "left" "center" "right")) + ("width" "string" "300") + ("height" "string" "300") + ("scale" "number" 80) + ("alt" "string" "") + ("target" "string" ""))) + +(defun rst-insert-figure () + "Insert a image." + (interactive) + (let (arg) + (setq arg (read-string "Providing figure and its path: ")) + (rst-insert-directive-type "figure" arg))) + +(defvar rst-option-figure + '(("align" "option" ("left" "center" "right")) + ("width" "string" "300") + ("height" "string" "300") + ("scale" "number" 80) + ("figwidth" "string" "350") + ("alt" "string" "") + ("target" "string" ""))) + +(defun rst-insert-topic () + "Insert a topic." + (interactive) + (let (arg) + (setq arg (read-string "Providing topic title: ")) + (rst-insert-directive-type "topic" arg))) + +(defun rst-insert-sidebar () + "Insert a sidebar." + (interactive) + (let (arg) + (setq arg (read-string "Providing sidebar title: ")) + (rst-insert-directive-type "sidebar" arg))) + +(defvar rst-option-sidebar +'(("subtitle" "string" ""))) + +(defun rst-insert-line-block () + "Insert a line block." + (interactive) + (rst-insert-directive-type "line-block")) + +(defun rst-insert-parsed-literal () + "Insert a parsed literal." + (interactive) + (rst-insert-directive-type "parsed-literal")) + +(defun rst-insert-rubric () + "Insert a rubric title." + (interactive) + (let (arg) + (setq arg (read-string "Providing rubric title: ")) + (rst-insert-directive-type "rubric" arg))) + +(defun rst-insert-epigraph () + "Insert a epigraph." + (interactive) + (rst-insert-directive-type "epigraph")) + +(defun rst-insert-highlights () + "Insert a highlights." + (interactive) + (rst-insert-directive-type "highlights")) + +(defun rst-insert-pull-quote () + "Insert a pull quote." + (interactive) + (rst-insert-directive-type "pull-quote")) + +(defun rst-insert-compound () + "Insert a compound." + (interactive) + (rst-insert-directive-type "compound")) + +(defun rst-insert-container () + "Insert a container." + (interactive) + (let (arg) + (setq arg (read-string "Providing container title: ")) + (rst-insert-directive-type "container" arg))) + +(defun rst-insert-table () + "Insert a table." + (interactive) + (let (arg) + (setq arg (read-string "Providing table title: ")) + (rst-insert-directive-type "table" arg))) + +(defun rst-insert-csv-table () + "Insert a table." + (interactive) + (let (arg) + (setq arg (read-string "Providing table title: ")) + (rst-insert-directive-type "csv-table" arg))) + +(defvar rst-option-csv-table +'(("widths" "string" "") + ("header-rows" "number" 0) + ("stub-columns" "number" 0) + ("header" "string" "") + ("file" "string" "") + ("url" "string" "") + ("encoding" "string" "") + ("delim" "string" "") + ("quote" "string" "") + ("keepspace" "flag" nil) + ("escape" "string" ""))) + +(defun rst-insert-list-table () + "Insert a table." + (interactive) + (let (arg) + (setq arg (read-string "Providing table title: ")) + (rst-insert-directive-type "list-table" arg))) + +(defvar rst-option-list-table +'(("widths" "string" "") + ("header-rows" "number" 0) + ("stub-columns" "number" 0))) + +(defun rst-insert-contents () + "Insert a contents." + (interactive) + (let (arg) + (setq arg (read-string "Providing contents title: ")) + (rst-insert-directive-type "contents" arg))) + +(defvar rst-option-contents +'(("backlinks" "option" ("entry" "top" "none")) + ("depth" "number" 2) + ("local" "flag" nil))) + +(defun rst-insert-sectnum () + "Set section-autonumbering." + (interactive) + (rst-insert-directive-type "sectnum")) + +(defvar rst-option-sectnum +'(("prefix" "string" "") + ("suffix" "string" "") + ("depth" "number" 2) + ("start" "number" 1))) + +(defun rst-insert-replace () + "Insert the head of word replace." + (interactive) + (let (tag) + (setq tag (read-string "Providing replaced word: ")) + (rst-insert-directive-type (concat "|" tag "| replace")))) + +(defun rst-insert-unicode () + "Insert unicode replacement." + (interactive) + (let (tag) + (setq tag (read-string "Providing unicode string: ")) + (rst-insert-directive-type (concat "|" tag "| unicode")))) + +(defvar rst-option-unicode + '(("ltrim" "flag" nil) + ("rtrim" "flag" nil) + ("trim" "flag" nil))) + +(defun rst-insert-date () + "Insert date or time." + (interactive) + (let (type value) + (setq type (completing-read "Insert date or time? " '("date" "time"))) + (cond + ((equal type "date") + (setq value (read-string "Providing date format: " nil nil "%Y-%m-%d"))) + ((equal type "time") + (setq value (read-string "Providing time format: " nil nil "%H:%M")))) + (rst-insert-directive-type (concat "|" type "| date") value))) + +(defun rst-insert-include () + "Insert an external file." + (interactive) + (let (arg) + (setq arg (read-string "Providing file path: ")) + (rst-insert-directive-type "include" arg))) + +(defvar rst-option-include + '(("start-after" "string" "") + ("end-before" "string" "") + ("encoding" "string" "") + ("literal" "flag" nil))) + +(defun rst-insert-raw () + "Insert raw data." + (interactive) + (let (arg) + (setq arg (read-string "Providing raw data type: ")) + (rst-insert-directive-type "raw" arg))) + +(defvar rst-option-raw + '(("file" "string" "") + ("url" "string" "") + ("encoding" "string" ""))) + + +;==== links ==== + +;--- link inserting --- + +(defvar rst-link-type-alist + '(("hyperlink" . rst-insert-inline-link) + ("footnote" . rst-insert-footnote) + ("citation" . rst-insert-citation)) + "List of link types.") + +(defvar rst-link-type + '("hyperlink" "footnote" "citation") + "List of link types for completion") + +(defun rst-insert-link () + "Meta-function of all directives." + (interactive) + (let (type) + (setq type (completing-read "Providing link type: " rst-link-type)) + (funcall (cdr (assoc type rst-link-type-alist))))) + +(defun rst-insert-inline-link () + "Insert a inline link with both target and reference." + (interactive) + (let (link target reference) + (setq link (read-string "Providing link name: ")) + (if (string-match " " link) + (progn + (setq target (concat "`" link "`_")) + (setq reference (concat ".. _`" link "`: "))) + (progn + (setq target (concat link "_")) + (setq reference (concat ".. _" link ": ")))) + (save-excursion + (if (equal (char-before) (string-to-char " ")) + (insert target " ") + (insert (concat " " target " "))) + (end-of-line) + (insert (concat "\n\n" reference))))) + +(defun rst-insert-footnote () + "Insert a inline footnote with both target and reference." + (interactive) + (let (footnote target reference) + (setq footnote (read-string "Providing footnote name: ")) + (setq target (concat "[#" footnote "]_")) + (setq reference (concat ".. [#" footnote "] ")) + (save-excursion + (if (equal (char-before) (string-to-char " ")) + (insert target " ") + (insert (concat " " target " "))) + (end-of-line) + (insert (concat "\n\n" reference))))) + +(defun rst-insert-citation () + "Insert a inline citation with both target and reference." + (interactive) + (let (citation target reference) + (setq citation (read-string "Providing citation name: ")) + (setq target (concat "[" citation "]_")) + (setq reference (concat ".. [" citation "] ")) + (save-excursion + (if (equal (char-before) (string-to-char " ")) + (insert target " ") + (insert (concat " " target " "))) + (end-of-line) + (insert (concat "\n\n" reference))))) + +;--- link jumping --- + +(defun rst-hyper-link-reference-match () + "Match link target around the point." + (let (link reference target) + (save-excursion + (if (search-forward "_" + (save-excursion + (forward-sentence) + (point)) + t 1) + (progn + (backward-char) + (unless (equal (char-before) ?\\) + (if (equal (char-before) ?`) + (progn + (forward-char) + (re-search-backward "`[[:alnum:][:punct:][:space:]]+`_" + (save-excursion + (search-backward "`" nil t 2) + (point)) + t) + (setq reference (match-string 0)) + (setq link (substring reference 0 -1))) + (progn + (forward-char) + (re-search-backward " \\sw+_" + (save-excursion + (search-backward " ") + (point)) + t) + (setq reference (match-string 0)) + (setq link (substring reference 1 -1))))) + (setq target (concat ".. _" link ":"))))) + (list reference target))) + +(defun rst-link-jump-to-target () + "Jump from intertext link reference to link target." + (interactive) + (let ((target (elt (rst-hyper-link-reference-match) 1))) + (when (save-excursion + (goto-char (point-min)) + (search-forward target)) + (goto-char (point-min)) + (search-forward target)))) + +(defun rst-link-jump-within-references () + "Jump within references of an intertext link, if exist." + (interactive) + (let ((reference (elt (rst-hyper-link-reference-match) 0))) + (if (save-excursion + (search-forward reference nil t 1)) + (progn + (search-forward reference nil t 1) + (backward-char 1)) + (when (y-or-n-p "No link reference behind. Search from the beginning?") + (goto-char (point-min)) + (search-forward reference nil t 1) + (backward-char 1))))) + +(defun rst-hyper-link-target-match () + "Match link reference at the target line." + (let (link reference target) + (save-excursion + (when (search-backward ".. _" (line-beginning-position) t 1) + (re-search-forward + "^\\.\\. _`?[[:alnum:][:punct:][:space:]]+`?: " + (line-end-position) t 1) + (setq target (match-string 0)))) + (setq link (substring target 4 -2)) + (if (equal (substring link 0 1) "`") + (setq reference (concat link "_")) + (setq reference (concat " " link "_"))) + (list reference target))) + +(defun rst-link-jump-to-reference () + "Jump from intertext link target to link reference." + (interactive) + (let ((reference (elt (rst-hyper-link-target-match) 0))) + (when (save-excursion + (goto-char (point-max)) + (search-backward reference)) + (goto-char (point-max)) + (search-backward reference)))) + +;--- footnote and citation --- + +(defun rst-footnote-citation-reference-match () + "Match footnote or citation reference around the point." + (let (link reference target) + (save-excursion + (when (search-forward "]_" + (save-excursion + (forward-sentence) + (point)) + t 1) + (re-search-backward "\\[[[:alnum:][:punct:][:space:]]+\\]_" + (save-excursion + (search-backward "[" nil t 1) + (point)) + t) + (setq reference (match-string 0)))) + (setq link (substring reference 1 -2)) + (setq target (concat ".. [" link "]")) + (list reference target))) + +(defun rst-footnote-citation-target-match () + "Match footnote or citation reference at the target line." + (let (link reference target) + (save-excursion + (when (search-backward ".. [" (line-beginning-position) t 1) + (re-search-forward + "^\\.\\. \\[[[:alnum:][:punct:][:space:]]+\\] " + (line-end-position) t 1) + (setq target (match-string 0)))) + (setq link (substring target 4 -2)) + (setq reference (concat " [" link "]_")) + (list reference target))) + +(defun rst-footnote-citation-jump-to-target () + "Jump from footnote or citation reference to target." + (interactive) + (let ((target (elt (rst-footnote-citation-reference-match) 1))) + (when (save-excursion + (goto-char (point-min)) + (search-forward target)) + (goto-char (point-min)) + (search-forward target)))) + +(defun rst-footnote-citation-jump-within-references () + "Jump within references of an footnote or citation, if exist." + (interactive) + (let ((reference (elt (rst-footnote-citation-reference-match) 0))) + (if (save-excursion + (search-forward reference nil t 1)) + (progn + (search-forward reference nil t 1) + (backward-char 1)) + (when (y-or-n-p "No link reference behind. Search from the beginning?") + (goto-char (point-min)) + (search-forward reference nil t 1) + (backward-char 2))))) + +(defun rst-footnote-citation-jump-to-reference () + "Jump from footnote or citation target to reference." + (interactive) + (let ((reference (elt (rst-footnote-citation-target-match) 0))) + (when (save-excursion + (goto-char (point-max)) + (search-backward reference)) + (goto-char (point-max)) + (search-backward reference))))