declare smobs in alloc.c
[bpt/emacs.git] / lisp / textmodes / rst.el
CommitLineData
94e9c286
SM
1;;; rst.el --- Mode for viewing and editing reStructuredText-documents.
2
ba318903 3;; Copyright (C) 2003-2014 Free Software Foundation, Inc.
94e9c286 4
d13c8be6 5;; Maintainer: Stefan Merten <smerten@oekonux.de>
6d3f7c2f
SM
6;; Author: Stefan Merten <smerten@oekonux.de>,
7;; Martin Blais <blais@furius.ca>,
d13c8be6
SM
8;; David Goodger <goodger@python.org>,
9;; Wei-Wei Guo <wwguocn@gmail.com>
94e9c286
SM
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software: you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
25
26;;; Commentary:
27
d13c8be6 28;; This package provides major mode rst-mode, which supports documents marked
6d3f7c2f
SM
29;; up using the reStructuredText format. Support includes font locking as well
30;; as a lot of convenience functions for editing. It does this by defining a
31;; Emacs major mode: rst-mode (ReST). This mode is derived from text-mode.
32;; This package also contains:
94e9c286
SM
33;;
34;; - Functions to automatically adjust and cycle the section underline
d13c8be6 35;; adornments;
94e9c286
SM
36;; - A mode that displays the table of contents and allows you to jump anywhere
37;; from it;
38;; - Functions to insert and automatically update a TOC in your source
39;; document;
d13c8be6
SM
40;; - Function to insert list, processing item bullets and enumerations
41;; automatically;
42;; - Font-lock highlighting of most reStructuredText structures;
43;; - Indentation and filling according to reStructuredText syntax;
44;; - Cursor movement according to reStructuredText syntax;
94e9c286
SM
45;; - Some other convenience functions.
46;;
47;; See the accompanying document in the docutils documentation about
48;; the contents of this package and how to use it.
49;;
50;; For more information about reStructuredText, see
51;; http://docutils.sourceforge.net/rst.html
52;;
53;; For full details on how to use the contents of this file, see
54;; http://docutils.sourceforge.net/docs/user/emacs.html
55;;
56;;
6d3f7c2f 57;; There are a number of convenient key bindings provided by rst-mode.
b4747519 58;; For more on bindings, see rst-mode-map below. There are also many variables
d13c8be6 59;; that can be customized, look for defcustom in this file.
94e9c286
SM
60;;
61;; If you use the table-of-contents feature, you may want to add a hook to
6d3f7c2f 62;; update the TOC automatically every time you adjust a section title::
94e9c286
SM
63;;
64;; (add-hook 'rst-adjust-hook 'rst-toc-update)
65;;
b4747519
SM
66;; Syntax highlighting: font-lock is enabled by default. If you want to turn
67;; off syntax highlighting to rst-mode, you can use the following::
94e9c286
SM
68;;
69;; (setq font-lock-global-modes '(not rst-mode ...))
70;;
94e9c286 71;;
94e9c286 72;;
d13c8be6 73;; Customization is done by customizable variables contained in customization
6d3f7c2f 74;; group "rst" and subgroups. Group "rst" is contained in the "wp" group.
94e9c286 75;;
94e9c286
SM
76
77;;; DOWNLOAD
78
d13c8be6
SM
79;; The latest release of this file lies in the docutils source code repository:
80;; http://docutils.svn.sourceforge.net/svnroot/docutils/trunk/docutils/tools/editors/emacs/rst.el
94e9c286
SM
81
82;;; INSTALLATION
83
865fe16f 84;; Add the following lines to your init file:
94e9c286
SM
85;;
86;; (require 'rst)
87;;
88;; If you are using `.txt' as a standard extension for reST files as
89;; http://docutils.sourceforge.net/FAQ.html#what-s-the-standard-filename-extension-for-a-restructuredtext-file
90;; suggests you may use one of the `Local Variables in Files' mechanism Emacs
b4747519 91;; provides to set the major mode automatically. For instance you may use::
94e9c286
SM
92;;
93;; .. -*- mode: rst -*-
94;;
b4747519
SM
95;; in the very first line of your file. The following code is useful if you
96;; want automatically enter rst-mode from any file with compatible extensions:
94e9c286
SM
97;;
98;; (setq auto-mode-alist
6d3f7c2f
SM
99;; (append '(("\\.txt\\'" . rst-mode)
100;; ("\\.rst\\'" . rst-mode)
101;; ("\\.rest\\'" . rst-mode)) auto-mode-alist))
94e9c286
SM
102;;
103
d13c8be6 104;;; Code:
94e9c286 105
64f6a736
SM
106;; FIXME: Check through major mode conventions again.
107
1f45e27e
SM
108;; FIXME: Add proper ";;;###autoload" comments.
109
7ae2ea10
SM
110;; FIXME: When 24.1 is common place remove use of `lexical-let' and put "-*-
111;; lexical-binding: t -*-" in the first line.
112
64f6a736
SM
113;; FIXME: Use `testcover'.
114
ee97deee
SM
115;; FIXME: The adornment classification often called `ado' should be a
116;; `defstruct'.
117
64f6a736
SM
118;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119;; Support for `testcover'
120
70efc5c9
SM
121(when (and (boundp 'testcover-1value-functions)
122 (boundp 'testcover-compose-functions))
64f6a736
SM
123 ;; Below `lambda' is used in a loop with varying parameters and is thus not
124 ;; 1valued.
125 (setq testcover-1value-functions
126 (delq 'lambda testcover-1value-functions))
127 (add-to-list 'testcover-compose-functions 'lambda))
128
129(defun rst-testcover-defcustom ()
130 "Remove all customized variables from `testcover-module-constants'.
131This seems to be a bug in `testcover': `defcustom' variables are
132considered constants. Revert it with this function after each `defcustom'."
133 (when (boundp 'testcover-module-constants)
134 (setq testcover-module-constants
135 (delq nil
136 (mapcar
137 (lambda (sym)
138 (if (not (plist-member (symbol-plist sym) 'standard-value))
139 sym))
140 testcover-module-constants)))))
141
142(defun rst-testcover-add-compose (fun)
143 "Add FUN to `testcover-compose-functions'."
144 (when (boundp 'testcover-compose-functions)
145 (add-to-list 'testcover-compose-functions fun)))
146
147(defun rst-testcover-add-1value (fun)
148 "Add FUN to `testcover-1value-functions'."
149 (when (boundp 'testcover-1value-functions)
150 (add-to-list 'testcover-1value-functions fun)))
151
152;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
153;; Common Lisp stuff
154
7b4cdbf4
SM
155;; Only use of macros is allowed - may be replaced by `cl-lib' some time.
156(eval-when-compile
157 (require 'cl))
158
159;; Redefine some functions from `cl.el' in a proper namespace until they may be
160;; used from there.
161
162(defun rst-signum (x)
163 "Return 1 if X is positive, -1 if negative, 0 if zero."
164 (cond
165 ((> x 0) 1)
166 ((< x 0) -1)
167 (t 0)))
168
169(defun rst-some (seq &optional pred)
170 "Return non-nil if any element of SEQ yields non-nil when PRED is applied.
171Apply PRED to each element of list SEQ until the first non-nil
1f45e27e 172result is yielded and return this result. PRED defaults to
7b4cdbf4
SM
173`identity'."
174 (unless pred
175 (setq pred 'identity))
176 (catch 'rst-some
177 (dolist (elem seq)
178 (let ((r (funcall pred elem)))
179 (when r
180 (throw 'rst-some r))))))
181
182(defun rst-position-if (pred seq)
183 "Return position of first element satisfying PRED in list SEQ or nil."
184 (catch 'rst-position-if
185 (let ((i 0))
186 (dolist (elem seq)
187 (when (funcall pred elem)
188 (throw 'rst-position-if i))
189 (incf i)))))
190
191(defun rst-position (elem seq)
192 "Return position of ELEM in list SEQ or nil.
193Comparison done with `equal'."
194 ;; Create a closure containing `elem' so the `lambda' always sees our
195 ;; parameter instead of an `elem' which may be in dynamic scope at the time
196 ;; of execution of the `lambda'.
197 (lexical-let ((elem elem))
198 (rst-position-if (function (lambda (e)
199 (equal elem e)))
200 seq)))
94e9c286 201
7ae2ea10 202;; FIXME: Embed complicated `defconst's in `eval-when-compile'.
7b4cdbf4 203
d13c8be6
SM
204;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
205;; Versions
206
64f6a736 207;; testcover: ok.
d13c8be6 208(defun rst-extract-version (delim-re head-re re tail-re var &optional default)
6d3f7c2f
SM
209 "Extract the version from a variable according to the given regexes.
210Return the version after regex DELIM-RE and HEAD-RE matching RE
211and before TAIL-RE and DELIM-RE in VAR or DEFAULT for no match."
d13c8be6
SM
212 (if (string-match
213 (concat delim-re head-re "\\(" re "\\)" tail-re delim-re)
214 var)
215 (match-string 1 var)
216 default))
217
218;; Use CVSHeader to really get information from CVS and not other version
6d3f7c2f 219;; control systems.
d13c8be6 220(defconst rst-cvs-header
557337e9 221 "$CVSHeader: sm/rst_el/rst.el,v 1.327.2.6 2012-10-07 13:05:50 stefan Exp $")
d13c8be6
SM
222(defconst rst-cvs-rev
223 (rst-extract-version "\\$" "CVSHeader: \\S + " "[0-9]+\\(?:\\.[0-9]+\\)+"
224 " .*" rst-cvs-header "0.0")
6d3f7c2f 225 "The CVS revision of this file. CVS revision is the development revision.")
d13c8be6
SM
226(defconst rst-cvs-timestamp
227 (rst-extract-version "\\$" "CVSHeader: \\S + \\S + "
228 "[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+" " .*"
229 rst-cvs-header "1970-01-01 00:00:00")
6d3f7c2f 230 "The CVS time stamp of this file.")
d13c8be6 231
6d3f7c2f 232;; Use LastChanged... to really get information from SVN.
d13c8be6
SM
233(defconst rst-svn-rev
234 (rst-extract-version "\\$" "LastChangedRevision: " "[0-9]+" " "
48a24920 235 "$LastChangedRevision: 7515 $")
d13c8be6
SM
236 "The SVN revision of this file.
237SVN revision is the upstream (docutils) revision.")
238(defconst rst-svn-timestamp
239 (rst-extract-version "\\$" "LastChangedDate: " ".+?+" " "
48a24920 240 "$LastChangedDate: 2012-09-20 23:28:53 +0200 (Thu, 20 Sep 2012) $")
6d3f7c2f 241 "The SVN time stamp of this file.")
d13c8be6 242
6d3f7c2f 243;; Maintained by the release process.
d13c8be6
SM
244(defconst rst-official-version
245 (rst-extract-version "%" "OfficialVersion: " "[0-9]+\\(?:\\.[0-9]+\\)+" " "
48a24920 246 "%OfficialVersion: 1.4.0 %")
d13c8be6
SM
247 "Official version of the package.")
248(defconst rst-official-cvs-rev
249 (rst-extract-version "[%$]" "Revision: " "[0-9]+\\(?:\\.[0-9]+\\)+" " "
557337e9 250 "%Revision: 1.327 %")
d13c8be6
SM
251 "CVS revision of this file in the official version.")
252
253(defconst rst-version
254 (if (equal rst-official-cvs-rev rst-cvs-rev)
255 rst-official-version
256 (format "%s (development %s [%s])" rst-official-version
257 rst-cvs-rev rst-cvs-timestamp))
258 "The version string.
6d3f7c2f
SM
259Starts with the current official version. For developer versions
260in parentheses follows the development revision and the time stamp.")
d13c8be6
SM
261
262(defconst rst-package-emacs-version-alist
2a1e2476
GM
263 '(("1.0.0" . "24.3")
264 ("1.1.0" . "24.3")
265 ("1.2.0" . "24.3")
266 ("1.2.1" . "24.3")
267 ("1.3.0" . "24.3")
268 ("1.3.1" . "24.3")
48a24920 269 ("1.4.0" . "24.3")
1f45e27e 270 ))
d13c8be6
SM
271
272(unless (assoc rst-official-version rst-package-emacs-version-alist)
273 (error "Version %s not listed in `rst-package-emacs-version-alist'"
274 rst-version))
275
276(add-to-list 'customize-package-emacs-version-alist
277 (cons 'ReST rst-package-emacs-version-alist))
94e9c286 278
d13c8be6
SM
279;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
280;; Initialize customization
94e9c286
SM
281
282\f
92439579 283(defgroup rst nil "Support for reStructuredText documents."
94e9c286
SM
284 :group 'wp
285 :version "23.1"
286 :link '(url-link "http://docutils.sourceforge.net/rst.html"))
287
94e9c286
SM
288\f
289;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d13c8be6
SM
290;; Facilities for regular expressions used everywhere
291
292;; The trailing numbers in the names give the number of referenceable regex
6d3f7c2f 293;; groups contained in the regex.
d13c8be6
SM
294
295;; Used to be customizable but really is not customizable but fixed by the reST
6d3f7c2f 296;; syntax.
d13c8be6 297(defconst rst-bullets
6d3f7c2f 298 ;; Sorted so they can form a character class when concatenated.
d13c8be6
SM
299 '(?- ?* ?+ ?\u2022 ?\u2023 ?\u2043)
300 "List of all possible bullet characters for bulleted lists.")
301
302(defconst rst-uri-schemes
303 '("acap" "cid" "data" "dav" "fax" "file" "ftp" "gopher" "http" "https" "imap"
304 "ldap" "mailto" "mid" "modem" "news" "nfs" "nntp" "pop" "prospero" "rtsp"
305 "service" "sip" "tel" "telnet" "tip" "urn" "vemmi" "wais")
306 "Supported URI schemes.")
307
308(defconst rst-adornment-chars
6d3f7c2f 309 ;; Sorted so they can form a character class when concatenated.
d13c8be6
SM
310 '(?\]
311 ?! ?\" ?# ?$ ?% ?& ?' ?\( ?\) ?* ?+ ?, ?. ?/ ?: ?\; ?< ?= ?> ?? ?@ ?\[ ?\\
312 ?^ ?_ ?` ?{ ?| ?} ?~
313 ?-)
314 "Characters which may be used in adornments for sections and transitions.")
315
316(defconst rst-max-inline-length
317 1000
318 "Maximum length of inline markup to recognize.")
319
320(defconst rst-re-alist-def
6d3f7c2f
SM
321 ;; `*-beg' matches * at the beginning of a line.
322 ;; `*-end' matches * at the end of a line.
323 ;; `*-prt' matches a part of *.
324 ;; `*-tag' matches *.
325 ;; `*-sta' matches the start of * which may be followed by respective content.
326 ;; `*-pfx' matches the delimiter left of *.
327 ;; `*-sfx' matches the delimiter right of *.
328 ;; `*-hlp' helper for *.
d13c8be6
SM
329 ;;
330 ;; A trailing number says how many referenceable groups are contained.
331 `(
332
333 ;; Horizontal white space (`hws')
334 (hws-prt "[\t ]")
6d3f7c2f
SM
335 (hws-tag hws-prt "*") ; Optional sequence of horizontal white space.
336 (hws-sta hws-prt "+") ; Mandatory sequence of horizontal white space.
d13c8be6
SM
337
338 ;; Lines (`lin')
6d3f7c2f
SM
339 (lin-beg "^" hws-tag) ; Beginning of a possibly indented line.
340 (lin-end hws-tag "$") ; End of a line with optional trailing white space.
341 (linemp-tag "^" hws-tag "$") ; Empty line with optional white space.
d13c8be6
SM
342
343 ;; Various tags and parts
344 (ell-tag "\\.\\.\\.") ; Ellipsis
6d3f7c2f
SM
345 (bul-tag ,(concat "[" rst-bullets "]")) ; A bullet.
346 (ltr-tag "[a-zA-Z]") ; A letter enumerator tag.
347 (num-prt "[0-9]") ; A number enumerator part.
348 (num-tag num-prt "+") ; A number enumerator tag.
349 (rom-prt "[IVXLCDMivxlcdm]") ; A roman enumerator part.
350 (rom-tag rom-prt "+") ; A roman enumerator tag.
351 (aut-tag "#") ; An automatic enumerator tag.
352 (dcl-tag "::") ; Double colon.
d13c8be6
SM
353
354 ;; Block lead in (`bli')
355 (bli-sfx (:alt hws-sta "$")) ; Suffix of a block lead-in with *optional*
6d3f7c2f 356 ; immediate content.
d13c8be6
SM
357
358 ;; Various starts
6d3f7c2f 359 (bul-sta bul-tag bli-sfx) ; Start of a bulleted item.
d13c8be6
SM
360
361 ;; Explicit markup tag (`exm')
362 (exm-tag "\\.\\.")
363 (exm-sta exm-tag hws-sta)
364 (exm-beg lin-beg exm-sta)
365
366 ;; Counters in enumerations (`cnt')
6d3f7c2f
SM
367 (cntany-tag (:alt ltr-tag num-tag rom-tag aut-tag)) ; An arbitrary counter.
368 (cntexp-tag (:alt ltr-tag num-tag rom-tag)) ; An arbitrary explicit counter.
d13c8be6
SM
369
370 ;; Enumerator (`enm')
371 (enmany-tag (:alt
372 (:seq cntany-tag "\\.")
6d3f7c2f 373 (:seq "(?" cntany-tag ")"))) ; An arbitrary enumerator.
d13c8be6
SM
374 (enmexp-tag (:alt
375 (:seq cntexp-tag "\\.")
376 (:seq "(?" cntexp-tag ")"))) ; An arbitrary explicit
6d3f7c2f 377 ; enumerator.
d13c8be6
SM
378 (enmaut-tag (:alt
379 (:seq aut-tag "\\.")
6d3f7c2f
SM
380 (:seq "(?" aut-tag ")"))) ; An automatic enumerator.
381 (enmany-sta enmany-tag bli-sfx) ; An arbitrary enumerator start.
382 (enmexp-sta enmexp-tag bli-sfx) ; An arbitrary explicit enumerator start.
d13c8be6 383 (enmexp-beg lin-beg enmexp-sta) ; An arbitrary explicit enumerator start
6d3f7c2f 384 ; at the beginning of a line.
d13c8be6
SM
385
386 ;; Items may be enumerated or bulleted (`itm')
6d3f7c2f 387 (itmany-tag (:alt enmany-tag bul-tag)) ; An arbitrary item tag.
d13c8be6 388 (itmany-sta-1 (:grp itmany-tag) bli-sfx) ; An arbitrary item start, group
6d3f7c2f 389 ; is the item tag.
d13c8be6
SM
390 (itmany-beg-1 lin-beg itmany-sta-1) ; An arbitrary item start at the
391 ; beginning of a line, group is the
6d3f7c2f 392 ; item tag.
d13c8be6
SM
393
394 ;; Inline markup (`ilm')
395 (ilm-pfx (:alt "^" hws-prt "[-'\"([{<\u2018\u201c\u00ab\u2019/:]"))
396 (ilm-sfx (:alt "$" hws-prt "[]-'\")}>\u2019\u201d\u00bb/:.,;!?\\]"))
397
398 ;; Inline markup content (`ilc')
6d3f7c2f
SM
399 (ilcsgl-tag "\\S ") ; A single non-white character.
400 (ilcast-prt (:alt "[^*\\]" "\\\\.")) ; Part of non-asterisk content.
401 (ilcbkq-prt (:alt "[^`\\]" "\\\\.")) ; Part of non-backquote content.
d13c8be6 402 (ilcbkqdef-prt (:alt "[^`\\\n]" "\\\\.")) ; Part of non-backquote
6d3f7c2f
SM
403 ; definition.
404 (ilcbar-prt (:alt "[^|\\]" "\\\\.")) ; Part of non-vertical-bar content.
d13c8be6 405 (ilcbardef-prt (:alt "[^|\\\n]" "\\\\.")) ; Part of non-vertical-bar
6d3f7c2f
SM
406 ; definition.
407 (ilcast-sfx "[^\t *\\]") ; Suffix of non-asterisk content.
408 (ilcbkq-sfx "[^\t `\\]") ; Suffix of non-backquote content.
409 (ilcbar-sfx "[^\t |\\]") ; Suffix of non-vertical-bar content.
410 (ilcrep-hlp ,(format "\\{0,%d\\}" rst-max-inline-length)) ; Repeat count.
d13c8be6
SM
411 (ilcast-tag (:alt ilcsgl-tag
412 (:seq ilcsgl-tag
413 ilcast-prt ilcrep-hlp
6d3f7c2f 414 ilcast-sfx))) ; Non-asterisk content.
d13c8be6
SM
415 (ilcbkq-tag (:alt ilcsgl-tag
416 (:seq ilcsgl-tag
417 ilcbkq-prt ilcrep-hlp
6d3f7c2f 418 ilcbkq-sfx))) ; Non-backquote content.
d13c8be6
SM
419 (ilcbkqdef-tag (:alt ilcsgl-tag
420 (:seq ilcsgl-tag
421 ilcbkqdef-prt ilcrep-hlp
6d3f7c2f 422 ilcbkq-sfx))) ; Non-backquote definition.
d13c8be6
SM
423 (ilcbar-tag (:alt ilcsgl-tag
424 (:seq ilcsgl-tag
425 ilcbar-prt ilcrep-hlp
6d3f7c2f 426 ilcbar-sfx))) ; Non-vertical-bar content.
d13c8be6
SM
427 (ilcbardef-tag (:alt ilcsgl-tag
428 (:seq ilcsgl-tag
429 ilcbardef-prt ilcrep-hlp
6d3f7c2f 430 ilcbar-sfx))) ; Non-vertical-bar definition.
d13c8be6
SM
431
432 ;; Fields (`fld')
6d3f7c2f
SM
433 (fldnam-prt (:alt "[^:\n]" "\\\\:")) ; Part of a field name.
434 (fldnam-tag fldnam-prt "+") ; A field name.
435 (fld-tag ":" fldnam-tag ":") ; A field marker.
d13c8be6
SM
436
437 ;; Options (`opt')
6d3f7c2f
SM
438 (optsta-tag (:alt "[-+/]" "--")) ; Start of an option.
439 (optnam-tag "\\sw" (:alt "-" "\\sw") "*") ; Name of an option.
440 (optarg-tag (:shy "[ =]\\S +")) ; Option argument.
441 (optsep-tag (:shy "," hws-prt)) ; Separator between options.
442 (opt-tag (:shy optsta-tag optnam-tag optarg-tag "?")) ; A complete option.
d13c8be6
SM
443
444 ;; Footnotes and citations (`fnc')
6d3f7c2f
SM
445 (fncnam-prt "[^\]\n]") ; Part of a footnote or citation name.
446 (fncnam-tag fncnam-prt "+") ; A footnote or citation name.
447 (fnc-tag "\\[" fncnam-tag "]") ; A complete footnote or citation tag.
d13c8be6
SM
448 (fncdef-tag-2 (:grp exm-sta)
449 (:grp fnc-tag)) ; A complete footnote or citation definition
6d3f7c2f 450 ; tag. First group is the explicit markup
d13c8be6 451 ; start, second group is the footnote /
6d3f7c2f 452 ; citation tag.
d13c8be6 453 (fnc-sta-2 fncdef-tag-2 bli-sfx) ; Start of a footnote or citation
6d3f7c2f 454 ; definition. First group is the explicit
d13c8be6 455 ; markup start, second group is the
6d3f7c2f 456 ; footnote / citation tag.
d13c8be6
SM
457
458 ;; Substitutions (`sub')
6d3f7c2f 459 (sub-tag "|" ilcbar-tag "|") ; A complete substitution tag.
d13c8be6 460 (subdef-tag "|" ilcbardef-tag "|") ; A complete substitution definition
6d3f7c2f 461 ; tag.
d13c8be6
SM
462
463 ;; Symbol (`sym')
7ae2ea10
SM
464 (sym-prt "[-+.:_]") ; Non-word part of a symbol.
465 (sym-tag (:shy "\\sw+" (:shy sym-prt "\\sw+") "*"))
d13c8be6
SM
466
467 ;; URIs (`uri')
468 (uri-tag (:alt ,@rst-uri-schemes))
469
470 ;; Adornment (`ado')
471 (ado-prt "[" ,(concat rst-adornment-chars) "]")
472 (adorep3-hlp "\\{3,\\}") ; There must be at least 3 characters because
473 ; otherwise explicit markup start would be
6d3f7c2f 474 ; recognized.
d13c8be6 475 (adorep2-hlp "\\{2,\\}") ; As `adorep3-hlp' but when the first of three
6d3f7c2f 476 ; characters is matched differently.
d13c8be6
SM
477 (ado-tag-1-1 (:grp ado-prt)
478 "\\1" adorep2-hlp) ; A complete adornment, group is the first
479 ; adornment character and MUST be the FIRST
6d3f7c2f 480 ; group in the whole expression.
d13c8be6
SM
481 (ado-tag-1-2 (:grp ado-prt)
482 "\\2" adorep2-hlp) ; A complete adornment, group is the first
483 ; adornment character and MUST be the
6d3f7c2f 484 ; SECOND group in the whole expression.
d13c8be6
SM
485 (ado-beg-2-1 "^" (:grp ado-tag-1-2)
486 lin-end) ; A complete adornment line; first group is the whole
487 ; adornment and MUST be the FIRST group in the whole
488 ; expression; second group is the first adornment
6d3f7c2f 489 ; character.
d13c8be6
SM
490
491 ;; Titles (`ttl')
6d3f7c2f
SM
492 (ttl-tag "\\S *\\w\\S *") ; A title text.
493 (ttl-beg lin-beg ttl-tag) ; A title text at the beginning of a line.
d13c8be6
SM
494
495 ;; Directives and substitution definitions (`dir')
496 (dir-tag-3 (:grp exm-sta)
497 (:grp (:shy subdef-tag hws-sta) "?")
498 (:grp sym-tag dcl-tag)) ; A directive or substitution definition
6d3f7c2f 499 ; tag. First group is explicit markup
d13c8be6
SM
500 ; start, second group is a possibly
501 ; empty substitution tag, third group is
502 ; the directive tag including the double
6d3f7c2f 503 ; colon.
d13c8be6 504 (dir-sta-3 dir-tag-3 bli-sfx) ; Start of a directive or substitution
6d3f7c2f 505 ; definition. Groups are as in dir-tag-3.
d13c8be6
SM
506
507 ;; Literal block (`lit')
508 (lit-sta-2 (:grp (:alt "[^.\n]" "\\.[^.\n]") ".*") "?"
6d3f7c2f 509 (:grp dcl-tag) "$") ; Start of a literal block. First group is
d13c8be6
SM
510 ; any text before the double colon tag which
511 ; may not exist, second group is the double
6d3f7c2f 512 ; colon tag.
d13c8be6
SM
513
514 ;; Comments (`cmt')
515 (cmt-sta-1 (:grp exm-sta) "[^\[|_\n]"
516 (:alt "[^:\n]" (:seq ":" (:alt "[^:\n]" "$")))
517 "*$") ; Start of a comment block; first group is explicit markup
6d3f7c2f 518 ; start.
d13c8be6
SM
519
520 ;; Paragraphs (`par')
521 (par-tag- (:alt itmany-tag fld-tag opt-tag fncdef-tag-2 dir-tag-3 exm-tag)
522 ) ; Tag at the beginning of a paragraph; there may be groups in
6d3f7c2f 523 ; certain cases.
d13c8be6
SM
524 )
525 "Definition alist of relevant regexes.
526Each entry consists of the symbol naming the regex and an
527argument list for `rst-re'.")
528
7b4cdbf4
SM
529(defvar rst-re-alist) ; Forward declare to use it in `rst-re'.
530
6d3f7c2f 531;; FIXME: Use `sregex` or `rx` instead of re-inventing the wheel.
64f6a736
SM
532(rst-testcover-add-compose 'rst-re)
533;; testcover: ok.
d13c8be6
SM
534(defun rst-re (&rest args)
535 "Interpret ARGS as regular expressions and return a regex string.
536Each element of ARGS may be one of the following:
537
538A string which is inserted unchanged.
539
540A character which is resolved to a quoted regex.
541
542A symbol which is resolved to a string using `rst-re-alist-def'.
543
6d3f7c2f
SM
544A list with a keyword in the car. Each element of the cdr of such
545a list is recursively interpreted as ARGS. The results of this
d13c8be6
SM
546interpretation are concatenated according to the keyword.
547
548For the keyword `:seq' the results are simply concatenated.
549
550For the keyword `:shy' the results are concatenated and
551surrounded by a shy-group (\"\\(?:...\\)\").
552
553For the keyword `:alt' the results form an alternative (\"\\|\")
554which is shy-grouped (\"\\(?:...\\)\").
555
556For the keyword `:grp' the results are concatenated and form a
6d3f7c2f 557referenceable group (\"\\(...\\)\").
d13c8be6
SM
558
559After interpretation of ARGS the results are concatenated as for
6d3f7c2f 560`:seq'."
d13c8be6
SM
561 (apply 'concat
562 (mapcar
563 (lambda (re)
564 (cond
565 ((stringp re)
566 re)
567 ((symbolp re)
568 (cadr (assoc re rst-re-alist)))
8f6b6da8 569 ((characterp re)
d13c8be6
SM
570 (regexp-quote (char-to-string re)))
571 ((listp re)
572 (let ((nested
573 (mapcar (lambda (elt)
574 (rst-re elt))
575 (cdr re))))
576 (cond
577 ((eq (car re) :seq)
578 (mapconcat 'identity nested ""))
579 ((eq (car re) :shy)
580 (concat "\\(?:" (mapconcat 'identity nested "") "\\)"))
581 ((eq (car re) :grp)
582 (concat "\\(" (mapconcat 'identity nested "") "\\)"))
583 ((eq (car re) :alt)
584 (concat "\\(?:" (mapconcat 'identity nested "\\|") "\\)"))
585 (t
586 (error "Unknown list car: %s" (car re))))))
587 (t
588 (error "Unknown object type for building regex: %s" re))))
589 args)))
590
51fa99f1 591;; FIXME: Remove circular dependency between `rst-re' and `rst-re-alist'.
7b4cdbf4
SM
592(with-no-warnings ; Silence byte-compiler about this construction.
593 (defconst rst-re-alist
594 ;; Shadow global value we are just defining so we can construct it step by
595 ;; step.
596 (let (rst-re-alist)
597 (dolist (re rst-re-alist-def rst-re-alist)
598 (setq rst-re-alist
599 (nconc rst-re-alist
600 (list (list (car re) (apply 'rst-re (cdr re))))))))
601 "Alist mapping symbols from `rst-re-alist-def' to regex strings."))
51fa99f1 602
94e9c286
SM
603\f
604;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6d3f7c2f 605;; Mode definition
d13c8be6 606
64f6a736 607;; testcover: ok.
d13c8be6 608(defun rst-define-key (keymap key def &rest deprecated)
6d3f7c2f
SM
609 "Bind like `define-key' but add deprecated key definitions.
610KEYMAP, KEY, and DEF are as in `define-key'. DEPRECATED key
57348c4d
JB
611definitions should be in vector notation. These are defined
612as well but give an additional message."
d13c8be6 613 (define-key keymap key def)
c176054f
DC
614 (when deprecated
615 (let* ((command-name (symbol-name def))
616 (forwarder-function-name
617 (if (string-match "^rst-\\(.*\\)$" command-name)
618 (concat "rst-deprecated-"
619 (match-string 1 command-name))
620 (error "not an RST command: %s" command-name)))
621 (forwarder-function (intern forwarder-function-name)))
622 (unless (fboundp forwarder-function)
623 (defalias forwarder-function
624 (lexical-let ((key key) (def def))
625 (lambda ()
626 (interactive)
627 (call-interactively def)
628 (message "[Deprecated use of key %s; use key %s instead]"
629 (key-description (this-command-keys))
630 (key-description key))))
631 (format "Deprecated binding for %s, use \\[%s] instead."
632 def def)))
633 (dolist (dep-key deprecated)
634 (define-key keymap dep-key forwarder-function)))))
635 ;; Key bindings.
94e9c286
SM
636(defvar rst-mode-map
637 (let ((map (make-sparse-keymap)))
638
6d3f7c2f 639 ;; \C-c is the general keymap.
d13c8be6
SM
640 (rst-define-key map [?\C-c ?\C-h] 'describe-prefix-bindings)
641
94e9c286 642 ;;
6d3f7c2f 643 ;; Section Adornments
94e9c286 644 ;;
d13c8be6
SM
645 ;; The adjustment function that adorns or rotates a section title.
646 (rst-define-key map [?\C-c ?\C-=] 'rst-adjust [?\C-c ?\C-a t])
1f45e27e
SM
647 (rst-define-key map [?\C-=] 'rst-adjust) ; Does not work on the Mac OSX and
648 ; on consoles.
d13c8be6 649
6d3f7c2f 650 ;; \C-c \C-a is the keymap for adornments.
d13c8be6 651 (rst-define-key map [?\C-c ?\C-a ?\C-h] 'describe-prefix-bindings)
1f45e27e
SM
652 ;; Another binding which works with all types of input.
653 (rst-define-key map [?\C-c ?\C-a ?\C-a] 'rst-adjust)
6d3f7c2f
SM
654 ;; Display the hierarchy of adornments implied by the current document
655 ;; contents.
d13c8be6
SM
656 (rst-define-key map [?\C-c ?\C-a ?\C-d] 'rst-display-adornments-hierarchy)
657 ;; Homogenize the adornments in the document.
658 (rst-define-key map [?\C-c ?\C-a ?\C-s] 'rst-straighten-adornments
659 [?\C-c ?\C-s])
94e9c286
SM
660
661 ;;
6d3f7c2f 662 ;; Section Movement and Selection
94e9c286
SM
663 ;;
664 ;; Mark the subsection where the cursor is.
d13c8be6 665 (rst-define-key map [?\C-\M-h] 'rst-mark-section
6d3f7c2f 666 ;; Same as mark-defun sgml-mark-current-element.
d13c8be6 667 [?\C-c ?\C-m])
4cf9b38d 668 ;; Move backward/forward between section titles.
7ae2ea10 669 ;; FIXME: Also bind similar to outline mode.
4cf9b38d 670 (rst-define-key map [?\C-\M-a] 'rst-backward-section
6d3f7c2f 671 ;; Same as beginning-of-defun.
d13c8be6 672 [?\C-c ?\C-n])
4cf9b38d 673 (rst-define-key map [?\C-\M-e] 'rst-forward-section
6d3f7c2f 674 ;; Same as end-of-defun.
d13c8be6 675 [?\C-c ?\C-p])
94e9c286
SM
676
677 ;;
6d3f7c2f 678 ;; Operating on regions
94e9c286 679 ;;
6d3f7c2f 680 ;; \C-c \C-r is the keymap for regions.
d13c8be6
SM
681 (rst-define-key map [?\C-c ?\C-r ?\C-h] 'describe-prefix-bindings)
682 ;; Makes region a line-block.
683 (rst-define-key map [?\C-c ?\C-r ?\C-l] 'rst-line-block-region
684 [?\C-c ?\C-d])
6d3f7c2f 685 ;; Shift region left or right according to tabs.
d13c8be6
SM
686 (rst-define-key map [?\C-c ?\C-r tab] 'rst-shift-region
687 [?\C-c ?\C-r t] [?\C-c ?\C-l t])
688
689 ;;
6d3f7c2f 690 ;; Operating on lists
d13c8be6 691 ;;
6d3f7c2f 692 ;; \C-c \C-l is the keymap for lists.
d13c8be6 693 (rst-define-key map [?\C-c ?\C-l ?\C-h] 'describe-prefix-bindings)
94e9c286 694 ;; Makes paragraphs in region as a bullet list.
d13c8be6
SM
695 (rst-define-key map [?\C-c ?\C-l ?\C-b] 'rst-bullet-list-region
696 [?\C-c ?\C-b])
94e9c286 697 ;; Makes paragraphs in region as a enumeration.
d13c8be6
SM
698 (rst-define-key map [?\C-c ?\C-l ?\C-e] 'rst-enumerate-region
699 [?\C-c ?\C-e])
94e9c286 700 ;; Converts bullets to an enumeration.
d13c8be6
SM
701 (rst-define-key map [?\C-c ?\C-l ?\C-c] 'rst-convert-bullets-to-enumeration
702 [?\C-c ?\C-v])
94e9c286 703 ;; Make sure that all the bullets in the region are consistent.
d13c8be6
SM
704 (rst-define-key map [?\C-c ?\C-l ?\C-s] 'rst-straighten-bullets-region
705 [?\C-c ?\C-w])
6d3f7c2f 706 ;; Insert a list item.
d13c8be6 707 (rst-define-key map [?\C-c ?\C-l ?\C-i] 'rst-insert-list)
94e9c286
SM
708
709 ;;
6d3f7c2f 710 ;; Table-of-Contents Features
94e9c286 711 ;;
6d3f7c2f 712 ;; \C-c \C-t is the keymap for table of contents.
d13c8be6 713 (rst-define-key map [?\C-c ?\C-t ?\C-h] 'describe-prefix-bindings)
94e9c286 714 ;; Enter a TOC buffer to view and move to a specific section.
d13c8be6 715 (rst-define-key map [?\C-c ?\C-t ?\C-t] 'rst-toc)
94e9c286 716 ;; Insert a TOC here.
d13c8be6
SM
717 (rst-define-key map [?\C-c ?\C-t ?\C-i] 'rst-toc-insert
718 [?\C-c ?\C-i])
94e9c286 719 ;; Update the document's TOC (without changing the cursor position).
d13c8be6
SM
720 (rst-define-key map [?\C-c ?\C-t ?\C-u] 'rst-toc-update
721 [?\C-c ?\C-u])
6d3f7c2f 722 ;; Go to the section under the cursor (cursor must be in TOC).
d13c8be6
SM
723 (rst-define-key map [?\C-c ?\C-t ?\C-j] 'rst-goto-section
724 [?\C-c ?\C-f])
94e9c286
SM
725
726 ;;
6d3f7c2f 727 ;; Converting Documents from Emacs
94e9c286 728 ;;
6d3f7c2f 729 ;; \C-c \C-c is the keymap for compilation.
d13c8be6 730 (rst-define-key map [?\C-c ?\C-c ?\C-h] 'describe-prefix-bindings)
94e9c286 731 ;; Run one of two pre-configured toolset commands on the document.
d13c8be6
SM
732 (rst-define-key map [?\C-c ?\C-c ?\C-c] 'rst-compile
733 [?\C-c ?1])
734 (rst-define-key map [?\C-c ?\C-c ?\C-a] 'rst-compile-alt-toolset
735 [?\C-c ?2])
94e9c286 736 ;; Convert the active region to pseudo-xml using the docutils tools.
d13c8be6
SM
737 (rst-define-key map [?\C-c ?\C-c ?\C-x] 'rst-compile-pseudo-region
738 [?\C-c ?3])
94e9c286 739 ;; Convert the current document to PDF and launch a viewer on the results.
d13c8be6
SM
740 (rst-define-key map [?\C-c ?\C-c ?\C-p] 'rst-compile-pdf-preview
741 [?\C-c ?4])
94e9c286 742 ;; Convert the current document to S5 slides and view in a web browser.
d13c8be6
SM
743 (rst-define-key map [?\C-c ?\C-c ?\C-s] 'rst-compile-slides-preview
744 [?\C-c ?5])
94e9c286
SM
745
746 map)
e6438428 747 "Keymap for reStructuredText mode commands.
b4747519 748This inherits from Text mode.")
94e9c286
SM
749
750
751;; Abbrevs.
94e9c286 752(define-abbrev-table 'rst-mode-abbrev-table
32845226
SM
753 (mapcar (lambda (x) (append x '(nil 0 system)))
754 '(("contents" ".. contents::\n..\n ")
755 ("con" ".. contents::\n..\n ")
756 ("cont" "[...]")
757 ("skip" "\n\n[...]\n\n ")
758 ("seq" "\n\n[...]\n\n ")
759 ;; FIXME: Add footnotes, links, and more.
6d3f7c2f
SM
760 ))
761 "Abbrev table used while in `rst-mode'.")
94e9c286
SM
762
763
764;; Syntax table.
765(defvar rst-mode-syntax-table
766 (let ((st (copy-syntax-table text-mode-syntax-table)))
94e9c286
SM
767 (modify-syntax-entry ?$ "." st)
768 (modify-syntax-entry ?% "." st)
769 (modify-syntax-entry ?& "." st)
770 (modify-syntax-entry ?' "." st)
771 (modify-syntax-entry ?* "." st)
7ae2ea10
SM
772 (modify-syntax-entry ?+ "." st)
773 (modify-syntax-entry ?- "." st)
94e9c286
SM
774 (modify-syntax-entry ?/ "." st)
775 (modify-syntax-entry ?< "." st)
776 (modify-syntax-entry ?= "." st)
777 (modify-syntax-entry ?> "." st)
778 (modify-syntax-entry ?\\ "\\" st)
7ae2ea10 779 (modify-syntax-entry ?_ "." st)
94e9c286 780 (modify-syntax-entry ?| "." st)
d13c8be6
SM
781 (modify-syntax-entry ?\u00ab "." st)
782 (modify-syntax-entry ?\u00bb "." st)
783 (modify-syntax-entry ?\u2018 "." st)
784 (modify-syntax-entry ?\u2019 "." st)
785 (modify-syntax-entry ?\u201c "." st)
786 (modify-syntax-entry ?\u201d "." st)
94e9c286
SM
787 st)
788 "Syntax table used while in `rst-mode'.")
789
94e9c286 790(defcustom rst-mode-hook nil
d13c8be6
SM
791 "Hook run when `rst-mode' is turned on.
792The hook for `text-mode' is run before this one."
94e9c286
SM
793 :group 'rst
794 :type '(hook))
64f6a736 795(rst-testcover-defcustom)
94e9c286 796
7b4cdbf4
SM
797;; Pull in variable definitions silencing byte-compiler.
798(require 'newcomment)
94e9c286 799
4f5fa755
SM
800(defvar electric-pair-pairs)
801
0667a132
SM
802;; Use rst-mode for *.rst and *.rest files. Many ReStructured-Text files
803;; use *.txt, but this is too generic to be set as a default.
1e8780b1 804;;;###autoload (add-to-list 'auto-mode-alist (purecopy '("\\.re?st\\'" . rst-mode)))
94e9c286
SM
805;;;###autoload
806(define-derived-mode rst-mode text-mode "ReST"
94e9c286 807 "Major mode for editing reStructuredText documents.
e6438428 808\\<rst-mode-map>
94e9c286 809
e6438428
JB
810Turning on `rst-mode' calls the normal hooks `text-mode-hook'
811and `rst-mode-hook'. This mode also supports font-lock
d13c8be6 812highlighting.
e6438428
JB
813
814\\{rst-mode-map}"
536db356
JPW
815 :abbrev-table rst-mode-abbrev-table
816 :syntax-table rst-mode-syntax-table
817 :group 'rst
94e9c286 818
6d3f7c2f 819 ;; Paragraph recognition.
d13c8be6
SM
820 (set (make-local-variable 'paragraph-separate)
821 (rst-re '(:alt
822 "\f"
823 lin-end)))
94e9c286 824 (set (make-local-variable 'paragraph-start)
d13c8be6
SM
825 (rst-re '(:alt
826 "\f"
827 lin-end
828 (:seq hws-tag par-tag- bli-sfx))))
94e9c286 829
6d3f7c2f 830 ;; Indenting and filling.
d13c8be6
SM
831 (set (make-local-variable 'indent-line-function) 'rst-indent-line)
832 (set (make-local-variable 'adaptive-fill-mode) t)
833 (set (make-local-variable 'adaptive-fill-regexp)
834 (rst-re 'hws-tag 'par-tag- "?" 'hws-tag))
835 (set (make-local-variable 'adaptive-fill-function) 'rst-adaptive-fill)
836 (set (make-local-variable 'fill-paragraph-handle-comment) nil)
94e9c286 837
6d3f7c2f 838 ;; Comments.
94e9c286 839 (set (make-local-variable 'comment-start) ".. ")
d13c8be6
SM
840 (set (make-local-variable 'comment-start-skip)
841 (rst-re 'lin-beg 'exm-tag 'bli-sfx))
842 (set (make-local-variable 'comment-continue) " ")
843 (set (make-local-variable 'comment-multi-line) t)
844 (set (make-local-variable 'comment-use-syntax) nil)
845 ;; reStructuredText has not really a comment ender but nil is not really a
6d3f7c2f 846 ;; permissible value.
d13c8be6
SM
847 (set (make-local-variable 'comment-end) "")
848 (set (make-local-variable 'comment-end-skip) nil)
849
6d3f7c2f
SM
850 ;; Commenting in reStructuredText is very special so use our own set of
851 ;; functions.
d13c8be6
SM
852 (set (make-local-variable 'comment-line-break-function)
853 'rst-comment-line-break)
854 (set (make-local-variable 'comment-indent-function)
855 'rst-comment-indent)
856 (set (make-local-variable 'comment-insert-comment-function)
857 'rst-comment-insert-comment)
858 (set (make-local-variable 'comment-region-function)
859 'rst-comment-region)
860 (set (make-local-variable 'uncomment-region-function)
861 'rst-uncomment-region)
94e9c286 862
4f5fa755
SM
863 (set (make-local-variable 'electric-pair-pairs)
864 '((?\" . ?\") (?\* . ?\*) (?\` . ?\`)))
865
ee97deee
SM
866 ;; Imenu and which function.
867 ;; FIXME: Check documentation of `which-function' for alternative ways to
868 ;; determine the current function name.
869 (set (make-local-variable 'imenu-create-index-function)
870 'rst-imenu-create-index)
871
6d3f7c2f
SM
872 ;; Font lock.
873 (set (make-local-variable 'font-lock-defaults)
874 '(rst-font-lock-keywords
875 t nil nil nil
876 (font-lock-multiline . t)
877 (font-lock-mark-block-function . mark-paragraph)))
d13c8be6
SM
878 (add-hook 'font-lock-extend-region-functions 'rst-font-lock-extend-region t)
879
6d3f7c2f 880 ;; Text after a changed line may need new fontification.
b82ace2f
DC
881 (set (make-local-variable 'jit-lock-contextually) t)
882
883 ;; Indentation is not deterministic.
884 (setq electric-indent-inhibit t))
94e9c286
SM
885
886;;;###autoload
887(define-minor-mode rst-minor-mode
ac6c8639
CY
888 "Toggle ReST minor mode.
889With a prefix argument ARG, enable ReST minor mode if ARG is
890positive, and disable it otherwise. If called from Lisp, enable
891the mode if ARG is omitted or nil.
94e9c286 892
92439579
JB
893When ReST minor mode is enabled, the ReST mode keybindings
894are installed on top of the major mode bindings. Use this
895for modes derived from Text mode, like Mail mode."
94e9c286
SM
896 ;; The initial value.
897 nil
898 ;; The indicator for the mode line.
899 " ReST"
900 ;; The minor mode bindings.
901 rst-mode-map
902 :group 'rst)
903
904;; FIXME: can I somehow install these too?
6d3f7c2f
SM
905;; :abbrev-table rst-mode-abbrev-table
906;; :syntax-table rst-mode-syntax-table
94e9c286 907
94e9c286
SM
908\f
909;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d13c8be6
SM
910;; Section Adornment Adjustment
911;; ============================
94e9c286
SM
912;;
913;; The following functions implement a smart automatic title sectioning feature.
914;; The idea is that with the cursor sitting on a section title, we try to get as
915;; much information from context and try to do the best thing automatically.
916;; This function can be invoked many times and/or with prefix argument to rotate
d13c8be6 917;; between the various sectioning adornments.
94e9c286
SM
918;;
919;; Definitions: the two forms of sectioning define semantically separate section
d13c8be6 920;; levels. A sectioning ADORNMENT consists in:
94e9c286
SM
921;;
922;; - a CHARACTER
923;;
924;; - a STYLE which can be either of 'simple' or 'over-and-under'.
925;;
926;; - an INDENT (meaningful for the over-and-under style only) which determines
927;; how many characters and over-and-under style is hanging outside of the
928;; title at the beginning and ending.
929;;
d13c8be6 930;; Here are two examples of adornments (| represents the window border, column
94e9c286
SM
931;; 0):
932;;
933;; |
934;; 1. char: '-' e |Some Title
935;; style: simple |----------
936;; |
937;; 2. char: '=' |==============
938;; style: over-and-under | Some Title
939;; indent: 2 |==============
940;; |
941;;
942;; Some notes:
943;;
944;; - The underlining character that is used depends on context. The file is
945;; scanned to find other sections and an appropriate character is selected.
946;; If the function is invoked on a section that is complete, the character is
d13c8be6 947;; rotated among the existing section adornments.
94e9c286
SM
948;;
949;; Note that when rotating the characters, if we come to the end of the
d13c8be6
SM
950;; hierarchy of adornments, the variable rst-preferred-adornments is
951;; consulted to propose a new underline adornment, and if continued, we cycle
952;; the adornments all over again. Set this variable to nil if you want to
953;; limit the underlining character propositions to the existing adornments in
94e9c286
SM
954;; the file.
955;;
94e9c286
SM
956;; - An underline/overline that is not extended to the column at which it should
957;; be hanging is dubbed INCOMPLETE. For example::
958;;
959;; |Some Title
960;; |-------
961;;
962;; Examples of default invocation:
963;;
964;; |Some Title ---> |Some Title
965;; | |----------
966;;
967;; |Some Title ---> |Some Title
968;; |----- |----------
969;;
970;; | |------------
971;; | Some Title ---> | Some Title
972;; | |------------
973;;
974;; In over-and-under style, when alternating the style, a variable is
975;; available to select how much default indent to use (it can be zero). Note
d13c8be6 976;; that if the current section adornment already has an indent, we don't
94e9c286
SM
977;; adjust it to the default, we rather use the current indent that is already
978;; there for adjustment (unless we cycle, in which case we use the indent
979;; that has been found previously).
980
981(defgroup rst-adjust nil
d13c8be6 982 "Settings for adjustment and cycling of section title adornments."
94e9c286
SM
983 :group 'rst
984 :version "21.1")
985
d13c8be6 986(define-obsolete-variable-alias
18dec750 987 'rst-preferred-decorations 'rst-preferred-adornments "rst 1.0.0")
d13c8be6
SM
988(defcustom rst-preferred-adornments '((?= over-and-under 1)
989 (?= simple 0)
990 (?- simple 0)
991 (?~ simple 0)
992 (?+ simple 0)
993 (?` simple 0)
994 (?# simple 0)
995 (?@ simple 0))
996 "Preferred hierarchy of section title adornments.
997
998A list consisting of lists of the form (CHARACTER STYLE INDENT).
6d3f7c2f 999CHARACTER is the character used. STYLE is one of the symbols
57348c4d
JB
1000`over-and-under' or `simple'. INDENT is an integer giving the
1001wanted indentation for STYLE `over-and-under'. CHARACTER and
1002STYLE are always used when a section adornment is described.
1003In other places, t instead of a list stands for a transition.
d13c8be6
SM
1004
1005This sequence is consulted to offer a new adornment suggestion
94e9c286
SM
1006when we rotate the underlines at the end of the existing
1007hierarchy of characters, or when there is no existing section
d13c8be6
SM
1008title in the file.
1009
1010Set this to an empty list to use only the adornment found in the
1011file."
1012 :group 'rst-adjust
1013 :type `(repeat
1014 (group :tag "Adornment specification"
1015 (choice :tag "Adornment character"
1016 ,@(mapcar (lambda (char)
1017 (list 'const
1018 :tag (char-to-string char) char))
1019 rst-adornment-chars))
1020 (radio :tag "Adornment type"
1021 (const :tag "Overline and underline" over-and-under)
1022 (const :tag "Underline only" simple))
1023 (integer :tag "Indentation for overline and underline type"
1024 :value 0))))
64f6a736 1025(rst-testcover-defcustom)
94e9c286
SM
1026
1027(defcustom rst-default-indent 1
1028 "Number of characters to indent the section title.
1029
d13c8be6
SM
1030This is used for when toggling adornment styles, when switching
1031from a simple adornment style to a over-and-under adornment
94e9c286 1032style."
d13c8be6
SM
1033 :group 'rst-adjust
1034 :type '(integer))
64f6a736 1035(rst-testcover-defcustom)
94e9c286 1036
d13c8be6
SM
1037(defun rst-compare-adornments (ado1 ado2)
1038 "Compare adornments.
1039Return true if both ADO1 and ADO2 adornments are equal,
57348c4d
JB
1040according to restructured text semantics (only the character
1041and the style are compared, the indentation does not matter)."
d13c8be6
SM
1042 (and (eq (car ado1) (car ado2))
1043 (eq (cadr ado1) (cadr ado2))))
94e9c286
SM
1044
1045
d13c8be6
SM
1046(defun rst-get-adornment-match (hier ado)
1047 "Return the index (level) in hierarchy HIER of adornment ADO.
94e9c286 1048This basically just searches for the item using the appropriate
92439579 1049comparison and returns the index. Return nil if the item is
94e9c286
SM
1050not found."
1051 (let ((cur hier))
d13c8be6 1052 (while (and cur (not (rst-compare-adornments (car cur) ado)))
94e9c286
SM
1053 (setq cur (cdr cur)))
1054 cur))
1055
64f6a736
SM
1056;; testcover: FIXME: Test with `rst-preferred-adornments' == nil. Add test
1057;; `rst-adjust-no-preference'.
d13c8be6
SM
1058(defun rst-suggest-new-adornment (allados &optional prev)
1059 "Suggest a new, different adornment from all that have been seen.
94e9c286 1060
d13c8be6
SM
1061ALLADOS is the set of all adornments, including the line numbers.
1062PREV is the optional previous adornment, in order to suggest a
92439579 1063better match."
94e9c286 1064
d13c8be6 1065 ;; For all the preferred adornments...
94e9c286
SM
1066 (let* (
1067 ;; If 'prev' is given, reorder the list to start searching after the
1068 ;; match.
1069 (fplist
d13c8be6 1070 (cdr (rst-get-adornment-match rst-preferred-adornments prev)))
94e9c286
SM
1071
1072 ;; List of candidates to search.
d13c8be6 1073 (curpotential (append fplist rst-preferred-adornments)))
94e9c286 1074 (while
d13c8be6
SM
1075 ;; For all the adornments...
1076 (let ((cur allados)
94e9c286
SM
1077 found)
1078 (while (and cur (not found))
d13c8be6 1079 (if (rst-compare-adornments (car cur) (car curpotential))
94e9c286
SM
1080 ;; Found it!
1081 (setq found (car curpotential))
1082 (setq cur (cdr cur))))
1083 found)
1084
1085 (setq curpotential (cdr curpotential)))
1086
b4747519 1087 (copy-sequence (car curpotential))))
94e9c286
SM
1088
1089(defun rst-delete-entire-line ()
1090 "Delete the entire current line without using the `kill-ring'."
b4747519
SM
1091 (delete-region (line-beginning-position)
1092 (line-beginning-position 2)))
94e9c286
SM
1093
1094(defun rst-update-section (char style &optional indent)
d13c8be6 1095 "Unconditionally update the style of a section adornment.
94e9c286 1096
57348c4d
JB
1097Do this using the given character CHAR, with STYLE `simple'
1098or `over-and-under', and with indent INDENT. If the STYLE
1099is `simple', whitespace before the title is removed (indent
92439579 1100is always assumed to be 0).
94e9c286
SM
1101
1102If there are existing overline and/or underline from the
d13c8be6
SM
1103existing adornment, they are removed before adding the
1104requested adornment."
1105 (end-of-line)
8bbb7dd8
SM
1106 (let ((marker (point-marker))
1107 len)
94e9c286 1108
6d3f7c2f 1109 ;; Fixup whitespace at the beginning and end of the line.
64f6a736 1110 (if (or (null indent) (eq style 'simple)) ;; testcover: ok.
94e9c286
SM
1111 (setq indent 0))
1112 (beginning-of-line)
1113 (delete-horizontal-space)
1114 (insert (make-string indent ? ))
1115
1116 (end-of-line)
1117 (delete-horizontal-space)
1118
6d3f7c2f 1119 ;; Set the current column, we're at the end of the title line.
94e9c286
SM
1120 (setq len (+ (current-column) indent))
1121
6d3f7c2f 1122 ;; Remove previous line if it is an adornment.
94e9c286 1123 (save-excursion
64f6a736
SM
1124 (forward-line -1) ;; testcover: FIXME: Doesn't work when in first line
1125 ;; of buffer.
d13c8be6
SM
1126 (if (and (looking-at (rst-re 'ado-beg-2-1))
1127 ;; Avoid removing the underline of a title right above us.
1128 (save-excursion (forward-line -1)
1129 (not (looking-at (rst-re 'ttl-beg)))))
1130 (rst-delete-entire-line)))
1131
6d3f7c2f 1132 ;; Remove following line if it is an adornment.
94e9c286 1133 (save-excursion
64f6a736
SM
1134 (forward-line +1) ;; testcover: FIXME: Doesn't work when in last line
1135 ;; of buffer.
d13c8be6
SM
1136 (if (looking-at (rst-re 'ado-beg-2-1))
1137 (rst-delete-entire-line))
94e9c286 1138 ;; Add a newline if we're at the end of the buffer, for the subsequence
6d3f7c2f 1139 ;; inserting of the underline.
94e9c286
SM
1140 (if (= (point) (buffer-end 1))
1141 (newline 1)))
1142
6d3f7c2f 1143 ;; Insert overline.
94e9c286
SM
1144 (if (eq style 'over-and-under)
1145 (save-excursion
1146 (beginning-of-line)
1147 (open-line 1)
1148 (insert (make-string len char))))
1149
6d3f7c2f 1150 ;; Insert underline.
64f6a736
SM
1151 (1value ;; Line has been inserted above.
1152 (forward-line +1))
94e9c286
SM
1153 (open-line 1)
1154 (insert (make-string len char))
1155
64f6a736
SM
1156 (1value ;; Line has been inserted above.
1157 (forward-line +1))
1158 (goto-char marker)))
94e9c286 1159
d13c8be6
SM
1160(defun rst-classify-adornment (adornment end)
1161 "Classify adornment for section titles and transitions.
1162ADORNMENT is the complete adornment string as found in the buffer
6d3f7c2f 1163with optional trailing whitespace. END is the point after the
d13c8be6 1164last character of ADORNMENT.
94e9c286 1165
6d3f7c2f
SM
1166Return a list. The first entry is t for a transition or a
1167cons (CHARACTER . STYLE). Check `rst-preferred-adornments' for
d13c8be6 1168the meaning of CHARACTER and STYLE.
94e9c286 1169
d13c8be6 1170The remaining list forms four match groups as returned by
6d3f7c2f
SM
1171`match-data'. Match group 0 matches the whole construct. Match
1172group 1 matches the overline adornment if present. Match group 2
1173matches the section title text or the transition. Match group 3
d13c8be6 1174matches the underline adornment.
94e9c286 1175
d13c8be6
SM
1176Return nil if no syntactically valid adornment is found."
1177 (save-excursion
1178 (save-match-data
1179 (when (string-match (rst-re 'ado-beg-2-1) adornment)
1180 (goto-char end)
1181 (let* ((ado-ch (string-to-char (match-string 2 adornment)))
1182 (ado-re (rst-re ado-ch 'adorep3-hlp))
1183 (end-pnt (point))
1184 (beg-pnt (progn
64f6a736
SM
1185 (1value ;; No lines may be left to move.
1186 (forward-line 0))
d13c8be6 1187 (point)))
c846da43 1188 (nxt-emp ; Next line nonexistent or empty
d13c8be6
SM
1189 (save-excursion
1190 (or (not (zerop (forward-line 1)))
64f6a736
SM
1191 ;; testcover: FIXME: Add test classifying at the end of
1192 ;; buffer.
d13c8be6 1193 (looking-at (rst-re 'lin-end)))))
c846da43 1194 (prv-emp ; Previous line nonexistent or empty
d13c8be6
SM
1195 (save-excursion
1196 (or (not (zerop (forward-line -1)))
1197 (looking-at (rst-re 'lin-end)))))
6d3f7c2f 1198 (ttl-blw ; Title found below starting here.
d13c8be6
SM
1199 (save-excursion
1200 (and
64f6a736
SM
1201 (zerop (forward-line 1)) ;; testcover: FIXME: Add test
1202 ;; classifying at the end of
1203 ;; buffer.
d13c8be6
SM
1204 (looking-at (rst-re 'ttl-beg))
1205 (point))))
6d3f7c2f 1206 (ttl-abv ; Title found above starting here.
d13c8be6
SM
1207 (save-excursion
1208 (and
1209 (zerop (forward-line -1))
1210 (looking-at (rst-re 'ttl-beg))
1211 (point))))
6d3f7c2f 1212 (und-fnd ; Matching underline found starting here.
d13c8be6
SM
1213 (save-excursion
1214 (and ttl-blw
64f6a736
SM
1215 (zerop (forward-line 2)) ;; testcover: FIXME: Add test
1216 ;; classifying at the end of
1217 ;; buffer.
d13c8be6
SM
1218 (looking-at (rst-re ado-re 'lin-end))
1219 (point))))
6d3f7c2f 1220 (ovr-fnd ; Matching overline found starting here.
d13c8be6
SM
1221 (save-excursion
1222 (and ttl-abv
1223 (zerop (forward-line -2))
1224 (looking-at (rst-re ado-re 'lin-end))
1225 (point))))
1226 key beg-ovr end-ovr beg-txt end-txt beg-und end-und)
1227 (cond
1228 ((and nxt-emp prv-emp)
6d3f7c2f 1229 ;; A transition.
d13c8be6
SM
1230 (setq key t
1231 beg-txt beg-pnt
1232 end-txt end-pnt))
1233 ((or und-fnd ovr-fnd)
6d3f7c2f 1234 ;; An overline with an underline.
d13c8be6 1235 (setq key (cons ado-ch 'over-and-under))
6d3f7c2f 1236 (let (;; Prefer overline match over underline match.
d13c8be6
SM
1237 (und-pnt (if ovr-fnd beg-pnt und-fnd))
1238 (ovr-pnt (if ovr-fnd ovr-fnd beg-pnt))
1239 (txt-pnt (if ovr-fnd ttl-abv ttl-blw)))
1240 (goto-char ovr-pnt)
1241 (setq beg-ovr (point)
1242 end-ovr (line-end-position))
1243 (goto-char txt-pnt)
1244 (setq beg-txt (point)
1245 end-txt (line-end-position))
1246 (goto-char und-pnt)
1247 (setq beg-und (point)
1248 end-und (line-end-position))))
1249 (ttl-abv
6d3f7c2f 1250 ;; An underline.
d13c8be6
SM
1251 (setq key (cons ado-ch 'simple)
1252 beg-und beg-pnt
1253 end-und end-pnt)
1254 (goto-char ttl-abv)
1255 (setq beg-txt (point)
1256 end-txt (line-end-position)))
1257 (t
6d3f7c2f 1258 ;; Invalid adornment.
d13c8be6
SM
1259 (setq key nil)))
1260 (if key
1261 (list key
64f6a736
SM
1262 (or beg-ovr beg-txt)
1263 (or end-und end-txt)
d13c8be6
SM
1264 beg-ovr end-ovr beg-txt end-txt beg-und end-und)))))))
1265
1266(defun rst-find-title-line ()
1267 "Find a section title line around point and return its characteristics.
1268If the point is on an adornment line find the respective title
6d3f7c2f
SM
1269line. If the point is on an empty line check previous or next
1270line whether it is a suitable title line and use it if so. If
d13c8be6
SM
1271point is on a suitable title line use it.
1272
1273If no title line is found return nil.
1274
6d3f7c2f 1275Otherwise return as `rst-classify-adornment' does. However, if
57348c4d
JB
1276the title line has no syntactically valid adornment, STYLE is nil
1277in the first element. If there is no adornment around the title,
d13c8be6
SM
1278CHARACTER is also nil and match groups for overline and underline
1279are nil."
1280 (save-excursion
64f6a736
SM
1281 (1value ;; No lines may be left to move.
1282 (forward-line 0))
d13c8be6
SM
1283 (let ((orig-pnt (point))
1284 (orig-end (line-end-position)))
1285 (cond
1286 ((looking-at (rst-re 'ado-beg-2-1))
1287 (let ((char (string-to-char (match-string-no-properties 2)))
1288 (r (rst-classify-adornment (match-string-no-properties 0)
1289 (match-end 0))))
1290 (cond
1291 ((not r)
6d3f7c2f 1292 ;; Invalid adornment - check whether this is an incomplete overline.
d13c8be6
SM
1293 (if (and
1294 (zerop (forward-line 1))
1295 (looking-at (rst-re 'ttl-beg)))
1296 (list (cons char nil) orig-pnt (line-end-position)
1297 orig-pnt orig-end (point) (line-end-position) nil nil)))
1298 ((consp (car r))
6d3f7c2f 1299 ;; A section title - not a transition.
d13c8be6
SM
1300 r))))
1301 ((looking-at (rst-re 'lin-end))
1302 (or
1303 (save-excursion
1304 (if (and (zerop (forward-line -1))
1305 (looking-at (rst-re 'ttl-beg)))
1306 (list (cons nil nil) (point) (line-end-position)
1307 nil nil (point) (line-end-position) nil nil)))
1308 (save-excursion
1309 (if (and (zerop (forward-line 1))
1310 (looking-at (rst-re 'ttl-beg)))
1311 (list (cons nil nil) (point) (line-end-position)
1312 nil nil (point) (line-end-position) nil nil)))))
1313 ((looking-at (rst-re 'ttl-beg))
6d3f7c2f 1314 ;; Try to use the underline.
d13c8be6 1315 (let ((r (rst-classify-adornment
8f6b6da8 1316 (buffer-substring-no-properties
d13c8be6
SM
1317 (line-beginning-position 2) (line-end-position 2))
1318 (line-end-position 2))))
1319 (if r
1320 r
6d3f7c2f 1321 ;; No valid adornment found.
d13c8be6
SM
1322 (list (cons nil nil) (point) (line-end-position)
1323 nil nil (point) (line-end-position) nil nil))))))))
1324
1325;; The following function and variables are used to maintain information about
1326;; current section adornment in a buffer local cache. Thus they can be used for
1327;; font-locking and manipulation commands.
1328
d13c8be6
SM
1329(defvar rst-all-sections nil
1330 "All section adornments in the buffer as found by `rst-find-all-adornments'.
57348c4d 1331Set to t when no section adornments were found.")
d13c8be6
SM
1332(make-variable-buffer-local 'rst-all-sections)
1333
1334;; FIXME: If this variable is set to a different value font-locking of section
6d3f7c2f 1335;; headers is wrong.
d13c8be6
SM
1336(defvar rst-section-hierarchy nil
1337 "Section hierarchy in the buffer as determined by `rst-get-hierarchy'.
57348c4d
JB
1338Set to t when no section adornments were found.
1339Value depends on `rst-all-sections'.")
d13c8be6
SM
1340(make-variable-buffer-local 'rst-section-hierarchy)
1341
64f6a736 1342(rst-testcover-add-1value 'rst-reset-section-caches)
8f6b6da8
JB
1343(defun rst-reset-section-caches ()
1344 "Reset all section cache variables.
1345Should be called by interactive functions which deal with sections."
1346 (setq rst-all-sections nil
1347 rst-section-hierarchy nil))
1348
d13c8be6
SM
1349(defun rst-find-all-adornments ()
1350 "Return all the section adornments in the current buffer.
1351Return a list of (LINE . ADORNMENT) with ascending LINE where
6d3f7c2f 1352LINE is the line containing the section title. ADORNMENT consists
d13c8be6
SM
1353of a (CHARACTER STYLE INDENT) triple as described for
1354`rst-preferred-adornments'.
1355
1356Uses and sets `rst-all-sections'."
1357 (unless rst-all-sections
1358 (let (positions)
1359 ;; Iterate over all the section titles/adornments in the file.
1360 (save-excursion
1361 (goto-char (point-min))
1362 (while (re-search-forward (rst-re 'ado-beg-2-1) nil t)
1363 (let ((ado-data (rst-classify-adornment
1364 (match-string-no-properties 0) (point))))
1365 (when (and ado-data
6d3f7c2f 1366 (consp (car ado-data))) ; Ignore transitions.
d13c8be6 1367 (set-match-data (cdr ado-data))
6d3f7c2f 1368 (goto-char (match-beginning 2)) ; Goto the title start.
d13c8be6
SM
1369 (push (cons (1+ (count-lines (point-min) (point)))
1370 (list (caar ado-data)
1371 (cdar ado-data)
1372 (current-indentation)))
1373 positions)
6d3f7c2f 1374 (goto-char (match-end 0))))) ; Go beyond the whole thing.
d13c8be6
SM
1375 (setq positions (nreverse positions))
1376 (setq rst-all-sections (or positions t)))))
1377 (if (eq rst-all-sections t)
1378 nil
1379 rst-all-sections))
1380
1381(defun rst-infer-hierarchy (adornments)
1382 "Build a hierarchy of adornments using the list of given ADORNMENTS.
1383
1384ADORNMENTS is a list of (CHARACTER STYLE INDENT) adornment
94e9c286 1385specifications, in order that they appear in a file, and will
d13c8be6
SM
1386infer a hierarchy of section levels by removing adornments that
1387have already been seen in a forward traversal of the adornments,
1388comparing just CHARACTER and STYLE.
94e9c286 1389
d13c8be6 1390Similarly returns a list of (CHARACTER STYLE INDENT), where each
94e9c286 1391list element should be unique."
d13c8be6
SM
1392 (let (hierarchy-alist)
1393 (dolist (x adornments)
94e9c286
SM
1394 (let ((char (car x))
1395 (style (cadr x)))
1396 (unless (assoc (cons char style) hierarchy-alist)
d13c8be6
SM
1397 (push (cons (cons char style) x) hierarchy-alist))))
1398 (mapcar 'cdr (nreverse hierarchy-alist))))
94e9c286 1399
d13c8be6 1400(defun rst-get-hierarchy (&optional ignore)
94e9c286
SM
1401 "Return the hierarchy of section titles in the file.
1402
d13c8be6 1403Return a list of adornments that represents the hierarchy of
6d3f7c2f
SM
1404section titles in the file. Each element consists of (CHARACTER
1405STYLE INDENT) as described for `rst-find-all-adornments'. If the
d13c8be6
SM
1406line number in IGNORE is specified, a possibly adornment found on
1407that line is not taken into account when building the hierarchy.
1408
1409Uses and sets `rst-section-hierarchy' unless IGNORE is given."
1410 (if (and (not ignore) rst-section-hierarchy)
1411 (if (eq rst-section-hierarchy t)
1412 nil
1413 rst-section-hierarchy)
1414 (let ((r (rst-infer-hierarchy
1415 (mapcar 'cdr
1416 (assq-delete-all
1417 ignore
1418 (rst-find-all-adornments))))))
1419 (setq rst-section-hierarchy
1420 (if ignore
1421 ;; Clear cache reflecting that a possible update is not
6d3f7c2f 1422 ;; reflected.
d13c8be6
SM
1423 nil
1424 (or r t)))
1425 r)))
1426
1427(defun rst-get-adornments-around ()
1428 "Return the adornments around point.
1429Return a list of the previous and next adornments."
1430 (let* ((all (rst-find-all-adornments))
94e9c286
SM
1431 (curline (line-number-at-pos))
1432 prev next
1433 (cur all))
1434
d13c8be6 1435 ;; Search for the adornments around the current line.
94e9c286
SM
1436 (while (and cur (< (caar cur) curline))
1437 (setq prev cur
1438 cur (cdr cur)))
d13c8be6 1439 ;; 'cur' is the following adornment.
94e9c286
SM
1440
1441 (if (and cur (caar cur))
1442 (setq next (if (= curline (caar cur)) (cdr cur) cur)))
1443
64f6a736 1444 (mapcar 'cdar (list prev next))))
94e9c286 1445
d13c8be6
SM
1446(defun rst-adornment-complete-p (ado)
1447 "Return true if the adornment ADO around point is complete."
94e9c286
SM
1448 ;; Note: we assume that the detection of the overline as being the underline
1449 ;; of a preceding title has already been detected, and has been eliminated
d13c8be6 1450 ;; from the adornment that is given to us.
94e9c286
SM
1451
1452 ;; There is some sectioning already present, so check if the current
1453 ;; sectioning is complete and correct.
d13c8be6
SM
1454 (let* ((char (car ado))
1455 (style (cadr ado))
1456 (indent (caddr ado))
64f6a736 1457 (endcol (save-excursion (end-of-line) (current-column))))
94e9c286 1458 (if char
d13c8be6 1459 (let ((exps (rst-re "^" char (format "\\{%d\\}" (+ endcol indent)) "$")))
94e9c286
SM
1460 (and
1461 (save-excursion (forward-line +1)
1462 (beginning-of-line)
1463 (looking-at exps))
1464 (or (not (eq style 'over-and-under))
1465 (save-excursion (forward-line -1)
1466 (beginning-of-line)
64f6a736 1467 (looking-at exps))))))))
94e9c286
SM
1468
1469
d13c8be6
SM
1470(defun rst-get-next-adornment
1471 (curado hier &optional suggestion reverse-direction)
1472 "Get the next adornment for CURADO, in given hierarchy HIER.
1473If suggesting, suggest for new adornment SUGGESTION.
94e9c286
SM
1474REVERSE-DIRECTION is used to reverse the cycling order."
1475
1476 (let* (
d13c8be6
SM
1477 (char (car curado))
1478 (style (cadr curado))
94e9c286 1479
d13c8be6
SM
1480 ;; Build a new list of adornments for the rotation.
1481 (rotados
94e9c286 1482 (append hier
d13c8be6 1483 ;; Suggest a new adornment.
94e9c286 1484 (list suggestion
d13c8be6 1485 ;; If nothing to suggest, use first adornment.
94e9c286
SM
1486 (car hier)))) )
1487 (or
d13c8be6 1488 ;; Search for next adornment.
94e9c286 1489 (cadr
d13c8be6
SM
1490 (let ((cur (if reverse-direction rotados
1491 (reverse rotados))))
94e9c286
SM
1492 (while (and cur
1493 (not (and (eq char (caar cur))
1494 (eq style (cadar cur)))))
1495 (setq cur (cdr cur)))
1496 cur))
1497
d13c8be6 1498 ;; If not found, take the first of all adornments.
64f6a736 1499 suggestion)))
94e9c286
SM
1500
1501
6d3f7c2f 1502;; FIXME: A line "``/`` full" is not accepted as a section title.
d13c8be6
SM
1503(defun rst-adjust (pfxarg)
1504 "Auto-adjust the adornment around point.
94e9c286 1505
6d3f7c2f
SM
1506Adjust/rotate the section adornment for the section title around
1507point or promote/demote the adornments inside the region,
57348c4d
JB
1508depending on whether the region is active. This function is meant
1509to be invoked possibly multiple times, and can vary its behavior
6d3f7c2f
SM
1510with a positive PFXARG (toggle style), or with a negative
1511PFXARG (alternate behavior).
94e9c286 1512
6d3f7c2f
SM
1513This function is a bit of a swiss knife. It is meant to adjust
1514the adornments of a section title in reStructuredText. It tries
d13c8be6
SM
1515to deal with all the possible cases gracefully and to do `the
1516right thing' in all cases.
94e9c286 1517
d13c8be6 1518See the documentations of `rst-adjust-adornment-work' and
94e9c286
SM
1519`rst-promote-region' for full details.
1520
1521Prefix Arguments
1522================
1523
1524The method can take either (but not both) of
1525
1526a. a (non-negative) prefix argument, which means to toggle the
6d3f7c2f 1527 adornment style. Invoke with a prefix argument for example;
94e9c286
SM
1528
1529b. a negative numerical argument, which generally inverts the
1530 direction of search in the file or hierarchy. Invoke with C--
1531 prefix for example."
d13c8be6 1532 (interactive "P")
94e9c286
SM
1533
1534 (let* (;; Save our original position on the current line.
8bbb7dd8 1535 (origpt (point-marker))
94e9c286 1536
d13c8be6
SM
1537 (reverse-direction (and pfxarg (< (prefix-numeric-value pfxarg) 0)))
1538 (toggle-style (and pfxarg (not reverse-direction))))
94e9c286 1539
64f6a736 1540 (if (use-region-p)
d13c8be6
SM
1541 ;; Adjust adornments within region.
1542 (rst-promote-region (and pfxarg t))
1543 ;; Adjust adornment around point.
1544 (rst-adjust-adornment-work toggle-style reverse-direction))
94e9c286
SM
1545
1546 ;; Run the hooks to run after adjusting.
1547 (run-hooks 'rst-adjust-hook)
1548
1549 ;; Make sure to reset the cursor position properly after we're done.
64f6a736 1550 (goto-char origpt)))
94e9c286 1551
d13c8be6
SM
1552(defcustom rst-adjust-hook nil
1553 "Hooks to be run after running `rst-adjust'."
1554 :group 'rst-adjust
1555 :type '(hook)
1556 :package-version '(rst . "1.1.0"))
64f6a736 1557(rst-testcover-defcustom)
94e9c286 1558
d13c8be6
SM
1559(defcustom rst-new-adornment-down nil
1560 "Controls level of new adornment for section headers."
1561 :group 'rst-adjust
1562 :type '(choice
1563 (const :tag "Same level as previous one" nil)
1564 (const :tag "One level down relative to the previous one" t))
1565 :package-version '(rst . "1.1.0"))
64f6a736 1566(rst-testcover-defcustom)
94e9c286 1567
d13c8be6
SM
1568(defun rst-adjust-adornment (pfxarg)
1569 "Call `rst-adjust-adornment-work' interactively.
1570
6d3f7c2f
SM
1571Keep this for compatibility for older bindings (are there any?).
1572Argument PFXARG has the same meaning as for `rst-adjust'."
d13c8be6
SM
1573 (interactive "P")
1574
1575 (let* ((reverse-direction (and pfxarg (< (prefix-numeric-value pfxarg) 0)))
1576 (toggle-style (and pfxarg (not reverse-direction))))
1577 (rst-adjust-adornment-work toggle-style reverse-direction)))
1578
1579(defun rst-adjust-adornment-work (toggle-style reverse-direction)
1580"Adjust/rotate the section adornment for the section title around point.
94e9c286
SM
1581
1582This function is meant to be invoked possibly multiple times, and
fffa137c 1583can vary its behavior with a true TOGGLE-STYLE argument, or with
94e9c286
SM
1584a REVERSE-DIRECTION argument.
1585
92439579
JB
1586General Behavior
1587================
94e9c286
SM
1588
1589The next action it takes depends on context around the point, and
1590it is meant to be invoked possibly more than once to rotate among
1591the various possibilities. Basically, this function deals with:
1592
d13c8be6 1593- adding a adornment if the title does not have one;
94e9c286
SM
1594
1595- adjusting the length of the underline characters to fit a
1596 modified title;
1597
d13c8be6
SM
1598- rotating the adornment in the set of already existing
1599 sectioning adornments used in the file;
94e9c286
SM
1600
1601- switching between simple and over-and-under styles.
1602
1603You should normally not have to read all the following, just
1604invoke the method and it will do the most obvious thing that you
1605would expect.
1606
1607
d13c8be6
SM
1608Adornment Definitions
1609=====================
94e9c286 1610
d13c8be6 1611The adornments consist in
94e9c286
SM
1612
16131. a CHARACTER
1614
57348c4d 16152. a STYLE which can be either `simple' or `over-and-under'.
94e9c286
SM
1616
16173. an INDENT (meaningful for the over-and-under style only)
1618 which determines how many characters and over-and-under
1619 style is hanging outside of the title at the beginning and
1620 ending.
1621
1622See source code for mode details.
1623
1624
92439579
JB
1625Detailed Behavior Description
1626=============================
94e9c286
SM
1627
1628Here are the gory details of the algorithm (it seems quite
1629complicated, but really, it does the most obvious thing in all
1630the particular cases):
1631
d13c8be6 1632Before applying the adornment change, the cursor is placed on
94e9c286
SM
1633the closest line that could contain a section title.
1634
d13c8be6
SM
1635Case 1: No Adornment
1636--------------------
94e9c286 1637
d13c8be6 1638If the current line has no adornment around it,
94e9c286 1639
d13c8be6
SM
1640- search backwards for the last previous adornment, and apply
1641 the adornment one level lower to the current line. If there
1642 is no defined level below this previous adornment, we suggest
1643 the most appropriate of the `rst-preferred-adornments'.
94e9c286
SM
1644
1645 If REVERSE-DIRECTION is true, we simply use the previous
d13c8be6 1646 adornment found directly.
94e9c286 1647
d13c8be6
SM
1648- if there is no adornment found in the given direction, we use
1649 the first of `rst-preferred-adornments'.
94e9c286 1650
d13c8be6 1651TOGGLE-STYLE forces a toggle of the prescribed adornment style.
94e9c286 1652
d13c8be6
SM
1653Case 2: Incomplete Adornment
1654----------------------------
94e9c286 1655
d13c8be6
SM
1656If the current line does have an existing adornment, but the
1657adornment is incomplete, that is, the underline/overline does
57348c4d
JB
1658not extend to exactly the end of the title line (it is either
1659too short or too long), we simply extend the length of the
94e9c286
SM
1660underlines/overlines to fit exactly the section title.
1661
d13c8be6 1662If TOGGLE-STYLE we toggle the style of the adornment as well.
94e9c286
SM
1663
1664REVERSE-DIRECTION has no effect in this case.
1665
d13c8be6
SM
1666Case 3: Complete Existing Adornment
1667-----------------------------------
94e9c286 1668
d13c8be6 1669If the adornment is complete (i.e. the underline (overline)
94e9c286
SM
1670length is already adjusted to the end of the title line), we
1671search/parse the file to establish the hierarchy of all the
d13c8be6
SM
1672adornments (making sure not to include the adornment around
1673point), and we rotate the current title's adornment from within
94e9c286
SM
1674that list (by default, going *down* the hierarchy that is present
1675in the file, i.e. to a lower section level). This is meant to be
d13c8be6 1676used potentially multiple times, until the desired adornment is
94e9c286
SM
1677found around the title.
1678
1679If we hit the boundary of the hierarchy, exactly one choice from
d13c8be6
SM
1680the list of preferred adornments is suggested/chosen, the first
1681of those adornment that has not been seen in the file yet (and
1682not including the adornment around point), and the next
94e9c286
SM
1683invocation rolls over to the other end of the hierarchy (i.e. it
1684cycles). This allows you to avoid having to set which character
92439579 1685to use.
94e9c286
SM
1686
1687If REVERSE-DIRECTION is true, the effect is to change the
d13c8be6 1688direction of rotation in the hierarchy of adornments, thus
94e9c286
SM
1689instead going *up* the hierarchy.
1690
d13c8be6
SM
1691However, if TOGGLE-STYLE, we do not rotate the adornment, but
1692instead simply toggle the style of the current adornment (this
1693should be the most common way to toggle the style of an existing
1694complete adornment).
94e9c286
SM
1695
1696
1697Point Location
1698==============
1699
1700The invocation of this function can be carried out anywhere
1701within the section title line, on an existing underline or
1702overline, as well as on an empty line following a section title.
1703This is meant to be as convenient as possible.
1704
1705
1706Indented Sections
1707=================
1708
1709Indented section titles such as ::
1710
1711 My Title
1712 --------
1713
d13c8be6 1714are invalid in reStructuredText and thus not recognized by the
94e9c286
SM
1715parser. This code will thus not work in a way that would support
1716indented sections (it would be ambiguous anyway).
1717
1718
1719Joint Sections
1720==============
1721
1722Section titles that are right next to each other may not be
1723treated well. More work might be needed to support those, and
d13c8be6 1724special conditions on the completeness of existing adornments
94e9c286
SM
1725might be required to make it non-ambiguous.
1726
d13c8be6
SM
1727For now we assume that the adornments are disjoint, that is,
1728there is at least a single line between the titles/adornment
1729lines."
1730 (rst-reset-section-caches)
1731 (let ((ttl-fnd (rst-find-title-line))
1732 (orig-pnt (point)))
1733 (when ttl-fnd
1734 (set-match-data (cdr ttl-fnd))
1735 (goto-char (match-beginning 2))
1736 (let* ((moved (- (line-number-at-pos) (line-number-at-pos orig-pnt)))
1737 (char (caar ttl-fnd))
1738 (style (cdar ttl-fnd))
1739 (indent (current-indentation))
1740 (curado (list char style indent))
1741 char-new style-new indent-new)
1742 (cond
1743 ;;-------------------------------------------------------------------
1744 ;; Case 1: No valid adornment
1745 ((not style)
1746 (let ((prev (car (rst-get-adornments-around)))
1747 cur
1748 (hier (rst-get-hierarchy)))
1749 ;; Advance one level down.
1750 (setq cur
1751 (if prev
1752 (if (or (and rst-new-adornment-down reverse-direction)
1753 (and (not rst-new-adornment-down)
1754 (not reverse-direction)))
1755 prev
1756 (or (cadr (rst-get-adornment-match hier prev))
1757 (rst-suggest-new-adornment hier prev)))
1758 (copy-sequence (car rst-preferred-adornments))))
1759 ;; Invert the style if requested.
1760 (if toggle-style
1761 (setcar (cdr cur) (if (eq (cadr cur) 'simple)
1762 'over-and-under 'simple)) )
1763 (setq char-new (car cur)
1764 style-new (cadr cur)
1765 indent-new (caddr cur))))
1766 ;;-------------------------------------------------------------------
1767 ;; Case 2: Incomplete Adornment
1768 ((not (rst-adornment-complete-p curado))
1769 ;; Invert the style if requested.
1770 (if toggle-style
1771 (setq style (if (eq style 'simple) 'over-and-under 'simple)))
1772 (setq char-new char
1773 style-new style
1774 indent-new indent))
1775 ;;-------------------------------------------------------------------
1776 ;; Case 3: Complete Existing Adornment
1777 (t
1778 (if toggle-style
1779 ;; Simply switch the style of the current adornment.
1780 (setq char-new char
1781 style-new (if (eq style 'simple) 'over-and-under 'simple)
1782 indent-new rst-default-indent)
1783 ;; Else, we rotate, ignoring the adornment around the current
1784 ;; line...
1785 (let* ((hier (rst-get-hierarchy (line-number-at-pos)))
6d3f7c2f 1786 ;; Suggestion, in case we need to come up with something new.
d13c8be6
SM
1787 (suggestion (rst-suggest-new-adornment
1788 hier
1789 (car (rst-get-adornments-around))))
1790 (nextado (rst-get-next-adornment
1791 curado hier suggestion reverse-direction)))
1792 ;; Indent, if present, always overrides the prescribed indent.
1793 (setq char-new (car nextado)
1794 style-new (cadr nextado)
1795 indent-new (caddr nextado))))))
1796 ;; Override indent with present indent!
1797 (setq indent-new (if (> indent 0) indent indent-new))
1798 (if (and char-new style-new)
1799 (rst-update-section char-new style-new indent-new))
1800 ;; Correct the position of the cursor to more accurately reflect where
1801 ;; it was located when the function was invoked.
1802 (unless (zerop moved)
1803 (forward-line (- moved))
1804 (end-of-line))))))
94e9c286
SM
1805
1806;; Maintain an alias for compatibility.
1807(defalias 'rst-adjust-section-title 'rst-adjust)
1808
1809
d13c8be6 1810(defun rst-promote-region (demote)
94e9c286
SM
1811 "Promote the section titles within the region.
1812
e6438428
JB
1813With argument DEMOTE or a prefix argument, demote the section
1814titles instead. The algorithm used at the boundaries of the
d13c8be6
SM
1815hierarchy is similar to that used by `rst-adjust-adornment-work'."
1816 (interactive "P")
1817 (rst-reset-section-caches)
1818 (let* ((cur (rst-find-all-adornments))
1819 (hier (rst-get-hierarchy))
1820 (suggestion (rst-suggest-new-adornment hier))
94e9c286
SM
1821
1822 (region-begin-line (line-number-at-pos (region-beginning)))
1823 (region-end-line (line-number-at-pos (region-end)))
1824
64f6a736 1825 marker-list)
94e9c286 1826
6d3f7c2f 1827 ;; Skip the markers that come before the region beginning.
94e9c286
SM
1828 (while (and cur (< (caar cur) region-begin-line))
1829 (setq cur (cdr cur)))
1830
d13c8be6 1831 ;; Create a list of markers for all the adornments which are found within
94e9c286
SM
1832 ;; the region.
1833 (save-excursion
8bbb7dd8 1834 (let (line)
94e9c286 1835 (while (and cur (< (setq line (caar cur)) region-end-line))
e6ce8c42
GM
1836 (goto-char (point-min))
1837 (forward-line (1- line))
8bbb7dd8 1838 (push (list (point-marker) (cdar cur)) marker-list)
94e9c286
SM
1839 (setq cur (cdr cur)) ))
1840
1841 ;; Apply modifications.
8bbb7dd8 1842 (dolist (p marker-list)
d13c8be6
SM
1843 ;; Go to the adornment to promote.
1844 (goto-char (car p))
8bbb7dd8 1845
d13c8be6
SM
1846 ;; Update the adornment.
1847 (apply 'rst-update-section
1848 ;; Rotate the next adornment.
1849 (rst-get-next-adornment
1850 (cadr p) hier suggestion demote))
8bbb7dd8 1851
d13c8be6
SM
1852 ;; Clear marker to avoid slowing down the editing after we're done.
1853 (set-marker (car p) nil))
64f6a736 1854 (setq deactivate-mark nil))))
94e9c286
SM
1855
1856
1857
d13c8be6
SM
1858(defun rst-display-adornments-hierarchy (&optional adornments)
1859 "Display the current file's section title adornments hierarchy.
1860This function expects a list of (CHARACTER STYLE INDENT) triples
1861in ADORNMENTS."
94e9c286 1862 (interactive)
d13c8be6
SM
1863 (rst-reset-section-caches)
1864 (if (not adornments)
1865 (setq adornments (rst-get-hierarchy)))
94e9c286
SM
1866 (with-output-to-temp-buffer "*rest section hierarchy*"
1867 (let ((level 1))
1868 (with-current-buffer standard-output
d13c8be6 1869 (dolist (x adornments)
94e9c286
SM
1870 (insert (format "\nSection Level %d" level))
1871 (apply 'rst-update-section x)
1872 (goto-char (point-max))
1873 (insert "\n")
64f6a736 1874 (incf level))))))
94e9c286 1875
d13c8be6
SM
1876(defun rst-straighten-adornments ()
1877 "Redo all the adornments in the current buffer.
1878This is done using our preferred set of adornments. This can be
94e9c286
SM
1879used, for example, when using somebody else's copy of a document,
1880in order to adapt it to our preferred style."
1881 (interactive)
d13c8be6 1882 (rst-reset-section-caches)
94e9c286 1883 (save-excursion
6d3f7c2f 1884 (let (;; Get a list of pairs of (level . marker).
d13c8be6
SM
1885 (levels-and-markers (mapcar
1886 (lambda (ado)
1887 (cons (rst-position (cdr ado)
1888 (rst-get-hierarchy))
1889 (progn
1890 (goto-char (point-min))
1891 (forward-line (1- (car ado)))
1892 (point-marker))))
1893 (rst-find-all-adornments))))
94e9c286 1894 (dolist (lm levels-and-markers)
6d3f7c2f 1895 ;; Go to the appropriate position.
94e9c286
SM
1896 (goto-char (cdr lm))
1897
6d3f7c2f 1898 ;; Apply the new style.
d13c8be6 1899 (apply 'rst-update-section (nth (car lm) rst-preferred-adornments))
94e9c286 1900
7ae2ea10 1901 ;; Reset the marker to avoid slowing down editing until it gets GC'ed.
64f6a736 1902 (set-marker (cdr lm) nil)))))
94e9c286 1903
d13c8be6
SM
1904\f
1905;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1906;; Insert list items
1907;; =================
1908
1909
1910;=================================================
6d3f7c2f 1911; Borrowed from a2r.el (version 1.3), by Lawrence Mitchell <wence@gmx.li>.
d13c8be6
SM
1912; I needed to make some tiny changes to the functions, so I put it here.
1913; -- Wei-Wei Guo
1914
1915(defconst rst-arabic-to-roman
1916 '((1000 . "M") (900 . "CM") (500 . "D") (400 . "CD")
1917 (100 . "C") (90 . "XC") (50 . "L") (40 . "XL")
1918 (10 . "X") (9 . "IX") (5 . "V") (4 . "IV")
1919 (1 . "I"))
1920 "List of maps between Arabic numbers and their Roman numeral equivalents.")
1921
1922(defun rst-arabic-to-roman (num &optional arg)
1923 "Convert Arabic number NUM to its Roman numeral representation.
1924
1925Obviously, NUM must be greater than zero. Don't blame me, blame the
1926Romans, I mean \"what have the Romans ever _done_ for /us/?\" (with
1927apologies to Monty Python).
57348c4d 1928If optional ARG is non-nil, insert in current buffer."
d13c8be6
SM
1929 (let ((map rst-arabic-to-roman)
1930 res)
1931 (while (and map (> num 0))
1932 (if (or (= num (caar map))
1933 (> num (caar map)))
1934 (setq res (concat res (cdar map))
1935 num (- num (caar map)))
1936 (setq map (cdr map))))
57348c4d 1937 (if arg (insert (or res "")) res)))
d13c8be6
SM
1938
1939(defun rst-roman-to-arabic (string &optional arg)
1940 "Convert STRING of Roman numerals to an Arabic number.
1941
57348c4d
JB
1942If STRING contains a letter which isn't a valid Roman numeral,
1943the rest of the string from that point onwards is ignored.
d13c8be6
SM
1944
1945Hence:
1946MMD == 2500
1947and
1948MMDFLXXVI == 2500.
1949If optional ARG is non-nil, insert in current buffer."
1950 (let ((res 0)
1951 (map rst-arabic-to-roman))
1952 (while map
1953 (if (string-match (concat "^" (cdar map)) string)
1954 (setq res (+ res (caar map))
1955 string (replace-match "" nil t string))
1956 (setq map (cdr map))))
57348c4d 1957 (if arg (insert res) res)))
d13c8be6 1958;=================================================
94e9c286
SM
1959
1960(defun rst-find-pfx-in-region (beg end pfx-re)
1961 "Find all the positions of prefixes in region between BEG and END.
6d3f7c2f 1962This is used to find bullets and enumerated list items. PFX-RE is
d13c8be6 1963a regular expression for matching the lines after indentation
6d3f7c2f 1964with items. Returns a list of cons cells consisting of the point
d13c8be6 1965and the column of the point."
8bbb7dd8 1966 (let ((pfx ()))
94e9c286
SM
1967 (save-excursion
1968 (goto-char beg)
1969 (while (< (point) end)
1970 (back-to-indentation)
1971 (when (and
d13c8be6 1972 (looking-at pfx-re) ; pfx found and...
94e9c286
SM
1973 (let ((pfx-col (current-column)))
1974 (save-excursion
d13c8be6 1975 (forward-line -1) ; ...previous line is...
94e9c286 1976 (back-to-indentation)
d13c8be6
SM
1977 (or (looking-at (rst-re 'lin-end)) ; ...empty,
1978 (> (current-column) pfx-col) ; ...deeper level, or
94e9c286 1979 (and (= (current-column) pfx-col)
6d3f7c2f 1980 (looking-at pfx-re)))))) ; ...pfx at same level.
b4747519
SM
1981 (push (cons (point) (current-column))
1982 pfx))
64f6a736 1983 (forward-line 1)))
94e9c286
SM
1984 (nreverse pfx)))
1985
d13c8be6 1986(defun rst-insert-list-pos (newitem)
6d3f7c2f 1987 "Arrange relative position of a newly inserted list item of style NEWITEM.
d13c8be6
SM
1988
1989Adding a new list might consider three situations:
94e9c286 1990
d13c8be6
SM
1991 (a) Current line is a blank line.
1992 (b) Previous line is a blank line.
1993 (c) Following line is a blank line.
94e9c286 1994
d13c8be6 1995When (a) and (b), just add the new list at current line.
94e9c286 1996
d13c8be6
SM
1997when (a) and not (b), a blank line is added before adding the new list.
1998
1999When not (a), first forward point to the end of the line, and add two
2000blank lines, then add the new list.
2001
2002Other situations are just ignored and left to users themselves."
2003 (if (save-excursion
2004 (beginning-of-line)
2005 (looking-at (rst-re 'lin-end)))
2006 (if (save-excursion
2007 (forward-line -1)
2008 (looking-at (rst-re 'lin-end)))
2009 (insert newitem " ")
2010 (insert "\n" newitem " "))
2011 (end-of-line)
2012 (insert "\n\n" newitem " ")))
2013
d8a52e15 2014;; FIXME: Isn't this a `defconst'?
d13c8be6
SM
2015(defvar rst-initial-enums
2016 (let (vals)
2017 (dolist (fmt '("%s." "(%s)" "%s)"))
2018 (dolist (c '("1" "a" "A" "I" "i"))
2019 (push (format fmt c) vals)))
2020 (cons "#." (nreverse vals)))
2021 "List of initial enumerations.")
2022
d8a52e15 2023;; FIXME: Isn't this a `defconst'?
d13c8be6
SM
2024(defvar rst-initial-items
2025 (append (mapcar 'char-to-string rst-bullets) rst-initial-enums)
57348c4d 2026 "List of initial items. It's a collection of bullets and enumerations.")
d13c8be6
SM
2027
2028(defun rst-insert-list-new-item ()
2029 "Insert a new list item.
2030
57348c4d
JB
2031User is asked to select the item style first, for example (a), i), +.
2032Use TAB for completion and choices.
d13c8be6
SM
2033
2034If user selects bullets or #, it's just added with position arranged by
2035`rst-insert-list-pos'.
2036
57348c4d
JB
2037If user selects enumerations, a further prompt is given. User need to
2038input a starting item, for example 'e' for 'A)' style. The position is
2039also arranged by `rst-insert-list-pos'."
d13c8be6 2040 (interactive)
6d3f7c2f 2041 ;; FIXME: Make this comply to `interactive' standards.
d13c8be6
SM
2042 (let* ((itemstyle (completing-read
2043 "Select preferred item style [#.]: "
2044 rst-initial-items nil t nil nil "#."))
2045 (cnt (if (string-match (rst-re 'cntexp-tag) itemstyle)
2046 (match-string 0 itemstyle)))
2047 (no
2048 (save-match-data
6d3f7c2f 2049 ;; FIXME: Make this comply to `interactive' standards.
d13c8be6
SM
2050 (cond
2051 ((equal cnt "a")
2052 (let ((itemno (read-string "Give starting value [a]: "
2053 nil nil "a")))
2054 (downcase (substring itemno 0 1))))
2055 ((equal cnt "A")
2056 (let ((itemno (read-string "Give starting value [A]: "
2057 nil nil "A")))
2058 (upcase (substring itemno 0 1))))
2059 ((equal cnt "I")
2060 (let ((itemno (read-number "Give starting value [1]: " 1)))
2061 (rst-arabic-to-roman itemno)))
2062 ((equal cnt "i")
2063 (let ((itemno (read-number "Give starting value [1]: " 1)))
2064 (downcase (rst-arabic-to-roman itemno))))
2065 ((equal cnt "1")
2066 (let ((itemno (read-number "Give starting value [1]: " 1)))
2067 (number-to-string itemno)))))))
2068 (if no
2069 (setq itemstyle (replace-match no t t itemstyle)))
2070 (rst-insert-list-pos itemstyle)))
2071
2072(defcustom rst-preferred-bullets
2073 '(?* ?- ?+)
2074 "List of favorite bullets."
2075 :group 'rst
2076 :type `(repeat
2077 (choice ,@(mapcar (lambda (char)
2078 (list 'const
2079 :tag (char-to-string char) char))
2080 rst-bullets)))
2081 :package-version '(rst . "1.1.0"))
64f6a736 2082(rst-testcover-defcustom)
d13c8be6
SM
2083
2084(defun rst-insert-list-continue (curitem prefer-roman)
6d3f7c2f
SM
2085 "Insert a list item with list start CURITEM including its indentation level.
2086If PREFER-ROMAN roman numbering is preferred over using letters."
d13c8be6
SM
2087 (end-of-line)
2088 (insert
6d3f7c2f 2089 "\n" ; FIXME: Separating lines must be possible.
d13c8be6
SM
2090 (cond
2091 ((string-match (rst-re '(:alt enmaut-tag
2092 bul-tag)) curitem)
2093 curitem)
2094 ((string-match (rst-re 'num-tag) curitem)
2095 (replace-match (number-to-string
2096 (1+ (string-to-number (match-string 0 curitem))))
2097 nil nil curitem))
2098 ((and (string-match (rst-re 'rom-tag) curitem)
2099 (save-match-data
6d3f7c2f 2100 (if (string-match (rst-re 'ltr-tag) curitem) ; Also a letter tag.
d13c8be6
SM
2101 (save-excursion
2102 ;; FIXME: Assumes one line list items without separating
6d3f7c2f 2103 ;; empty lines.
d13c8be6
SM
2104 (if (and (zerop (forward-line -1))
2105 (looking-at (rst-re 'enmexp-beg)))
2106 (string-match
2107 (rst-re 'rom-tag)
6d3f7c2f
SM
2108 (match-string 0)) ; Previous was a roman tag.
2109 prefer-roman)) ; Don't know - use flag.
2110 t))) ; Not a letter tag.
d13c8be6
SM
2111 (replace-match
2112 (let* ((old (match-string 0 curitem))
2113 (new (save-match-data
2114 (rst-arabic-to-roman
2115 (1+ (rst-roman-to-arabic
2116 (upcase old)))))))
2117 (if (equal old (upcase old))
2118 (upcase new)
2119 (downcase new)))
2120 t nil curitem))
2121 ((string-match (rst-re 'ltr-tag) curitem)
2122 (replace-match (char-to-string
2123 (1+ (string-to-char (match-string 0 curitem))))
2124 nil nil curitem)))))
2125
2126
2127(defun rst-insert-list (&optional prefer-roman)
2128 "Insert a list item at the current point.
2129
6d3f7c2f
SM
2130The command can insert a new list or a continuing list. When it is called at a
2131non-list line, it will promote to insert new list. When it is called at a list
d13c8be6
SM
2132line, it will insert a list with the same list style.
2133
21341. When inserting a new list:
2135
6d3f7c2f 2136User is asked to select the item style first, for example (a), i), +. Use TAB
c846da43 2137for completion and choices.
d13c8be6
SM
2138
2139 (a) If user selects bullets or #, it's just added.
2140 (b) If user selects enumerations, a further prompt is given. User needs to
2141 input a starting item, for example 'e' for 'A)' style.
2142
2143The position of the new list is arranged according to whether or not the
2144current line and the previous line are blank lines.
2145
57348c4d 21462. When continuing a list, one thing needs to be noticed:
d13c8be6
SM
2147
2148List style alphabetical list, such as 'a.', and roman numerical list, such as
2149'i.', have some overlapping items, for example 'v.' The function can deal with
2150the problem elegantly in most situations. But when those overlapped list are
2151preceded by a blank line, it is hard to determine which type to use
2152automatically. The function uses alphabetical list by default. If you want
6d3f7c2f 2153roman numerical list, just use a prefix to set PREFER-ROMAN."
d13c8be6
SM
2154 (interactive "P")
2155 (beginning-of-line)
2156 (if (looking-at (rst-re 'itmany-beg-1))
2157 (rst-insert-list-continue (match-string 0) prefer-roman)
2158 (rst-insert-list-new-item)))
94e9c286
SM
2159
2160(defun rst-straighten-bullets-region (beg end)
2161 "Make all the bulleted list items in the region consistent.
2162The region is specified between BEG and END. You can use this
2163after you have merged multiple bulleted lists to make them use
2164the same/correct/consistent bullet characters.
2165
2166See variable `rst-preferred-bullets' for the list of bullets to
2167adjust. If bullets are found on levels beyond the
2168`rst-preferred-bullets' list, they are not modified."
2169 (interactive "r")
2170
d13c8be6 2171 (let ((bullets (rst-find-pfx-in-region beg end (rst-re 'bul-sta)))
94e9c286
SM
2172 (levtable (make-hash-table :size 4)))
2173
2174 ;; Create a map of levels to list of positions.
2175 (dolist (x bullets)
2176 (let ((key (cdr x)))
2177 (puthash key
2178 (append (gethash key levtable (list))
2179 (list (car x)))
2180 levtable)))
2181
2182 ;; Sort this map and create a new map of prefix char and list of positions.
b4747519
SM
2183 (let ((poslist ())) ; List of (indent . positions).
2184 (maphash (lambda (x y) (push (cons x y) poslist)) levtable)
2185
2186 (let ((bullets rst-preferred-bullets))
2187 (dolist (x (sort poslist 'car-less-than-car))
2188 (when bullets
2189 ;; Apply the characters.
2190 (dolist (pos (cdr x))
2191 (goto-char pos)
2192 (delete-char 1)
2193 (insert (string (car bullets))))
2194 (setq bullets (cdr bullets))))))))
94e9c286 2195
d13c8be6
SM
2196\f
2197;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2198;; Table of contents
2199;; =================
94e9c286 2200
ee97deee 2201;; FIXME: Return value should be a `defstruct'.
d13c8be6 2202(defun rst-section-tree ()
ee97deee
SM
2203 "Return the hierarchical tree of section titles.
2204A tree entry looks like ((TITLE MARKER) CHILD...). TITLE is the
2205stripped text of the section title. MARKER is a marker for the
2206beginning of the title text. For the top node or a missing
2207section level node TITLE is nil and MARKER points to the title
2208text of the first child. Each CHILD is another tree entry. The
2209CHILD list may be empty."
d13c8be6 2210 (let ((hier (rst-get-hierarchy))
ee97deee
SM
2211 (ch-sty2level (make-hash-table :test 'equal :size 10))
2212 lev-ttl-mrk-l)
94e9c286
SM
2213
2214 (let ((lev 0))
d13c8be6 2215 (dolist (ado hier)
94e9c286 2216 ;; Compare just the character and indent in the hash table.
ee97deee 2217 (puthash (cons (car ado) (cadr ado)) lev ch-sty2level)
94e9c286
SM
2218 (incf lev)))
2219
ee97deee 2220 ;; Create a list that contains (LEVEL TITLE MARKER) for each adornment.
94e9c286 2221 (save-excursion
ee97deee 2222 (setq lev-ttl-mrk-l
d13c8be6 2223 (mapcar (lambda (ado)
e6ce8c42 2224 (goto-char (point-min))
ee97deee
SM
2225 (1value ;; This should really succeed.
2226 (forward-line (1- (car ado))))
2227 (list (gethash (cons (cadr ado) (caddr ado)) ch-sty2level)
2228 ;; Get title.
2229 (save-excursion
2230 (if (re-search-forward
2231 (rst-re "\\S .*\\S ") (line-end-position) t)
2232 (buffer-substring-no-properties
2233 (match-beginning 0) (match-end 0))
2234 ""))
2235 (point-marker)))
d13c8be6 2236 (rst-find-all-adornments))))
ee97deee
SM
2237 (cdr (rst-section-tree-rec lev-ttl-mrk-l -1))))
2238
2239;; FIXME: Return value should be a `defstruct'.
2240(defun rst-section-tree-rec (remaining lev)
2241 "Process the first entry of REMAINING expected to be on level LEV.
2242REMAINING is the remaining list of adornments consisting
2243of (LEVEL TITLE MARKER) entries.
2244
2245Return (UNPROCESSED (TITLE MARKER) CHILD...) for the first entry
2246of REMAINING where TITLE is nil if the expected level is not
2247matched. UNPROCESSED is the list of still unprocessed entries.
2248Each CHILD is a child of this entry in the same format but
2249without UNPROCESSED."
2250 (let ((cur (car remaining))
2251 (unprocessed remaining)
2252 ttl-mrk children)
2253 ;; If the current adornment matches expected level.
2254 (when (and cur (= (car cur) lev))
2255 ;; Consume the current entry and create the current node with it.
2256 (setq unprocessed (cdr remaining))
2257 (setq ttl-mrk (cdr cur)))
2258
2259 ;; Build the child nodes as long as they have deeper level.
2260 (while (and unprocessed (> (caar unprocessed) lev))
2261 (let ((rem-children (rst-section-tree-rec unprocessed (1+ lev))))
2262 (setq children (cons (cdr rem-children) children))
2263 (setq unprocessed (car rem-children))))
94e9c286
SM
2264 (setq children (reverse children))
2265
ee97deee
SM
2266 (cons unprocessed
2267 (cons (or ttl-mrk
2268 ;; Node on this level missing - use nil as text and the
2269 ;; marker of the first child.
2270 (cons nil (cdaar children)))
2271 children))))
2272
2273(defun rst-section-tree-point (tree &optional point)
2274 "Return section containing POINT by returning the closest node in TREE.
2275TREE is a section tree as returned by `rst-section-tree'
2276consisting of (NODE CHILD...) entries. POINT defaults to the
57348c4d 2277current point. A NODE must have the structure (IGNORED MARKER...).
ee97deee
SM
2278
2279Return (PATH NODE CHILD...). NODE is the node where POINT is in
2280if any. PATH is a list of nodes from the top of the tree down to
57348c4d 2281and including NODE. List of CHILD are the children of NODE if any."
ee97deee
SM
2282 (setq point (or point (point)))
2283 (let ((cur (car tree))
2284 (children (cdr tree)))
2285 ;; Point behind current node?
2286 (if (and (cadr cur) (>= point (cadr cur)))
2287 ;; Iterate all the children, looking for one that might contain the
2288 ;; current section.
2289 (let (found)
2290 (while (and children (>= point (cadaar children)))
2291 (setq found children
2292 children (cdr children)))
2293 (if found
2294 ;; Found section containing point in children.
2295 (let ((sub (rst-section-tree-point (car found) point)))
2296 ;; Extend path with current node and return NODE CHILD... from
2297 ;; sub.
2298 (cons (cons cur (car sub)) (cdr sub)))
2299 ;; Point in this section: Start a new path with current node and
2300 ;; return current NODE CHILD...
2301 (cons (list cur) tree)))
2302 ;; Current node behind point: start a new path with current node and
2303 ;; no NODE CHILD...
2304 (list (list cur)))))
94e9c286 2305
b4747519
SM
2306(defgroup rst-toc nil
2307 "Settings for reStructuredText table of contents."
2308 :group 'rst
2309 :version "21.1")
2310
2311(defcustom rst-toc-indent 2
2312 "Indentation for table-of-contents display.
2313Also used for formatting insertion, when numbering is disabled."
9c5a5c77 2314 :type 'integer
b4747519 2315 :group 'rst-toc)
64f6a736 2316(rst-testcover-defcustom)
b4747519
SM
2317
2318(defcustom rst-toc-insert-style 'fixed
2319 "Insertion style for table-of-contents.
2320Set this to one of the following values to determine numbering and
2321indentation style:
57348c4d
JB
2322- `plain': no numbering (fixed indentation)
2323- `fixed': numbering, but fixed indentation
2324- `aligned': numbering, titles aligned under each other
2325- `listed': numbering, with dashes like list items (EXPERIMENTAL)"
9c5a5c77
GM
2326 :type '(choice (const plain)
2327 (const fixed)
2328 (const aligned)
2329 (const listed))
b4747519 2330 :group 'rst-toc)
64f6a736 2331(rst-testcover-defcustom)
b4747519
SM
2332
2333(defcustom rst-toc-insert-number-separator " "
2334 "Separator that goes between the TOC number and the title."
9c5a5c77 2335 :type 'string
b4747519 2336 :group 'rst-toc)
64f6a736 2337(rst-testcover-defcustom)
b4747519
SM
2338
2339;; This is used to avoid having to change the user's mode.
2340(defvar rst-toc-insert-click-keymap
2341 (let ((map (make-sparse-keymap)))
2342 (define-key map [mouse-1] 'rst-toc-mode-mouse-goto)
2343 map)
2344 "(Internal) What happens when you click on propertized text in the TOC.")
2345
2346(defcustom rst-toc-insert-max-level nil
2347 "If non-nil, maximum depth of the inserted TOC."
9c5a5c77 2348 :type '(choice (const nil) integer)
b4747519 2349 :group 'rst-toc)
64f6a736 2350(rst-testcover-defcustom)
b4747519 2351
94e9c286
SM
2352(defun rst-toc-insert (&optional pfxarg)
2353 "Insert a simple text rendering of the table of contents.
2354By default the top level is ignored if there is only one, because
2355we assume that the document will have a single title.
2356
2357If a numeric prefix argument PFXARG is given, insert the TOC up
2358to the specified level.
2359
2360The TOC is inserted indented at the current column."
94e9c286 2361 (interactive "P")
d13c8be6 2362 (rst-reset-section-caches)
6d3f7c2f 2363 (let* (;; Check maximum level override.
94e9c286
SM
2364 (rst-toc-insert-max-level
2365 (if (and (integerp pfxarg) (> (prefix-numeric-value pfxarg) 0))
2366 (prefix-numeric-value pfxarg) rst-toc-insert-max-level))
2367
2368 ;; Get the section tree for the current cursor point.
2369 (sectree-pair
2370 (rst-section-tree-point
d13c8be6 2371 (rst-section-tree)))
94e9c286
SM
2372
2373 ;; Figure out initial indent.
2374 (initial-indent (make-string (current-column) ? ))
2375 (init-point (point)))
2376
2377 (when (cddr sectree-pair)
2378 (rst-toc-insert-node (cdr sectree-pair) 0 initial-indent "")
2379
2380 ;; Fixup for the first line.
2381 (delete-region init-point (+ init-point (length initial-indent)))
2382
2383 ;; Delete the last newline added.
64f6a736 2384 (delete-char -1))))
94e9c286 2385
94e9c286
SM
2386(defun rst-toc-insert-node (node level indent pfx)
2387 "Insert tree node NODE in table-of-contents.
57348c4d 2388Recursive function that does printing of the inserted TOC.
92439579
JB
2389LEVEL is the depth level of the sections in the tree.
2390INDENT is the indentation string. PFX is the prefix numbering,
2391that includes the alignment necessary for all the children of
2392level to align."
94e9c286
SM
2393
2394 ;; Note: we do child numbering from the parent, so we start number the
2395 ;; children one level before we print them.
2396 (let ((do-print (> level 0))
2397 (count 1))
2398 (when do-print
2399 (insert indent)
2400 (let ((b (point)))
2401 (unless (equal rst-toc-insert-style 'plain)
2402 (insert pfx rst-toc-insert-number-separator))
2403 (insert (or (caar node) "[missing node]"))
2404 ;; Add properties to the text, even though in normal text mode it
2405 ;; won't be doing anything for now. Not sure that I want to change
2406 ;; mode stuff. At least the highlighting gives the idea that this
2407 ;; is generated automatically.
2408 (put-text-property b (point) 'mouse-face 'highlight)
2409 (put-text-property b (point) 'rst-toc-target (cadar node))
64f6a736 2410 (put-text-property b (point) 'keymap rst-toc-insert-click-keymap))
94e9c286
SM
2411 (insert "\n")
2412
2413 ;; Prepare indent for children.
2414 (setq indent
2415 (cond
2416 ((eq rst-toc-insert-style 'plain)
2417 (concat indent (make-string rst-toc-indent ? )))
2418
2419 ((eq rst-toc-insert-style 'fixed)
2420 (concat indent (make-string rst-toc-indent ? )))
2421
2422 ((eq rst-toc-insert-style 'aligned)
2423 (concat indent (make-string (+ (length pfx) 2) ? )))
2424
2425 ((eq rst-toc-insert-style 'listed)
2426 (concat (substring indent 0 -3)
64f6a736 2427 (concat (make-string (+ (length pfx) 2) ? ) " - "))))))
94e9c286
SM
2428
2429 (if (or (eq rst-toc-insert-max-level nil)
2430 (< level rst-toc-insert-max-level))
2431 (let ((do-child-numbering (>= level 0))
2432 fmt)
2433 (if do-child-numbering
2434 (progn
6d3f7c2f 2435 ;; Add a separating dot if there is already a prefix.
d13c8be6
SM
2436 (when (> (length pfx) 0)
2437 (string-match (rst-re "[ \t\n]*\\'") pfx)
2438 (setq pfx (concat (replace-match "" t t pfx) ".")))
94e9c286
SM
2439
2440 ;; Calculate the amount of space that the prefix will require
2441 ;; for the numbers.
2442 (if (cdr node)
2443 (setq fmt (format "%%-%dd"
89561f72
PE
2444 (1+ (floor (log (length (cdr node))
2445 10))))))))
94e9c286
SM
2446
2447 (dolist (child (cdr node))
2448 (rst-toc-insert-node child
2449 (1+ level)
2450 indent
2451 (if do-child-numbering
2452 (concat pfx (format fmt count)) pfx))
64f6a736 2453 (incf count))))))
94e9c286
SM
2454
2455
94e9c286
SM
2456(defun rst-toc-update ()
2457 "Automatically find the contents section of a document and update.
2458Updates the inserted TOC if present. You can use this in your
2459file-write hook to always make it up-to-date automatically."
2460 (interactive)
d13c8be6
SM
2461 (save-excursion
2462 ;; Find and delete an existing comment after the first contents directive.
2463 ;; Delete that region.
2464 (goto-char (point-min))
2465 ;; We look for the following and the following only (in other words, if your
2466 ;; syntax differs, this won't work.).
2467 ;;
2468 ;; .. contents:: [...anything here...]
2469 ;; [:field: value]...
2470 ;; ..
2471 ;; XXXXXXXX
2472 ;; XXXXXXXX
2473 ;; [more lines]
2474 (let ((beg (re-search-forward
2475 (rst-re "^" 'exm-sta "contents" 'dcl-tag ".*\n"
2476 "\\(?:" 'hws-sta 'fld-tag ".*\n\\)*" 'exm-tag) nil t))
2477 last-real)
2478 (when beg
2479 ;; Look for the first line that starts at the first column.
2480 (forward-line 1)
2481 (while (and
2482 (< (point) (point-max))
2483 (or (if (looking-at
6d3f7c2f 2484 (rst-re 'hws-sta "\\S ")) ; indented content.
d13c8be6 2485 (setq last-real (point)))
6d3f7c2f 2486 (looking-at (rst-re 'lin-end)))) ; empty line.
d13c8be6
SM
2487 (forward-line 1))
2488 (if last-real
2489 (progn
2490 (goto-char last-real)
2491 (end-of-line)
2492 (delete-region beg (point)))
2493 (goto-char beg))
2494 (insert "\n ")
2495 (rst-toc-insert))))
94e9c286 2496 ;; Note: always return nil, because this may be used as a hook.
d13c8be6 2497 nil)
94e9c286
SM
2498
2499;; Note: we cannot bind the TOC update on file write because it messes with
2500;; undo. If we disable undo, since it adds and removes characters, the
2501;; positions in the undo list are not making sense anymore. Dunno what to do
2502;; with this, it would be nice to update when saving.
2503;;
2504;; (add-hook 'write-contents-hooks 'rst-toc-update-fun)
2505;; (defun rst-toc-update-fun ()
2506;; ;; Disable undo for the write file hook.
2507;; (let ((buffer-undo-list t)) (rst-toc-update) ))
2508
d13c8be6 2509(defalias 'rst-toc-insert-update 'rst-toc-update) ; backwards compat.
94e9c286
SM
2510
2511;;------------------------------------------------------------------------------
2512
2513(defun rst-toc-node (node level)
2514 "Recursive function that does insert NODE at LEVEL in the table-of-contents."
2515
2516 (if (> level 0)
2517 (let ((b (point)))
2518 ;; Insert line text.
2519 (insert (make-string (* rst-toc-indent (1- level)) ? ))
2520 (insert (or (caar node) "[missing node]"))
2521
2522 ;; Highlight lines.
2523 (put-text-property b (point) 'mouse-face 'highlight)
2524
2525 ;; Add link on lines.
2526 (put-text-property b (point) 'rst-toc-target (cadar node))
2527
64f6a736 2528 (insert "\n")))
94e9c286
SM
2529
2530 (dolist (child (cdr node))
2531 (rst-toc-node child (1+ level))))
2532
2533(defun rst-toc-count-lines (node target-node)
2534 "Count the number of lines from NODE to the TARGET-NODE node.
2535This recursive function returns a cons of the number of
92439579
JB
2536additional lines that have been counted for its node and
2537children, and t if the node has been found."
94e9c286
SM
2538
2539 (let ((count 1)
2540 found)
2541 (if (eq node target-node)
2542 (setq found t)
2543 (let ((child (cdr node)))
2544 (while (and child (not found))
2545 (let ((cl (rst-toc-count-lines (car child) target-node)))
2546 (setq count (+ count (car cl))
2547 found (cdr cl)
2548 child (cdr child))))))
2549 (cons count found)))
2550
b4747519
SM
2551(defvar rst-toc-buffer-name "*Table of Contents*"
2552 "Name of the Table of Contents buffer.")
2553
d13c8be6
SM
2554(defvar rst-toc-return-wincfg nil
2555 "Window configuration to which to return when leaving the TOC.")
b4747519 2556
94e9c286
SM
2557
2558(defun rst-toc ()
2559 "Display a table-of-contents.
d13c8be6 2560Finds all the section titles and their adornments in the
94e9c286
SM
2561file, and displays a hierarchically-organized list of the
2562titles, which is essentially a table-of-contents of the
2563document.
2564
2565The Emacs buffer can be navigated, and selecting a section
2566brings the cursor in that section."
2567 (interactive)
d13c8be6
SM
2568 (rst-reset-section-caches)
2569 (let* ((curbuf (list (current-window-configuration) (point-marker)))
2570 (sectree (rst-section-tree))
94e9c286
SM
2571
2572 (our-node (cdr (rst-section-tree-point sectree)))
2573 line
2574
2575 ;; Create a temporary buffer.
64f6a736 2576 (buf (get-buffer-create rst-toc-buffer-name)))
94e9c286
SM
2577
2578 (with-current-buffer buf
2579 (let ((inhibit-read-only t))
2580 (rst-toc-mode)
2581 (delete-region (point-min) (point-max))
2582 (insert (format "Table of Contents: %s\n" (or (caar sectree) "")))
2583 (put-text-property (point-min) (point)
2584 'face (list '(background-color . "gray")))
2585 (rst-toc-node sectree 0)
2586
2587 ;; Count the lines to our found node.
2588 (let ((linefound (rst-toc-count-lines sectree our-node)))
64f6a736 2589 (setq line (if (cdr linefound) (car linefound) 0)))))
94e9c286
SM
2590 (display-buffer buf)
2591 (pop-to-buffer buf)
2592
2593 ;; Save the buffer to return to.
d13c8be6 2594 (set (make-local-variable 'rst-toc-return-wincfg) curbuf)
94e9c286
SM
2595
2596 ;; Move the cursor near the right section in the TOC.
e6ce8c42 2597 (goto-char (point-min))
64f6a736 2598 (forward-line (1- line))))
94e9c286
SM
2599
2600
2601(defun rst-toc-mode-find-section ()
2602 "Get the section from text property at point."
2603 (let ((pos (get-text-property (point) 'rst-toc-target)))
2604 (unless pos
2605 (error "No section on this line"))
2606 (unless (buffer-live-p (marker-buffer pos))
2607 (error "Buffer for this section was killed"))
2608 pos))
2609
d13c8be6
SM
2610;; FIXME: Cursor before or behind the list must be handled properly; before the
2611;; list should jump to the top and behind the list to the last normal
6d3f7c2f 2612;; paragraph.
94e9c286 2613(defun rst-goto-section (&optional kill)
6d3f7c2f 2614 "Go to the section the current line describes.
57348c4d 2615If KILL a TOC buffer is destroyed."
94e9c286
SM
2616 (interactive)
2617 (let ((pos (rst-toc-mode-find-section)))
2618 (when kill
6d3f7c2f 2619 ;; FIXME: This should rather go to `rst-toc-mode-goto-section'.
d13c8be6 2620 (set-window-configuration (car rst-toc-return-wincfg))
94e9c286
SM
2621 (kill-buffer (get-buffer rst-toc-buffer-name)))
2622 (pop-to-buffer (marker-buffer pos))
2623 (goto-char pos)
2624 ;; FIXME: make the recentering conditional on scroll.
2625 (recenter 5)))
2626
2627(defun rst-toc-mode-goto-section ()
92439579 2628 "Go to the section the current line describes and kill the TOC buffer."
94e9c286
SM
2629 (interactive)
2630 (rst-goto-section t))
2631
2632(defun rst-toc-mode-mouse-goto (event)
2633 "In `rst-toc' mode, go to the occurrence whose line you click on.
2634EVENT is the input event."
2635 (interactive "e")
8bbb7dd8 2636 (let ((pos
d13c8be6
SM
2637 (with-current-buffer (window-buffer (posn-window (event-end event)))
2638 (save-excursion
2639 (goto-char (posn-point (event-end event)))
8bbb7dd8 2640 (rst-toc-mode-find-section)))))
94e9c286
SM
2641 (pop-to-buffer (marker-buffer pos))
2642 (goto-char pos)
2643 (recenter 5)))
2644
2645(defun rst-toc-mode-mouse-goto-kill (event)
6d3f7c2f
SM
2646 "Same as `rst-toc-mode-mouse-goto', but kill TOC buffer as well.
2647EVENT is the input event."
94e9c286
SM
2648 (interactive "e")
2649 (call-interactively 'rst-toc-mode-mouse-goto event)
2650 (kill-buffer (get-buffer rst-toc-buffer-name)))
2651
94e9c286 2652(defun rst-toc-quit-window ()
b4747519 2653 "Leave the current TOC buffer."
94e9c286 2654 (interactive)
d13c8be6
SM
2655 (let ((retbuf rst-toc-return-wincfg))
2656 (set-window-configuration (car retbuf))
2657 (goto-char (cadr retbuf))))
94e9c286
SM
2658
2659(defvar rst-toc-mode-map
2660 (let ((map (make-sparse-keymap)))
2661 (define-key map [mouse-1] 'rst-toc-mode-mouse-goto-kill)
2662 (define-key map [mouse-2] 'rst-toc-mode-mouse-goto)
2663 (define-key map "\C-m" 'rst-toc-mode-goto-section)
2664 (define-key map "f" 'rst-toc-mode-goto-section)
2665 (define-key map "q" 'rst-toc-quit-window)
2666 (define-key map "z" 'kill-this-buffer)
2667 map)
2668 "Keymap for `rst-toc-mode'.")
2669
2670(put 'rst-toc-mode 'mode-class 'special)
2671
b4747519
SM
2672;; Could inherit from the new `special-mode'.
2673(define-derived-mode rst-toc-mode nil "ReST-TOC"
94e9c286 2674 "Major mode for output from \\[rst-toc], the table-of-contents for the document."
b4747519 2675 (setq buffer-read-only t))
94e9c286
SM
2676
2677;; Note: use occur-mode (replace.el) as a good example to complete missing
2678;; features.
2679
94e9c286 2680;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d13c8be6
SM
2681;; Section movement commands
2682;; =========================
94e9c286
SM
2683
2684(defun rst-forward-section (&optional offset)
d13c8be6 2685 "Skip to the next reStructuredText section title.
57348c4d
JB
2686OFFSET specifies how many titles to skip. Use a negative OFFSET
2687to move backwards in the file (default is to use 1)."
94e9c286 2688 (interactive)
d13c8be6 2689 (rst-reset-section-caches)
94e9c286
SM
2690 (let* (;; Default value for offset.
2691 (offset (or offset 1))
2692
d13c8be6
SM
2693 ;; Get all the adornments in the file, with their line numbers.
2694 (allados (rst-find-all-adornments))
94e9c286
SM
2695
2696 ;; Get the current line.
2697 (curline (line-number-at-pos))
2698
d13c8be6 2699 (cur allados)
64f6a736 2700 (idx 0))
94e9c286 2701
d13c8be6 2702 ;; Find the index of the "next" adornment w.r.t. to the current line.
94e9c286
SM
2703 (while (and cur (< (caar cur) curline))
2704 (setq cur (cdr cur))
2705 (incf idx))
d13c8be6 2706 ;; 'cur' is the adornment on or following the current line.
94e9c286
SM
2707
2708 (if (and (> offset 0) cur (= (caar cur) curline))
2709 (incf idx))
2710
2711 ;; Find the final index.
2712 (setq idx (+ idx (if (> offset 0) (- offset 1) offset)))
d13c8be6 2713 (setq cur (nth idx allados))
94e9c286
SM
2714
2715 ;; If the index is positive, goto the line, otherwise go to the buffer
2716 ;; boundaries.
2717 (if (and cur (>= idx 0))
e6ce8c42
GM
2718 (progn
2719 (goto-char (point-min))
2720 (forward-line (1- (car cur))))
64f6a736 2721 (if (> offset 0) (goto-char (point-max)) (goto-char (point-min))))))
94e9c286
SM
2722
2723(defun rst-backward-section ()
e6438428 2724 "Like `rst-forward-section', except move back one title."
94e9c286
SM
2725 (interactive)
2726 (rst-forward-section -1))
2727
6d3f7c2f
SM
2728;; FIXME: What is `allow-extend' for?
2729(defun rst-mark-section (&optional count allow-extend)
2730 "Select COUNT sections around point.
2731Mark following sections for positive COUNT or preceding sections
2732for negative COUNT."
94e9c286
SM
2733 ;; Cloned from mark-paragraph.
2734 (interactive "p\np")
6d3f7c2f
SM
2735 (unless count (setq count 1))
2736 (when (zerop count)
94e9c286
SM
2737 (error "Cannot mark zero sections"))
2738 (cond ((and allow-extend
2739 (or (and (eq last-command this-command) (mark t))
64f6a736 2740 (use-region-p)))
94e9c286
SM
2741 (set-mark
2742 (save-excursion
2743 (goto-char (mark))
6d3f7c2f 2744 (rst-forward-section count)
94e9c286
SM
2745 (point))))
2746 (t
6d3f7c2f 2747 (rst-forward-section count)
94e9c286 2748 (push-mark nil t t)
6d3f7c2f 2749 (rst-forward-section (- count)))))
94e9c286 2750
94e9c286
SM
2751\f
2752;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2753;; Functions to work on item lists (e.g. indent/dedent, enumerate), which are
2754;; always 2 or 3 characters apart horizontally with rest.
2755
94e9c286 2756(defun rst-find-leftmost-column (beg end)
d13c8be6
SM
2757 "Return the leftmost column in region BEG to END."
2758 (let (mincol)
94e9c286
SM
2759 (save-excursion
2760 (goto-char beg)
2761 (while (< (point) end)
2762 (back-to-indentation)
d13c8be6
SM
2763 (unless (looking-at (rst-re 'lin-end))
2764 (setq mincol (if mincol
2765 (min mincol (current-column))
2766 (current-column))))
2767 (forward-line 1)))
94e9c286
SM
2768 mincol))
2769
6d3f7c2f
SM
2770;; FIXME: This definition is old and deprecated. We need to move to the newer
2771;; version below.
94e9c286
SM
2772(defmacro rst-iterate-leftmost-paragraphs
2773 (beg end first-only body-consequent body-alternative)
6d3f7c2f
SM
2774 ;; FIXME: The following comment is pretty useless.
2775 "Call FUN at the beginning of each line, with an argument that
94e9c286
SM
2776specifies whether we are at the first line of a paragraph that
2777starts at the leftmost column of the given region BEG and END.
2778Set FIRST-ONLY to true if you want to callback on the first line
2779of each paragraph only."
2780 `(save-excursion
2781 (let ((leftcol (rst-find-leftmost-column ,beg ,end))
8bbb7dd8 2782 (endm (copy-marker ,end)))
94e9c286 2783
6d3f7c2f 2784 (do* (;; Iterate lines.
94e9c286
SM
2785 (l (progn (goto-char ,beg) (back-to-indentation))
2786 (progn (forward-line 1) (back-to-indentation)))
2787
2788 (previous nil valid)
2789
2790 (curcol (current-column)
2791 (current-column))
2792
2793 (valid (and (= curcol leftcol)
d13c8be6 2794 (not (looking-at (rst-re 'lin-end))))
94e9c286 2795 (and (= curcol leftcol)
64f6a736 2796 (not (looking-at (rst-re 'lin-end))))))
b4747519 2797 ((>= (point) endm))
94e9c286
SM
2798
2799 (if (if ,first-only
2800 (and valid (not previous))
2801 valid)
2802 ,body-consequent
64f6a736 2803 ,body-alternative)))))
94e9c286 2804
6d3f7c2f
SM
2805;; FIXME: This needs to be refactored. Probably this is simply a function
2806;; applying BODY rather than a macro.
94e9c286
SM
2807(defmacro rst-iterate-leftmost-paragraphs-2 (spec &rest body)
2808 "Evaluate BODY for each line in region defined by BEG END.
2809LEFTMOST is set to true if the line is one of the leftmost of the
b4747519 2810entire paragraph. PARABEGIN is set to true if the line is the
94e9c286 2811first of a paragraph."
b4747519 2812 (declare (indent 1) (debug (sexp body)))
94e9c286
SM
2813 (destructuring-bind
2814 (beg end parabegin leftmost isleftmost isempty) spec
2815
2816 `(save-excursion
2817 (let ((,leftmost (rst-find-leftmost-column ,beg ,end))
8bbb7dd8 2818 (endm (copy-marker ,end)))
94e9c286 2819
6d3f7c2f 2820 (do* (;; Iterate lines.
94e9c286
SM
2821 (l (progn (goto-char ,beg) (back-to-indentation))
2822 (progn (forward-line 1) (back-to-indentation)))
2823
2824 (empty-line-previous nil ,isempty)
2825
d13c8be6
SM
2826 (,isempty (looking-at (rst-re 'lin-end))
2827 (looking-at (rst-re 'lin-end)))
94e9c286
SM
2828
2829 (,parabegin (not ,isempty)
2830 (and empty-line-previous
2831 (not ,isempty)))
2832
2833 (,isleftmost (and (not ,isempty)
2834 (= (current-column) ,leftmost))
2835 (and (not ,isempty)
64f6a736 2836 (= (current-column) ,leftmost))))
b4747519 2837 ((>= (point) endm))
94e9c286 2838
64f6a736 2839 (progn ,@body))))))
94e9c286 2840
d13c8be6
SM
2841;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2842;; Indentation
2843
2844;; FIXME: At the moment only block comments with leading empty comment line are
6d3f7c2f
SM
2845;; supported. Comment lines with leading comment markup should be also
2846;; supported. May be a customizable option could control which style to
2847;; prefer.
d13c8be6 2848
c846da43 2849(defgroup rst-indent nil "Settings for indentation in reStructuredText.
d13c8be6 2850
c846da43 2851In reStructuredText indentation points are usually determined by
57348c4d
JB
2852preceding lines. Sometimes the syntax allows arbitrary indentation
2853points such as where to start the first line following a directive.
2854These indentation widths can be customized here."
d13c8be6
SM
2855 :group 'rst
2856 :package-version '(rst . "1.1.0"))
2857
2858(define-obsolete-variable-alias
18dec750 2859 'rst-shift-basic-offset 'rst-indent-width "rst 1.0.0")
d13c8be6
SM
2860(defcustom rst-indent-width 2
2861 "Indentation when there is no more indentation point given."
2862 :group 'rst-indent
2863 :type '(integer))
64f6a736 2864(rst-testcover-defcustom)
d13c8be6
SM
2865
2866(defcustom rst-indent-field 3
6d3f7c2f 2867 "Indentation for first line after a field or 0 to always indent for content."
d13c8be6 2868 :group 'rst-indent
78d876b9 2869 :package-version '(rst . "1.1.0")
d13c8be6 2870 :type '(integer))
64f6a736 2871(rst-testcover-defcustom)
d13c8be6
SM
2872
2873(defcustom rst-indent-literal-normal 3
6d3f7c2f 2874 "Default indentation for literal block after a markup on an own line."
d13c8be6 2875 :group 'rst-indent
78d876b9 2876 :package-version '(rst . "1.1.0")
d13c8be6 2877 :type '(integer))
64f6a736 2878(rst-testcover-defcustom)
d13c8be6
SM
2879
2880(defcustom rst-indent-literal-minimized 2
6d3f7c2f 2881 "Default indentation for literal block after a minimized markup."
d13c8be6 2882 :group 'rst-indent
78d876b9 2883 :package-version '(rst . "1.1.0")
d13c8be6 2884 :type '(integer))
64f6a736 2885(rst-testcover-defcustom)
d13c8be6
SM
2886
2887(defcustom rst-indent-comment 3
c846da43 2888 "Default indentation for first line of a comment."
d13c8be6 2889 :group 'rst-indent
78d876b9 2890 :package-version '(rst . "1.1.0")
d13c8be6 2891 :type '(integer))
64f6a736 2892(rst-testcover-defcustom)
d13c8be6
SM
2893
2894;; FIXME: Must consider other tabs:
6d3f7c2f
SM
2895;; * Line blocks
2896;; * Definition lists
2897;; * Option lists
d13c8be6
SM
2898(defun rst-line-tabs ()
2899 "Return tabs of the current line or nil for no tab.
2900The list is sorted so the tab where writing continues most likely
6d3f7c2f
SM
2901is the first one. Each tab is of the form (COLUMN . INNER).
2902COLUMN is the column of the tab. INNER is non-nil if this is an
2903inner tab. I.e. a tab which does come from the basic indentation
d13c8be6
SM
2904and not from inner alignment points."
2905 (save-excursion
2906 (forward-line 0)
2907 (save-match-data
2908 (unless (looking-at (rst-re 'lin-end))
2909 (back-to-indentation)
48d1354e 2910 ;; Current indentation is always the least likely tab.
d13c8be6 2911 (let ((tabs (list (list (point) 0 nil)))) ; (POINT OFFSET INNER)
6d3f7c2f 2912 ;; Push inner tabs more likely to continue writing.
d13c8be6 2913 (cond
6d3f7c2f 2914 ;; Item.
d13c8be6
SM
2915 ((looking-at (rst-re '(:grp itmany-tag hws-sta) '(:grp "\\S ") "?"))
2916 (when (match-string 2)
2917 (push (list (match-beginning 2) 0 t) tabs)))
6d3f7c2f 2918 ;; Field.
d13c8be6
SM
2919 ((looking-at (rst-re '(:grp fld-tag) '(:grp hws-tag)
2920 '(:grp "\\S ") "?"))
2921 (unless (zerop rst-indent-field)
2922 (push (list (match-beginning 1) rst-indent-field t) tabs))
2923 (if (match-string 3)
2924 (push (list (match-beginning 3) 0 t) tabs)
2925 (if (zerop rst-indent-field)
2926 (push (list (match-end 2)
2927 (if (string= (match-string 2) "") 1 0)
2928 t) tabs))))
6d3f7c2f 2929 ;; Directive.
d13c8be6
SM
2930 ((looking-at (rst-re 'dir-sta-3 '(:grp "\\S ") "?"))
2931 (push (list (match-end 1) 0 t) tabs)
2932 (unless (string= (match-string 2) "")
2933 (push (list (match-end 2) 0 t) tabs))
2934 (when (match-string 4)
2935 (push (list (match-beginning 4) 0 t) tabs)))
6d3f7c2f 2936 ;; Footnote or citation definition.
d13c8be6
SM
2937 ((looking-at (rst-re 'fnc-sta-2 '(:grp "\\S ") "?"))
2938 (push (list (match-end 1) 0 t) tabs)
2939 (when (match-string 3)
2940 (push (list (match-beginning 3) 0 t) tabs)))
6d3f7c2f 2941 ;; Comment.
d13c8be6
SM
2942 ((looking-at (rst-re 'cmt-sta-1))
2943 (push (list (point) rst-indent-comment t) tabs)))
6d3f7c2f 2944 ;; Start of literal block.
d13c8be6
SM
2945 (when (looking-at (rst-re 'lit-sta-2))
2946 (let ((tab0 (first tabs)))
2947 (push (list (first tab0)
2948 (+ (second tab0)
2949 (if (match-string 1)
2950 rst-indent-literal-minimized
2951 rst-indent-literal-normal))
2952 t) tabs)))
2953 (mapcar (lambda (tab)
2954 (goto-char (first tab))
2955 (cons (+ (current-column) (second tab)) (third tab)))
2956 tabs))))))
2957
2958(defun rst-compute-tabs (pt)
2959 "Build the list of possible tabs for all lines above.
57348c4d
JB
2960Search backwards from point PT to build the list of possible tabs.
2961Return a list of tabs sorted by likeliness to continue writing
2962like `rst-line-tabs'. Nearer lines have generally a higher
2963likeliness than farther lines. Return nil if no tab is found in
2964the text above."
d13c8be6
SM
2965 (save-excursion
2966 (goto-char pt)
6d3f7c2f
SM
2967 (let (leftmost ; Leftmost column found so far.
2968 innermost ; Leftmost column for inner tab.
d13c8be6
SM
2969 tablist)
2970 (while (and (zerop (forward-line -1))
2971 (or (not leftmost)
2972 (> leftmost 0)))
2973 (let* ((tabs (rst-line-tabs))
2974 (leftcol (if tabs (apply 'min (mapcar 'car tabs)))))
2975 (when tabs
6d3f7c2f 2976 ;; Consider only lines indented less or same if not INNERMOST.
d13c8be6
SM
2977 (when (or (not leftmost)
2978 (< leftcol leftmost)
2979 (and (not innermost) (= leftcol leftmost)))
2980 (dolist (tab tabs)
2981 (let ((inner (cdr tab))
2982 (newcol (car tab)))
2983 (when (and
2984 (or
2985 (and (not inner)
2986 (or (not leftmost)
2987 (< newcol leftmost)))
2988 (and inner
2989 (or (not innermost)
2990 (< newcol innermost))))
2991 (not (memq newcol tablist)))
2992 (push newcol tablist))))
7b4cdbf4 2993 (setq innermost (if (rst-some (mapcar 'cdr tabs)) ; Has inner.
d13c8be6
SM
2994 leftcol
2995 innermost))
2996 (setq leftmost leftcol)))))
2997 (nreverse tablist))))
2998
2999(defun rst-indent-line (&optional dflt)
3000 "Indent current line to next best reStructuredText tab.
3001The next best tab is taken from the tab list returned by
6d3f7c2f
SM
3002`rst-compute-tabs' which is used in a cyclic manner. If the
3003current indentation does not end on a tab use the first one. If
3004the current indentation is on a tab use the next tab. This allows
d13c8be6 3005a repeated use of \\[indent-for-tab-command] to cycle through all
6d3f7c2f
SM
3006possible tabs. If no indentation is possible return `noindent' or
3007use DFLT. Return the indentation indented to. When point is in
3008indentation it ends up at its end. Otherwise the point is kept
d13c8be6
SM
3009relative to the content."
3010 (let* ((pt (point-marker))
3011 (cur (current-indentation))
3012 (clm (current-column))
3013 (tabs (rst-compute-tabs (point)))
7b4cdbf4 3014 (fnd (rst-position cur tabs))
d13c8be6
SM
3015 ind)
3016 (if (and (not tabs) (not dflt))
3017 'noindent
3018 (if (not tabs)
3019 (setq ind dflt)
3020 (if (not fnd)
3021 (setq fnd 0)
3022 (setq fnd (1+ fnd))
3023 (if (>= fnd (length tabs))
3024 (setq fnd 0)))
3025 (setq ind (nth fnd tabs)))
3026 (indent-line-to ind)
3027 (if (> clm cur)
3028 (goto-char pt))
3029 (set-marker pt nil)
3030 ind)))
3031
3032(defun rst-shift-region (beg end cnt)
3033 "Shift region BEG to END by CNT tabs.
3034Shift by one tab to the right (CNT > 0) or left (CNT < 0) or
6d3f7c2f
SM
3035remove all indentation (CNT = 0). A tab is taken from the text
3036above. If no suitable tab is found `rst-indent-width' is used."
d13c8be6
SM
3037 (interactive "r\np")
3038 (let ((tabs (sort (rst-compute-tabs beg) (lambda (x y) (<= x y))))
3039 (leftmostcol (rst-find-leftmost-column beg end)))
3040 (when (or (> leftmostcol 0) (> cnt 0))
6d3f7c2f 3041 ;; Apply the indent.
d13c8be6
SM
3042 (indent-rigidly
3043 beg end
3044 (if (zerop cnt)
3045 (- leftmostcol)
6d3f7c2f 3046 ;; Find the next tab after the leftmost column.
d13c8be6
SM
3047 (let* ((cmp (if (> cnt 0) '> '<))
3048 (tabs (if (> cnt 0) tabs (reverse tabs)))
3049 (len (length tabs))
7b4cdbf4 3050 (dir (rst-signum cnt)) ; Direction to take.
6d3f7c2f
SM
3051 (abs (abs cnt)) ; Absolute number of steps to take.
3052 ;; Get the position of the first tab beyond leftmostcol.
7b4cdbf4
SM
3053 (fnd (lexical-let ((cmp cmp)
3054 (leftmostcol leftmostcol)) ; Create closure.
3055 (rst-position-if (lambda (elt)
3056 (funcall cmp elt leftmostcol))
3057 tabs)))
6d3f7c2f 3058 ;; Virtual position of tab.
d13c8be6
SM
3059 (pos (+ (or fnd len) (1- abs)))
3060 (tab (if (< pos len)
6d3f7c2f 3061 ;; Tab exists - use it.
d13c8be6 3062 (nth pos tabs)
6d3f7c2f 3063 ;; Column needs to be computed.
d13c8be6 3064 (let ((col (+ (or (car (last tabs)) leftmostcol)
6d3f7c2f
SM
3065 ;; Base on last known column.
3066 (* (- pos (1- len)) ; Distance left.
3067 dir ; Direction to take.
d13c8be6
SM
3068 rst-indent-width))))
3069 (if (< col 0) 0 col)))))
3070 (- tab leftmostcol)))))))
3071
3072;; FIXME: A paragraph with an (incorrectly) indented second line is not filled
6d3f7c2f 3073;; correctly::
d13c8be6 3074;;
6d3f7c2f
SM
3075;; Some start
3076;; continued wrong
d13c8be6
SM
3077(defun rst-adaptive-fill ()
3078 "Return fill prefix found at point.
3079Value for `adaptive-fill-function'."
3080 (let ((fnd (if (looking-at adaptive-fill-regexp)
3081 (match-string-no-properties 0))))
3082 (if (save-match-data
3083 (not (string-match comment-start-skip fnd)))
6d3f7c2f 3084 ;; An non-comment prefix is fine.
d13c8be6 3085 fnd
6d3f7c2f 3086 ;; Matches a comment - return whitespace instead.
d13c8be6
SM
3087 (make-string (-
3088 (save-excursion
3089 (goto-char (match-end 0))
3090 (current-column))
3091 (save-excursion
3092 (goto-char (match-beginning 0))
3093 (current-column))) ? ))))
3094
3095;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3096;; Comments
3097
3098(defun rst-comment-line-break (&optional soft)
3099 "Break line and indent, continuing reStructuredText comment if within one.
6d3f7c2f
SM
3100Value for `comment-line-break-function'. If SOFT use soft
3101newlines as mandated by `comment-line-break-function'."
d13c8be6
SM
3102 (if soft
3103 (insert-and-inherit ?\n)
3104 (newline 1))
3105 (save-excursion
3106 (forward-char -1)
3107 (delete-horizontal-space))
3108 (delete-horizontal-space)
3109 (let ((tabs (rst-compute-tabs (point))))
3110 (when tabs
3111 (indent-line-to (car tabs)))))
3112
3113(defun rst-comment-indent ()
3114 "Return indentation for current comment line."
3115 (car (rst-compute-tabs (point))))
3116
3117(defun rst-comment-insert-comment ()
3118 "Insert a comment in the current line."
3119 (rst-indent-line 0)
3120 (insert comment-start))
3121
3122(defun rst-comment-region (beg end &optional arg)
6d3f7c2f 3123 "Comment or uncomment the current region.
33848c48 3124Region is from BEG to END. Uncomment if ARG."
d13c8be6
SM
3125 (save-excursion
3126 (if (consp arg)
3127 (rst-uncomment-region beg end arg)
3128 (goto-char beg)
3129 (let ((ind (current-indentation))
3130 bol)
3131 (forward-line 0)
3132 (setq bol (point))
3133 (indent-rigidly bol end rst-indent-comment)
3134 (goto-char bol)
3135 (open-line 1)
3136 (indent-line-to ind)
3137 (insert (comment-string-strip comment-start t t))))))
3138
57348c4d 3139(defun rst-uncomment-region (beg end &optional _arg)
d13c8be6 3140 "Uncomment the current region.
6d3f7c2f 3141Region is from BEG to END. ARG is ignored"
d13c8be6
SM
3142 (save-excursion
3143 (let (bol eol)
3144 (goto-char beg)
3145 (forward-line 0)
3146 (setq bol (point))
3147 (forward-line 1)
3148 (setq eol (point))
3149 (indent-rigidly eol end (- rst-indent-comment))
3150 (delete-region bol eol))))
94e9c286 3151
b4747519
SM
3152;;------------------------------------------------------------------------------
3153
6d3f7c2f
SM
3154;; FIXME: These next functions should become part of a larger effort to redo
3155;; the bullets in bulleted lists. The enumerate would just be one of
3156;; the possible outputs.
b4747519 3157;;
d13c8be6 3158;; FIXME: We need to do the enumeration removal as well.
b4747519 3159
d13c8be6 3160(defun rst-enumerate-region (beg end all)
b4747519 3161 "Add enumeration to all the leftmost paragraphs in the given region.
d13c8be6 3162The region is specified between BEG and END. With ALL,
b4747519 3163do all lines instead of just paragraphs."
d13c8be6 3164 (interactive "r\nP")
b4747519
SM
3165 (let ((count 0)
3166 (last-insert-len nil))
3167 (rst-iterate-leftmost-paragraphs
d13c8be6 3168 beg end (not all)
b4747519
SM
3169 (let ((ins-string (format "%d. " (incf count))))
3170 (setq last-insert-len (length ins-string))
3171 (insert ins-string))
64f6a736 3172 (insert (make-string last-insert-len ?\ )))))
b4747519 3173
d13c8be6 3174(defun rst-bullet-list-region (beg end all)
b4747519 3175 "Add bullets to all the leftmost paragraphs in the given region.
d13c8be6 3176The region is specified between BEG and END. With ALL,
b4747519 3177do all lines instead of just paragraphs."
d13c8be6 3178 (interactive "r\nP")
b4747519 3179 (rst-iterate-leftmost-paragraphs
d13c8be6
SM
3180 beg end (not all)
3181 (insert (car rst-preferred-bullets) " ")
64f6a736 3182 (insert " ")))
b4747519 3183
6d3f7c2f
SM
3184;; FIXME: Does not deal with a varying number of digits appropriately.
3185;; FIXME: Does not deal with multiple levels independently.
3186;; FIXME: Does not indent a multiline item correctly.
94e9c286 3187(defun rst-convert-bullets-to-enumeration (beg end)
d13c8be6 3188 "Convert the bulleted and enumerated items in the region to enumerated lists.
6d3f7c2f 3189Renumber as necessary. Region is from BEG to END."
94e9c286
SM
3190 (interactive "r")
3191 (let* (;; Find items and convert the positions to markers.
3192 (items (mapcar
3193 (lambda (x)
8bbb7dd8 3194 (cons (copy-marker (car x))
94e9c286 3195 (cdr x)))
d13c8be6 3196 (rst-find-pfx-in-region beg end (rst-re 'itmany-sta-1))))
64f6a736 3197 (count 1))
94e9c286
SM
3198 (save-excursion
3199 (dolist (x items)
3200 (goto-char (car x))
d13c8be6
SM
3201 (looking-at (rst-re 'itmany-beg-1))
3202 (replace-match (format "%d." count) nil nil nil 1)
64f6a736 3203 (incf count)))))
94e9c286
SM
3204
3205;;------------------------------------------------------------------------------
3206
3207(defun rst-line-block-region (rbeg rend &optional pfxarg)
b4747519 3208 "Toggle line block prefixes for a region.
6d3f7c2f 3209Region is from RBEG to REND. With PFXARG set the empty lines too."
94e9c286
SM
3210 (interactive "r\nP")
3211 (let ((comment-start "| ")
3212 (comment-end "")
3213 (comment-start-skip "| ")
3214 (comment-style 'indent)
b4747519 3215 (force (not (not pfxarg))))
94e9c286 3216 (rst-iterate-leftmost-paragraphs-2
b4747519
SM
3217 (rbeg rend parbegin leftmost isleft isempty)
3218 (when (or force (not isempty))
3219 (move-to-column leftmost force)
3220 (delete-region (point) (+ (point) (- (current-indentation) leftmost)))
3221 (insert "| ")))))
94e9c286
SM
3222
3223
3224\f
3225;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d13c8be6
SM
3226;; Font lock
3227;; =========
94e9c286
SM
3228
3229(require 'font-lock)
3230
6d3f7c2f 3231;; FIXME: The obsolete variables need to disappear.
d13c8be6 3232
7b4cdbf4
SM
3233;; The following versions have been done inside Emacs and should not be
3234;; replaced by `:package-version' attributes until a change.
d8a52e15 3235
92439579 3236(defgroup rst-faces nil "Faces used in Rst Mode."
94e9c286
SM
3237 :group 'rst
3238 :group 'faces
3239 :version "21.1")
3240
2b1400b9
GM
3241(defface rst-block '((t :inherit font-lock-keyword-face))
3242 "Face used for all syntax marking up a special block."
3243 :version "24.1"
3244 :group 'rst-faces)
3245
3246(defcustom rst-block-face 'rst-block
b4747519 3247 "All syntax marking up a special block."
2b1400b9 3248 :version "24.1"
94e9c286
SM
3249 :group 'rst-faces
3250 :type '(face))
64f6a736 3251(rst-testcover-defcustom)
2b1400b9
GM
3252(make-obsolete-variable 'rst-block-face
3253 "customize the face `rst-block' instead."
3254 "24.1")
3255
3256(defface rst-external '((t :inherit font-lock-type-face))
3257 "Face used for field names and interpreted text."
3258 :version "24.1"
3259 :group 'rst-faces)
94e9c286 3260
2b1400b9 3261(defcustom rst-external-face 'rst-external
b4747519 3262 "Field names and interpreted text."
2b1400b9 3263 :version "24.1"
94e9c286
SM
3264 :group 'rst-faces
3265 :type '(face))
64f6a736 3266(rst-testcover-defcustom)
2b1400b9
GM
3267(make-obsolete-variable 'rst-external-face
3268 "customize the face `rst-external' instead."
3269 "24.1")
94e9c286 3270
2b1400b9
GM
3271(defface rst-definition '((t :inherit font-lock-function-name-face))
3272 "Face used for all other defining constructs."
3273 :version "24.1"
3274 :group 'rst-faces)
3275
3276(defcustom rst-definition-face 'rst-definition
b4747519 3277 "All other defining constructs."
2b1400b9 3278 :version "24.1"
94e9c286
SM
3279 :group 'rst-faces
3280 :type '(face))
64f6a736 3281(rst-testcover-defcustom)
2b1400b9
GM
3282(make-obsolete-variable 'rst-definition-face
3283 "customize the face `rst-definition' instead."
3284 "24.1")
3285
3286;; XEmacs compatibility (?).
3287(defface rst-directive (if (boundp 'font-lock-builtin-face)
3288 '((t :inherit font-lock-builtin-face))
3289 '((t :inherit font-lock-preprocessor-face)))
3290 "Face used for directives and roles."
3291 :version "24.1"
3292 :group 'rst-faces)
3293
3294(defcustom rst-directive-face 'rst-directive
b4747519 3295 "Directives and roles."
94e9c286
SM
3296 :group 'rst-faces
3297 :type '(face))
64f6a736 3298(rst-testcover-defcustom)
2b1400b9
GM
3299(make-obsolete-variable 'rst-directive-face
3300 "customize the face `rst-directive' instead."
3301 "24.1")
94e9c286 3302
2b1400b9
GM
3303(defface rst-comment '((t :inherit font-lock-comment-face))
3304 "Face used for comments."
3305 :version "24.1"
3306 :group 'rst-faces)
3307
3308(defcustom rst-comment-face 'rst-comment
b4747519 3309 "Comments."
2b1400b9 3310 :version "24.1"
94e9c286
SM
3311 :group 'rst-faces
3312 :type '(face))
64f6a736 3313(rst-testcover-defcustom)
2b1400b9
GM
3314(make-obsolete-variable 'rst-comment-face
3315 "customize the face `rst-comment' instead."
3316 "24.1")
3317
3318(defface rst-emphasis1 '((t :inherit italic))
3319 "Face used for simple emphasis."
3320 :version "24.1"
3321 :group 'rst-faces)
94e9c286 3322
2b1400b9 3323(defcustom rst-emphasis1-face 'rst-emphasis1
b4747519 3324 "Simple emphasis."
2b1400b9 3325 :version "24.1"
94e9c286
SM
3326 :group 'rst-faces
3327 :type '(face))
64f6a736 3328(rst-testcover-defcustom)
2b1400b9
GM
3329(make-obsolete-variable 'rst-emphasis1-face
3330 "customize the face `rst-emphasis1' instead."
3331 "24.1")
94e9c286 3332
2b1400b9
GM
3333(defface rst-emphasis2 '((t :inherit bold))
3334 "Face used for double emphasis."
3335 :version "24.1"
3336 :group 'rst-faces)
3337
3338(defcustom rst-emphasis2-face 'rst-emphasis2
b4747519 3339 "Double emphasis."
94e9c286
SM
3340 :group 'rst-faces
3341 :type '(face))
64f6a736 3342(rst-testcover-defcustom)
2b1400b9
GM
3343(make-obsolete-variable 'rst-emphasis2-face
3344 "customize the face `rst-emphasis2' instead."
3345 "24.1")
3346
3347(defface rst-literal '((t :inherit font-lock-string-face))
3348 "Face used for literal text."
3349 :version "24.1"
3350 :group 'rst-faces)
94e9c286 3351
2b1400b9 3352(defcustom rst-literal-face 'rst-literal
b4747519 3353 "Literal text."
2b1400b9 3354 :version "24.1"
94e9c286
SM
3355 :group 'rst-faces
3356 :type '(face))
64f6a736 3357(rst-testcover-defcustom)
2b1400b9
GM
3358(make-obsolete-variable 'rst-literal-face
3359 "customize the face `rst-literal' instead."
3360 "24.1")
94e9c286 3361
2b1400b9
GM
3362(defface rst-reference '((t :inherit font-lock-variable-name-face))
3363 "Face used for references to a definition."
3364 :version "24.1"
3365 :group 'rst-faces)
3366
3367(defcustom rst-reference-face 'rst-reference
b4747519 3368 "References to a definition."
2b1400b9 3369 :version "24.1"
94e9c286
SM
3370 :group 'rst-faces
3371 :type '(face))
64f6a736 3372(rst-testcover-defcustom)
2b1400b9
GM
3373(make-obsolete-variable 'rst-reference-face
3374 "customize the face `rst-reference' instead."
3375 "24.1")
94e9c286 3376
d8a52e15
SM
3377(defface rst-transition '((t :inherit font-lock-keyword-face))
3378 "Face used for a transition."
7b4cdbf4 3379 :package-version '(rst . "1.3.0")
d8a52e15
SM
3380 :group 'rst-faces)
3381
3382(defface rst-adornment '((t :inherit font-lock-keyword-face))
3383 "Face used for the adornment of a section header."
7b4cdbf4 3384 :package-version '(rst . "1.3.0")
d8a52e15
SM
3385 :group 'rst-faces)
3386
94e9c286
SM
3387;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3388
48a24920
SM
3389(dolist (var '(rst-level-face-max rst-level-face-base-color
3390 rst-level-face-base-light
3391 rst-level-face-format-light
3392 rst-level-face-step-light
3393 rst-level-1-face
3394 rst-level-2-face
3395 rst-level-3-face
3396 rst-level-4-face
3397 rst-level-5-face
3398 rst-level-6-face))
3399 (make-obsolete-variable var "customize the faces `rst-level-*' instead."
3400 "24.3"))
3401
3402;; Define faces for the first 6 levels. More levels are possible, however.
3403(defface rst-level-1 '((((background light)) (:background "grey85"))
3404 (((background dark)) (:background "grey15")))
3405 "Default face for section title text at level 1."
3406 :package-version '(rst . "1.4.0"))
3407
3408(defface rst-level-2 '((((background light)) (:background "grey78"))
3409 (((background dark)) (:background "grey22")))
3410 "Default face for section title text at level 2."
3411 :package-version '(rst . "1.4.0"))
3412
3413(defface rst-level-3 '((((background light)) (:background "grey71"))
3414 (((background dark)) (:background "grey29")))
3415 "Default face for section title text at level 3."
3416 :package-version '(rst . "1.4.0"))
3417
3418(defface rst-level-4 '((((background light)) (:background "grey64"))
3419 (((background dark)) (:background "grey36")))
3420 "Default face for section title text at level 4."
3421 :package-version '(rst . "1.4.0"))
3422
3423(defface rst-level-5 '((((background light)) (:background "grey57"))
3424 (((background dark)) (:background "grey43")))
3425 "Default face for section title text at level 5."
3426 :package-version '(rst . "1.4.0"))
3427
3428(defface rst-level-6 '((((background light)) (:background "grey50"))
3429 (((background dark)) (:background "grey50")))
3430 "Default face for section title text at level 6."
3431 :package-version '(rst . "1.4.0"))
94e9c286
SM
3432
3433(defcustom rst-adornment-faces-alist
48a24920
SM
3434 '((t . rst-transition)
3435 (nil . rst-adornment)
3436 (1 . rst-level-1)
3437 (2 . rst-level-2)
3438 (3 . rst-level-3)
3439 (4 . rst-level-4)
3440 (5 . rst-level-5)
3441 (6 . rst-level-6))
3442 "Faces for the various adornment types.
d8a52e15
SM
3443Key is a number (for the section title text of that level
3444starting with 1), t (for transitions) or nil (for section title
48a24920
SM
3445adornment). If you need levels beyond 6 you have to define faces
3446of your own."
94e9c286
SM
3447 :group 'rst-faces
3448 :type '(alist
3449 :key-type
3450 (choice
d8a52e15
SM
3451 (integer :tag "Section level")
3452 (const :tag "transitions" t)
3453 (const :tag "section title adornment" nil))
48a24920 3454 :value-type (face)))
64f6a736 3455(rst-testcover-defcustom)
94e9c286 3456
94e9c286 3457;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
94e9c286 3458
d13c8be6 3459(defvar rst-font-lock-keywords
94e9c286 3460 ;; The reST-links in the comments below all relate to sections in
6d3f7c2f 3461 ;; http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html.
d13c8be6 3462 `(;; FIXME: Block markup is not recognized in blocks after explicit markup
6d3f7c2f 3463 ;; start.
d13c8be6
SM
3464
3465 ;; Simple `Body Elements`_
3466 ;; `Bullet Lists`_
6d3f7c2f 3467 ;; FIXME: A bullet directly after a field name is not recognized.
d13c8be6
SM
3468 (,(rst-re 'lin-beg '(:grp bul-sta))
3469 1 rst-block-face)
3470 ;; `Enumerated Lists`_
3471 (,(rst-re 'lin-beg '(:grp enmany-sta))
3472 1 rst-block-face)
6d3f7c2f
SM
3473 ;; `Definition Lists`_
3474 ;; FIXME: missing.
d13c8be6
SM
3475 ;; `Field Lists`_
3476 (,(rst-re 'lin-beg '(:grp fld-tag) 'bli-sfx)
3477 1 rst-external-face)
3478 ;; `Option Lists`_
3479 (,(rst-re 'lin-beg '(:grp opt-tag (:shy optsep-tag opt-tag) "*")
3480 '(:alt "$" (:seq hws-prt "\\{2\\}")))
3481 1 rst-block-face)
3482 ;; `Line Blocks`_
6d3f7c2f 3483 ;; Only for lines containing no more bar - to distinguish from tables.
d13c8be6
SM
3484 (,(rst-re 'lin-beg '(:grp "|" bli-sfx) "[^|\n]*$")
3485 1 rst-block-face)
3486
6d3f7c2f
SM
3487 ;; `Tables`_
3488 ;; FIXME: missing
d13c8be6
SM
3489
3490 ;; All the `Explicit Markup Blocks`_
3491 ;; `Footnotes`_ / `Citations`_
3492 (,(rst-re 'lin-beg 'fnc-sta-2)
3493 (1 rst-definition-face)
3494 (2 rst-definition-face))
3495 ;; `Directives`_ / `Substitution Definitions`_
3496 (,(rst-re 'lin-beg 'dir-sta-3)
3497 (1 rst-directive-face)
3498 (2 rst-definition-face)
3499 (3 rst-directive-face))
3500 ;; `Hyperlink Targets`_
3501 (,(rst-re 'lin-beg
3502 '(:grp exm-sta "_" (:alt
3503 (:seq "`" ilcbkqdef-tag "`")
3504 (:seq (:alt "[^:\\\n]" "\\\\.") "+")) ":")
3505 'bli-sfx)
3506 1 rst-definition-face)
3507 (,(rst-re 'lin-beg '(:grp "__") 'bli-sfx)
3508 1 rst-definition-face)
3509
6d3f7c2f
SM
3510 ;; All `Inline Markup`_
3511 ;; Most of them may be multiline though this is uninteresting.
d13c8be6
SM
3512
3513 ;; FIXME: Condition 5 preventing fontification of e.g. "*" not implemented
6d3f7c2f 3514 ;; `Strong Emphasis`_.
d13c8be6
SM
3515 (,(rst-re 'ilm-pfx '(:grp "\\*\\*" ilcast-tag "\\*\\*") 'ilm-sfx)
3516 1 rst-emphasis2-face)
3517 ;; `Emphasis`_
3518 (,(rst-re 'ilm-pfx '(:grp "\\*" ilcast-tag "\\*") 'ilm-sfx)
3519 1 rst-emphasis1-face)
3520 ;; `Inline Literals`_
3521 (,(rst-re 'ilm-pfx '(:grp "``" ilcbkq-tag "``") 'ilm-sfx)
3522 1 rst-literal-face)
3523 ;; `Inline Internal Targets`_
3524 (,(rst-re 'ilm-pfx '(:grp "_`" ilcbkq-tag "`") 'ilm-sfx)
3525 1 rst-definition-face)
3526 ;; `Hyperlink References`_
6d3f7c2f 3527 ;; FIXME: `Embedded URIs`_ not considered.
c846da43 3528 ;; FIXME: Directly adjacent marked up words are not fontified correctly
6d3f7c2f 3529 ;; unless they are not separated by two spaces: foo_ bar_.
d13c8be6
SM
3530 (,(rst-re 'ilm-pfx '(:grp (:alt (:seq "`" ilcbkq-tag "`")
3531 (:seq "\\sw" (:alt "\\sw" "-") "+\\sw"))
3532 "__?") 'ilm-sfx)
3533 1 rst-reference-face)
3534 ;; `Interpreted Text`_
3535 (,(rst-re 'ilm-pfx '(:grp (:shy ":" sym-tag ":") "?")
3536 '(:grp "`" ilcbkq-tag "`")
3537 '(:grp (:shy ":" sym-tag ":") "?") 'ilm-sfx)
3538 (1 rst-directive-face)
3539 (2 rst-external-face)
3540 (3 rst-directive-face))
3541 ;; `Footnote References`_ / `Citation References`_
3542 (,(rst-re 'ilm-pfx '(:grp fnc-tag "_") 'ilm-sfx)
3543 1 rst-reference-face)
3544 ;; `Substitution References`_
3545 ;; FIXME: References substitutions like |this|_ or |this|__ are not
6d3f7c2f 3546 ;; fontified correctly.
d13c8be6
SM
3547 (,(rst-re 'ilm-pfx '(:grp sub-tag) 'ilm-sfx)
3548 1 rst-reference-face)
3549 ;; `Standalone Hyperlinks`_
6d3f7c2f 3550 ;; FIXME: This takes it easy by using a whitespace as delimiter.
d13c8be6
SM
3551 (,(rst-re 'ilm-pfx '(:grp uri-tag ":\\S +") 'ilm-sfx)
3552 1 rst-definition-face)
3553 (,(rst-re 'ilm-pfx '(:grp sym-tag "@" sym-tag ) 'ilm-sfx)
3554 1 rst-definition-face)
3555
6d3f7c2f 3556 ;; Do all block fontification as late as possible so 'append works.
d13c8be6 3557
6d3f7c2f
SM
3558 ;; Sections_ / Transitions_
3559 ;; For sections this is multiline.
d13c8be6
SM
3560 (,(rst-re 'ado-beg-2-1)
3561 (rst-font-lock-handle-adornment-matcher
3562 (rst-font-lock-handle-adornment-pre-match-form
3563 (match-string-no-properties 1) (match-end 1))
3564 nil
3565 (1 (cdr (assoc nil rst-adornment-faces-alist)) append t)
3566 (2 (cdr (assoc rst-font-lock-adornment-level
3567 rst-adornment-faces-alist)) append t)
3568 (3 (cdr (assoc nil rst-adornment-faces-alist)) append t)))
3569
3570 ;; FIXME: FACESPEC could be used instead of ordinary faces to set
3571 ;; properties on comments and literal blocks so they are *not*
6d3f7c2f 3572 ;; inline fontified. See (elisp)Search-based Fontification.
d13c8be6
SM
3573
3574 ;; FIXME: And / or use `syntax-propertize` functions as in `octave-mod.el`
6d3f7c2f
SM
3575 ;; and other V24 modes. May make `font-lock-extend-region`
3576 ;; superfluous.
d13c8be6 3577
6d3f7c2f
SM
3578 ;; `Comments`_
3579 ;; This is multiline.
d13c8be6
SM
3580 (,(rst-re 'lin-beg 'cmt-sta-1)
3581 (1 rst-comment-face)
3582 (rst-font-lock-find-unindented-line-match
3583 (rst-font-lock-find-unindented-line-limit (match-end 1))
3584 nil
3585 (0 rst-comment-face append)))
3586 (,(rst-re 'lin-beg '(:grp exm-tag) '(:grp hws-tag) "$")
3587 (1 rst-comment-face)
3588 (2 rst-comment-face)
3589 (rst-font-lock-find-unindented-line-match
3590 (rst-font-lock-find-unindented-line-limit 'next)
3591 nil
3592 (0 rst-comment-face append)))
3593
3594 ;; FIXME: This is not rendered as comment::
6d3f7c2f
SM
3595 ;; .. .. list-table::
3596 ;; :stub-columns: 1
3597 ;; :header-rows: 1
d13c8be6
SM
3598
3599 ;; FIXME: This is rendered wrong::
3600 ;;
3601 ;; xxx yyy::
3602 ;;
3603 ;; ----|> KKKKK <|----
3604 ;; / \
3605 ;; -|> AAAAAAAAAAPPPPPP <|- -|> AAAAAAAAAABBBBBBB <|-
3606 ;; | | | |
3607 ;; | | | |
3608 ;; PPPPPP PPPPPPDDDDDDD BBBBBBB PPPPPPBBBBBBB
3609 ;;
3610 ;; Indentation needs to be taken from the line with the ``::`` and not from
3611 ;; the first content line.
94e9c286 3612
6d3f7c2f
SM
3613 ;; `Indented Literal Blocks`_
3614 ;; This is multiline.
d13c8be6
SM
3615 (,(rst-re 'lin-beg 'lit-sta-2)
3616 (2 rst-block-face)
3617 (rst-font-lock-find-unindented-line-match
3618 (rst-font-lock-find-unindented-line-limit t)
3619 nil
3620 (0 rst-literal-face append)))
94e9c286 3621
6d3f7c2f
SM
3622 ;; FIXME: `Quoted Literal Blocks`_ missing.
3623 ;; This is multiline.
94e9c286 3624
d13c8be6
SM
3625 ;; `Doctest Blocks`_
3626 ;; FIXME: This is wrong according to the specification:
3627 ;;
3628 ;; Doctest blocks are text blocks which begin with ">>> ", the Python
3629 ;; interactive interpreter main prompt, and end with a blank line.
3630 ;; Doctest blocks are treated as a special case of literal blocks,
3631 ;; without requiring the literal block syntax. If both are present, the
3632 ;; literal block syntax takes priority over Doctest block syntax:
3633 ;;
3634 ;; This is an ordinary paragraph.
3635 ;;
3636 ;; >>> print 'this is a Doctest block'
3637 ;; this is a Doctest block
3638 ;;
3639 ;; The following is a literal block::
3640 ;;
3641 ;; >>> This is not recognized as a doctest block by
3642 ;; reStructuredText. It *will* be recognized by the doctest
3643 ;; module, though!
3644 ;;
3645 ;; Indentation is not required for doctest blocks.
3646 (,(rst-re 'lin-beg '(:grp (:alt ">>>" ell-tag)) '(:grp ".+"))
3647 (1 rst-block-face)
64f6a736 3648 (2 rst-literal-face)))
d13c8be6
SM
3649 "Keywords to highlight in rst mode.")
3650
8f6b6da8
JB
3651(defvar font-lock-beg)
3652(defvar font-lock-end)
3653
d13c8be6 3654(defun rst-font-lock-extend-region ()
6d3f7c2f
SM
3655 "Extend the font-lock region if it might be in a multi-line construct.
3656Return non-nil if so. Font-lock region is from `font-lock-beg'
3657to `font-lock-end'."
d13c8be6
SM
3658 (let ((r (rst-font-lock-extend-region-internal font-lock-beg font-lock-end)))
3659 (when r
3660 (setq font-lock-beg (car r))
3661 (setq font-lock-end (cdr r))
3662 t)))
3663
3664(defun rst-font-lock-extend-region-internal (beg end)
6d3f7c2f 3665 "Check the region BEG / END for being in the middle of a multi-line construct.
d13c8be6
SM
3666Return nil if not or a cons with new values for BEG / END"
3667 (let ((nbeg (rst-font-lock-extend-region-extend beg -1))
3668 (nend (rst-font-lock-extend-region-extend end 1)))
3669 (if (or nbeg nend)
3670 (cons (or nbeg beg) (or nend end)))))
3671
3672(defun rst-forward-line (&optional n)
6d3f7c2f
SM
3673 "Like `forward-line' but always end up in column 0 and return accordingly.
3674Move N lines forward just as `forward-line'."
d13c8be6
SM
3675 (let ((moved (forward-line n)))
3676 (if (bolp)
3677 moved
3678 (forward-line 0)
7b4cdbf4 3679 (- moved (rst-signum n)))))
d13c8be6 3680
d8a52e15
SM
3681;; FIXME: If a single line is made a section header by `rst-adjust' the header
3682;; is not always fontified immediately.
d13c8be6
SM
3683(defun rst-font-lock-extend-region-extend (pt dir)
3684 "Extend the region starting at point PT and extending in direction DIR.
3685Return extended point or nil if not moved."
3686 ;; There are many potential multiline constructs but there are two groups
3687 ;; which are really relevant. The first group consists of
3688 ;;
3689 ;; * comment lines without leading explicit markup tag and
3690 ;;
3691 ;; * literal blocks following "::"
3692 ;;
c846da43 3693 ;; which are both indented. Thus indentation is the first thing recognized
d13c8be6
SM
3694 ;; here. The second criteria is an explicit markup tag which may be a comment
3695 ;; or a double colon at the end of a line.
3696 ;;
3697 ;; The second group consists of the adornment cases.
3698 (if (not (get-text-property pt 'font-lock-multiline))
6d3f7c2f 3699 ;; Move only if we don't start inside a multiline construct already.
d13c8be6 3700 (save-excursion
6d3f7c2f
SM
3701 (let (;; Non-empty non-indented line, explicit markup tag or literal
3702 ;; block tag.
d13c8be6
SM
3703 (stop-re (rst-re '(:alt "[^ \t\n]"
3704 (:seq hws-tag exm-tag)
3705 (:seq ".*" dcl-tag lin-end)))))
6d3f7c2f 3706 ;; The comments below are for dir == -1 / dir == 1.
d13c8be6
SM
3707 (goto-char pt)
3708 (forward-line 0)
3709 (setq pt (point))
3710 (while (and (not (looking-at stop-re))
3711 (zerop (rst-forward-line dir)))) ; try previous / next
6d3f7c2f 3712 ; line if it exists.
d13c8be6 3713 (if (looking-at (rst-re 'ado-beg-2-1)) ; may be an underline /
6d3f7c2f 3714 ; overline.
d13c8be6
SM
3715 (if (zerop (rst-forward-line dir))
3716 (if (looking-at (rst-re 'ttl-beg)) ; title found, i.e.
3717 ; underline / overline
6d3f7c2f 3718 ; found.
d13c8be6
SM
3719 (if (zerop (rst-forward-line dir))
3720 (if (not
3721 (looking-at (rst-re 'ado-beg-2-1))) ; no
3722 ; overline /
6d3f7c2f 3723 ; underline.
d13c8be6 3724 (rst-forward-line (- dir)))) ; step back to title
6d3f7c2f
SM
3725 ; / adornment.
3726 (if (< dir 0) ; keep downward adornment.
3727 (rst-forward-line (- dir))))) ; step back to adornment.
3728 (if (looking-at (rst-re 'ttl-beg)) ; may be a title.
d13c8be6
SM
3729 (if (zerop (rst-forward-line dir))
3730 (if (not
3731 (looking-at (rst-re 'ado-beg-2-1))) ; no overline /
6d3f7c2f
SM
3732 ; underline.
3733 (rst-forward-line (- dir)))))) ; step back to line.
d13c8be6
SM
3734 (if (not (= (point) pt))
3735 (point))))))
94e9c286
SM
3736
3737;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3738;; Indented blocks
3739
3740(defun rst-forward-indented-block (&optional column limit)
3741 "Move forward across one indented block.
3742Find the next non-empty line which is not indented at least to COLUMN (defaults
b4747519
SM
3743to the column of the point). Moves point to first character of this line or the
3744first empty line immediately before it and returns that position. If there is
94e9c286
SM
3745no such line before LIMIT (defaults to the end of the buffer) returns nil and
3746point is not moved."
3747 (interactive)
3748 (let ((clm (or column (current-column)))
3749 (start (point))
3750 fnd beg cand)
3751 (if (not limit)
3752 (setq limit (point-max)))
3753 (save-match-data
3754 (while (and (not fnd) (< (point) limit))
3755 (forward-line 1)
3756 (when (< (point) limit)
3757 (setq beg (point))
d13c8be6 3758 (if (looking-at (rst-re 'lin-end))
6d3f7c2f 3759 (setq cand (or cand beg)) ; An empty line is a candidate.
94e9c286
SM
3760 (move-to-column clm)
3761 ;; FIXME: No indentation [(zerop clm)] must be handled in some
6d3f7c2f
SM
3762 ;; useful way - though it is not clear what this should mean
3763 ;; at all.
94e9c286 3764 (if (string-match
d13c8be6
SM
3765 (rst-re 'linemp-tag)
3766 (buffer-substring-no-properties beg (point)))
6d3f7c2f 3767 (setq cand nil) ; An indented line resets a candidate.
94e9c286
SM
3768 (setq fnd (or cand beg)))))))
3769 (goto-char (or fnd start))
3770 fnd))
3771
d13c8be6 3772(defvar rst-font-lock-find-unindented-line-begin nil
6d3f7c2f 3773 "Beginning of the match if `rst-font-lock-find-unindented-line-end'.")
d13c8be6
SM
3774
3775(defvar rst-font-lock-find-unindented-line-end nil
3776 "End of the match as determined by `rst-font-lock-find-unindented-line-limit'.
57348c4d 3777Also used as a trigger for `rst-font-lock-find-unindented-line-match'.")
d13c8be6
SM
3778
3779(defun rst-font-lock-find-unindented-line-limit (ind-pnt)
c846da43 3780 "Find the next unindented line relative to indentation at IND-PNT.
d13c8be6
SM
3781Return this point, the end of the buffer or nil if nothing found.
3782If IND-PNT is `next' take the indentation from the next line if
6d3f7c2f 3783this is not empty and indented more than the current one. If
d13c8be6 3784IND-PNT is non-nil but not a number take the indentation from the
57348c4d 3785next non-empty line if this is indented more than the current one."
d13c8be6
SM
3786 (setq rst-font-lock-find-unindented-line-begin ind-pnt)
3787 (setq rst-font-lock-find-unindented-line-end
3788 (save-excursion
3789 (when (not (numberp ind-pnt))
6d3f7c2f 3790 ;; Find indentation point in next line if any.
d13c8be6
SM
3791 (setq ind-pnt
3792 ;; FIXME: Should be refactored to two different functions
3793 ;; giving their result to this function, may be
6d3f7c2f 3794 ;; integrated in caller.
d13c8be6
SM
3795 (save-match-data
3796 (let ((cur-ind (current-indentation)))
3797 (if (eq ind-pnt 'next)
3798 (when (and (zerop (forward-line 1))
3799 (< (point) (point-max)))
6d3f7c2f 3800 ;; Not at EOF.
d13c8be6
SM
3801 (setq rst-font-lock-find-unindented-line-begin
3802 (point))
3803 (when (and (not (looking-at (rst-re 'lin-end)))
3804 (> (current-indentation) cur-ind))
6d3f7c2f 3805 ;; Use end of indentation if non-empty line.
d13c8be6
SM
3806 (looking-at (rst-re 'hws-tag))
3807 (match-end 0)))
6d3f7c2f 3808 ;; Skip until non-empty line or EOF.
d13c8be6
SM
3809 (while (and (zerop (forward-line 1))
3810 (< (point) (point-max))
3811 (looking-at (rst-re 'lin-end))))
3812 (when (< (point) (point-max))
6d3f7c2f 3813 ;; Not at EOF.
d13c8be6
SM
3814 (setq rst-font-lock-find-unindented-line-begin
3815 (point))
3816 (when (> (current-indentation) cur-ind)
6d3f7c2f 3817 ;; Indentation bigger than line of departure.
d13c8be6
SM
3818 (looking-at (rst-re 'hws-tag))
3819 (match-end 0))))))))
3820 (when ind-pnt
3821 (goto-char ind-pnt)
3822 (or (rst-forward-indented-block nil (point-max))
3823 (point-max))))))
3824
57348c4d 3825(defun rst-font-lock-find-unindented-line-match (_limit)
6d3f7c2f 3826 "Set the match found earlier if match were found.
57348c4d
JB
3827Match has been found by `rst-font-lock-find-unindented-line-limit'
3828the first time called or no match is found. Return non-nil if
3829match was found. LIMIT is not used but mandated by the caller."
d13c8be6
SM
3830 (when rst-font-lock-find-unindented-line-end
3831 (set-match-data
3832 (list rst-font-lock-find-unindented-line-begin
3833 rst-font-lock-find-unindented-line-end))
3834 (put-text-property rst-font-lock-find-unindented-line-begin
3835 rst-font-lock-find-unindented-line-end
3836 'font-lock-multiline t)
6d3f7c2f 3837 ;; Make sure this is called only once.
d13c8be6
SM
3838 (setq rst-font-lock-find-unindented-line-end nil)
3839 t))
94e9c286
SM
3840
3841;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3842;; Adornments
3843
d13c8be6
SM
3844(defvar rst-font-lock-adornment-level nil
3845 "Storage for `rst-font-lock-handle-adornment-matcher'.
3846Either section level of the current adornment or t for a transition.")
3847
3848(defun rst-adornment-level (key)
3849 "Return section level for adornment KEY.
57348c4d
JB
3850KEY is the first element of the return list of `rst-classify-adornment'.
3851If KEY is not a cons return it. If KEY is found in the hierarchy return
3852its level. Otherwise return a level one beyond the existing hierarchy."
d13c8be6
SM
3853 (if (not (consp key))
3854 key
3855 (let* ((hier (rst-get-hierarchy))
3856 (char (car key))
3857 (style (cdr key)))
7b4cdbf4
SM
3858 (1+ (or (lexical-let ((char char)
3859 (style style)
3860 (hier hier)) ; Create closure.
3861 (rst-position-if (lambda (elt)
3862 (and (equal (car elt) char)
3863 (equal (cadr elt) style))) hier))
d13c8be6
SM
3864 (length hier))))))
3865
3866(defvar rst-font-lock-adornment-match nil
3867 "Storage for match for current adornment.
6d3f7c2f 3868Set by `rst-font-lock-handle-adornment-pre-match-form'. Also used
d13c8be6
SM
3869as a trigger for `rst-font-lock-handle-adornment-matcher'.")
3870
3871(defun rst-font-lock-handle-adornment-pre-match-form (ado ado-end)
6d3f7c2f
SM
3872 "Determine limit for adornments.
3873Determine all things necessary for font-locking section titles
57348c4d
JB
3874and transitions and put the result to `rst-font-lock-adornment-match'
3875and `rst-font-lock-adornment-level'. ADO is the complete adornment
6d3f7c2f 3876matched. ADO-END is the point where ADO ends. Return the point
d13c8be6
SM
3877where the whole adorned construct ends.
3878
3879Called as a PRE-MATCH-FORM in the sense of `font-lock-keywords'."
3880 (let ((ado-data (rst-classify-adornment ado ado-end)))
3881 (if (not ado-data)
3882 (setq rst-font-lock-adornment-level nil
3883 rst-font-lock-adornment-match nil)
3884 (setq rst-font-lock-adornment-level
3885 (rst-adornment-level (car ado-data)))
3886 (setq rst-font-lock-adornment-match (cdr ado-data))
6d3f7c2f
SM
3887 (goto-char (nth 1 ado-data)) ; Beginning of construct.
3888 (nth 2 ado-data)))) ; End of construct.
d13c8be6 3889
57348c4d 3890(defun rst-font-lock-handle-adornment-matcher (_limit)
6d3f7c2f
SM
3891 "Set the match found earlier if match were found.
3892Match has been found by
3893`rst-font-lock-handle-adornment-pre-match-form' the first time
3894called or no match is found. Return non-nil if match was found.
d13c8be6 3895
6d3f7c2f
SM
3896Called as a MATCHER in the sense of `font-lock-keywords'.
3897LIMIT is not used but mandated by the caller."
d13c8be6 3898 (let ((match rst-font-lock-adornment-match))
6d3f7c2f 3899 ;; May run only once - enforce this.
d13c8be6
SM
3900 (setq rst-font-lock-adornment-match nil)
3901 (when match
3902 (set-match-data match)
3903 (goto-char (match-end 0))
3904 (put-text-property (match-beginning 0) (match-end 0)
3905 'font-lock-multiline t)
3906 t)))
94e9c286
SM
3907
3908\f
3909;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d13c8be6 3910;; Compilation
94e9c286
SM
3911
3912(defgroup rst-compile nil
3913 "Settings for support of conversion of reStructuredText
3914document with \\[rst-compile]."
3915 :group 'rst
3916 :version "21.1")
3917
ef4271fe
GM
3918(defcustom rst-compile-toolsets
3919 `((html ,(if (executable-find "rst2html.py") "rst2html.py" "rst2html")
3920 ".html" nil)
3921 (latex ,(if (executable-find "rst2latex.py") "rst2latex.py" "rst2latex")
3922 ".tex" nil)
3923 (newlatex ,(if (executable-find "rst2newlatex.py") "rst2newlatex.py"
3924 "rst2newlatex")
3925 ".tex" nil)
3926 (pseudoxml ,(if (executable-find "rst2pseudoxml.py") "rst2pseudoxml.py"
3927 "rst2pseudoxml")
3928 ".xml" nil)
3929 (xml ,(if (executable-find "rst2xml.py") "rst2xml.py" "rst2xml")
3930 ".xml" nil)
3931 (pdf ,(if (executable-find "rst2pdf.py") "rst2pdf.py" "rst2pdf")
3932 ".pdf" nil)
3933 (s5 ,(if (executable-find "rst2s5.py") "rst2s5.py" "rst2s5")
3934 ".html" nil))
6d3f7c2f
SM
3935 "Table describing the command to use for each tool-set.
3936An association list of the tool-set to a list of the (command to use,
94e9c286 3937extension of produced filename, options to the tool (nil or a
ef4271fe 3938string)) to be used for converting the document."
d13c8be6 3939 ;; FIXME: These are not options but symbols which may be referenced by
1f45e27e
SM
3940 ;; `rst-compile-*-toolset` below. The `:validate' keyword of
3941 ;; `defcustom' may help to define this properly in newer Emacs
3942 ;; versions (> 23.1).
3f1b6eb2 3943 :type '(alist :options (html latex newlatex pseudoxml xml pdf s5)
ef4271fe
GM
3944 :key-type symbol
3945 :value-type (list :tag "Specification"
3946 (file :tag "Command")
3947 (string :tag "File extension")
3948 (choice :tag "Command options"
3949 (const :tag "No options" nil)
3950 (string :tag "Options"))))
ed8be7ff 3951 :group 'rst-compile
7b4cdbf4 3952 :package-version "1.2.0")
64f6a736 3953(rst-testcover-defcustom)
94e9c286 3954
6d3f7c2f 3955;; FIXME: Must be `defcustom`.
94e9c286 3956(defvar rst-compile-primary-toolset 'html
6d3f7c2f 3957 "The default tool-set for `rst-compile'.")
94e9c286 3958
6d3f7c2f 3959;; FIXME: Must be `defcustom`.
94e9c286 3960(defvar rst-compile-secondary-toolset 'latex
6d3f7c2f 3961 "The default tool-set for `rst-compile' with a prefix argument.")
94e9c286
SM
3962
3963(defun rst-compile-find-conf ()
3964 "Look for the configuration file in the parents of the current path."
3965 (interactive)
3966 (let ((file-name "docutils.conf")
3967 (buffer-file (buffer-file-name)))
3968 ;; Move up in the dir hierarchy till we find a change log file.
3969 (let* ((dir (file-name-directory buffer-file))
3970 (prevdir nil))
3971 (while (and (or (not (string= dir prevdir))
3972 (setq dir nil)
3973 nil)
3974 (not (file-exists-p (concat dir file-name))))
3975 ;; Move up to the parent dir and try again.
3976 (setq prevdir dir)
3977 (setq dir (expand-file-name (file-name-directory
3978 (directory-file-name
64f6a736
SM
3979 (file-name-directory dir))))))
3980 (or (and dir (concat dir file-name)) nil))))
94e9c286
SM
3981
3982(require 'compile)
3983
d13c8be6 3984(defun rst-compile (&optional use-alt)
94e9c286
SM
3985 "Compile command to convert reST document into some output file.
3986Attempts to find configuration file, if it can, overrides the
57348c4d 3987options. There are two commands to choose from; with USE-ALT,
6d3f7c2f 3988select the alternative tool-set."
94e9c286
SM
3989 (interactive "P")
3990 ;; Note: maybe we want to check if there is a Makefile too and not do anything
3991 ;; if that is the case. I dunno.
d13c8be6 3992 (let* ((toolset (cdr (assq (if use-alt
94e9c286
SM
3993 rst-compile-secondary-toolset
3994 rst-compile-primary-toolset)
3995 rst-compile-toolsets)))
3996 (command (car toolset))
3997 (extension (cadr toolset))
3998 (options (caddr toolset))
3999 (conffile (rst-compile-find-conf))
4000 (bufname (file-name-nondirectory buffer-file-name))
4001 (outname (file-name-sans-extension bufname)))
4002
4003 ;; Set compile-command before invocation of compile.
4004 (set (make-local-variable 'compile-command)
4005 (mapconcat 'identity
4006 (list command
4007 (or options "")
4008 (if conffile
d13c8be6 4009 (concat "--config=" (shell-quote-argument conffile))
94e9c286 4010 "")
d13c8be6
SM
4011 (shell-quote-argument bufname)
4012 (shell-quote-argument (concat outname extension)))
94e9c286
SM
4013 " "))
4014
4015 ;; Invoke the compile command.
d13c8be6 4016 (if (or compilation-read-command use-alt)
94e9c286 4017 (call-interactively 'compile)
64f6a736 4018 (compile compile-command))))
94e9c286
SM
4019
4020(defun rst-compile-alt-toolset ()
6d3f7c2f 4021 "Compile command with the alternative tool-set."
94e9c286 4022 (interactive)
d13c8be6 4023 (rst-compile t))
94e9c286
SM
4024
4025(defun rst-compile-pseudo-region ()
6d3f7c2f
SM
4026 "Show pseudo-XML rendering.
4027Rendering is done of the current active region, or of the entire
4028buffer, if the region is not selected."
4029 ;; FIXME: The region should be given interactively.
94e9c286
SM
4030 (interactive)
4031 (with-output-to-temp-buffer "*pseudoxml*"
4032 (shell-command-on-region
4033 (if mark-active (region-beginning) (point-min))
4034 (if mark-active (region-end) (point-max))
ef4271fe 4035 (cadr (assq 'pseudoxml rst-compile-toolsets))
94e9c286
SM
4036 standard-output)))
4037
6d3f7c2f 4038;; FIXME: Should be `defcustom`.
94e9c286
SM
4039(defvar rst-pdf-program "xpdf"
4040 "Program used to preview PDF files.")
4041
4042(defun rst-compile-pdf-preview ()
4043 "Convert the document to a PDF file and launch a preview program."
4044 (interactive)
d13c8be6
SM
4045 (let* ((tmp-filename (make-temp-file "rst_el" nil ".pdf"))
4046 (command (format "%s %s %s && %s %s ; rm %s"
ef4271fe 4047 (cadr (assq 'pdf rst-compile-toolsets))
94e9c286 4048 buffer-file-name tmp-filename
d13c8be6 4049 rst-pdf-program tmp-filename tmp-filename)))
94e9c286
SM
4050 (start-process-shell-command "rst-pdf-preview" nil command)
4051 ;; Note: you could also use (compile command) to view the compilation
4052 ;; output.
4053 ))
4054
6d3f7c2f 4055;; FIXME: Should be `defcustom` or use something like `browse-url`.
94e9c286
SM
4056(defvar rst-slides-program "firefox"
4057 "Program used to preview S5 slides.")
4058
4059(defun rst-compile-slides-preview ()
4060 "Convert the document to an S5 slide presentation and launch a preview program."
4061 (interactive)
d13c8be6
SM
4062 (let* ((tmp-filename (make-temp-file "rst_el" nil ".html"))
4063 (command (format "%s %s %s && %s %s ; rm %s"
ef4271fe 4064 (cadr (assq 's5 rst-compile-toolsets))
94e9c286 4065 buffer-file-name tmp-filename
d13c8be6 4066 rst-slides-program tmp-filename tmp-filename)))
94e9c286
SM
4067 (start-process-shell-command "rst-slides-preview" nil command)
4068 ;; Note: you could also use (compile command) to view the compilation
4069 ;; output.
4070 ))
4071
94e9c286 4072\f
ee97deee
SM
4073;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4074;; Imenu support.
4075
4076;; FIXME: Integrate this properly. Consider a key binding.
4077
4078;; Based on code from Masatake YAMATO <yamato@redhat.com>.
4079
4080(defun rst-imenu-find-adornments-for-position (adornments pos)
4081 "Find adornments cell in ADORNMENTS for position POS."
4082 (let ((a nil))
4083 (while adornments
4084 (if (and (car adornments)
4085 (eq (car (car adornments)) pos))
4086 (setq a adornments
4087 adornments nil)
4088 (setq adornments (cdr adornments))))
4089 a))
4090
4091(defun rst-imenu-convert-cell (elt adornments)
57348c4d 4092 "Convert a cell ELT in a tree returned from `rst-section-tree' to Imenu index.
ee97deee
SM
4093ADORNMENTS is used as hint information for conversion."
4094 (let* ((kar (car elt))
4095 (kdr (cdr elt))
4096 (title (car kar)))
4097 (if kar
4098 (let* ((p (marker-position (cadr kar)))
4099 (adornments
4100 (rst-imenu-find-adornments-for-position adornments p))
4101 (a (car adornments))
4102 (adornments (cdr adornments))
4103 ;; FIXME: Overline adornment characters need to be in front so
4104 ;; they become visible even for long title lines. May be
4105 ;; an additional level number is also useful.
4106 (title (format "%s%s%s"
4107 (make-string (1+ (nth 3 a)) (nth 1 a))
4108 title
4109 (if (eq (nth 2 a) 'simple)
4110 ""
4111 (char-to-string (nth 1 a))))))
4112 (cons title
4113 (if (null kdr)
4114 p
4115 (cons
4116 ;; A bit ugly but this make which-func happy.
4117 (cons title p)
4118 (mapcar (lambda (elt0)
4119 (rst-imenu-convert-cell elt0 adornments))
4120 kdr)))))
4121 nil)))
4122
4123;; FIXME: Document title and subtitle need to be handled properly. They should
4124;; get an own "Document" top level entry.
4125(defun rst-imenu-create-index ()
57348c4d 4126 "Create index for Imenu.
ee97deee
SM
4127Return as described for `imenu--index-alist'."
4128 (rst-reset-section-caches)
4129 (let ((tree (rst-section-tree))
4130 ;; Translate line notation to point notation.
4131 (adornments (save-excursion
4132 (mapcar (lambda (ln-ado)
4133 (cons (progn
4134 (goto-char (point-min))
4135 (forward-line (1- (car ln-ado)))
4136 ;; FIXME: Need to consider
4137 ;; `imenu-use-markers' here?
4138 (point))
4139 (cdr ln-ado)))
4140 (rst-find-all-adornments)))))
4141 (delete nil (mapcar (lambda (elt)
4142 (rst-imenu-convert-cell elt adornments))
4143 tree))))
4144
4145\f
94e9c286 4146;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
94e9c286 4147;; Generic text functions that are more convenient than the defaults.
94e9c286 4148
6d3f7c2f 4149;; FIXME: Unbound command - should be bound or removed.
94e9c286 4150(defun rst-replace-lines (fromchar tochar)
6d3f7c2f 4151 "Replace flush-left lines of FROMCHAR with equal-length lines of TOCHAR."
94e9c286
SM
4152 (interactive "\
4153cSearch for flush-left lines of char:
4154cand replace with char: ")
4155 (save-excursion
d13c8be6 4156 (let ((searchre (rst-re "^" fromchar "+\\( *\\)$"))
b4747519
SM
4157 (found 0))
4158 (while (search-forward-regexp searchre nil t)
4159 (setq found (1+ found))
4160 (goto-char (match-beginning 1))
4161 (let ((width (current-column)))
4162 (rst-delete-entire-line)
4163 (insert-char tochar width)))
4164 (message (format "%d lines replaced." found)))))
94e9c286 4165
6d3f7c2f 4166;; FIXME: Unbound command - should be bound or removed.
94e9c286
SM
4167(defun rst-join-paragraph ()
4168 "Join lines in current paragraph into one line, removing end-of-lines."
4169 (interactive)
7b4cdbf4 4170 (let ((fill-column 65000)) ; Some big number.
94e9c286
SM
4171 (call-interactively 'fill-paragraph)))
4172
6d3f7c2f 4173;; FIXME: Unbound command - should be bound or removed.
94e9c286
SM
4174(defun rst-force-fill-paragraph ()
4175 "Fill paragraph at point, first joining the paragraph's lines into one.
4176This is useful for filling list item paragraphs."
4177 (interactive)
4178 (rst-join-paragraph)
4179 (fill-paragraph nil))
4180
4181
6d3f7c2f 4182;; FIXME: Unbound command - should be bound or removed.
94e9c286
SM
4183;; Generic character repeater function.
4184;; For sections, better to use the specialized function above, but this can
4185;; be useful for creating separators.
d13c8be6 4186(defun rst-repeat-last-character (use-next)
6d3f7c2f 4187 "Fill the current line using the last character on the current line.
57348c4d
JB
4188Fill up to the length of the preceding line or up to `fill-column' if preceding
4189line is empty.
94e9c286 4190
d13c8be6 4191If USE-NEXT, use the next line rather than the preceding line.
94e9c286
SM
4192
4193If the current line is longer than the desired length, shave the characters off
4194the current line to fit the desired length.
4195
4196As an added convenience, if the command is repeated immediately, the alternative
4197column is used (fill-column vs. end of previous/next line)."
d13c8be6 4198 (interactive "P")
94e9c286
SM
4199 (let* ((curcol (current-column))
4200 (curline (+ (count-lines (point-min) (point))
d13c8be6 4201 (if (zerop curcol) 1 0)))
94e9c286 4202 (lbp (line-beginning-position 0))
d13c8be6 4203 (prevcol (if (and (= curline 1) (not use-next))
94e9c286
SM
4204 fill-column
4205 (save-excursion
d13c8be6 4206 (forward-line (if use-next 1 -1))
94e9c286
SM
4207 (end-of-line)
4208 (skip-chars-backward " \t" lbp)
4209 (let ((cc (current-column)))
d13c8be6 4210 (if (zerop cc) fill-column cc)))))
94e9c286 4211 (rightmost-column
d13c8be6 4212 (cond ((equal last-command 'rst-repeat-last-character)
94e9c286
SM
4213 (if (= curcol fill-column) prevcol fill-column))
4214 (t (save-excursion
64f6a736 4215 (if (zerop prevcol) fill-column prevcol))))))
94e9c286
SM
4216 (end-of-line)
4217 (if (> (current-column) rightmost-column)
6d3f7c2f 4218 ;; Shave characters off the end.
94e9c286
SM
4219 (delete-region (- (point)
4220 (- (current-column) rightmost-column))
4221 (point))
6d3f7c2f 4222 ;; Fill with last characters.
94e9c286 4223 (insert-char (preceding-char)
64f6a736 4224 (- rightmost-column (current-column))))))
94e9c286
SM
4225
4226\f
6d3f7c2f
SM
4227
4228;; LocalWords: docutils http sourceforge rst html wp svn svnroot txt reST regex
4229;; LocalWords: regexes alist seq alt grp keymap abbrev overline overlines toc
d8a52e15 4230;; LocalWords: XML PNT propertized
6d3f7c2f
SM
4231
4232;; Local Variables:
4233;; sentence-end-double-space: t
4234;; End:
4235
d8a52e15
SM
4236(provide 'rst)
4237
4238;;; rst.el ends here