Commit | Line | Data |
---|---|---|
94e9c286 SM |
1 | ;;; rst.el --- Mode for viewing and editing reStructuredText-documents. |
2 | ||
95df8112 | 3 | ;; Copyright (C) 2003-2011 Free Software Foundation, Inc. |
94e9c286 SM |
4 | |
5 | ;; Authors: Martin Blais <blais@furius.ca>, | |
6 | ;; Stefan Merten <smerten@oekonux.de>, | |
7 | ;; David Goodger <goodger@python.org> | |
8 | ||
9 | ;; This file is part of GNU Emacs. | |
10 | ||
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation, either version 3 of the License, or | |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
23 | ||
24 | ;;; Commentary: | |
25 | ||
26 | ;; This package provides major mode rst-mode, which supports documents marked up | |
b4747519 SM |
27 | ;; using the reStructuredText format. Support includes font locking as well as |
28 | ;; some convenience functions for editing. It does this by defining a Emacs | |
29 | ;; major mode: rst-mode (ReST). This mode is derived from text-mode (and | |
30 | ;; inherits much of it). This package also contains: | |
94e9c286 SM |
31 | ;; |
32 | ;; - Functions to automatically adjust and cycle the section underline | |
33 | ;; decorations; | |
34 | ;; - A mode that displays the table of contents and allows you to jump anywhere | |
35 | ;; from it; | |
36 | ;; - Functions to insert and automatically update a TOC in your source | |
37 | ;; document; | |
38 | ;; - Font-lock highlighting of notable reStructuredText structures; | |
39 | ;; - Some other convenience functions. | |
40 | ;; | |
41 | ;; See the accompanying document in the docutils documentation about | |
42 | ;; the contents of this package and how to use it. | |
43 | ;; | |
44 | ;; For more information about reStructuredText, see | |
45 | ;; http://docutils.sourceforge.net/rst.html | |
46 | ;; | |
47 | ;; For full details on how to use the contents of this file, see | |
48 | ;; http://docutils.sourceforge.net/docs/user/emacs.html | |
49 | ;; | |
50 | ;; | |
b4747519 SM |
51 | ;; There are a number of convenient keybindings provided by rst-mode. |
52 | ;; The main one is | |
94e9c286 SM |
53 | ;; |
54 | ;; C-c C-a (also C-=): rst-adjust | |
55 | ;; | |
56 | ;; Updates or rotates the section title around point or promotes/demotes the | |
b4747519 | 57 | ;; decorations within the region (see full details below). Note that C-= is a |
94e9c286 SM |
58 | ;; good binding, since it allows you to specify a negative arg easily with C-- |
59 | ;; C-= (easy to type), as well as ordinary prefix arg with C-u C-=. | |
60 | ;; | |
b4747519 | 61 | ;; For more on bindings, see rst-mode-map below. There are also many variables |
94e9c286 SM |
62 | ;; that can be customized, look for defcustom and defvar in this file. |
63 | ;; | |
64 | ;; If you use the table-of-contents feature, you may want to add a hook to | |
65 | ;; update the TOC automatically everytime you adjust a section title:: | |
66 | ;; | |
67 | ;; (add-hook 'rst-adjust-hook 'rst-toc-update) | |
68 | ;; | |
b4747519 SM |
69 | ;; Syntax highlighting: font-lock is enabled by default. If you want to turn |
70 | ;; off syntax highlighting to rst-mode, you can use the following:: | |
94e9c286 SM |
71 | ;; |
72 | ;; (setq font-lock-global-modes '(not rst-mode ...)) | |
73 | ;; | |
74 | ||
75 | ||
76 | ;; CUSTOMIZATION | |
77 | ;; | |
78 | ;; rst | |
79 | ;; --- | |
80 | ;; This group contains some general customizable features. | |
81 | ;; | |
82 | ;; The group is contained in the wp group. | |
83 | ;; | |
84 | ;; rst-faces | |
85 | ;; --------- | |
b4747519 | 86 | ;; This group contains all necessary for customizing fonts. The default |
94e9c286 SM |
87 | ;; settings use standard font-lock-*-face's so if you set these to your |
88 | ;; liking they are probably good in rst-mode also. | |
89 | ;; | |
90 | ;; The group is contained in the faces group as well as in the rst group. | |
91 | ;; | |
92 | ;; rst-faces-defaults | |
93 | ;; ------------------ | |
94 | ;; This group contains all necessary for customizing the default fonts used for | |
95 | ;; section title faces. | |
96 | ;; | |
97 | ;; The general idea for section title faces is to have a non-default background | |
b4747519 SM |
98 | ;; but do not change the background. The section level is shown by the |
99 | ;; lightness of the background color. If you like this general idea of | |
94e9c286 | 100 | ;; generating faces for section titles but do not like the details this group |
b4747519 | 101 | ;; is the point where you can customize the details. If you do not like the |
94e9c286 SM |
102 | ;; general idea, however, you should customize the faces used in |
103 | ;; rst-adornment-faces-alist. | |
104 | ;; | |
105 | ;; Note: If you are using a dark background please make sure the variable | |
b4747519 | 106 | ;; frame-background-mode is set to the symbol dark. This triggers |
94e9c286 SM |
107 | ;; some default values which are probably right for you. |
108 | ;; | |
109 | ;; The group is contained in the rst-faces group. | |
110 | ;; | |
b4747519 SM |
111 | ;; All customizable features have a comment explaining their meaning. |
112 | ;; Refer to the customization of your Emacs (try ``M-x customize``). | |
94e9c286 SM |
113 | |
114 | ||
115 | ;;; DOWNLOAD | |
116 | ||
117 | ;; The latest version of this file lies in the docutils source code repository: | |
118 | ;; http://svn.berlios.de/svnroot/repos/docutils/trunk/docutils/tools/editors/emacs/rst.el | |
119 | ||
120 | ||
121 | ;;; INSTALLATION | |
122 | ||
123 | ;; Add the following lines to your `.emacs' file: | |
124 | ;; | |
125 | ;; (require 'rst) | |
126 | ;; | |
127 | ;; If you are using `.txt' as a standard extension for reST files as | |
128 | ;; http://docutils.sourceforge.net/FAQ.html#what-s-the-standard-filename-extension-for-a-restructuredtext-file | |
129 | ;; suggests you may use one of the `Local Variables in Files' mechanism Emacs | |
b4747519 | 130 | ;; provides to set the major mode automatically. For instance you may use:: |
94e9c286 SM |
131 | ;; |
132 | ;; .. -*- mode: rst -*- | |
133 | ;; | |
b4747519 SM |
134 | ;; in the very first line of your file. The following code is useful if you |
135 | ;; want automatically enter rst-mode from any file with compatible extensions: | |
94e9c286 SM |
136 | ;; |
137 | ;; (setq auto-mode-alist | |
138 | ;; (append '(("\\.txt$" . rst-mode) | |
139 | ;; ("\\.rst$" . rst-mode) | |
140 | ;; ("\\.rest$" . rst-mode)) auto-mode-alist)) | |
141 | ;; | |
142 | ||
143 | ;;; BUGS | |
144 | ||
145 | ;; - rst-enumeration-region: Select a single paragraph, with the top at one | |
146 | ;; blank line before the beginning, and it will fail. | |
147 | ;; - The active region goes away when we shift it left or right, and this | |
148 | ;; prevents us from refilling it automatically when shifting many times. | |
149 | ;; - The suggested decorations when adjusting should not have to cycle | |
150 | ;; below one below the last section decoration level preceding the | |
151 | ;; cursor. We need to fix that. | |
152 | ||
153 | ;;; TODO LIST | |
154 | ||
155 | ;; rst-toc-insert features | |
156 | ;; ------------------------ | |
157 | ;; - rst-toc-insert: We should parse the contents:: options to figure out how | |
158 | ;; deep to render the inserted TOC. | |
159 | ;; - On load, detect any existing TOCs and set the properties for links. | |
160 | ;; - TOC insertion should have an option to add empty lines. | |
161 | ;; - TOC insertion should deal with multiple lines. | |
162 | ;; - There is a bug on redo after undo of adjust when rst-adjust-hook uses the | |
163 | ;; automatic toc update. The cursor ends up in the TOC and this is | |
164 | ;; annoying. Gotta fix that. | |
165 | ;; - numbering: automatically detect if we have a section-numbering directive in | |
166 | ;; the corresponding section, to render the toc. | |
167 | ;; | |
168 | ;; bulleted and enumerated list items | |
169 | ;; ---------------------------------- | |
170 | ;; - We need to provide way to rebullet bulleted lists, and that would include | |
171 | ;; automatic enumeration as well. | |
172 | ;; | |
173 | ;; Other | |
174 | ;; ----- | |
175 | ;; - It would be nice to differentiate between text files using | |
176 | ;; reStructuredText_ and other general text files. If we had a | |
177 | ;; function to automatically guess whether a .txt file is following the | |
178 | ;; reStructuredText_ conventions, we could trigger rst-mode without | |
179 | ;; having to hard-code this in every text file, nor forcing the user to | |
180 | ;; add a local mode variable at the top of the file. | |
181 | ;; We could perform this guessing by searching for a valid decoration | |
182 | ;; at the top of the document or searching for reStructuredText_ | |
183 | ;; directives further on. | |
184 | ;; | |
185 | ;; - We should support imenu in our major mode, with the menu filled with the | |
186 | ;; section titles (this should be really easy). | |
187 | ;; | |
188 | ;; - We should rename "adornment" to "decoration" or vice-versa in this | |
189 | ;; document (Stefan's code ("adornment") vs Martin ("decoration")), maybe some | |
190 | ;; functions even overlap. | |
191 | ;; | |
192 | ;; - We need to automatically recenter on rst-forward-section movement commands. | |
193 | ||
194 | ||
195 | ;;; HISTORY | |
196 | ;; | |
197 | ||
b4747519 | 198 | ;;; Code: |
94e9c286 SM |
199 | |
200 | \f | |
92439579 | 201 | (defgroup rst nil "Support for reStructuredText documents." |
94e9c286 SM |
202 | :group 'wp |
203 | :version "23.1" | |
204 | :link '(url-link "http://docutils.sourceforge.net/rst.html")) | |
205 | ||
206 | ||
207 | ||
208 | \f | |
209 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
210 | ;; Define some generic support functions. | |
211 | ||
212 | (eval-when-compile (require 'cl)) ;; We need this for destructuring-bind below. | |
213 | ||
214 | ||
215 | ;; From Emacs-22 | |
216 | (unless (fboundp 'line-number-at-pos) | |
217 | (defun line-number-at-pos (&optional pos) | |
218 | "Return (narrowed) buffer line number at position POS. | |
219 | If POS is nil, use current buffer location." | |
220 | (let ((opoint (or pos (point))) start) | |
221 | (save-excursion | |
222 | (goto-char (point-min)) | |
223 | (setq start (point)) | |
224 | (goto-char opoint) | |
225 | (forward-line 0) | |
226 | (1+ (count-lines start (point)))))) ) | |
227 | ||
228 | ||
229 | \f | |
230 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
231 | ;; Mode definition. | |
232 | ||
233 | ;; Key bindings. | |
234 | (defvar rst-mode-map | |
235 | (let ((map (make-sparse-keymap))) | |
236 | ||
237 | ;; | |
238 | ;; Section Decorations. | |
239 | ;; | |
240 | ;; The adjustment function that decorates or rotates a section title. | |
241 | (define-key map [(control c) (control a)] 'rst-adjust) | |
242 | (define-key map [(control c) (control ?=)] 'rst-adjust) | |
243 | (define-key map [(control ?=)] 'rst-adjust) ;; (Does not work on the Mac OSX.) | |
244 | ;; Display the hierarchy of decorations implied by the current document contents. | |
245 | (define-key map [(control c) (control h)] 'rst-display-decorations-hierarchy) | |
99d99081 | 246 | ;; Homogenize the decorations in the document. |
94e9c286 SM |
247 | (define-key map [(control c) (control s)] 'rst-straighten-decorations) |
248 | ;; (define-key map [(control c) (control s)] 'rst-straighten-deco-spacing) | |
249 | ||
250 | ;; | |
251 | ;; Section Movement and Selection. | |
252 | ;; | |
253 | ;; Mark the subsection where the cursor is. | |
254 | (define-key map [(control c) (control m)] 'rst-mark-section) | |
255 | ;; Move forward/backward between section titles. | |
256 | (define-key map [(control c) (control n)] 'rst-forward-section) | |
257 | (define-key map [(control c) (control p)] 'rst-backward-section) | |
258 | ||
259 | ;; | |
260 | ;; Operating on Blocks of Text. | |
261 | ;; | |
262 | ;; Makes paragraphs in region as a bullet list. | |
263 | (define-key map [(control c) (control b)] 'rst-bullet-list-region) | |
264 | ;; Makes paragraphs in region as a enumeration. | |
265 | (define-key map [(control c) (control e)] 'rst-enumerate-region) | |
266 | ;; Converts bullets to an enumeration. | |
267 | (define-key map [(control c) (control v)] 'rst-convert-bullets-to-enumeration) | |
268 | ;; Makes region a line-block. | |
269 | (define-key map [(control c) (control d)] 'rst-line-block-region) | |
270 | ;; Make sure that all the bullets in the region are consistent. | |
271 | (define-key map [(control c) (control w)] 'rst-straighten-bullets-region) | |
272 | ;; Shift region left or right (taking into account of enumerations/bullets, etc.). | |
273 | (define-key map [(control c) (control l)] 'rst-shift-region-left) | |
274 | (define-key map [(control c) (control r)] 'rst-shift-region-right) | |
275 | ;; Comment/uncomment the active region. | |
276 | (define-key map [(control c) (control c)] 'comment-region) | |
277 | ||
278 | ;; | |
279 | ;; Table-of-Contents Features. | |
280 | ;; | |
281 | ;; Enter a TOC buffer to view and move to a specific section. | |
282 | (define-key map [(control c) (control t)] 'rst-toc) | |
283 | ;; Insert a TOC here. | |
284 | (define-key map [(control c) (control i)] 'rst-toc-insert) | |
285 | ;; Update the document's TOC (without changing the cursor position). | |
286 | (define-key map [(control c) (control u)] 'rst-toc-update) | |
287 | ;; Got to the section under the cursor (cursor must be in TOC). | |
288 | (define-key map [(control c) (control f)] 'rst-goto-section) | |
289 | ||
290 | ;; | |
291 | ;; Converting Documents from Emacs. | |
292 | ;; | |
293 | ;; Run one of two pre-configured toolset commands on the document. | |
294 | (define-key map [(control c) (?1)] 'rst-compile) | |
295 | (define-key map [(control c) (?2)] 'rst-compile-alt-toolset) | |
296 | ;; Convert the active region to pseudo-xml using the docutils tools. | |
297 | (define-key map [(control c) (?3)] 'rst-compile-pseudo-region) | |
298 | ;; Convert the current document to PDF and launch a viewer on the results. | |
299 | (define-key map [(control c) (?4)] 'rst-compile-pdf-preview) | |
300 | ;; Convert the current document to S5 slides and view in a web browser. | |
301 | (define-key map [(control c) (?5)] 'rst-compile-slides-preview) | |
302 | ||
303 | map) | |
e6438428 | 304 | "Keymap for reStructuredText mode commands. |
b4747519 | 305 | This inherits from Text mode.") |
94e9c286 SM |
306 | |
307 | ||
308 | ;; Abbrevs. | |
309 | (defvar rst-mode-abbrev-table nil | |
92439579 | 310 | "Abbrev table used while in Rst mode.") |
94e9c286 | 311 | (define-abbrev-table 'rst-mode-abbrev-table |
32845226 SM |
312 | (mapcar (lambda (x) (append x '(nil 0 system))) |
313 | '(("contents" ".. contents::\n..\n ") | |
314 | ("con" ".. contents::\n..\n ") | |
315 | ("cont" "[...]") | |
316 | ("skip" "\n\n[...]\n\n ") | |
317 | ("seq" "\n\n[...]\n\n ") | |
318 | ;; FIXME: Add footnotes, links, and more. | |
319 | ))) | |
94e9c286 SM |
320 | |
321 | ||
322 | ;; Syntax table. | |
323 | (defvar rst-mode-syntax-table | |
324 | (let ((st (copy-syntax-table text-mode-syntax-table))) | |
325 | ||
326 | (modify-syntax-entry ?$ "." st) | |
327 | (modify-syntax-entry ?% "." st) | |
328 | (modify-syntax-entry ?& "." st) | |
329 | (modify-syntax-entry ?' "." st) | |
330 | (modify-syntax-entry ?* "." st) | |
331 | (modify-syntax-entry ?+ "." st) | |
332 | (modify-syntax-entry ?. "_" st) | |
333 | (modify-syntax-entry ?/ "." st) | |
334 | (modify-syntax-entry ?< "." st) | |
335 | (modify-syntax-entry ?= "." st) | |
336 | (modify-syntax-entry ?> "." st) | |
337 | (modify-syntax-entry ?\\ "\\" st) | |
338 | (modify-syntax-entry ?| "." st) | |
339 | (modify-syntax-entry ?_ "." st) | |
340 | ||
341 | st) | |
342 | "Syntax table used while in `rst-mode'.") | |
343 | ||
344 | ||
345 | (defcustom rst-mode-hook nil | |
e6438428 JB |
346 | "Hook run when Rst mode is turned on. |
347 | The hook for Text mode is run before this one." | |
94e9c286 SM |
348 | :group 'rst |
349 | :type '(hook)) | |
350 | ||
351 | ||
352 | (defcustom rst-mode-lazy t | |
92439579 | 353 | "If non-nil Rst mode tries to font-lock multi-line elements correctly. |
b4747519 SM |
354 | Because this is really slow it should be set to nil if neither `jit-lock-mode' |
355 | not `lazy-lock-mode' and activated. | |
94e9c286 | 356 | |
b4747519 | 357 | If nil, comments and literal blocks are font-locked only on the line they start. |
94e9c286 | 358 | |
92439579 | 359 | The value of this variable is used when Rst mode is turned on." |
94e9c286 SM |
360 | :group 'rst |
361 | :type '(boolean)) | |
362 | ||
0667a132 SM |
363 | ;; Use rst-mode for *.rst and *.rest files. Many ReStructured-Text files |
364 | ;; use *.txt, but this is too generic to be set as a default. | |
1e8780b1 | 365 | ;;;###autoload (add-to-list 'auto-mode-alist (purecopy '("\\.re?st\\'" . rst-mode))) |
94e9c286 SM |
366 | ;;;###autoload |
367 | (define-derived-mode rst-mode text-mode "ReST" | |
94e9c286 | 368 | "Major mode for editing reStructuredText documents. |
e6438428 | 369 | \\<rst-mode-map> |
94e9c286 | 370 | There are a number of convenient keybindings provided by |
e6438428 | 371 | Rst mode. The main one is \\[rst-adjust], it updates or rotates |
94e9c286 | 372 | the section title around point or promotes/demotes the |
92439579 JB |
373 | decorations within the region (see full details below). |
374 | Use negative prefix arg to rotate in the other direction. | |
94e9c286 | 375 | |
e6438428 JB |
376 | Turning on `rst-mode' calls the normal hooks `text-mode-hook' |
377 | and `rst-mode-hook'. This mode also supports font-lock | |
378 | highlighting. You may customize `rst-mode-lazy' to toggle | |
379 | font-locking of blocks. | |
380 | ||
381 | \\{rst-mode-map}" | |
536db356 JPW |
382 | :abbrev-table rst-mode-abbrev-table |
383 | :syntax-table rst-mode-syntax-table | |
384 | :group 'rst | |
94e9c286 SM |
385 | |
386 | (set (make-local-variable 'paragraph-separate) paragraph-start) | |
387 | (set (make-local-variable 'indent-line-function) 'indent-relative-maybe) | |
388 | (set (make-local-variable 'paragraph-start) | |
389 | "\f\\|>*[ \t]*$\\|>*[ \t]*[-+*] \\|>*[ \t]*[0-9#]+\\. ") | |
390 | (set (make-local-variable 'adaptive-fill-mode) t) | |
391 | ||
392 | ;; FIXME: No need to reset this. | |
393 | ;; (set (make-local-variable 'indent-line-function) 'indent-relative) | |
394 | ||
395 | ;; The details of the following comment setup is important because it affects | |
396 | ;; auto-fill, and it is pretty common in running text to have an ellipsis | |
397 | ;; ("...") which trips because of the rest comment syntax (".. "). | |
398 | (set (make-local-variable 'comment-start) ".. ") | |
399 | (set (make-local-variable 'comment-start-skip) "^\\.\\. ") | |
400 | (set (make-local-variable 'comment-multi-line) nil) | |
401 | ||
402 | ;; Special variables | |
403 | (make-local-variable 'rst-adornment-level-alist) | |
404 | ||
405 | ;; Font lock | |
406 | (set (make-local-variable 'font-lock-defaults) | |
407 | '(rst-font-lock-keywords-function | |
408 | t nil nil nil | |
94e9c286 | 409 | (font-lock-mark-block-function . mark-paragraph))) |
b4747519 SM |
410 | ;; `jit-lock-mode' has been the default since Emacs-21.1, so there's no |
411 | ;; point messing around with font-lock-support-mode any more. | |
412 | ;; (when (boundp 'font-lock-support-mode) | |
413 | ;; ;; rst-mode has its own mind about font-lock-support-mode | |
414 | ;; (make-local-variable 'font-lock-support-mode) | |
415 | ;; ;; jit-lock-mode replaced lazy-lock-mode in GNU Emacs 21. | |
416 | ;; (let ((jit-or-lazy-lock-mode | |
417 | ;; (cond | |
418 | ;; ((fboundp 'lazy-lock-mode) 'lazy-lock-mode) | |
419 | ;; ((fboundp 'jit-lock-mode) 'jit-lock-mode) | |
420 | ;; ;; if neither lazy-lock nor jit-lock is supported, | |
421 | ;; ;; tell user and disable rst-mode-lazy | |
422 | ;; (t (when rst-mode-lazy | |
423 | ;; (message "Disabled lazy fontification, because no known support mode found.") | |
424 | ;; (setq rst-mode-lazy nil)))))) | |
425 | ;; (cond | |
426 | ;; ((and (not rst-mode-lazy) (not font-lock-support-mode))) | |
427 | ;; ;; No support mode set and none required - leave it alone | |
428 | ;; ((or (not font-lock-support-mode) ;; No support mode set (but required) | |
429 | ;; (symbolp font-lock-support-mode)) ;; or a fixed mode for all | |
430 | ;; (setq font-lock-support-mode | |
431 | ;; (list (cons 'rst-mode (and rst-mode-lazy jit-or-lazy-lock-mode)) | |
432 | ;; (cons t font-lock-support-mode)))) | |
433 | ;; ((and (listp font-lock-support-mode) | |
434 | ;; (not (assoc 'rst-mode font-lock-support-mode))) | |
435 | ;; ;; A list of modes missing rst-mode | |
436 | ;; (setq font-lock-support-mode | |
437 | ;; (cons (cons 'rst-mode (and rst-mode-lazy jit-or-lazy-lock-mode)) | |
438 | ;; font-lock-support-mode)))))) | |
94e9c286 SM |
439 | |
440 | ) | |
441 | ||
442 | ||
443 | ;;;###autoload | |
444 | (define-minor-mode rst-minor-mode | |
ac6c8639 CY |
445 | "Toggle ReST minor mode. |
446 | With a prefix argument ARG, enable ReST minor mode if ARG is | |
447 | positive, and disable it otherwise. If called from Lisp, enable | |
448 | the mode if ARG is omitted or nil. | |
94e9c286 | 449 | |
92439579 JB |
450 | When ReST minor mode is enabled, the ReST mode keybindings |
451 | are installed on top of the major mode bindings. Use this | |
452 | for modes derived from Text mode, like Mail mode." | |
94e9c286 SM |
453 | ;; The initial value. |
454 | nil | |
455 | ;; The indicator for the mode line. | |
456 | " ReST" | |
457 | ;; The minor mode bindings. | |
458 | rst-mode-map | |
459 | :group 'rst) | |
460 | ||
461 | ;; FIXME: can I somehow install these too? | |
462 | ;; :abbrev-table rst-mode-abbrev-table | |
463 | ;; :syntax-table rst-mode-syntax-table | |
464 | ||
465 | ||
466 | ||
467 | ||
468 | \f | |
469 | ;; Bulleted item lists. | |
470 | (defcustom rst-bullets | |
471 | '(?- ?* ?+) | |
472 | "List of all possible bullet characters for bulleted lists." | |
473 | :group 'rst) | |
474 | ||
475 | ||
476 | ||
477 | \f | |
478 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
fe3c5669 PE |
479 | ;; Section Decoration Adjustment |
480 | ;; ============================= | |
94e9c286 SM |
481 | ;; |
482 | ;; The following functions implement a smart automatic title sectioning feature. | |
483 | ;; The idea is that with the cursor sitting on a section title, we try to get as | |
484 | ;; much information from context and try to do the best thing automatically. | |
485 | ;; This function can be invoked many times and/or with prefix argument to rotate | |
486 | ;; between the various sectioning decorations. | |
487 | ;; | |
488 | ;; Definitions: the two forms of sectioning define semantically separate section | |
489 | ;; levels. A sectioning DECORATION consists in: | |
490 | ;; | |
491 | ;; - a CHARACTER | |
492 | ;; | |
493 | ;; - a STYLE which can be either of 'simple' or 'over-and-under'. | |
494 | ;; | |
495 | ;; - an INDENT (meaningful for the over-and-under style only) which determines | |
496 | ;; how many characters and over-and-under style is hanging outside of the | |
497 | ;; title at the beginning and ending. | |
498 | ;; | |
499 | ;; Important note: an existing decoration must be formed by at least two | |
500 | ;; characters to be recognized. | |
501 | ;; | |
502 | ;; Here are two examples of decorations (| represents the window border, column | |
503 | ;; 0): | |
504 | ;; | |
505 | ;; | | |
506 | ;; 1. char: '-' e |Some Title | |
507 | ;; style: simple |---------- | |
508 | ;; | | |
509 | ;; 2. char: '=' |============== | |
510 | ;; style: over-and-under | Some Title | |
511 | ;; indent: 2 |============== | |
512 | ;; | | |
513 | ;; | |
514 | ;; Some notes: | |
515 | ;; | |
516 | ;; - The underlining character that is used depends on context. The file is | |
517 | ;; scanned to find other sections and an appropriate character is selected. | |
518 | ;; If the function is invoked on a section that is complete, the character is | |
519 | ;; rotated among the existing section decorations. | |
520 | ;; | |
521 | ;; Note that when rotating the characters, if we come to the end of the | |
522 | ;; hierarchy of decorations, the variable rst-preferred-decorations is | |
523 | ;; consulted to propose a new underline decoration, and if continued, we cycle | |
524 | ;; the decorations all over again. Set this variable to nil if you want to | |
525 | ;; limit the underlining character propositions to the existing decorations in | |
526 | ;; the file. | |
527 | ;; | |
528 | ;; - A prefix argument can be used to alternate the style. | |
529 | ;; | |
530 | ;; - An underline/overline that is not extended to the column at which it should | |
531 | ;; be hanging is dubbed INCOMPLETE. For example:: | |
532 | ;; | |
533 | ;; |Some Title | |
534 | ;; |------- | |
535 | ;; | |
536 | ;; Examples of default invocation: | |
537 | ;; | |
538 | ;; |Some Title ---> |Some Title | |
539 | ;; | |---------- | |
540 | ;; | |
541 | ;; |Some Title ---> |Some Title | |
542 | ;; |----- |---------- | |
543 | ;; | |
544 | ;; | |------------ | |
545 | ;; | Some Title ---> | Some Title | |
546 | ;; | |------------ | |
547 | ;; | |
548 | ;; In over-and-under style, when alternating the style, a variable is | |
549 | ;; available to select how much default indent to use (it can be zero). Note | |
550 | ;; that if the current section decoration already has an indent, we don't | |
551 | ;; adjust it to the default, we rather use the current indent that is already | |
552 | ;; there for adjustment (unless we cycle, in which case we use the indent | |
553 | ;; that has been found previously). | |
554 | ||
555 | (defgroup rst-adjust nil | |
92439579 | 556 | "Settings for adjustment and cycling of section title decorations." |
94e9c286 SM |
557 | :group 'rst |
558 | :version "21.1") | |
559 | ||
560 | (defcustom rst-preferred-decorations '( (?= over-and-under 1) | |
561 | (?= simple 0) | |
562 | (?- simple 0) | |
563 | (?~ simple 0) | |
564 | (?+ simple 0) | |
565 | (?` simple 0) | |
566 | (?# simple 0) | |
567 | (?@ simple 0) ) | |
568 | "Preferred ordering of section title decorations. | |
569 | ||
570 | This sequence is consulted to offer a new decoration suggestion | |
571 | when we rotate the underlines at the end of the existing | |
572 | hierarchy of characters, or when there is no existing section | |
573 | title in the file." | |
574 | :group 'rst-adjust) | |
575 | ||
576 | ||
577 | (defcustom rst-default-indent 1 | |
578 | "Number of characters to indent the section title. | |
579 | ||
92439579 | 580 | This is used for when toggling decoration styles, when switching |
94e9c286 SM |
581 | from a simple decoration style to a over-and-under decoration |
582 | style." | |
583 | :group 'rst-adjust) | |
584 | ||
585 | ||
586 | (defvar rst-section-text-regexp "^[ \t]*\\S-*\\w\\S-*" | |
587 | "Regular expression for valid section title text.") | |
588 | ||
589 | ||
590 | (defun rst-line-homogeneous-p (&optional accept-special) | |
591 | "Return true if the line is homogeneous. | |
592 | ||
593 | Predicate that returns the unique char if the current line is | |
92439579 JB |
594 | composed only of a single repeated non-whitespace character. |
595 | This returns the char even if there is whitespace at the | |
596 | beginning of the line. | |
94e9c286 SM |
597 | |
598 | If ACCEPT-SPECIAL is specified we do not ignore special sequences | |
599 | which normally we would ignore when doing a search on many lines. | |
92439579 | 600 | For example, normally we have cases to ignore commonly occurring |
94e9c286 SM |
601 | patterns, such as :: or ...; with the flag do not ignore them." |
602 | (save-excursion | |
603 | (back-to-indentation) | |
604 | (unless (looking-at "\n") | |
605 | (let ((c (thing-at-point 'char))) | |
606 | (if (and (looking-at (format "[%s]+[ \t]*$" c)) | |
607 | (or accept-special | |
608 | (and | |
609 | ;; Common patterns. | |
610 | (not (looking-at "::[ \t]*$")) | |
611 | (not (looking-at "\\.\\.\\.[ \t]*$")) | |
612 | ;; Discard one char line | |
613 | (not (looking-at ".[ \t]*$")) | |
614 | ))) | |
615 | (string-to-char c)) | |
616 | )) | |
617 | )) | |
618 | ||
619 | (defun rst-line-homogeneous-nodent-p (&optional accept-special) | |
620 | "Return true if the line is homogeneous with no indent. | |
621 | See `rst-line-homogeneous-p' about ACCEPT-SPECIAL." | |
622 | (save-excursion | |
623 | (beginning-of-line) | |
624 | (if (looking-at "^[ \t]+") | |
625 | nil | |
626 | (rst-line-homogeneous-p accept-special) | |
627 | ))) | |
628 | ||
629 | ||
630 | (defun rst-compare-decorations (deco1 deco2) | |
631 | "Compare decorations. | |
92439579 | 632 | Return true if both DECO1 and DECO2 decorations are equal, |
94e9c286 | 633 | according to restructured text semantics (only the character and |
92439579 | 634 | the style are compared, the indentation does not matter)." |
94e9c286 SM |
635 | (and (eq (car deco1) (car deco2)) |
636 | (eq (cadr deco1) (cadr deco2)))) | |
637 | ||
638 | ||
639 | (defun rst-get-decoration-match (hier deco) | |
640 | "Return the index (level) in hierarchy HIER of decoration DECO. | |
641 | This basically just searches for the item using the appropriate | |
92439579 | 642 | comparison and returns the index. Return nil if the item is |
94e9c286 SM |
643 | not found." |
644 | (let ((cur hier)) | |
645 | (while (and cur (not (rst-compare-decorations (car cur) deco))) | |
646 | (setq cur (cdr cur))) | |
647 | cur)) | |
648 | ||
649 | ||
650 | (defun rst-suggest-new-decoration (alldecos &optional prev) | |
651 | "Suggest a new, different decoration from all that have been seen. | |
652 | ||
92439579 JB |
653 | ALLDECOS is the set of all decorations, including the line numbers. |
654 | PREV is the optional previous decoration, in order to suggest a | |
655 | better match." | |
94e9c286 SM |
656 | |
657 | ;; For all the preferred decorations... | |
658 | (let* ( | |
659 | ;; If 'prev' is given, reorder the list to start searching after the | |
660 | ;; match. | |
661 | (fplist | |
662 | (cdr (rst-get-decoration-match rst-preferred-decorations prev))) | |
663 | ||
664 | ;; List of candidates to search. | |
665 | (curpotential (append fplist rst-preferred-decorations))) | |
666 | (while | |
667 | ;; For all the decorations... | |
668 | (let ((cur alldecos) | |
669 | found) | |
670 | (while (and cur (not found)) | |
671 | (if (rst-compare-decorations (car cur) (car curpotential)) | |
672 | ;; Found it! | |
673 | (setq found (car curpotential)) | |
674 | (setq cur (cdr cur)))) | |
675 | found) | |
676 | ||
677 | (setq curpotential (cdr curpotential))) | |
678 | ||
b4747519 | 679 | (copy-sequence (car curpotential)))) |
94e9c286 SM |
680 | |
681 | (defun rst-delete-entire-line () | |
682 | "Delete the entire current line without using the `kill-ring'." | |
b4747519 SM |
683 | (delete-region (line-beginning-position) |
684 | (line-beginning-position 2))) | |
94e9c286 SM |
685 | |
686 | (defun rst-update-section (char style &optional indent) | |
687 | "Unconditionally update the style of a section decoration. | |
688 | ||
92439579 JB |
689 | Do this using the given character CHAR, with STYLE 'simple |
690 | or 'over-and-under, and with indent INDENT. If the STYLE | |
691 | is 'simple, whitespace before the title is removed (indent | |
692 | is always assumed to be 0). | |
94e9c286 SM |
693 | |
694 | If there are existing overline and/or underline from the | |
695 | existing decoration, they are removed before adding the | |
696 | requested decoration." | |
697 | ||
698 | (interactive) | |
94e9c286 | 699 | (end-of-line) |
8bbb7dd8 SM |
700 | (let ((marker (point-marker)) |
701 | len) | |
94e9c286 SM |
702 | |
703 | ;; Fixup whitespace at the beginning and end of the line | |
704 | (if (or (null indent) (eq style 'simple)) | |
705 | (setq indent 0)) | |
706 | (beginning-of-line) | |
707 | (delete-horizontal-space) | |
708 | (insert (make-string indent ? )) | |
709 | ||
710 | (end-of-line) | |
711 | (delete-horizontal-space) | |
712 | ||
713 | ;; Set the current column, we're at the end of the title line | |
714 | (setq len (+ (current-column) indent)) | |
715 | ||
716 | ;; Remove previous line if it consists only of a single repeated character | |
717 | (save-excursion | |
718 | (forward-line -1) | |
719 | (and (rst-line-homogeneous-p 1) | |
720 | ;; Avoid removing the underline of a title right above us. | |
721 | (save-excursion (forward-line -1) | |
722 | (not (looking-at rst-section-text-regexp))) | |
723 | (rst-delete-entire-line))) | |
724 | ||
725 | ;; Remove following line if it consists only of a single repeated | |
726 | ;; character | |
727 | (save-excursion | |
728 | (forward-line +1) | |
729 | (and (rst-line-homogeneous-p 1) | |
730 | (rst-delete-entire-line)) | |
731 | ;; Add a newline if we're at the end of the buffer, for the subsequence | |
732 | ;; inserting of the underline | |
733 | (if (= (point) (buffer-end 1)) | |
734 | (newline 1))) | |
735 | ||
736 | ;; Insert overline | |
737 | (if (eq style 'over-and-under) | |
738 | (save-excursion | |
739 | (beginning-of-line) | |
740 | (open-line 1) | |
741 | (insert (make-string len char)))) | |
742 | ||
743 | ;; Insert underline | |
744 | (forward-line +1) | |
745 | (open-line 1) | |
746 | (insert (make-string len char)) | |
747 | ||
748 | (forward-line +1) | |
749 | (goto-char marker) | |
750 | )) | |
751 | ||
752 | ||
753 | (defun rst-normalize-cursor-position () | |
754 | "Normalize the cursor position. | |
755 | If the cursor is on a decoration line or an empty line , place it | |
756 | on the section title line (at the end). Returns the line offset | |
757 | by which the cursor was moved. This works both over or under a | |
758 | line." | |
759 | (if (save-excursion (beginning-of-line) | |
760 | (or (rst-line-homogeneous-p 1) | |
761 | (looking-at "^[ \t]*$"))) | |
762 | (progn | |
763 | (beginning-of-line) | |
764 | (cond | |
765 | ((save-excursion (forward-line -1) | |
766 | (beginning-of-line) | |
767 | (and (looking-at rst-section-text-regexp) | |
768 | (not (rst-line-homogeneous-p 1)))) | |
769 | (progn (forward-line -1) -1)) | |
770 | ((save-excursion (forward-line +1) | |
771 | (beginning-of-line) | |
772 | (and (looking-at rst-section-text-regexp) | |
773 | (not (rst-line-homogeneous-p 1)))) | |
774 | (progn (forward-line +1) +1)) | |
775 | (t 0))) | |
776 | 0 )) | |
777 | ||
778 | ||
779 | (defun rst-find-all-decorations () | |
780 | "Find all the decorations in the file. | |
781 | Return a list of (line, decoration) pairs. Each decoration | |
782 | consists in a (char, style, indent) triple. | |
783 | ||
784 | This function does not detect the hierarchy of decorations, it | |
785 | just finds all of them in a file. You can then invoke another | |
786 | function to remove redundancies and inconsistencies." | |
787 | ||
8bbb7dd8 | 788 | (let ((positions ()) |
94e9c286 SM |
789 | (curline 1)) |
790 | ;; Iterate over all the section titles/decorations in the file. | |
791 | (save-excursion | |
792 | (goto-char (point-min)) | |
793 | (while (< (point) (buffer-end 1)) | |
794 | (if (rst-line-homogeneous-nodent-p) | |
795 | (progn | |
796 | (setq curline (+ curline (rst-normalize-cursor-position))) | |
797 | ||
798 | ;; Here we have found a potential site for a decoration, | |
799 | ;; characterize it. | |
800 | (let ((deco (rst-get-decoration))) | |
801 | (if (cadr deco) ;; Style is existing. | |
802 | ;; Found a real decoration site. | |
803 | (progn | |
804 | (push (cons curline deco) positions) | |
805 | ;; Push beyond the underline. | |
806 | (forward-line 1) | |
807 | (setq curline (+ curline 1)) | |
808 | ))) | |
809 | )) | |
810 | (forward-line 1) | |
811 | (setq curline (+ curline 1)) | |
812 | )) | |
813 | (reverse positions))) | |
814 | ||
815 | ||
816 | (defun rst-infer-hierarchy (decorations) | |
817 | "Build a hierarchy of decorations using the list of given DECORATIONS. | |
818 | ||
819 | This function expects a list of (char, style, indent) decoration | |
820 | specifications, in order that they appear in a file, and will | |
821 | infer a hierarchy of section levels by removing decorations that | |
822 | have already been seen in a forward traversal of the decorations, | |
823 | comparing just the character and style. | |
824 | ||
825 | Similarly returns a list of (char, style, indent), where each | |
826 | list element should be unique." | |
827 | ||
828 | (let ((hierarchy-alist (list))) | |
829 | (dolist (x decorations) | |
830 | (let ((char (car x)) | |
831 | (style (cadr x))) | |
832 | (unless (assoc (cons char style) hierarchy-alist) | |
833 | (push (cons (cons char style) x) hierarchy-alist)) | |
834 | )) | |
835 | ||
836 | (mapcar 'cdr (nreverse hierarchy-alist)) | |
837 | )) | |
838 | ||
839 | ||
840 | (defun rst-get-hierarchy (&optional alldecos ignore) | |
841 | "Return the hierarchy of section titles in the file. | |
842 | ||
843 | Return a list of decorations that represents the hierarchy of | |
844 | section titles in the file. Reuse the list of decorations | |
845 | already computed in ALLDECOS if present. If the line number in | |
846 | IGNORE is specified, the decoration found on that line (if there | |
847 | is one) is not taken into account when building the hierarchy." | |
848 | (let ((all (or alldecos (rst-find-all-decorations)))) | |
849 | (setq all (assq-delete-all ignore all)) | |
850 | (rst-infer-hierarchy (mapcar 'cdr all)))) | |
851 | ||
852 | ||
853 | (defun rst-get-decoration (&optional point) | |
854 | "Get the decoration at POINT. | |
855 | ||
856 | Looks around point and finds the characteristics of the | |
92439579 | 857 | decoration that is found there. Assumes that the cursor is |
94e9c286 SM |
858 | already placed on the title line (and not on the overline or |
859 | underline). | |
860 | ||
861 | This function returns a (char, style, indent) triple. If the | |
92439579 JB |
862 | characters of overline and underline are different, return |
863 | the underline character. The indent is always calculated. | |
864 | A decoration can be said to exist if the style is not nil. | |
94e9c286 SM |
865 | |
866 | A point can be specified to go to the given location before | |
867 | extracting the decoration." | |
868 | ||
8bbb7dd8 | 869 | (let (char style) |
94e9c286 SM |
870 | (save-excursion |
871 | (if point (goto-char point)) | |
872 | (beginning-of-line) | |
873 | (if (looking-at rst-section-text-regexp) | |
874 | (let* ((over (save-excursion | |
875 | (forward-line -1) | |
876 | (rst-line-homogeneous-nodent-p))) | |
877 | ||
8bbb7dd8 SM |
878 | (under (save-excursion |
879 | (forward-line +1) | |
880 | (rst-line-homogeneous-nodent-p))) | |
881 | ) | |
94e9c286 SM |
882 | |
883 | ;; Check that the line above the overline is not part of a title | |
884 | ;; above it. | |
885 | (if (and over | |
886 | (save-excursion | |
887 | (and (equal (forward-line -2) 0) | |
888 | (looking-at rst-section-text-regexp)))) | |
889 | (setq over nil)) | |
890 | ||
891 | (cond | |
892 | ;; No decoration found, leave all return values nil. | |
893 | ((and (eq over nil) (eq under nil))) | |
894 | ||
895 | ;; Overline only, leave all return values nil. | |
896 | ;; | |
897 | ;; Note: we don't return the overline character, but it could | |
898 | ;; perhaps in some cases be used to do something. | |
899 | ((and over (eq under nil))) | |
900 | ||
901 | ;; Underline only. | |
902 | ((and under (eq over nil)) | |
903 | (setq char under | |
904 | style 'simple)) | |
905 | ||
906 | ;; Both overline and underline. | |
907 | (t | |
908 | (setq char under | |
8bbb7dd8 SM |
909 | style 'over-and-under))))) |
910 | ;; Return values. | |
911 | (list char style | |
912 | ;; Find indentation. | |
913 | (save-excursion (back-to-indentation) (current-column)))))) | |
94e9c286 SM |
914 | |
915 | ||
916 | (defun rst-get-decorations-around (&optional alldecos) | |
917 | "Return the decorations around point. | |
918 | ||
92439579 JB |
919 | Given the list of all decorations ALLDECOS (with positions), |
920 | find the decorations before and after the given point. | |
921 | A list of the previous and next decorations is returned." | |
94e9c286 SM |
922 | (let* ((all (or alldecos (rst-find-all-decorations))) |
923 | (curline (line-number-at-pos)) | |
924 | prev next | |
925 | (cur all)) | |
926 | ||
927 | ;; Search for the decorations around the current line. | |
928 | (while (and cur (< (caar cur) curline)) | |
929 | (setq prev cur | |
930 | cur (cdr cur))) | |
931 | ;; 'cur' is the following decoration. | |
932 | ||
933 | (if (and cur (caar cur)) | |
934 | (setq next (if (= curline (caar cur)) (cdr cur) cur))) | |
935 | ||
936 | (mapcar 'cdar (list prev next)) | |
937 | )) | |
938 | ||
939 | ||
940 | (defun rst-decoration-complete-p (deco) | |
92439579 | 941 | "Return true if the decoration DECO around point is complete." |
94e9c286 SM |
942 | ;; Note: we assume that the detection of the overline as being the underline |
943 | ;; of a preceding title has already been detected, and has been eliminated | |
944 | ;; from the decoration that is given to us. | |
945 | ||
946 | ;; There is some sectioning already present, so check if the current | |
947 | ;; sectioning is complete and correct. | |
948 | (let* ((char (car deco)) | |
949 | (style (cadr deco)) | |
950 | (indent (caddr deco)) | |
951 | (endcol (save-excursion (end-of-line) (current-column))) | |
952 | ) | |
953 | (if char | |
954 | (let ((exps (concat "^" | |
955 | (regexp-quote (make-string (+ endcol indent) char)) | |
956 | "$"))) | |
957 | (and | |
958 | (save-excursion (forward-line +1) | |
959 | (beginning-of-line) | |
960 | (looking-at exps)) | |
961 | (or (not (eq style 'over-and-under)) | |
962 | (save-excursion (forward-line -1) | |
963 | (beginning-of-line) | |
964 | (looking-at exps)))) | |
965 | )) | |
966 | )) | |
967 | ||
968 | ||
969 | (defun rst-get-next-decoration | |
970 | (curdeco hier &optional suggestion reverse-direction) | |
971 | "Get the next decoration for CURDECO, in given hierarchy HIER. | |
972 | If suggesting, suggest for new decoration SUGGESTION. | |
973 | REVERSE-DIRECTION is used to reverse the cycling order." | |
974 | ||
975 | (let* ( | |
976 | (char (car curdeco)) | |
977 | (style (cadr curdeco)) | |
978 | ||
979 | ;; Build a new list of decorations for the rotation. | |
980 | (rotdecos | |
981 | (append hier | |
982 | ;; Suggest a new decoration. | |
983 | (list suggestion | |
984 | ;; If nothing to suggest, use first decoration. | |
985 | (car hier)))) ) | |
986 | (or | |
987 | ;; Search for next decoration. | |
988 | (cadr | |
989 | (let ((cur (if reverse-direction rotdecos | |
990 | (reverse rotdecos)))) | |
991 | (while (and cur | |
992 | (not (and (eq char (caar cur)) | |
993 | (eq style (cadar cur))))) | |
994 | (setq cur (cdr cur))) | |
995 | cur)) | |
996 | ||
997 | ;; If not found, take the first of all decorations. | |
998 | suggestion | |
999 | ))) | |
1000 | ||
1001 | ||
1002 | (defun rst-adjust () | |
1003 | "Auto-adjust the decoration around point. | |
1004 | ||
1005 | Adjust/rotate the section decoration for the section title | |
1006 | around point or promote/demote the decorations inside the region, | |
1007 | depending on if the region is active. This function is meant to | |
fffa137c | 1008 | be invoked possibly multiple times, and can vary its behavior |
94e9c286 | 1009 | with a positive prefix argument (toggle style), or with a |
fffa137c | 1010 | negative prefix argument (alternate behavior). |
94e9c286 SM |
1011 | |
1012 | This function is the main focus of this module and is a bit of a | |
1013 | swiss knife. It is meant as the single most essential function | |
1014 | to be bound to invoke to adjust the decorations of a section | |
1015 | title in restructuredtext. It tries to deal with all the | |
1016 | possible cases gracefully and to do `the right thing' in all | |
1017 | cases. | |
1018 | ||
1019 | See the documentations of `rst-adjust-decoration' and | |
1020 | `rst-promote-region' for full details. | |
1021 | ||
1022 | Prefix Arguments | |
1023 | ================ | |
1024 | ||
1025 | The method can take either (but not both) of | |
1026 | ||
1027 | a. a (non-negative) prefix argument, which means to toggle the | |
1028 | decoration style. Invoke with a prefix arg for example; | |
1029 | ||
1030 | b. a negative numerical argument, which generally inverts the | |
1031 | direction of search in the file or hierarchy. Invoke with C-- | |
1032 | prefix for example." | |
1033 | (interactive) | |
1034 | ||
1035 | (let* (;; Save our original position on the current line. | |
8bbb7dd8 | 1036 | (origpt (point-marker)) |
94e9c286 SM |
1037 | |
1038 | ;; Parse the positive and negative prefix arguments. | |
1039 | (reverse-direction | |
1040 | (and current-prefix-arg | |
1041 | (< (prefix-numeric-value current-prefix-arg) 0))) | |
1042 | (toggle-style | |
1043 | (and current-prefix-arg (not reverse-direction)))) | |
1044 | ||
1045 | (if (rst-portable-mark-active-p) | |
1046 | ;; Adjust decorations within region. | |
1047 | (rst-promote-region current-prefix-arg) | |
1048 | ;; Adjust decoration around point. | |
1049 | (rst-adjust-decoration toggle-style reverse-direction)) | |
1050 | ||
1051 | ;; Run the hooks to run after adjusting. | |
1052 | (run-hooks 'rst-adjust-hook) | |
1053 | ||
1054 | ;; Make sure to reset the cursor position properly after we're done. | |
1055 | (goto-char origpt) | |
1056 | ||
1057 | )) | |
1058 | ||
1059 | (defvar rst-adjust-hook nil | |
1060 | "Hooks to be run after running `rst-adjust'.") | |
1061 | ||
1062 | (defvar rst-new-decoration-down nil | |
b4747519 SM |
1063 | "Non-nil if new decoration is added deeper. |
1064 | If non-nil, a new decoration being added will be initialized to | |
1065 | be one level down from the previous decoration. If nil, a new | |
1066 | decoration will be equal to the level of the previous | |
1067 | decoration.") | |
94e9c286 SM |
1068 | |
1069 | (defun rst-adjust-decoration (&optional toggle-style reverse-direction) | |
1070 | "Adjust/rotate the section decoration for the section title around point. | |
1071 | ||
1072 | This function is meant to be invoked possibly multiple times, and | |
fffa137c | 1073 | can vary its behavior with a true TOGGLE-STYLE argument, or with |
94e9c286 SM |
1074 | a REVERSE-DIRECTION argument. |
1075 | ||
92439579 JB |
1076 | General Behavior |
1077 | ================ | |
94e9c286 SM |
1078 | |
1079 | The next action it takes depends on context around the point, and | |
1080 | it is meant to be invoked possibly more than once to rotate among | |
1081 | the various possibilities. Basically, this function deals with: | |
1082 | ||
1083 | - adding a decoration if the title does not have one; | |
1084 | ||
1085 | - adjusting the length of the underline characters to fit a | |
1086 | modified title; | |
1087 | ||
1088 | - rotating the decoration in the set of already existing | |
1089 | sectioning decorations used in the file; | |
1090 | ||
1091 | - switching between simple and over-and-under styles. | |
1092 | ||
1093 | You should normally not have to read all the following, just | |
1094 | invoke the method and it will do the most obvious thing that you | |
1095 | would expect. | |
1096 | ||
1097 | ||
1098 | Decoration Definitions | |
1099 | ====================== | |
1100 | ||
1101 | The decorations consist in | |
1102 | ||
1103 | 1. a CHARACTER | |
1104 | ||
1105 | 2. a STYLE which can be either of 'simple' or 'over-and-under'. | |
1106 | ||
1107 | 3. an INDENT (meaningful for the over-and-under style only) | |
1108 | which determines how many characters and over-and-under | |
1109 | style is hanging outside of the title at the beginning and | |
1110 | ending. | |
1111 | ||
1112 | See source code for mode details. | |
1113 | ||
1114 | ||
92439579 JB |
1115 | Detailed Behavior Description |
1116 | ============================= | |
94e9c286 SM |
1117 | |
1118 | Here are the gory details of the algorithm (it seems quite | |
1119 | complicated, but really, it does the most obvious thing in all | |
1120 | the particular cases): | |
1121 | ||
1122 | Before applying the decoration change, the cursor is placed on | |
1123 | the closest line that could contain a section title. | |
1124 | ||
1125 | Case 1: No Decoration | |
1126 | --------------------- | |
1127 | ||
1128 | If the current line has no decoration around it, | |
1129 | ||
1130 | - search backwards for the last previous decoration, and apply | |
1131 | the decoration one level lower to the current line. If there | |
1132 | is no defined level below this previous decoration, we suggest | |
1133 | the most appropriate of the `rst-preferred-decorations'. | |
1134 | ||
1135 | If REVERSE-DIRECTION is true, we simply use the previous | |
1136 | decoration found directly. | |
1137 | ||
1138 | - if there is no decoration found in the given direction, we use | |
1139 | the first of `rst-preferred-decorations'. | |
1140 | ||
1141 | The prefix argument forces a toggle of the prescribed decoration | |
1142 | style. | |
1143 | ||
1144 | Case 2: Incomplete Decoration | |
1145 | ----------------------------- | |
1146 | ||
1147 | If the current line does have an existing decoration, but the | |
1148 | decoration is incomplete, that is, the underline/overline does | |
1149 | not extend to exactly the end of the title line (it is either too | |
1150 | short or too long), we simply extend the length of the | |
1151 | underlines/overlines to fit exactly the section title. | |
1152 | ||
1153 | If the prefix argument is given, we toggle the style of the | |
1154 | decoration as well. | |
1155 | ||
1156 | REVERSE-DIRECTION has no effect in this case. | |
1157 | ||
1158 | Case 3: Complete Existing Decoration | |
1159 | ------------------------------------ | |
1160 | ||
1161 | If the decoration is complete (i.e. the underline (overline) | |
1162 | length is already adjusted to the end of the title line), we | |
1163 | search/parse the file to establish the hierarchy of all the | |
1164 | decorations (making sure not to include the decoration around | |
1165 | point), and we rotate the current title's decoration from within | |
1166 | that list (by default, going *down* the hierarchy that is present | |
1167 | in the file, i.e. to a lower section level). This is meant to be | |
1168 | used potentially multiple times, until the desired decoration is | |
1169 | found around the title. | |
1170 | ||
1171 | If we hit the boundary of the hierarchy, exactly one choice from | |
1172 | the list of preferred decorations is suggested/chosen, the first | |
1173 | of those decoration that has not been seen in the file yet (and | |
1174 | not including the decoration around point), and the next | |
1175 | invocation rolls over to the other end of the hierarchy (i.e. it | |
1176 | cycles). This allows you to avoid having to set which character | |
92439579 | 1177 | to use. |
94e9c286 SM |
1178 | |
1179 | If REVERSE-DIRECTION is true, the effect is to change the | |
1180 | direction of rotation in the hierarchy of decorations, thus | |
1181 | instead going *up* the hierarchy. | |
1182 | ||
1183 | However, if there is a non-negative prefix argument, we do not | |
1184 | rotate the decoration, but instead simply toggle the style of the | |
1185 | current decoration (this should be the most common way to toggle | |
1186 | the style of an existing complete decoration). | |
1187 | ||
1188 | ||
1189 | Point Location | |
1190 | ============== | |
1191 | ||
1192 | The invocation of this function can be carried out anywhere | |
1193 | within the section title line, on an existing underline or | |
1194 | overline, as well as on an empty line following a section title. | |
1195 | This is meant to be as convenient as possible. | |
1196 | ||
1197 | ||
1198 | Indented Sections | |
1199 | ================= | |
1200 | ||
1201 | Indented section titles such as :: | |
1202 | ||
1203 | My Title | |
1204 | -------- | |
1205 | ||
92439579 | 1206 | are invalid in restructuredtext and thus not recognized by the |
94e9c286 SM |
1207 | parser. This code will thus not work in a way that would support |
1208 | indented sections (it would be ambiguous anyway). | |
1209 | ||
1210 | ||
1211 | Joint Sections | |
1212 | ============== | |
1213 | ||
1214 | Section titles that are right next to each other may not be | |
1215 | treated well. More work might be needed to support those, and | |
1216 | special conditions on the completeness of existing decorations | |
1217 | might be required to make it non-ambiguous. | |
1218 | ||
1219 | For now we assume that the decorations are disjoint, that is, | |
1220 | there is at least a single line between the titles/decoration | |
1221 | lines. | |
1222 | ||
1223 | ||
1224 | Suggested Binding | |
1225 | ================= | |
1226 | ||
1227 | We suggest that you bind this function on C-=. It is close to | |
1228 | C-- so a negative argument can be easily specified with a flick | |
1229 | of the right hand fingers and the binding is unused in `text-mode'." | |
1230 | (interactive) | |
1231 | ||
1232 | ;; If we were invoked directly, parse the prefix arguments into the | |
1233 | ;; arguments of the function. | |
1234 | (if current-prefix-arg | |
1235 | (setq reverse-direction | |
1236 | (and current-prefix-arg | |
1237 | (< (prefix-numeric-value current-prefix-arg) 0)) | |
1238 | ||
1239 | toggle-style | |
1240 | (and current-prefix-arg (not reverse-direction)))) | |
1241 | ||
1242 | (let* (;; Check if we're on an underline around a section title, and move the | |
1243 | ;; cursor to the title if this is the case. | |
1244 | (moved (rst-normalize-cursor-position)) | |
1245 | ||
1246 | ;; Find the decoration and completeness around point. | |
1247 | (curdeco (rst-get-decoration)) | |
1248 | (char (car curdeco)) | |
1249 | (style (cadr curdeco)) | |
1250 | (indent (caddr curdeco)) | |
1251 | ||
1252 | ;; New values to be computed. | |
1253 | char-new style-new indent-new | |
1254 | ) | |
1255 | ||
1256 | ;; We've moved the cursor... if we're not looking at some text, we have | |
1257 | ;; nothing to do. | |
1258 | (if (save-excursion (beginning-of-line) | |
1259 | (looking-at rst-section-text-regexp)) | |
1260 | (progn | |
1261 | (cond | |
1262 | ;;------------------------------------------------------------------- | |
1263 | ;; Case 1: No Decoration | |
1264 | ((and (eq char nil) (eq style nil)) | |
1265 | ||
1266 | (let* ((alldecos (rst-find-all-decorations)) | |
1267 | ||
1268 | (around (rst-get-decorations-around alldecos)) | |
1269 | (prev (car around)) | |
1270 | cur | |
1271 | ||
1272 | (hier (rst-get-hierarchy alldecos)) | |
1273 | ) | |
1274 | ||
1275 | ;; Advance one level down. | |
1276 | (setq cur | |
1277 | (if prev | |
1278 | (if (not reverse-direction) | |
1279 | (or (funcall (if rst-new-decoration-down 'cadr 'car) | |
1280 | (rst-get-decoration-match hier prev)) | |
1281 | (rst-suggest-new-decoration hier prev)) | |
1282 | prev) | |
b4747519 | 1283 | (copy-sequence (car rst-preferred-decorations)))) |
94e9c286 SM |
1284 | |
1285 | ;; Invert the style if requested. | |
1286 | (if toggle-style | |
1287 | (setcar (cdr cur) (if (eq (cadr cur) 'simple) | |
1288 | 'over-and-under 'simple)) ) | |
1289 | ||
1290 | (setq char-new (car cur) | |
1291 | style-new (cadr cur) | |
1292 | indent-new (caddr cur)) | |
1293 | )) | |
1294 | ||
1295 | ;;------------------------------------------------------------------- | |
1296 | ;; Case 2: Incomplete Decoration | |
1297 | ((not (rst-decoration-complete-p curdeco)) | |
1298 | ||
1299 | ;; Invert the style if requested. | |
1300 | (if toggle-style | |
1301 | (setq style (if (eq style 'simple) 'over-and-under 'simple))) | |
1302 | ||
1303 | (setq char-new char | |
1304 | style-new style | |
1305 | indent-new indent)) | |
1306 | ||
1307 | ;;------------------------------------------------------------------- | |
1308 | ;; Case 3: Complete Existing Decoration | |
1309 | (t | |
1310 | (if toggle-style | |
1311 | ||
1312 | ;; Simply switch the style of the current decoration. | |
1313 | (setq char-new char | |
1314 | style-new (if (eq style 'simple) 'over-and-under 'simple) | |
1315 | indent-new rst-default-indent) | |
1316 | ||
1317 | ;; Else, we rotate, ignoring the decoration around the current | |
1318 | ;; line... | |
1319 | (let* ((alldecos (rst-find-all-decorations)) | |
1320 | ||
1321 | (hier (rst-get-hierarchy alldecos (line-number-at-pos))) | |
1322 | ||
1323 | ;; Suggestion, in case we need to come up with something | |
1324 | ;; new | |
1325 | (suggestion (rst-suggest-new-decoration | |
1326 | hier | |
1327 | (car (rst-get-decorations-around alldecos)))) | |
1328 | ||
1329 | (nextdeco (rst-get-next-decoration | |
1330 | curdeco hier suggestion reverse-direction)) | |
1331 | ||
1332 | ) | |
1333 | ||
1334 | ;; Indent, if present, always overrides the prescribed indent. | |
1335 | (setq char-new (car nextdeco) | |
1336 | style-new (cadr nextdeco) | |
1337 | indent-new (caddr nextdeco)) | |
1338 | ||
1339 | ))) | |
1340 | ) | |
1341 | ||
1342 | ;; Override indent with present indent! | |
1343 | (setq indent-new (if (> indent 0) indent indent-new)) | |
1344 | ||
1345 | (if (and char-new style-new) | |
1346 | (rst-update-section char-new style-new indent-new)) | |
1347 | )) | |
1348 | ||
1349 | ||
1350 | ;; Correct the position of the cursor to more accurately reflect where it | |
1351 | ;; was located when the function was invoked. | |
1352 | (unless (= moved 0) | |
1353 | (forward-line (- moved)) | |
1354 | (end-of-line)) | |
1355 | ||
1356 | )) | |
1357 | ||
1358 | ;; Maintain an alias for compatibility. | |
1359 | (defalias 'rst-adjust-section-title 'rst-adjust) | |
1360 | ||
1361 | ||
1362 | (defun rst-promote-region (&optional demote) | |
1363 | "Promote the section titles within the region. | |
1364 | ||
e6438428 JB |
1365 | With argument DEMOTE or a prefix argument, demote the section |
1366 | titles instead. The algorithm used at the boundaries of the | |
1367 | hierarchy is similar to that used by `rst-adjust-decoration'." | |
94e9c286 SM |
1368 | (interactive) |
1369 | ||
1370 | (let* ((demote (or current-prefix-arg demote)) | |
1371 | (alldecos (rst-find-all-decorations)) | |
1372 | (cur alldecos) | |
1373 | ||
1374 | (hier (rst-get-hierarchy alldecos)) | |
1375 | (suggestion (rst-suggest-new-decoration hier)) | |
1376 | ||
1377 | (region-begin-line (line-number-at-pos (region-beginning))) | |
1378 | (region-end-line (line-number-at-pos (region-end))) | |
1379 | ||
1380 | marker-list | |
1381 | ) | |
1382 | ||
1383 | ;; Skip the markers that come before the region beginning | |
1384 | (while (and cur (< (caar cur) region-begin-line)) | |
1385 | (setq cur (cdr cur))) | |
1386 | ||
1387 | ;; Create a list of markers for all the decorations which are found within | |
1388 | ;; the region. | |
1389 | (save-excursion | |
8bbb7dd8 | 1390 | (let (line) |
94e9c286 | 1391 | (while (and cur (< (setq line (caar cur)) region-end-line)) |
e6ce8c42 GM |
1392 | (goto-char (point-min)) |
1393 | (forward-line (1- line)) | |
8bbb7dd8 | 1394 | (push (list (point-marker) (cdar cur)) marker-list) |
94e9c286 SM |
1395 | (setq cur (cdr cur)) )) |
1396 | ||
1397 | ;; Apply modifications. | |
8bbb7dd8 SM |
1398 | (dolist (p marker-list) |
1399 | ;; Go to the decoration to promote. | |
1400 | (goto-char (car p)) | |
1401 | ||
1402 | ;; Update the decoration. | |
1403 | (apply 'rst-update-section | |
1404 | ;; Rotate the next decoration. | |
1405 | (rst-get-next-decoration | |
1406 | (cadr p) hier suggestion demote)) | |
1407 | ||
1408 | ;; Clear marker to avoid slowing down the editing after we're done. | |
1409 | (set-marker (car p) nil)) | |
94e9c286 | 1410 | (setq deactivate-mark nil) |
8bbb7dd8 | 1411 | ))) |
94e9c286 SM |
1412 | |
1413 | ||
1414 | ||
1415 | (defun rst-display-decorations-hierarchy (&optional decorations) | |
1416 | "Display the current file's section title decorations hierarchy. | |
1417 | This function expects a list of (char, style, indent) triples in | |
1418 | DECORATIONS." | |
1419 | (interactive) | |
1420 | ||
1421 | (if (not decorations) | |
1422 | (setq decorations (rst-get-hierarchy))) | |
1423 | (with-output-to-temp-buffer "*rest section hierarchy*" | |
1424 | (let ((level 1)) | |
1425 | (with-current-buffer standard-output | |
1426 | (dolist (x decorations) | |
1427 | (insert (format "\nSection Level %d" level)) | |
1428 | (apply 'rst-update-section x) | |
1429 | (goto-char (point-max)) | |
1430 | (insert "\n") | |
1431 | (incf level) | |
1432 | )) | |
1433 | ))) | |
1434 | ||
b4747519 SM |
1435 | (defun rst-position (elem list) |
1436 | "Return position of ELEM in LIST or nil." | |
1437 | (let ((tail (member elem list))) | |
1438 | (if tail (- (length list) (length tail))))) | |
1439 | ||
94e9c286 SM |
1440 | (defun rst-straighten-decorations () |
1441 | "Redo all the decorations in the current buffer. | |
1442 | This is done using our preferred set of decorations. This can be | |
1443 | used, for example, when using somebody else's copy of a document, | |
1444 | in order to adapt it to our preferred style." | |
1445 | (interactive) | |
1446 | (save-excursion | |
1447 | (let* ((alldecos (rst-find-all-decorations)) | |
1448 | (hier (rst-get-hierarchy alldecos)) | |
1449 | ||
1450 | ;; Get a list of pairs of (level . marker) | |
1451 | (levels-and-markers (mapcar | |
1452 | (lambda (deco) | |
b4747519 | 1453 | (cons (rst-position (cdr deco) hier) |
8bbb7dd8 | 1454 | (progn |
e6ce8c42 GM |
1455 | (goto-char (point-min)) |
1456 | (forward-line (1- (car deco))) | |
8bbb7dd8 | 1457 | (point-marker)))) |
94e9c286 SM |
1458 | alldecos)) |
1459 | ) | |
1460 | (dolist (lm levels-and-markers) | |
1461 | ;; Go to the appropriate position | |
1462 | (goto-char (cdr lm)) | |
1463 | ||
1464 | ;; Apply the new styule | |
1465 | (apply 'rst-update-section (nth (car lm) rst-preferred-decorations)) | |
1466 | ||
1467 | ;; Reset the market to avoid slowing down editing until it gets GC'ed | |
1468 | (set-marker (cdr lm) nil) | |
1469 | ) | |
1470 | ))) | |
1471 | ||
1472 | ||
1473 | ||
1474 | ||
1475 | (defun rst-straighten-deco-spacing () | |
1476 | "Adjust the spacing before and after decorations in the entire document. | |
1477 | The spacing will be set to two blank lines before the first two | |
1478 | section levels, and one blank line before any of the other | |
1479 | section levels." | |
1480 | ;; FIXME: we need to take care of subtitle at some point. | |
1481 | (interactive) | |
1482 | (save-excursion | |
1483 | (let* ((alldecos (rst-find-all-decorations))) | |
1484 | ||
1485 | ;; Work the list from the end, so that we don't have to use markers to | |
1486 | ;; adjust for the changes in the document. | |
1487 | (dolist (deco (nreverse alldecos)) | |
1488 | ;; Go to the appropriate position. | |
e6ce8c42 GM |
1489 | (goto-char (point-min)) |
1490 | (forward-line (1- (car deco))) | |
94e9c286 SM |
1491 | (insert "@\n") |
1492 | ;; FIXME: todo, we | |
1493 | ) | |
1494 | ))) | |
1495 | ||
1496 | ||
1497 | (defun rst-find-pfx-in-region (beg end pfx-re) | |
1498 | "Find all the positions of prefixes in region between BEG and END. | |
1499 | This is used to find bullets and enumerated list items. PFX-RE | |
1500 | is a regular expression for matching the lines with items." | |
8bbb7dd8 | 1501 | (let ((pfx ())) |
94e9c286 SM |
1502 | (save-excursion |
1503 | (goto-char beg) | |
1504 | (while (< (point) end) | |
1505 | (back-to-indentation) | |
1506 | (when (and | |
1507 | (looking-at pfx-re) | |
1508 | (let ((pfx-col (current-column))) | |
1509 | (save-excursion | |
1510 | (forward-line -1) | |
1511 | (back-to-indentation) | |
1512 | (or (looking-at "^[ \t]*$") | |
1513 | (> (current-column) pfx-col) | |
1514 | (and (= (current-column) pfx-col) | |
1515 | (looking-at pfx-re)))))) | |
b4747519 SM |
1516 | (push (cons (point) (current-column)) |
1517 | pfx)) | |
94e9c286 SM |
1518 | (forward-line 1)) ) |
1519 | (nreverse pfx))) | |
1520 | ||
1521 | (defvar rst-re-bullets | |
1522 | (format "\\([%s][ \t]\\)[^ \t]" (regexp-quote (concat rst-bullets))) | |
1523 | "Regexp for finding bullets.") | |
1524 | ||
92439579 JB |
1525 | ;; (defvar rst-re-enumerations |
1526 | ;; "\\(\\(#\\|[0-9]+\\)\\.[ \t]\\)[^ \t]" | |
1527 | ;; "Regexp for finding bullets.") | |
94e9c286 SM |
1528 | |
1529 | (defvar rst-re-items | |
1530 | (format "\\(%s\\|%s\\)[^ \t]" | |
1531 | (format "[%s][ \t]" (regexp-quote (concat rst-bullets))) | |
1532 | "\\(#\\|[0-9]+\\)\\.[ \t]") | |
1533 | "Regexp for finding bullets.") | |
1534 | ||
1535 | (defvar rst-preferred-bullets | |
1536 | '(?- ?* ?+) | |
c5e87d10 | 1537 | "List of favorite bullets to set for straightening bullets.") |
94e9c286 SM |
1538 | |
1539 | (defun rst-straighten-bullets-region (beg end) | |
1540 | "Make all the bulleted list items in the region consistent. | |
1541 | The region is specified between BEG and END. You can use this | |
1542 | after you have merged multiple bulleted lists to make them use | |
1543 | the same/correct/consistent bullet characters. | |
1544 | ||
1545 | See variable `rst-preferred-bullets' for the list of bullets to | |
1546 | adjust. If bullets are found on levels beyond the | |
1547 | `rst-preferred-bullets' list, they are not modified." | |
1548 | (interactive "r") | |
1549 | ||
1550 | (let ((bullets (rst-find-pfx-in-region beg end | |
1551 | rst-re-bullets)) | |
1552 | (levtable (make-hash-table :size 4))) | |
1553 | ||
1554 | ;; Create a map of levels to list of positions. | |
1555 | (dolist (x bullets) | |
1556 | (let ((key (cdr x))) | |
1557 | (puthash key | |
1558 | (append (gethash key levtable (list)) | |
1559 | (list (car x))) | |
1560 | levtable))) | |
1561 | ||
1562 | ;; Sort this map and create a new map of prefix char and list of positions. | |
b4747519 SM |
1563 | (let ((poslist ())) ; List of (indent . positions). |
1564 | (maphash (lambda (x y) (push (cons x y) poslist)) levtable) | |
1565 | ||
1566 | (let ((bullets rst-preferred-bullets)) | |
1567 | (dolist (x (sort poslist 'car-less-than-car)) | |
1568 | (when bullets | |
1569 | ;; Apply the characters. | |
1570 | (dolist (pos (cdr x)) | |
1571 | (goto-char pos) | |
1572 | (delete-char 1) | |
1573 | (insert (string (car bullets)))) | |
1574 | (setq bullets (cdr bullets)))))))) | |
94e9c286 SM |
1575 | |
1576 | (defun rst-rstrip (str) | |
1577 | "Strips the whitespace at the end of string STR." | |
1578 | (string-match "[ \t\n]*\\'" str) | |
1579 | (substring str 0 (match-beginning 0))) | |
1580 | ||
1581 | (defun rst-get-stripped-line () | |
1582 | "Return the line at cursor, stripped from whitespace." | |
1583 | (re-search-forward "\\S-.*\\S-" (line-end-position)) | |
1584 | (buffer-substring-no-properties (match-beginning 0) | |
1585 | (match-end 0)) ) | |
1586 | ||
1587 | (defun rst-section-tree (alldecos) | |
1588 | "Get the hierarchical tree of section titles. | |
1589 | ||
1590 | Returns a hierarchical tree of the sections titles in the | |
1591 | document, for decorations ALLDECOS. This can be used to generate | |
1592 | a table of contents for the document. The top node will always | |
1593 | be a nil node, with the top level titles as children (there may | |
1594 | potentially be more than one). | |
1595 | ||
1596 | Each section title consists in a cons of the stripped title | |
1597 | string and a marker to the section in the original text document. | |
1598 | ||
1599 | If there are missing section levels, the section titles are | |
1600 | inserted automatically, and the title string is set to nil, and | |
1601 | the marker set to the first non-nil child of itself. | |
92439579 | 1602 | Conceptually, the nil nodes--i.e. those which have no title--are |
94e9c286 SM |
1603 | to be considered as being the same line as their first non-nil |
1604 | child. This has advantages later in processing the graph." | |
1605 | ||
1606 | (let* ((hier (rst-get-hierarchy alldecos)) | |
1607 | (levels (make-hash-table :test 'equal :size 10)) | |
1608 | lines) | |
1609 | ||
1610 | (let ((lev 0)) | |
1611 | (dolist (deco hier) | |
1612 | ;; Compare just the character and indent in the hash table. | |
1613 | (puthash (cons (car deco) (cadr deco)) lev levels) | |
1614 | (incf lev))) | |
1615 | ||
1616 | ;; Create a list of lines that contains (text, level, marker) for each | |
1617 | ;; decoration. | |
1618 | (save-excursion | |
1619 | (setq lines | |
1620 | (mapcar (lambda (deco) | |
e6ce8c42 GM |
1621 | (goto-char (point-min)) |
1622 | (forward-line (1- (car deco))) | |
94e9c286 SM |
1623 | (list (gethash (cons (cadr deco) (caddr deco)) levels) |
1624 | (rst-get-stripped-line) | |
8bbb7dd8 | 1625 | (progn |
94e9c286 | 1626 | (beginning-of-line 1) |
8bbb7dd8 | 1627 | (point-marker)))) |
94e9c286 SM |
1628 | alldecos))) |
1629 | ||
1630 | (let ((lcontnr (cons nil lines))) | |
1631 | (rst-section-tree-rec lcontnr -1)))) | |
1632 | ||
1633 | ||
1634 | (defun rst-section-tree-rec (decos lev) | |
1635 | "Recursive guts of the section tree construction. | |
1636 | DECOS is a cons cell whose cdr is the remaining list of | |
e6438428 JB |
1637 | decorations, and we change it as we consume them. LEV is |
1638 | the current level of that node. This function returns a | |
1639 | pair of the subtree that was built. This treats the DECOS | |
1640 | list destructively." | |
94e9c286 SM |
1641 | |
1642 | (let ((ndeco (cadr decos)) | |
1643 | node | |
1644 | children) | |
1645 | ||
1646 | ;; If the next decoration matches our level | |
1647 | (when (and ndeco (= (car ndeco) lev)) | |
1648 | ;; Pop the next decoration and create the current node with it | |
1649 | (setcdr decos (cddr decos)) | |
1650 | (setq node (cdr ndeco)) ) | |
1651 | ;; Else we let the node title/marker be unset. | |
1652 | ||
1653 | ;; Build the child nodes | |
1654 | (while (and (cdr decos) (> (caadr decos) lev)) | |
1655 | (setq children | |
1656 | (cons (rst-section-tree-rec decos (1+ lev)) | |
1657 | children))) | |
1658 | (setq children (reverse children)) | |
1659 | ||
1660 | ;; If node is still unset, we use the marker of the first child. | |
1661 | (when (eq node nil) | |
1662 | (setq node (cons nil (cdaar children)))) | |
1663 | ||
1664 | ;; Return this node with its children. | |
1665 | (cons node children) | |
1666 | )) | |
1667 | ||
1668 | ||
1669 | (defun rst-section-tree-point (node &optional point) | |
1670 | "Find tree node at point. | |
1671 | Given a computed and valid section tree in NODE and a point | |
1672 | POINT (default being the current point in the current buffer), | |
1673 | find and return the node within the sectree where the cursor | |
1674 | lives. | |
1675 | ||
92439579 JB |
1676 | Return values: a pair of (parent path, container subtree). |
1677 | The parent path is simply a list of the nodes above the | |
1678 | container subtree node that we're returning." | |
94e9c286 SM |
1679 | |
1680 | (let (path outtree) | |
1681 | ||
1682 | (let* ((curpoint (or point (point)))) | |
1683 | ||
1684 | ;; Check if we are before the current node. | |
1685 | (if (and (cadar node) (>= curpoint (cadar node))) | |
1686 | ||
1687 | ;; Iterate all the children, looking for one that might contain the | |
1688 | ;; current section. | |
1689 | (let ((curnode (cdr node)) | |
1690 | last) | |
1691 | ||
1692 | (while (and curnode (>= curpoint (cadaar curnode))) | |
1693 | (setq last curnode | |
1694 | curnode (cdr curnode))) | |
1695 | ||
1696 | (if last | |
1697 | (let ((sub (rst-section-tree-point (car last) curpoint))) | |
1698 | (setq path (car sub) | |
1699 | outtree (cdr sub))) | |
1700 | (setq outtree node)) | |
1701 | ||
1702 | ))) | |
1703 | (cons (cons (car node) path) outtree) | |
1704 | )) | |
1705 | ||
1706 | ||
b4747519 SM |
1707 | (defgroup rst-toc nil |
1708 | "Settings for reStructuredText table of contents." | |
1709 | :group 'rst | |
1710 | :version "21.1") | |
1711 | ||
1712 | (defcustom rst-toc-indent 2 | |
1713 | "Indentation for table-of-contents display. | |
1714 | Also used for formatting insertion, when numbering is disabled." | |
1715 | :group 'rst-toc) | |
1716 | ||
1717 | (defcustom rst-toc-insert-style 'fixed | |
1718 | "Insertion style for table-of-contents. | |
1719 | Set this to one of the following values to determine numbering and | |
1720 | indentation style: | |
1721 | - plain: no numbering (fixed indentation) | |
1722 | - fixed: numbering, but fixed indentation | |
1723 | - aligned: numbering, titles aligned under each other | |
1724 | - listed: numbering, with dashes like list items (EXPERIMENTAL)" | |
1725 | :group 'rst-toc) | |
1726 | ||
1727 | (defcustom rst-toc-insert-number-separator " " | |
1728 | "Separator that goes between the TOC number and the title." | |
1729 | :group 'rst-toc) | |
1730 | ||
1731 | ;; This is used to avoid having to change the user's mode. | |
1732 | (defvar rst-toc-insert-click-keymap | |
1733 | (let ((map (make-sparse-keymap))) | |
1734 | (define-key map [mouse-1] 'rst-toc-mode-mouse-goto) | |
1735 | map) | |
1736 | "(Internal) What happens when you click on propertized text in the TOC.") | |
1737 | ||
1738 | (defcustom rst-toc-insert-max-level nil | |
1739 | "If non-nil, maximum depth of the inserted TOC." | |
1740 | :group 'rst-toc) | |
1741 | ||
1742 | ||
94e9c286 SM |
1743 | (defun rst-toc-insert (&optional pfxarg) |
1744 | "Insert a simple text rendering of the table of contents. | |
1745 | By default the top level is ignored if there is only one, because | |
1746 | we assume that the document will have a single title. | |
1747 | ||
1748 | If a numeric prefix argument PFXARG is given, insert the TOC up | |
1749 | to the specified level. | |
1750 | ||
1751 | The TOC is inserted indented at the current column." | |
1752 | ||
1753 | (interactive "P") | |
1754 | ||
1755 | (let* (;; Check maximum level override | |
1756 | (rst-toc-insert-max-level | |
1757 | (if (and (integerp pfxarg) (> (prefix-numeric-value pfxarg) 0)) | |
1758 | (prefix-numeric-value pfxarg) rst-toc-insert-max-level)) | |
1759 | ||
1760 | ;; Get the section tree for the current cursor point. | |
1761 | (sectree-pair | |
1762 | (rst-section-tree-point | |
1763 | (rst-section-tree (rst-find-all-decorations)))) | |
1764 | ||
1765 | ;; Figure out initial indent. | |
1766 | (initial-indent (make-string (current-column) ? )) | |
1767 | (init-point (point))) | |
1768 | ||
1769 | (when (cddr sectree-pair) | |
1770 | (rst-toc-insert-node (cdr sectree-pair) 0 initial-indent "") | |
1771 | ||
1772 | ;; Fixup for the first line. | |
1773 | (delete-region init-point (+ init-point (length initial-indent))) | |
1774 | ||
1775 | ;; Delete the last newline added. | |
d355a0b7 | 1776 | (delete-char -1) |
94e9c286 SM |
1777 | ))) |
1778 | ||
94e9c286 SM |
1779 | (defun rst-toc-insert-node (node level indent pfx) |
1780 | "Insert tree node NODE in table-of-contents. | |
92439579 JB |
1781 | Recursive function that does printing of the inserted toc. |
1782 | LEVEL is the depth level of the sections in the tree. | |
1783 | INDENT is the indentation string. PFX is the prefix numbering, | |
1784 | that includes the alignment necessary for all the children of | |
1785 | level to align." | |
94e9c286 SM |
1786 | |
1787 | ;; Note: we do child numbering from the parent, so we start number the | |
1788 | ;; children one level before we print them. | |
1789 | (let ((do-print (> level 0)) | |
1790 | (count 1)) | |
1791 | (when do-print | |
1792 | (insert indent) | |
1793 | (let ((b (point))) | |
1794 | (unless (equal rst-toc-insert-style 'plain) | |
1795 | (insert pfx rst-toc-insert-number-separator)) | |
1796 | (insert (or (caar node) "[missing node]")) | |
1797 | ;; Add properties to the text, even though in normal text mode it | |
1798 | ;; won't be doing anything for now. Not sure that I want to change | |
1799 | ;; mode stuff. At least the highlighting gives the idea that this | |
1800 | ;; is generated automatically. | |
1801 | (put-text-property b (point) 'mouse-face 'highlight) | |
1802 | (put-text-property b (point) 'rst-toc-target (cadar node)) | |
1803 | (put-text-property b (point) 'keymap rst-toc-insert-click-keymap) | |
1804 | ||
1805 | ) | |
1806 | (insert "\n") | |
1807 | ||
1808 | ;; Prepare indent for children. | |
1809 | (setq indent | |
1810 | (cond | |
1811 | ((eq rst-toc-insert-style 'plain) | |
1812 | (concat indent (make-string rst-toc-indent ? ))) | |
1813 | ||
1814 | ((eq rst-toc-insert-style 'fixed) | |
1815 | (concat indent (make-string rst-toc-indent ? ))) | |
1816 | ||
1817 | ((eq rst-toc-insert-style 'aligned) | |
1818 | (concat indent (make-string (+ (length pfx) 2) ? ))) | |
1819 | ||
1820 | ((eq rst-toc-insert-style 'listed) | |
1821 | (concat (substring indent 0 -3) | |
1822 | (concat (make-string (+ (length pfx) 2) ? ) " - "))) | |
1823 | )) | |
1824 | ) | |
1825 | ||
1826 | (if (or (eq rst-toc-insert-max-level nil) | |
1827 | (< level rst-toc-insert-max-level)) | |
1828 | (let ((do-child-numbering (>= level 0)) | |
1829 | fmt) | |
1830 | (if do-child-numbering | |
1831 | (progn | |
1832 | ;; Add a separating dot if there is already a prefix | |
1833 | (if (> (length pfx) 0) | |
1834 | (setq pfx (concat (rst-rstrip pfx) "."))) | |
1835 | ||
1836 | ;; Calculate the amount of space that the prefix will require | |
1837 | ;; for the numbers. | |
1838 | (if (cdr node) | |
1839 | (setq fmt (format "%%-%dd" | |
1840 | (1+ (floor (log10 (length | |
1841 | (cdr node)))))))) | |
1842 | )) | |
1843 | ||
1844 | (dolist (child (cdr node)) | |
1845 | (rst-toc-insert-node child | |
1846 | (1+ level) | |
1847 | indent | |
1848 | (if do-child-numbering | |
1849 | (concat pfx (format fmt count)) pfx)) | |
1850 | (incf count))) | |
1851 | ||
1852 | ))) | |
1853 | ||
1854 | ||
1855 | (defun rst-toc-insert-find-delete-contents () | |
92439579 | 1856 | "Find and delete an existing comment after the first contents directive. |
94e9c286 SM |
1857 | Delete that region. Return t if found and the cursor is left after the comment." |
1858 | (goto-char (point-min)) | |
1859 | ;; We look for the following and the following only (in other words, if your | |
1860 | ;; syntax differs, this won't work. If you would like a more flexible thing, | |
1861 | ;; contact the author, I just can't imagine that this requirement is | |
1862 | ;; unreasonable for now). | |
1863 | ;; | |
1864 | ;; .. contents:: [...anything here...] | |
1865 | ;; .. | |
1866 | ;; XXXXXXXX | |
1867 | ;; XXXXXXXX | |
1868 | ;; [more lines] | |
1869 | ;; | |
1870 | (let ((beg | |
1871 | (re-search-forward "^\\.\\. contents[ \t]*::\\(.*\\)\n\\.\\." | |
1872 | nil t)) | |
1873 | last-real) | |
1874 | (when beg | |
1875 | ;; Look for the first line that starts at the first column. | |
1876 | (forward-line 1) | |
1877 | (beginning-of-line) | |
1878 | (while (and | |
1879 | (< (point) (point-max)) | |
1880 | (or (and (looking-at "[ \t]+[^ \t]") (setq last-real (point)) t) | |
1881 | (looking-at "[ \t]*$"))) | |
1882 | (forward-line 1) | |
1883 | ) | |
1884 | (if last-real | |
1885 | (progn | |
1886 | (goto-char last-real) | |
1887 | (end-of-line) | |
1888 | (delete-region beg (point))) | |
1889 | (goto-char beg)) | |
1890 | t | |
1891 | ))) | |
1892 | ||
1893 | (defun rst-toc-update () | |
1894 | "Automatically find the contents section of a document and update. | |
1895 | Updates the inserted TOC if present. You can use this in your | |
1896 | file-write hook to always make it up-to-date automatically." | |
1897 | (interactive) | |
1898 | (let ((p (point))) | |
1899 | (save-excursion | |
1900 | (when (rst-toc-insert-find-delete-contents) | |
1901 | (insert "\n ") | |
1902 | (rst-toc-insert) | |
1903 | )) | |
1904 | ;; Somehow save-excursion does not really work well. | |
1905 | (goto-char p)) | |
1906 | ;; Note: always return nil, because this may be used as a hook. | |
1907 | ) | |
1908 | ||
1909 | ;; Note: we cannot bind the TOC update on file write because it messes with | |
1910 | ;; undo. If we disable undo, since it adds and removes characters, the | |
1911 | ;; positions in the undo list are not making sense anymore. Dunno what to do | |
1912 | ;; with this, it would be nice to update when saving. | |
1913 | ;; | |
1914 | ;; (add-hook 'write-contents-hooks 'rst-toc-update-fun) | |
1915 | ;; (defun rst-toc-update-fun () | |
1916 | ;; ;; Disable undo for the write file hook. | |
1917 | ;; (let ((buffer-undo-list t)) (rst-toc-update) )) | |
1918 | ||
1919 | (defalias 'rst-toc-insert-update 'rst-toc-update) ;; backwards compat. | |
1920 | ||
1921 | ;;------------------------------------------------------------------------------ | |
1922 | ||
1923 | (defun rst-toc-node (node level) | |
1924 | "Recursive function that does insert NODE at LEVEL in the table-of-contents." | |
1925 | ||
1926 | (if (> level 0) | |
1927 | (let ((b (point))) | |
1928 | ;; Insert line text. | |
1929 | (insert (make-string (* rst-toc-indent (1- level)) ? )) | |
1930 | (insert (or (caar node) "[missing node]")) | |
1931 | ||
1932 | ;; Highlight lines. | |
1933 | (put-text-property b (point) 'mouse-face 'highlight) | |
1934 | ||
1935 | ;; Add link on lines. | |
1936 | (put-text-property b (point) 'rst-toc-target (cadar node)) | |
1937 | ||
1938 | (insert "\n") | |
1939 | )) | |
1940 | ||
1941 | (dolist (child (cdr node)) | |
1942 | (rst-toc-node child (1+ level)))) | |
1943 | ||
1944 | (defun rst-toc-count-lines (node target-node) | |
1945 | "Count the number of lines from NODE to the TARGET-NODE node. | |
1946 | This recursive function returns a cons of the number of | |
92439579 JB |
1947 | additional lines that have been counted for its node and |
1948 | children, and t if the node has been found." | |
94e9c286 SM |
1949 | |
1950 | (let ((count 1) | |
1951 | found) | |
1952 | (if (eq node target-node) | |
1953 | (setq found t) | |
1954 | (let ((child (cdr node))) | |
1955 | (while (and child (not found)) | |
1956 | (let ((cl (rst-toc-count-lines (car child) target-node))) | |
1957 | (setq count (+ count (car cl)) | |
1958 | found (cdr cl) | |
1959 | child (cdr child)))))) | |
1960 | (cons count found))) | |
1961 | ||
b4747519 SM |
1962 | (defvar rst-toc-buffer-name "*Table of Contents*" |
1963 | "Name of the Table of Contents buffer.") | |
1964 | ||
1965 | (defvar rst-toc-return-buffer nil | |
1966 | "Buffer to which to return when leaving the TOC.") | |
1967 | ||
94e9c286 SM |
1968 | |
1969 | (defun rst-toc () | |
1970 | "Display a table-of-contents. | |
1971 | Finds all the section titles and their decorations in the | |
1972 | file, and displays a hierarchically-organized list of the | |
1973 | titles, which is essentially a table-of-contents of the | |
1974 | document. | |
1975 | ||
1976 | The Emacs buffer can be navigated, and selecting a section | |
1977 | brings the cursor in that section." | |
1978 | (interactive) | |
1979 | (let* ((curbuf (current-buffer)) | |
1980 | ||
1981 | ;; Get the section tree | |
1982 | (alldecos (rst-find-all-decorations)) | |
1983 | (sectree (rst-section-tree alldecos)) | |
1984 | ||
1985 | (our-node (cdr (rst-section-tree-point sectree))) | |
1986 | line | |
1987 | ||
1988 | ;; Create a temporary buffer. | |
1989 | (buf (get-buffer-create rst-toc-buffer-name)) | |
1990 | ) | |
1991 | ||
1992 | (with-current-buffer buf | |
1993 | (let ((inhibit-read-only t)) | |
1994 | (rst-toc-mode) | |
1995 | (delete-region (point-min) (point-max)) | |
1996 | (insert (format "Table of Contents: %s\n" (or (caar sectree) ""))) | |
1997 | (put-text-property (point-min) (point) | |
1998 | 'face (list '(background-color . "gray"))) | |
1999 | (rst-toc-node sectree 0) | |
2000 | ||
2001 | ;; Count the lines to our found node. | |
2002 | (let ((linefound (rst-toc-count-lines sectree our-node))) | |
2003 | (setq line (if (cdr linefound) (car linefound) 0))) | |
2004 | )) | |
2005 | (display-buffer buf) | |
2006 | (pop-to-buffer buf) | |
2007 | ||
2008 | ;; Save the buffer to return to. | |
2009 | (set (make-local-variable 'rst-toc-return-buffer) curbuf) | |
2010 | ||
2011 | ;; Move the cursor near the right section in the TOC. | |
e6ce8c42 GM |
2012 | (goto-char (point-min)) |
2013 | (forward-line (1- line)) | |
94e9c286 SM |
2014 | )) |
2015 | ||
2016 | ||
2017 | (defun rst-toc-mode-find-section () | |
2018 | "Get the section from text property at point." | |
2019 | (let ((pos (get-text-property (point) 'rst-toc-target))) | |
2020 | (unless pos | |
2021 | (error "No section on this line")) | |
2022 | (unless (buffer-live-p (marker-buffer pos)) | |
2023 | (error "Buffer for this section was killed")) | |
2024 | pos)) | |
2025 | ||
94e9c286 SM |
2026 | (defun rst-goto-section (&optional kill) |
2027 | "Go to the section the current line describes." | |
2028 | (interactive) | |
2029 | (let ((pos (rst-toc-mode-find-section))) | |
2030 | (when kill | |
2031 | (kill-buffer (get-buffer rst-toc-buffer-name))) | |
2032 | (pop-to-buffer (marker-buffer pos)) | |
2033 | (goto-char pos) | |
2034 | ;; FIXME: make the recentering conditional on scroll. | |
2035 | (recenter 5))) | |
2036 | ||
2037 | (defun rst-toc-mode-goto-section () | |
92439579 | 2038 | "Go to the section the current line describes and kill the TOC buffer." |
94e9c286 SM |
2039 | (interactive) |
2040 | (rst-goto-section t)) | |
2041 | ||
2042 | (defun rst-toc-mode-mouse-goto (event) | |
2043 | "In `rst-toc' mode, go to the occurrence whose line you click on. | |
2044 | EVENT is the input event." | |
2045 | (interactive "e") | |
8bbb7dd8 | 2046 | (let ((pos |
b4747519 | 2047 | (with-current-buffer (window-buffer (posn-window (event-end event))) |
94e9c286 SM |
2048 | (save-excursion |
2049 | (goto-char (posn-point (event-end event))) | |
8bbb7dd8 | 2050 | (rst-toc-mode-find-section))))) |
94e9c286 SM |
2051 | (pop-to-buffer (marker-buffer pos)) |
2052 | (goto-char pos) | |
2053 | (recenter 5))) | |
2054 | ||
2055 | (defun rst-toc-mode-mouse-goto-kill (event) | |
b4747519 | 2056 | "Same as `rst-toc-mode-mouse-goto', but kill TOC buffer as well." |
94e9c286 SM |
2057 | (interactive "e") |
2058 | (call-interactively 'rst-toc-mode-mouse-goto event) | |
2059 | (kill-buffer (get-buffer rst-toc-buffer-name))) | |
2060 | ||
94e9c286 | 2061 | (defun rst-toc-quit-window () |
b4747519 | 2062 | "Leave the current TOC buffer." |
94e9c286 SM |
2063 | (interactive) |
2064 | (quit-window) | |
2065 | (pop-to-buffer rst-toc-return-buffer)) | |
2066 | ||
2067 | (defvar rst-toc-mode-map | |
2068 | (let ((map (make-sparse-keymap))) | |
2069 | (define-key map [mouse-1] 'rst-toc-mode-mouse-goto-kill) | |
2070 | (define-key map [mouse-2] 'rst-toc-mode-mouse-goto) | |
2071 | (define-key map "\C-m" 'rst-toc-mode-goto-section) | |
2072 | (define-key map "f" 'rst-toc-mode-goto-section) | |
2073 | (define-key map "q" 'rst-toc-quit-window) | |
2074 | (define-key map "z" 'kill-this-buffer) | |
2075 | map) | |
2076 | "Keymap for `rst-toc-mode'.") | |
2077 | ||
2078 | (put 'rst-toc-mode 'mode-class 'special) | |
2079 | ||
b4747519 SM |
2080 | ;; Could inherit from the new `special-mode'. |
2081 | (define-derived-mode rst-toc-mode nil "ReST-TOC" | |
94e9c286 | 2082 | "Major mode for output from \\[rst-toc], the table-of-contents for the document." |
b4747519 | 2083 | (setq buffer-read-only t)) |
94e9c286 SM |
2084 | |
2085 | ;; Note: use occur-mode (replace.el) as a good example to complete missing | |
2086 | ;; features. | |
2087 | ||
2088 | ||
2089 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2090 | ;; | |
2091 | ;; Section movement commands. | |
2092 | ;; | |
2093 | ||
2094 | (defun rst-forward-section (&optional offset) | |
2095 | "Skip to the next restructured text section title. | |
b4747519 SM |
2096 | OFFSET specifies how many titles to skip. Use a negative OFFSET to move |
2097 | backwards in the file (default is to use 1)." | |
94e9c286 SM |
2098 | (interactive) |
2099 | (let* (;; Default value for offset. | |
2100 | (offset (or offset 1)) | |
2101 | ||
2102 | ;; Get all the decorations in the file, with their line numbers. | |
2103 | (alldecos (rst-find-all-decorations)) | |
2104 | ||
2105 | ;; Get the current line. | |
2106 | (curline (line-number-at-pos)) | |
2107 | ||
2108 | (cur alldecos) | |
2109 | (idx 0) | |
2110 | ) | |
2111 | ||
2112 | ;; Find the index of the "next" decoration w.r.t. to the current line. | |
2113 | (while (and cur (< (caar cur) curline)) | |
2114 | (setq cur (cdr cur)) | |
2115 | (incf idx)) | |
2116 | ;; 'cur' is the decoration on or following the current line. | |
2117 | ||
2118 | (if (and (> offset 0) cur (= (caar cur) curline)) | |
2119 | (incf idx)) | |
2120 | ||
2121 | ;; Find the final index. | |
2122 | (setq idx (+ idx (if (> offset 0) (- offset 1) offset))) | |
2123 | (setq cur (nth idx alldecos)) | |
2124 | ||
2125 | ;; If the index is positive, goto the line, otherwise go to the buffer | |
2126 | ;; boundaries. | |
2127 | (if (and cur (>= idx 0)) | |
e6ce8c42 GM |
2128 | (progn |
2129 | (goto-char (point-min)) | |
2130 | (forward-line (1- (car cur)))) | |
94e9c286 SM |
2131 | (if (> offset 0) (goto-char (point-max)) (goto-char (point-min)))) |
2132 | )) | |
2133 | ||
2134 | (defun rst-backward-section () | |
e6438428 | 2135 | "Like `rst-forward-section', except move back one title." |
94e9c286 SM |
2136 | (interactive) |
2137 | (rst-forward-section -1)) | |
2138 | ||
2139 | (defun rst-mark-section (&optional arg allow-extend) | |
2140 | "Select the section that point is currently in." | |
2141 | ;; Cloned from mark-paragraph. | |
2142 | (interactive "p\np") | |
2143 | (unless arg (setq arg 1)) | |
2144 | (when (zerop arg) | |
2145 | (error "Cannot mark zero sections")) | |
2146 | (cond ((and allow-extend | |
2147 | (or (and (eq last-command this-command) (mark t)) | |
2148 | (rst-portable-mark-active-p))) | |
2149 | (set-mark | |
2150 | (save-excursion | |
2151 | (goto-char (mark)) | |
2152 | (rst-forward-section arg) | |
2153 | (point)))) | |
2154 | (t | |
2155 | (rst-forward-section arg) | |
2156 | (push-mark nil t t) | |
2157 | (rst-forward-section (- arg))))) | |
2158 | ||
2159 | ||
2160 | ||
2161 | ||
2162 | ||
2163 | \f | |
2164 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2165 | ;; Functions to work on item lists (e.g. indent/dedent, enumerate), which are | |
2166 | ;; always 2 or 3 characters apart horizontally with rest. | |
2167 | ||
2168 | ;; (FIXME: there is currently a bug that makes the region go away when we do that.) | |
2169 | (defvar rst-shift-fill-region nil | |
b4747519 | 2170 | "If non-nil, automatically re-fill the region that is being shifted.") |
94e9c286 SM |
2171 | |
2172 | (defun rst-find-leftmost-column (beg end) | |
b4747519 | 2173 | "Find the leftmost column in the region." |
94e9c286 SM |
2174 | (let ((mincol 1000)) |
2175 | (save-excursion | |
2176 | (goto-char beg) | |
2177 | (while (< (point) end) | |
2178 | (back-to-indentation) | |
2179 | (unless (looking-at "[ \t]*$") | |
2180 | (setq mincol (min mincol (current-column)))) | |
2181 | (forward-line 1) | |
2182 | )) | |
2183 | mincol)) | |
2184 | ||
2185 | ||
2186 | ;; What we really need to do is compute all the possible alignment possibilities | |
2187 | ;; and then select one. | |
2188 | ;; | |
2189 | ;; .. line-block:: | |
2190 | ;; | |
2191 | ;; a) sdjsds | |
2192 | ;; | |
2193 | ;; - sdjsd jsjds | |
2194 | ;; | |
2195 | ;; sdsdsjdsj | |
2196 | ;; | |
2197 | ;; 11. sjdss jddjs | |
2198 | ;; | |
2199 | ;; * * * * * * * | |
2200 | ;; | |
2201 | ;; Move backwards, accumulate the beginning positions, and also the second | |
2202 | ;; positions, in case the line matches the bullet pattern, and then sort. | |
2203 | ||
2204 | (defun rst-compute-bullet-tabs (&optional pt) | |
b4747519 SM |
2205 | "Build the list of possible horizontal alignment points. |
2206 | Search backwards from point (or point PT if specified) to | |
94e9c286 SM |
2207 | build the list of possible horizontal alignment points that |
2208 | includes the beginning and contents of a restructuredtext | |
2209 | bulleted or enumerated list item. Return a sorted list | |
b4747519 | 2210 | of (COLUMN-NUMBER . LINE) pairs." |
94e9c286 SM |
2211 | (save-excursion |
2212 | (when pt (goto-char pt)) | |
2213 | ||
2214 | ;; We work our way backwards and towards the left. | |
2215 | (let ((leftcol 100000) ;; Current column. | |
2216 | (tablist nil) ;; List of tab positions. | |
2217 | ) | |
2218 | ||
2219 | ;; Start by skipping the current line. | |
2220 | (beginning-of-line 0) | |
2221 | ||
2222 | ;; Search backwards for each line. | |
2223 | (while (and (> (point) (point-min)) | |
2224 | (> leftcol 0)) | |
2225 | ||
2226 | ;; Skip empty lines. | |
2227 | (unless (looking-at "^[ \t]*$") | |
2228 | ;; Inspect the current non-empty line | |
2229 | (back-to-indentation) | |
2230 | ||
2231 | ;; Skip lines that are beyond the current column (we want to move | |
2232 | ;; towards the left). | |
2233 | (let ((col (current-column))) | |
2234 | (when (< col leftcol) | |
2235 | ||
2236 | ;; Add the beginning of the line as a tabbing point. | |
2237 | (unless (memq col (mapcar 'car tablist)) | |
b4747519 | 2238 | (push (cons col (point)) tablist)) |
94e9c286 SM |
2239 | |
2240 | ;; Look at the line to figure out if it is a bulleted or enumerate | |
2241 | ;; list item. | |
2242 | (when (looking-at | |
2243 | (concat | |
2244 | "\\(?:" | |
2245 | "\\(\\(?:[0-9a-zA-Z#]\\{1,3\\}[.):-]\\|[*+-]\\)[ \t]+\\)[^ \t\n]" | |
2246 | "\\|" | |
2247 | (format "\\(%s%s+[ \t]+\\)[^ \t\n]" | |
2248 | (regexp-quote (thing-at-point 'char)) | |
2249 | (regexp-quote (thing-at-point 'char))) | |
2250 | "\\)" | |
2251 | )) | |
2252 | ;; Add the column of the contained item. | |
2253 | (let* ((matchlen (length (or (match-string 1) (match-string 2)))) | |
2254 | (newcol (+ col matchlen))) | |
2255 | (unless (or (>= newcol leftcol) | |
2256 | (memq (+ col matchlen) (mapcar 'car tablist))) | |
b4747519 SM |
2257 | (push (cons (+ col matchlen) (+ (point) matchlen)) |
2258 | tablist))) | |
94e9c286 SM |
2259 | ) |
2260 | ||
2261 | (setq leftcol col) | |
2262 | ))) | |
2263 | ||
2264 | ;; Move backwards one line. | |
2265 | (beginning-of-line 0)) | |
2266 | ||
2267 | (sort tablist (lambda (x y) (<= (car x) (car y)))) | |
2268 | ))) | |
2269 | ||
2270 | (defun rst-debug-print-tabs (tablist) | |
b4747519 | 2271 | "Insert a line and place special characters at the tab points in TABLIST." |
94e9c286 SM |
2272 | (beginning-of-line) |
2273 | (insert (concat "\n" (make-string 1000 ? ) "\n")) | |
2274 | (beginning-of-line 0) | |
2275 | (dolist (col tablist) | |
2276 | (beginning-of-line) | |
2277 | (forward-char (car col)) | |
2278 | (delete-char 1) | |
2279 | (insert "@") | |
2280 | )) | |
2281 | ||
2282 | (defun rst-debug-mark-found (tablist) | |
b4747519 | 2283 | "Insert a line and place special characters at the tab points in TABLIST." |
94e9c286 SM |
2284 | (dolist (col tablist) |
2285 | (when (cdr col) | |
2286 | (goto-char (cdr col)) | |
2287 | (insert "@")))) | |
2288 | ||
2289 | ||
2290 | (defvar rst-shift-basic-offset 2 | |
2291 | "Basic horizontal shift distance when there is no preceding alignment tabs.") | |
2292 | ||
2293 | (defun rst-shift-region-guts (find-next-fun offset-fun) | |
b4747519 | 2294 | "(See `rst-shift-region-right' for a description)." |
8bbb7dd8 SM |
2295 | (let* ((mbeg (copy-marker (region-beginning))) |
2296 | (mend (copy-marker (region-end))) | |
94e9c286 SM |
2297 | (tabs (rst-compute-bullet-tabs mbeg)) |
2298 | (leftmostcol (rst-find-leftmost-column (region-beginning) (region-end))) | |
2299 | ) | |
2300 | ;; Add basic offset tabs at the end of the list. This is a better | |
2301 | ;; implementation technique than hysteresis and a basic offset because it | |
2302 | ;; insures that movement in both directions is consistently using the same | |
2303 | ;; column positions. This makes it more predictable. | |
2304 | (setq tabs | |
2305 | (append tabs | |
2306 | (mapcar (lambda (x) (cons x nil)) | |
2307 | (let ((maxcol 120) | |
2308 | (max-lisp-eval-depth 2000)) | |
2309 | (flet ((addnum (x) | |
2310 | (if (> x maxcol) | |
2311 | nil | |
2312 | (cons x (addnum | |
2313 | (+ x rst-shift-basic-offset)))))) | |
2314 | (addnum (or (caar (last tabs)) 0)))) | |
2315 | ))) | |
2316 | ||
2317 | ;; (For debugging.) | |
2318 | ;;; (save-excursion (goto-char mbeg) (forward-char -1) (rst-debug-print-tabs tabs)))) | |
2319 | ;;; (print tabs) | |
2320 | ;;; (save-excursion (rst-debug-mark-found tabs)) | |
2321 | ||
2322 | ;; Apply the indent. | |
2323 | (indent-rigidly | |
2324 | mbeg mend | |
2325 | ||
da6062e6 | 2326 | ;; Find the next tab after the leftmost column. |
94e9c286 SM |
2327 | (let ((tab (funcall find-next-fun tabs leftmostcol))) |
2328 | ||
2329 | (if tab | |
2330 | (progn | |
2331 | (when (cdar tab) | |
2332 | (message "Aligned on '%s'" | |
2333 | (save-excursion | |
2334 | (goto-char (cdar tab)) | |
2335 | (buffer-substring-no-properties | |
2336 | (line-beginning-position) | |
2337 | (line-end-position)))) | |
2338 | ) | |
2339 | (- (caar tab) leftmostcol)) ;; Num chars. | |
2340 | ||
2341 | ;; Otherwise use the basic offset | |
2342 | (funcall offset-fun rst-shift-basic-offset) | |
2343 | ))) | |
2344 | ||
2345 | ;; Optionally reindent. | |
2346 | (when rst-shift-fill-region | |
2347 | (fill-region mbeg mend)) | |
2348 | )) | |
2349 | ||
2350 | (defun rst-shift-region-right (pfxarg) | |
9858f6c3 | 2351 | "Indent region rigidly, by a few characters to the right. |
b4747519 | 2352 | This function first computes all possible alignment columns by |
94e9c286 SM |
2353 | inspecting the lines preceding the region for bulleted or |
2354 | enumerated list items. If the leftmost column is beyond the | |
2355 | preceding lines, the region is moved to the right by | |
b4747519 | 2356 | `rst-shift-basic-offset'. With a prefix argument, do not |
94e9c286 SM |
2357 | automatically fill the region." |
2358 | (interactive "P") | |
2359 | (let ((rst-shift-fill-region | |
2360 | (if (not pfxarg) rst-shift-fill-region))) | |
2361 | (rst-shift-region-guts (lambda (tabs leftmostcol) | |
2362 | (let ((cur tabs)) | |
2363 | (while (and cur (<= (caar cur) leftmostcol)) | |
2364 | (setq cur (cdr cur))) | |
2365 | cur)) | |
2366 | 'identity | |
2367 | ))) | |
2368 | ||
2369 | (defun rst-shift-region-left (pfxarg) | |
b4747519 | 2370 | "Like `rst-shift-region-right', except we move to the left. |
94e9c286 SM |
2371 | Also, if invoked with a negative prefix arg, the entire |
2372 | indentation is removed, up to the leftmost character in the | |
2373 | region, and automatic filling is disabled." | |
2374 | (interactive "P") | |
8bbb7dd8 SM |
2375 | (let ((mbeg (copy-marker (region-beginning))) |
2376 | (mend (copy-marker (region-end))) | |
94e9c286 SM |
2377 | (leftmostcol (rst-find-leftmost-column |
2378 | (region-beginning) (region-end))) | |
2379 | (rst-shift-fill-region | |
2380 | (if (not pfxarg) rst-shift-fill-region))) | |
2381 | ||
2382 | (when (> leftmostcol 0) | |
2383 | (if (and pfxarg (< (prefix-numeric-value pfxarg) 0)) | |
2384 | (progn | |
2385 | (indent-rigidly (region-beginning) (region-end) (- leftmostcol)) | |
2386 | (when rst-shift-fill-region | |
2387 | (fill-region mbeg mend)) | |
2388 | ) | |
2389 | (rst-shift-region-guts (lambda (tabs leftmostcol) | |
2390 | (let ((cur (reverse tabs))) | |
2391 | (while (and cur (>= (caar cur) leftmostcol)) | |
2392 | (setq cur (cdr cur))) | |
2393 | cur)) | |
2394 | '- | |
2395 | )) | |
2396 | ))) | |
2397 | ||
94e9c286 SM |
2398 | (defmacro rst-iterate-leftmost-paragraphs |
2399 | (beg end first-only body-consequent body-alternative) | |
2400 | "FIXME This definition is old and deprecated / we need to move | |
2401 | to the newer version below: | |
2402 | ||
2403 | Call FUN at the beginning of each line, with an argument that | |
2404 | specifies whether we are at the first line of a paragraph that | |
2405 | starts at the leftmost column of the given region BEG and END. | |
2406 | Set FIRST-ONLY to true if you want to callback on the first line | |
2407 | of each paragraph only." | |
2408 | `(save-excursion | |
2409 | (let ((leftcol (rst-find-leftmost-column ,beg ,end)) | |
8bbb7dd8 | 2410 | (endm (copy-marker ,end))) |
94e9c286 SM |
2411 | |
2412 | (do* (;; Iterate lines | |
2413 | (l (progn (goto-char ,beg) (back-to-indentation)) | |
2414 | (progn (forward-line 1) (back-to-indentation))) | |
2415 | ||
2416 | (previous nil valid) | |
2417 | ||
2418 | (curcol (current-column) | |
2419 | (current-column)) | |
2420 | ||
2421 | (valid (and (= curcol leftcol) | |
2422 | (not (looking-at "[ \t]*$"))) | |
2423 | (and (= curcol leftcol) | |
2424 | (not (looking-at "[ \t]*$")))) | |
2425 | ) | |
b4747519 | 2426 | ((>= (point) endm)) |
94e9c286 SM |
2427 | |
2428 | (if (if ,first-only | |
2429 | (and valid (not previous)) | |
2430 | valid) | |
2431 | ,body-consequent | |
2432 | ,body-alternative) | |
2433 | ||
2434 | )))) | |
2435 | ||
2436 | ||
2437 | (defmacro rst-iterate-leftmost-paragraphs-2 (spec &rest body) | |
2438 | "Evaluate BODY for each line in region defined by BEG END. | |
2439 | LEFTMOST is set to true if the line is one of the leftmost of the | |
b4747519 | 2440 | entire paragraph. PARABEGIN is set to true if the line is the |
94e9c286 | 2441 | first of a paragraph." |
b4747519 | 2442 | (declare (indent 1) (debug (sexp body))) |
94e9c286 SM |
2443 | (destructuring-bind |
2444 | (beg end parabegin leftmost isleftmost isempty) spec | |
2445 | ||
2446 | `(save-excursion | |
2447 | (let ((,leftmost (rst-find-leftmost-column ,beg ,end)) | |
8bbb7dd8 | 2448 | (endm (copy-marker ,end))) |
94e9c286 SM |
2449 | |
2450 | (do* (;; Iterate lines | |
2451 | (l (progn (goto-char ,beg) (back-to-indentation)) | |
2452 | (progn (forward-line 1) (back-to-indentation))) | |
2453 | ||
2454 | (empty-line-previous nil ,isempty) | |
2455 | ||
2456 | (,isempty (looking-at "[ \t]*$") | |
2457 | (looking-at "[ \t]*$")) | |
2458 | ||
2459 | (,parabegin (not ,isempty) | |
2460 | (and empty-line-previous | |
2461 | (not ,isempty))) | |
2462 | ||
2463 | (,isleftmost (and (not ,isempty) | |
2464 | (= (current-column) ,leftmost)) | |
2465 | (and (not ,isempty) | |
2466 | (= (current-column) ,leftmost))) | |
2467 | ) | |
b4747519 | 2468 | ((>= (point) endm)) |
94e9c286 SM |
2469 | |
2470 | (progn ,@body) | |
2471 | ||
2472 | ))))) | |
2473 | ||
2474 | ||
b4747519 SM |
2475 | ;;------------------------------------------------------------------------------ |
2476 | ||
2477 | ;; FIXME: these next functions should become part of a larger effort to redo the | |
91af3942 | 2478 | ;; bullets in bulleted lists. The enumerate would just be one of the possible |
b4747519 SM |
2479 | ;; outputs. |
2480 | ;; | |
2481 | ;; FIXME: TODO we need to do the enumeration removal as well. | |
2482 | ||
2483 | (defun rst-enumerate-region (beg end) | |
2484 | "Add enumeration to all the leftmost paragraphs in the given region. | |
2485 | The region is specified between BEG and END. With prefix argument, | |
2486 | do all lines instead of just paragraphs." | |
2487 | (interactive "r") | |
2488 | (let ((count 0) | |
2489 | (last-insert-len nil)) | |
2490 | (rst-iterate-leftmost-paragraphs | |
2491 | beg end (not current-prefix-arg) | |
2492 | (let ((ins-string (format "%d. " (incf count)))) | |
2493 | (setq last-insert-len (length ins-string)) | |
2494 | (insert ins-string)) | |
2495 | (insert (make-string last-insert-len ?\ )) | |
2496 | ))) | |
2497 | ||
2498 | (defun rst-bullet-list-region (beg end) | |
2499 | "Add bullets to all the leftmost paragraphs in the given region. | |
2500 | The region is specified between BEG and END. With prefix argument, | |
2501 | do all lines instead of just paragraphs." | |
2502 | (interactive "r") | |
2503 | (rst-iterate-leftmost-paragraphs | |
2504 | beg end (not current-prefix-arg) | |
2505 | (insert "- ") | |
2506 | (insert " ") | |
2507 | )) | |
2508 | ||
2509 | ||
94e9c286 SM |
2510 | ;; FIXME: there are some problems left with the following function |
2511 | ;; implementation: | |
2512 | ;; | |
2513 | ;; * It does not deal with a varying number of digits appropriately | |
2514 | ;; * It does not deal with multiple levels independently, and it should. | |
2515 | ;; | |
2516 | ;; I suppose it does 90% of the job for now. | |
2517 | ||
2518 | (defun rst-convert-bullets-to-enumeration (beg end) | |
2519 | "Convert all the bulleted items and enumerated items in the | |
b4747519 | 2520 | region to enumerated lists, renumbering as necessary." |
94e9c286 SM |
2521 | (interactive "r") |
2522 | (let* (;; Find items and convert the positions to markers. | |
2523 | (items (mapcar | |
2524 | (lambda (x) | |
8bbb7dd8 | 2525 | (cons (copy-marker (car x)) |
94e9c286 SM |
2526 | (cdr x))) |
2527 | (rst-find-pfx-in-region beg end rst-re-items))) | |
2528 | (count 1) | |
2529 | ) | |
2530 | (save-excursion | |
2531 | (dolist (x items) | |
2532 | (goto-char (car x)) | |
2533 | (looking-at rst-re-items) | |
2534 | (replace-match (format "%d. " count) nil nil nil 1) | |
2535 | (incf count) | |
2536 | )) | |
2537 | )) | |
2538 | ||
2539 | ||
2540 | ||
2541 | ;;------------------------------------------------------------------------------ | |
2542 | ||
2543 | (defun rst-line-block-region (rbeg rend &optional pfxarg) | |
b4747519 SM |
2544 | "Toggle line block prefixes for a region. |
2545 | With prefix argument set the empty lines too." | |
94e9c286 SM |
2546 | (interactive "r\nP") |
2547 | (let ((comment-start "| ") | |
2548 | (comment-end "") | |
2549 | (comment-start-skip "| ") | |
2550 | (comment-style 'indent) | |
b4747519 | 2551 | (force (not (not pfxarg)))) |
94e9c286 | 2552 | (rst-iterate-leftmost-paragraphs-2 |
b4747519 SM |
2553 | (rbeg rend parbegin leftmost isleft isempty) |
2554 | (when (or force (not isempty)) | |
2555 | (move-to-column leftmost force) | |
2556 | (delete-region (point) (+ (point) (- (current-indentation) leftmost))) | |
2557 | (insert "| "))))) | |
94e9c286 SM |
2558 | |
2559 | ||
2560 | \f | |
2561 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2562 | ||
2563 | (require 'font-lock) | |
2564 | ||
92439579 | 2565 | (defgroup rst-faces nil "Faces used in Rst Mode." |
94e9c286 SM |
2566 | :group 'rst |
2567 | :group 'faces | |
2568 | :version "21.1") | |
2569 | ||
2b1400b9 GM |
2570 | (defface rst-block '((t :inherit font-lock-keyword-face)) |
2571 | "Face used for all syntax marking up a special block." | |
2572 | :version "24.1" | |
2573 | :group 'rst-faces) | |
2574 | ||
2575 | (defcustom rst-block-face 'rst-block | |
b4747519 | 2576 | "All syntax marking up a special block." |
2b1400b9 | 2577 | :version "24.1" |
94e9c286 SM |
2578 | :group 'rst-faces |
2579 | :type '(face)) | |
2b1400b9 GM |
2580 | (make-obsolete-variable 'rst-block-face |
2581 | "customize the face `rst-block' instead." | |
2582 | "24.1") | |
2583 | ||
2584 | (defface rst-external '((t :inherit font-lock-type-face)) | |
2585 | "Face used for field names and interpreted text." | |
2586 | :version "24.1" | |
2587 | :group 'rst-faces) | |
94e9c286 | 2588 | |
2b1400b9 | 2589 | (defcustom rst-external-face 'rst-external |
b4747519 | 2590 | "Field names and interpreted text." |
2b1400b9 | 2591 | :version "24.1" |
94e9c286 SM |
2592 | :group 'rst-faces |
2593 | :type '(face)) | |
2b1400b9 GM |
2594 | (make-obsolete-variable 'rst-external-face |
2595 | "customize the face `rst-external' instead." | |
2596 | "24.1") | |
94e9c286 | 2597 | |
2b1400b9 GM |
2598 | (defface rst-definition '((t :inherit font-lock-function-name-face)) |
2599 | "Face used for all other defining constructs." | |
2600 | :version "24.1" | |
2601 | :group 'rst-faces) | |
2602 | ||
2603 | (defcustom rst-definition-face 'rst-definition | |
b4747519 | 2604 | "All other defining constructs." |
2b1400b9 | 2605 | :version "24.1" |
94e9c286 SM |
2606 | :group 'rst-faces |
2607 | :type '(face)) | |
2b1400b9 GM |
2608 | (make-obsolete-variable 'rst-definition-face |
2609 | "customize the face `rst-definition' instead." | |
2610 | "24.1") | |
2611 | ||
2612 | ;; XEmacs compatibility (?). | |
2613 | (defface rst-directive (if (boundp 'font-lock-builtin-face) | |
2614 | '((t :inherit font-lock-builtin-face)) | |
2615 | '((t :inherit font-lock-preprocessor-face))) | |
2616 | "Face used for directives and roles." | |
2617 | :version "24.1" | |
2618 | :group 'rst-faces) | |
2619 | ||
2620 | (defcustom rst-directive-face 'rst-directive | |
b4747519 | 2621 | "Directives and roles." |
94e9c286 SM |
2622 | :group 'rst-faces |
2623 | :type '(face)) | |
2b1400b9 GM |
2624 | (make-obsolete-variable 'rst-directive-face |
2625 | "customize the face `rst-directive' instead." | |
2626 | "24.1") | |
94e9c286 | 2627 | |
2b1400b9 GM |
2628 | (defface rst-comment '((t :inherit font-lock-comment-face)) |
2629 | "Face used for comments." | |
2630 | :version "24.1" | |
2631 | :group 'rst-faces) | |
2632 | ||
2633 | (defcustom rst-comment-face 'rst-comment | |
b4747519 | 2634 | "Comments." |
2b1400b9 | 2635 | :version "24.1" |
94e9c286 SM |
2636 | :group 'rst-faces |
2637 | :type '(face)) | |
2b1400b9 GM |
2638 | (make-obsolete-variable 'rst-comment-face |
2639 | "customize the face `rst-comment' instead." | |
2640 | "24.1") | |
2641 | ||
2642 | (defface rst-emphasis1 '((t :inherit italic)) | |
2643 | "Face used for simple emphasis." | |
2644 | :version "24.1" | |
2645 | :group 'rst-faces) | |
94e9c286 | 2646 | |
2b1400b9 | 2647 | (defcustom rst-emphasis1-face 'rst-emphasis1 |
b4747519 | 2648 | "Simple emphasis." |
2b1400b9 | 2649 | :version "24.1" |
94e9c286 SM |
2650 | :group 'rst-faces |
2651 | :type '(face)) | |
2b1400b9 GM |
2652 | (make-obsolete-variable 'rst-emphasis1-face |
2653 | "customize the face `rst-emphasis1' instead." | |
2654 | "24.1") | |
94e9c286 | 2655 | |
2b1400b9 GM |
2656 | (defface rst-emphasis2 '((t :inherit bold)) |
2657 | "Face used for double emphasis." | |
2658 | :version "24.1" | |
2659 | :group 'rst-faces) | |
2660 | ||
2661 | (defcustom rst-emphasis2-face 'rst-emphasis2 | |
b4747519 | 2662 | "Double emphasis." |
94e9c286 SM |
2663 | :group 'rst-faces |
2664 | :type '(face)) | |
2b1400b9 GM |
2665 | (make-obsolete-variable 'rst-emphasis2-face |
2666 | "customize the face `rst-emphasis2' instead." | |
2667 | "24.1") | |
2668 | ||
2669 | (defface rst-literal '((t :inherit font-lock-string-face)) | |
2670 | "Face used for literal text." | |
2671 | :version "24.1" | |
2672 | :group 'rst-faces) | |
94e9c286 | 2673 | |
2b1400b9 | 2674 | (defcustom rst-literal-face 'rst-literal |
b4747519 | 2675 | "Literal text." |
2b1400b9 | 2676 | :version "24.1" |
94e9c286 SM |
2677 | :group 'rst-faces |
2678 | :type '(face)) | |
2b1400b9 GM |
2679 | (make-obsolete-variable 'rst-literal-face |
2680 | "customize the face `rst-literal' instead." | |
2681 | "24.1") | |
94e9c286 | 2682 | |
2b1400b9 GM |
2683 | (defface rst-reference '((t :inherit font-lock-variable-name-face)) |
2684 | "Face used for references to a definition." | |
2685 | :version "24.1" | |
2686 | :group 'rst-faces) | |
2687 | ||
2688 | (defcustom rst-reference-face 'rst-reference | |
b4747519 | 2689 | "References to a definition." |
2b1400b9 | 2690 | :version "24.1" |
94e9c286 SM |
2691 | :group 'rst-faces |
2692 | :type '(face)) | |
2b1400b9 GM |
2693 | (make-obsolete-variable 'rst-reference-face |
2694 | "customize the face `rst-reference' instead." | |
2695 | "24.1") | |
94e9c286 SM |
2696 | |
2697 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2698 | ||
2699 | (defgroup rst-faces-defaults nil | |
2700 | "Values used to generate default faces for section titles on all levels. | |
2701 | Tweak these if you are content with how section title faces are built in | |
2702 | general but you do not like the details." | |
2703 | :group 'rst-faces | |
2704 | :version "21.1") | |
2705 | ||
94e9c286 | 2706 | (defun rst-set-level-default (sym val) |
b4747519 | 2707 | "Set custom var SYM affecting section title text face and recompute the faces." |
94e9c286 SM |
2708 | (custom-set-default sym val) |
2709 | ;; Also defines the faces initially when all values are available | |
2710 | (and (boundp 'rst-level-face-max) | |
2711 | (boundp 'rst-level-face-format-light) | |
2712 | (boundp 'rst-level-face-base-color) | |
2713 | (boundp 'rst-level-face-step-light) | |
2714 | (boundp 'rst-level-face-base-light) | |
b4747519 | 2715 | (fboundp 'rst-define-level-faces) |
94e9c286 SM |
2716 | (rst-define-level-faces))) |
2717 | ||
2718 | ;; Faces for displaying items on several levels; these definitions define | |
e4769531 | 2719 | ;; different shades of gray where the lightest one (i.e. least contrasting) is |
94e9c286 SM |
2720 | ;; used for level 1 |
2721 | (defcustom rst-level-face-max 6 | |
2722 | "Maximum depth of levels for which section title faces are defined." | |
2723 | :group 'rst-faces-defaults | |
2724 | :type '(integer) | |
2725 | :set 'rst-set-level-default) | |
2726 | (defcustom rst-level-face-base-color "grey" | |
2727 | "The base name of the color to be used for creating background colors in | |
92439579 | 2728 | section title faces for all levels." |
94e9c286 SM |
2729 | :group 'rst-faces-defaults |
2730 | :type '(string) | |
2731 | :set 'rst-set-level-default) | |
2732 | (defcustom rst-level-face-base-light | |
2733 | (if (eq frame-background-mode 'dark) | |
2734 | 15 | |
2735 | 85) | |
b4747519 SM |
2736 | "The lightness factor for the base color. This value is used for level 1. |
2737 | The default depends on whether the value of `frame-background-mode' is | |
2738 | `dark' or not." | |
94e9c286 SM |
2739 | :group 'rst-faces-defaults |
2740 | :type '(integer) | |
2741 | :set 'rst-set-level-default) | |
2742 | (defcustom rst-level-face-format-light "%2d" | |
2743 | "The format for the lightness factor appended to the base name of the color. | |
2744 | This value is expanded by `format' with an integer." | |
2745 | :group 'rst-faces-defaults | |
2746 | :type '(string) | |
2747 | :set 'rst-set-level-default) | |
2748 | (defcustom rst-level-face-step-light | |
2749 | (if (eq frame-background-mode 'dark) | |
2750 | 7 | |
2751 | -7) | |
b4747519 SM |
2752 | "The step width to use for the next color. |
2753 | The formula | |
94e9c286 SM |
2754 | |
2755 | `rst-level-face-base-light' | |
2756 | + (`rst-level-face-max' - 1) * `rst-level-face-step-light' | |
2757 | ||
2758 | must result in a color level which appended to `rst-level-face-base-color' | |
2759 | using `rst-level-face-format-light' results in a valid color such as `grey50'. | |
2760 | This color is used as background for section title text on level | |
2761 | `rst-level-face-max'." | |
2762 | :group 'rst-faces-defaults | |
2763 | :type '(integer) | |
2764 | :set 'rst-set-level-default) | |
2765 | ||
2766 | (defcustom rst-adornment-faces-alist | |
2767 | (let ((alist '((t . font-lock-keyword-face) | |
2768 | (nil . font-lock-keyword-face))) | |
2769 | (i 1)) | |
2770 | (while (<= i rst-level-face-max) | |
2771 | (nconc alist (list (cons i (intern (format "rst-level-%d-face" i))))) | |
2772 | (setq i (1+ i))) | |
2773 | alist) | |
b4747519 SM |
2774 | "Faces for the various adornment types. |
2775 | Key is a number (for the section title text of that level), | |
2776 | t (for transitions) or nil (for section title adornment). | |
2777 | If you generally do not like how section title text faces are | |
2778 | set up tweak here. If the general idea is ok for you but you do not like the | |
94e9c286 SM |
2779 | details check the Rst Faces Defaults group." |
2780 | :group 'rst-faces | |
2781 | :type '(alist | |
2782 | :key-type | |
2783 | (choice | |
2784 | (integer | |
2785 | :tag | |
2786 | "Section level (may not be bigger than `rst-level-face-max')") | |
2787 | (boolean :tag "transitions (on) / section title adornment (off)")) | |
2788 | :value-type (face)) | |
2789 | :set-after '(rst-level-face-max)) | |
2790 | ||
b4747519 SM |
2791 | (defun rst-define-level-faces () |
2792 | "Define the faces for the section title text faces from the values." | |
2793 | ;; All variables used here must be checked in `rst-set-level-default' | |
2794 | (let ((i 1)) | |
2795 | (while (<= i rst-level-face-max) | |
2796 | (let ((sym (intern (format "rst-level-%d-face" i))) | |
2797 | (doc (format "Face for showing section title text at level %d" i)) | |
2798 | (col (format (concat "%s" rst-level-face-format-light) | |
2799 | rst-level-face-base-color | |
2800 | (+ (* (1- i) rst-level-face-step-light) | |
2801 | rst-level-face-base-light)))) | |
1d3cdbc7 | 2802 | (unless (facep sym) |
51d5b4ec JD |
2803 | (make-empty-face sym) |
2804 | (set-face-doc-string sym doc) | |
2805 | (set-face-background sym col) | |
2806 | (set sym sym)) | |
2807 | (setq i (1+ i)))))) | |
b4747519 | 2808 | |
0e90a43c | 2809 | (rst-define-level-faces) |
94e9c286 SM |
2810 | |
2811 | \f | |
2812 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
2813 | ;; Font lock | |
2814 | ||
2815 | (defvar rst-use-char-classes | |
2816 | (string-match "[[:alpha:]]" "b") | |
2817 | "Non-nil if we can use the character classes in our regexps.") | |
2818 | ||
2819 | (defun rst-font-lock-keywords-function () | |
92439579 | 2820 | "Return keywords to highlight in Rst mode according to current settings." |
94e9c286 SM |
2821 | ;; The reST-links in the comments below all relate to sections in |
2822 | ;; http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html | |
2823 | (let* ( ;; This gets big - so let's define some abbreviations | |
2824 | ;; horizontal white space | |
2825 | (re-hws "[\t ]") | |
2826 | ;; beginning of line with possible indentation | |
2827 | (re-bol (concat "^" re-hws "*")) | |
2828 | ;; Separates block lead-ins from their content | |
2829 | (re-blksep1 (concat "\\(" re-hws "+\\|$\\)")) | |
2830 | ;; explicit markup tag | |
2831 | (re-emt "\\.\\.") | |
2832 | ;; explicit markup start | |
2833 | (re-ems (concat re-emt re-hws "+")) | |
2834 | ;; inline markup prefix | |
2835 | (re-imp1 (concat "\\(^\\|" re-hws "\\|[-'\"([{</:]\\)")) | |
2836 | ;; inline markup suffix | |
2837 | (re-ims1 (concat "\\(" re-hws "\\|[]-'\")}>/:.,;!?\\]\\|$\\)")) | |
2838 | ;; symbol character | |
2839 | (re-sym1 "\\(\\sw\\|\\s_\\)") | |
2840 | ;; inline markup content begin | |
2841 | (re-imbeg2 "\\(\\S \\|\\S \\([^") | |
2842 | ||
2843 | ;; There seems to be a bug leading to error "Stack overflow in regexp | |
2844 | ;; matcher" when "|" or "\\*" are the characters searched for | |
aee3c6f7 | 2845 | (re-imendbeg "\\]\\|\\\\.") |
94e9c286 SM |
2846 | ;; inline markup content end |
2847 | (re-imend (concat re-imendbeg "\\)*[^\t \\\\]\\)")) | |
2848 | ;; inline markup content without asterisk | |
2849 | (re-ima2 (concat re-imbeg2 "*" re-imend)) | |
2850 | ;; inline markup content without backquote | |
2851 | (re-imb2 (concat re-imbeg2 "`" re-imend)) | |
2852 | ;; inline markup content without vertical bar | |
2853 | (re-imv2 (concat re-imbeg2 "|" re-imend)) | |
2854 | ;; Supported URI schemes | |
2855 | (re-uris1 "\\(acap\\|cid\\|data\\|dav\\|fax\\|file\\|ftp\\|gopher\\|http\\|https\\|imap\\|ldap\\|mailto\\|mid\\|modem\\|news\\|nfs\\|nntp\\|pop\\|prospero\\|rtsp\\|service\\|sip\\|tel\\|telnet\\|tip\\|urn\\|vemmi\\|wais\\)") | |
2856 | ;; Line starting with adornment and optional whitespace; complete | |
2857 | ;; adornment is in (match-string 1); there must be at least 3 | |
2858 | ;; characters because otherwise explicit markup start would be | |
2859 | ;; recognized | |
2860 | (re-ado2 (concat "^\\(\\([" | |
2861 | (if rst-use-char-classes | |
2862 | "^[:word:][:space:][:cntrl:]" "^\\w \t\x00-\x1F") | |
2863 | "]\\)\\2\\2+\\)" re-hws "*$")) | |
2864 | ) | |
2865 | (list | |
2866 | ;; FIXME: Block markup is not recognized in blocks after explicit markup | |
2867 | ;; start | |
2868 | ||
2869 | ;; Simple `Body Elements`_ | |
2870 | ;; `Bullet Lists`_ | |
2b1400b9 GM |
2871 | `(,(concat re-bol "\\([-*+]" re-blksep1 "\\)") |
2872 | 1 rst-block-face) | |
94e9c286 | 2873 | ;; `Enumerated Lists`_ |
2b1400b9 GM |
2874 | `(,(concat re-bol "\\((?\\(#\\|[0-9]+\\|[A-Za-z]\\|[IVXLCMivxlcm]+\\)[.)]" |
2875 | re-blksep1 "\\)") | |
2876 | 1 rst-block-face) | |
94e9c286 SM |
2877 | ;; `Definition Lists`_ FIXME: missing |
2878 | ;; `Field Lists`_ | |
2b1400b9 GM |
2879 | `(,(concat re-bol "\\(:[^:\n]+:\\)" re-blksep1) |
2880 | 1 rst-external-face) | |
94e9c286 | 2881 | ;; `Option Lists`_ |
2b1400b9 GM |
2882 | `(,(concat re-bol "\\(\\(\\(\\([-+/]\\|--\\)\\sw\\(-\\|\\sw\\)*" |
2883 | "\\([ =]\\S +\\)?\\)\\(,[\t ]\\)?\\)+\\)\\($\\|[\t ]\\{2\\}\\)") | |
2884 | 1 rst-block-face) | |
94e9c286 SM |
2885 | |
2886 | ;; `Tables`_ FIXME: missing | |
2887 | ||
2888 | ;; All the `Explicit Markup Blocks`_ | |
2889 | ;; `Footnotes`_ / `Citations`_ | |
2b1400b9 | 2890 | `(,(concat re-bol "\\(" re-ems "\\[[^[\n]+\\]\\)" re-blksep1) |
94e9c286 SM |
2891 | 1 rst-definition-face) |
2892 | ;; `Directives`_ / `Substitution Definitions`_ | |
2b1400b9 GM |
2893 | `(,(concat re-bol "\\(" re-ems "\\)\\(\\(|[^|\n]+|[\t ]+\\)?\\)\\(" |
2894 | re-sym1 "+::\\)" re-blksep1) | |
2895 | (1 rst-directive-face) | |
2896 | (2 rst-definition-face) | |
2897 | (4 rst-directive-face)) | |
94e9c286 | 2898 | ;; `Hyperlink Targets`_ |
2b1400b9 GM |
2899 | `(,(concat re-bol "\\(" re-ems "_\\([^:\\`\n]\\|\\\\.\\|`[^`\n]+`\\)+:\\)" |
2900 | re-blksep1) | |
2901 | 1 rst-definition-face) | |
2902 | `(,(concat re-bol "\\(__\\)" re-blksep1) | |
2903 | 1 rst-definition-face) | |
94e9c286 SM |
2904 | |
2905 | ;; All `Inline Markup`_ | |
2906 | ;; FIXME: Condition 5 preventing fontification of e.g. "*" not implemented | |
2907 | ;; `Strong Emphasis`_ | |
2b1400b9 GM |
2908 | `(,(concat re-imp1 "\\(\\*\\*" re-ima2 "\\*\\*\\)" re-ims1) |
2909 | 2 rst-emphasis2-face) | |
94e9c286 | 2910 | ;; `Emphasis`_ |
2b1400b9 GM |
2911 | `(,(concat re-imp1 "\\(\\*" re-ima2 "\\*\\)" re-ims1) |
2912 | 2 rst-emphasis1-face) | |
94e9c286 | 2913 | ;; `Inline Literals`_ |
2b1400b9 GM |
2914 | `(,(concat re-imp1 "\\(``" re-imb2 "``\\)" re-ims1) |
2915 | 2 rst-literal-face) | |
94e9c286 | 2916 | ;; `Inline Internal Targets`_ |
2b1400b9 GM |
2917 | `(,(concat re-imp1 "\\(_`" re-imb2 "`\\)" re-ims1) |
2918 | 2 rst-definition-face) | |
94e9c286 SM |
2919 | ;; `Hyperlink References`_ |
2920 | ;; FIXME: `Embedded URIs`_ not considered | |
2b1400b9 | 2921 | `(,(concat re-imp1 "\\(\\(`" re-imb2 "`\\|\\(\\sw\\(\\sw\\|-\\)+\\sw\\)\\)__?\\)" re-ims1) |
94e9c286 SM |
2922 | 2 rst-reference-face) |
2923 | ;; `Interpreted Text`_ | |
2b1400b9 GM |
2924 | `(,(concat re-imp1 "\\(\\(:" re-sym1 "+:\\)?\\)\\(`" re-imb2 "`\\)\\(\\(:" |
2925 | re-sym1 "+:\\)?\\)" re-ims1) | |
2926 | (2 rst-directive-face) | |
2927 | (5 rst-external-face) | |
2928 | (8 rst-directive-face)) | |
94e9c286 | 2929 | ;; `Footnote References`_ / `Citation References`_ |
2b1400b9 GM |
2930 | `(,(concat re-imp1 "\\(\\[[^]]+\\]_\\)" re-ims1) |
2931 | 2 rst-reference-face) | |
94e9c286 | 2932 | ;; `Substitution References`_ |
2b1400b9 GM |
2933 | `(,(concat re-imp1 "\\(|" re-imv2 "|\\)" re-ims1) |
2934 | 2 rst-reference-face) | |
94e9c286 | 2935 | ;; `Standalone Hyperlinks`_ |
2b1400b9 GM |
2936 | `(;; FIXME: This takes it easy by using a whitespace as delimiter |
2937 | ,(concat re-imp1 "\\(" re-uris1 ":\\S +\\)" re-ims1) | |
2938 | 2 rst-definition-face) | |
2939 | `(,(concat re-imp1 "\\(" re-sym1 "+@" re-sym1 "+\\)" re-ims1) | |
2940 | 2 rst-definition-face) | |
94e9c286 SM |
2941 | |
2942 | ;; Do all block fontification as late as possible so 'append works | |
2943 | ||
2944 | ;; Sections_ / Transitions_ | |
2945 | (append | |
2946 | (list | |
2947 | re-ado2) | |
2948 | (if (not rst-mode-lazy) | |
2b1400b9 | 2949 | '(1 rst-block-face) |
94e9c286 SM |
2950 | (list |
2951 | (list 'rst-font-lock-handle-adornment | |
2952 | '(progn | |
2953 | (setq rst-font-lock-adornment-point (match-end 1)) | |
2954 | (point-max)) | |
2955 | nil | |
2956 | (list 1 '(cdr (assoc nil rst-adornment-faces-alist)) | |
2957 | 'append t) | |
2958 | (list 2 '(cdr (assoc rst-font-lock-level | |
2959 | rst-adornment-faces-alist)) | |
2960 | 'append t) | |
2961 | (list 3 '(cdr (assoc nil rst-adornment-faces-alist)) | |
2962 | 'append t))))) | |
2963 | ||
2964 | ;; `Comments`_ | |
2965 | (append | |
2966 | (list | |
2967 | (concat re-bol "\\(" re-ems "\\)\[^[|_]\\([^:\n]\\|:\\([^:\n]\\|$\\)\\)*$") | |
2968 | ||
2b1400b9 | 2969 | '(1 rst-comment-face)) |
94e9c286 SM |
2970 | (if rst-mode-lazy |
2971 | (list | |
2972 | (list 'rst-font-lock-find-unindented-line | |
2973 | '(progn | |
2974 | (setq rst-font-lock-indentation-point (match-end 1)) | |
2975 | (point-max)) | |
2976 | nil | |
2b1400b9 | 2977 | '(0 rst-comment-face append))))) |
94e9c286 SM |
2978 | (append |
2979 | (list | |
2980 | (concat re-bol "\\(" re-emt "\\)\\(\\s *\\)$") | |
2b1400b9 GM |
2981 | '(1 rst-comment-face) |
2982 | '(2 rst-comment-face)) | |
94e9c286 SM |
2983 | (if rst-mode-lazy |
2984 | (list | |
2985 | (list 'rst-font-lock-find-unindented-line | |
2986 | '(progn | |
2987 | (setq rst-font-lock-indentation-point 'next) | |
2988 | (point-max)) | |
2989 | nil | |
2b1400b9 | 2990 | '(0 rst-comment-face append))))) |
94e9c286 SM |
2991 | |
2992 | ;; `Literal Blocks`_ | |
2993 | (append | |
2994 | (list | |
2995 | (concat re-bol "\\(\\([^.\n]\\|\\.[^.\n]\\).*\\)?\\(::\\)$") | |
2b1400b9 | 2996 | '(3 rst-block-face)) |
94e9c286 SM |
2997 | (if rst-mode-lazy |
2998 | (list | |
2999 | (list 'rst-font-lock-find-unindented-line | |
3000 | '(progn | |
3001 | (setq rst-font-lock-indentation-point t) | |
3002 | (point-max)) | |
3003 | nil | |
2b1400b9 | 3004 | '(0 rst-literal-face append))))) |
94e9c286 SM |
3005 | |
3006 | ;; `Doctest Blocks`_ | |
3007 | (append | |
3008 | (list | |
3009 | (concat re-bol "\\(>>>\\|\\.\\.\\.\\)\\(.+\\)") | |
2b1400b9 GM |
3010 | '(1 rst-block-face) |
3011 | '(2 rst-literal-face))) | |
94e9c286 SM |
3012 | ))) |
3013 | ||
3014 | ||
3015 | ||
3016 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3017 | ;; Indented blocks | |
3018 | ||
3019 | (defun rst-forward-indented-block (&optional column limit) | |
3020 | "Move forward across one indented block. | |
3021 | Find the next non-empty line which is not indented at least to COLUMN (defaults | |
b4747519 SM |
3022 | to the column of the point). Moves point to first character of this line or the |
3023 | first empty line immediately before it and returns that position. If there is | |
94e9c286 SM |
3024 | no such line before LIMIT (defaults to the end of the buffer) returns nil and |
3025 | point is not moved." | |
3026 | (interactive) | |
3027 | (let ((clm (or column (current-column))) | |
3028 | (start (point)) | |
3029 | fnd beg cand) | |
3030 | (if (not limit) | |
3031 | (setq limit (point-max))) | |
3032 | (save-match-data | |
3033 | (while (and (not fnd) (< (point) limit)) | |
3034 | (forward-line 1) | |
3035 | (when (< (point) limit) | |
3036 | (setq beg (point)) | |
3037 | (if (looking-at "\\s *$") | |
3038 | (setq cand (or cand beg)) ; An empty line is a candidate | |
3039 | (move-to-column clm) | |
3040 | ;; FIXME: No indentation [(zerop clm)] must be handled in some | |
3041 | ;; useful way - though it is not clear what this should mean at all | |
3042 | (if (string-match | |
3043 | "^\\s *$" (buffer-substring-no-properties beg (point))) | |
3044 | (setq cand nil) ; An indented line resets a candidate | |
3045 | (setq fnd (or cand beg))))))) | |
3046 | (goto-char (or fnd start)) | |
3047 | fnd)) | |
3048 | ||
3049 | ;; Stores the point where the current indentation ends if a number. If `next' | |
3050 | ;; indicates `rst-font-lock-find-unindented-line' shall take the indentation | |
3051 | ;; from the next line if this is not empty. If non-nil indicates | |
3052 | ;; `rst-font-lock-find-unindented-line' shall take the indentation from the | |
3053 | ;; next non-empty line. Also used as a trigger for | |
3054 | ;; `rst-font-lock-find-unindented-line'. | |
3055 | (defvar rst-font-lock-indentation-point nil) | |
3056 | ||
3057 | (defun rst-font-lock-find-unindented-line (limit) | |
3058 | (let* ((ind-pnt rst-font-lock-indentation-point) | |
3059 | (beg-pnt ind-pnt)) | |
3060 | ;; May run only once - enforce this | |
3061 | (setq rst-font-lock-indentation-point nil) | |
3062 | (when (and ind-pnt (not (numberp ind-pnt))) | |
3063 | ;; Find indentation point in next line if any | |
3064 | (setq ind-pnt | |
3065 | (save-excursion | |
3066 | (save-match-data | |
3067 | (if (eq ind-pnt 'next) | |
3068 | (when (and (zerop (forward-line 1)) (< (point) limit)) | |
3069 | (setq beg-pnt (point)) | |
3070 | (when (not (looking-at "\\s *$")) | |
3071 | (looking-at "\\s *") | |
3072 | (match-end 0))) | |
3073 | (while (and (zerop (forward-line 1)) (< (point) limit) | |
3074 | (looking-at "\\s *$"))) | |
3075 | (when (< (point) limit) | |
3076 | (setq beg-pnt (point)) | |
3077 | (looking-at "\\s *") | |
3078 | (match-end 0))))))) | |
3079 | (when ind-pnt | |
3080 | (goto-char ind-pnt) | |
3081 | ;; Always succeeds because the limit set by PRE-MATCH-FORM is the | |
3082 | ;; ultimate point to find | |
3083 | (goto-char (or (rst-forward-indented-block nil limit) limit)) | |
0e90a43c SM |
3084 | (save-excursion |
3085 | ;; Include subsequent empty lines in the font-lock block, | |
3086 | ;; in case the user subsequently changes the indentation of the next | |
3087 | ;; non-empty line to move it into the indented element. | |
3088 | (skip-chars-forward " \t\n") | |
3089 | (put-text-property beg-pnt (point) 'font-lock-multiline t)) | |
94e9c286 SM |
3090 | (set-match-data (list beg-pnt (point))) |
3091 | t))) | |
3092 | ||
3093 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3094 | ;; Adornments | |
3095 | ||
0e90a43c | 3096 | (defvar rst-font-lock-adornment-point nil |
92439579 JB |
3097 | "Stores the point where the current adornment ends. |
3098 | Also used as a trigger for `rst-font-lock-handle-adornment'.") | |
94e9c286 SM |
3099 | |
3100 | ;; Here `rst-font-lock-handle-adornment' stores the section level of the | |
3101 | ;; current adornment or t for a transition. | |
3102 | (defvar rst-font-lock-level nil) | |
3103 | ||
3104 | ;; FIXME: It would be good if this could be used to markup section titles of | |
3105 | ;; given level with a special key; it would be even better to be able to | |
3106 | ;; customize this so it can be used for a generally available personal style | |
3107 | ;; | |
3108 | ;; FIXME: There should be some way to reset and reload this variable - probably | |
3109 | ;; a special key | |
3110 | ;; | |
3111 | ;; FIXME: Some support for `outline-mode' would be nice which should be based | |
3112 | ;; on this information | |
3113 | (defvar rst-adornment-level-alist nil | |
3114 | "Associates adornments with section levels. | |
b4747519 SM |
3115 | The key is a two character string. The first character is the adornment |
3116 | character. The second character distinguishes underline section titles (`u') | |
3117 | from overline/underline section titles (`o'). The value is the section level. | |
94e9c286 SM |
3118 | |
3119 | This is made buffer local on start and adornments found during font lock are | |
3120 | entered.") | |
3121 | ||
3122 | ;; Returns section level for adornment key KEY. Adds new section level if KEY | |
3123 | ;; is not found and ADD. If KEY is not a string it is simply returned. | |
3124 | (defun rst-adornment-level (key &optional add) | |
3125 | (let ((fnd (assoc key rst-adornment-level-alist)) | |
3126 | (new 1)) | |
3127 | (cond | |
3128 | ((not (stringp key)) | |
3129 | key) | |
3130 | (fnd | |
3131 | (cdr fnd)) | |
3132 | (add | |
3133 | (while (rassoc new rst-adornment-level-alist) | |
3134 | (setq new (1+ new))) | |
3135 | (setq rst-adornment-level-alist | |
3136 | (append rst-adornment-level-alist (list (cons key new)))) | |
3137 | new)))) | |
3138 | ||
3139 | ;; Classifies adornment for section titles and transitions. ADORNMENT is the | |
3140 | ;; complete adornment string as found in the buffer. END is the point after the | |
3141 | ;; last character of ADORNMENT. For overline section adornment LIMIT limits the | |
3142 | ;; search for the matching underline. Returns a list. The first entry is t for | |
3143 | ;; a transition, or a key string for `rst-adornment-level' for a section title. | |
3144 | ;; The following eight values forming four match groups as can be used for | |
3145 | ;; `set-match-data'. First match group contains the maximum points of the whole | |
3146 | ;; construct. Second and last match group matched pure section title adornment | |
3147 | ;; while third match group matched the section title text or the transition. | |
3148 | ;; Each group but the first may or may not exist. | |
3149 | (defun rst-classify-adornment (adornment end limit) | |
3150 | (save-excursion | |
3151 | (save-match-data | |
3152 | (goto-char end) | |
3153 | (let ((ado-ch (aref adornment 0)) | |
3154 | (ado-re (regexp-quote adornment)) | |
3155 | (end-pnt (point)) | |
3156 | (beg-pnt (progn | |
3157 | (forward-line 0) | |
3158 | (point))) | |
3159 | (nxt-emp | |
3160 | (save-excursion | |
3161 | (or (not (zerop (forward-line 1))) | |
3162 | (looking-at "\\s *$")))) | |
3163 | (prv-emp | |
3164 | (save-excursion | |
3165 | (or (not (zerop (forward-line -1))) | |
3166 | (looking-at "\\s *$")))) | |
3167 | key beg-ovr end-ovr beg-txt end-txt beg-und end-und) | |
3168 | (cond | |
3169 | ((and nxt-emp prv-emp) | |
3170 | ;; A transition | |
3171 | (setq key t) | |
3172 | (setq beg-txt beg-pnt) | |
3173 | (setq end-txt end-pnt)) | |
3174 | (prv-emp | |
3175 | ;; An overline | |
3176 | (setq key (concat (list ado-ch) "o")) | |
3177 | (setq beg-ovr beg-pnt) | |
3178 | (setq end-ovr end-pnt) | |
3179 | (forward-line 1) | |
3180 | (setq beg-txt (point)) | |
3181 | (while (and (< (point) limit) (not end-txt)) | |
3182 | (if (looking-at "\\s *$") | |
3183 | ;; No underline found | |
3184 | (setq end-txt (1- (point))) | |
3185 | (when (looking-at (concat "\\(" ado-re "\\)\\s *$")) | |
3186 | (setq end-und (match-end 1)) | |
3187 | (setq beg-und (point)) | |
3188 | (setq end-txt (1- beg-und)))) | |
3189 | (forward-line 1))) | |
3190 | (t | |
3191 | ;; An underline | |
3192 | (setq key (concat (list ado-ch) "u")) | |
3193 | (setq beg-und beg-pnt) | |
3194 | (setq end-und end-pnt) | |
3195 | (setq end-txt (1- beg-und)) | |
3196 | (setq beg-txt (progn | |
3197 | (if (re-search-backward "^\\s *$" 1 'move) | |
3198 | (forward-line 1)) | |
3199 | (point))))) | |
3200 | (list key | |
3201 | (or beg-ovr beg-txt beg-und) | |
3202 | (or end-und end-txt end-und) | |
3203 | beg-ovr end-ovr beg-txt end-txt beg-und end-und))))) | |
3204 | ||
3205 | ;; Handles adornments for font-locking section titles and transitions. Returns | |
3206 | ;; three match groups. First and last match group matched pure overline / | |
3207 | ;; underline adornment while second group matched section title text. Each | |
3208 | ;; group may not exist. | |
3209 | (defun rst-font-lock-handle-adornment (limit) | |
3210 | (let ((ado-pnt rst-font-lock-adornment-point)) | |
3211 | ;; May run only once - enforce this | |
3212 | (setq rst-font-lock-adornment-point nil) | |
3213 | (if ado-pnt | |
3214 | (let* ((ado (rst-classify-adornment (match-string-no-properties 1) | |
3215 | ado-pnt limit)) | |
3216 | (key (car ado)) | |
3217 | (mtc (cdr ado))) | |
3218 | (setq rst-font-lock-level (rst-adornment-level key t)) | |
3219 | (goto-char (nth 1 mtc)) | |
0e90a43c | 3220 | (put-text-property (nth 0 mtc) (nth 1 mtc) 'font-lock-multiline t) |
94e9c286 SM |
3221 | (set-match-data mtc) |
3222 | t)))) | |
3223 | ||
3224 | ||
3225 | ||
3226 | \f | |
3227 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3228 | ;; Support for conversion from within Emacs | |
3229 | ||
3230 | (defgroup rst-compile nil | |
3231 | "Settings for support of conversion of reStructuredText | |
3232 | document with \\[rst-compile]." | |
3233 | :group 'rst | |
3234 | :version "21.1") | |
3235 | ||
ef4271fe GM |
3236 | (defcustom rst-compile-toolsets |
3237 | `((html ,(if (executable-find "rst2html.py") "rst2html.py" "rst2html") | |
3238 | ".html" nil) | |
3239 | (latex ,(if (executable-find "rst2latex.py") "rst2latex.py" "rst2latex") | |
3240 | ".tex" nil) | |
3241 | (newlatex ,(if (executable-find "rst2newlatex.py") "rst2newlatex.py" | |
3242 | "rst2newlatex") | |
3243 | ".tex" nil) | |
3244 | (pseudoxml ,(if (executable-find "rst2pseudoxml.py") "rst2pseudoxml.py" | |
3245 | "rst2pseudoxml") | |
3246 | ".xml" nil) | |
3247 | (xml ,(if (executable-find "rst2xml.py") "rst2xml.py" "rst2xml") | |
3248 | ".xml" nil) | |
3249 | (pdf ,(if (executable-find "rst2pdf.py") "rst2pdf.py" "rst2pdf") | |
3250 | ".pdf" nil) | |
3251 | (s5 ,(if (executable-find "rst2s5.py") "rst2s5.py" "rst2s5") | |
3252 | ".html" nil)) | |
b4747519 SM |
3253 | "Table describing the command to use for each toolset. |
3254 | An association list of the toolset to a list of the (command to use, | |
94e9c286 | 3255 | extension of produced filename, options to the tool (nil or a |
ef4271fe | 3256 | string)) to be used for converting the document." |
3f1b6eb2 | 3257 | :type '(alist :options (html latex newlatex pseudoxml xml pdf s5) |
ef4271fe GM |
3258 | :key-type symbol |
3259 | :value-type (list :tag "Specification" | |
3260 | (file :tag "Command") | |
3261 | (string :tag "File extension") | |
3262 | (choice :tag "Command options" | |
3263 | (const :tag "No options" nil) | |
3264 | (string :tag "Options")))) | |
3265 | :group 'rst | |
3266 | :version "24.1") | |
94e9c286 SM |
3267 | |
3268 | ;; Note for Python programmers not familiar with association lists: you can set | |
3269 | ;; values in an alists like this, e.g. : | |
3270 | ;; (setcdr (assq 'html rst-compile-toolsets) | |
3271 | ;; '("rst2html.py" ".htm" "--stylesheet=/docutils.css")) | |
3272 | ||
3273 | ||
3274 | (defvar rst-compile-primary-toolset 'html | |
b4747519 | 3275 | "The default toolset for `rst-compile'.") |
94e9c286 SM |
3276 | |
3277 | (defvar rst-compile-secondary-toolset 'latex | |
b4747519 | 3278 | "The default toolset for `rst-compile' with a prefix argument.") |
94e9c286 SM |
3279 | |
3280 | (defun rst-compile-find-conf () | |
3281 | "Look for the configuration file in the parents of the current path." | |
3282 | (interactive) | |
3283 | (let ((file-name "docutils.conf") | |
3284 | (buffer-file (buffer-file-name))) | |
3285 | ;; Move up in the dir hierarchy till we find a change log file. | |
3286 | (let* ((dir (file-name-directory buffer-file)) | |
3287 | (prevdir nil)) | |
3288 | (while (and (or (not (string= dir prevdir)) | |
3289 | (setq dir nil) | |
3290 | nil) | |
3291 | (not (file-exists-p (concat dir file-name)))) | |
3292 | ;; Move up to the parent dir and try again. | |
3293 | (setq prevdir dir) | |
3294 | (setq dir (expand-file-name (file-name-directory | |
3295 | (directory-file-name | |
3296 | (file-name-directory dir))))) | |
3297 | ) | |
3298 | (or (and dir (concat dir file-name)) nil) | |
3299 | ))) | |
3300 | ||
3301 | ||
3302 | (require 'compile) | |
3303 | ||
3304 | (defun rst-compile (&optional pfxarg) | |
3305 | "Compile command to convert reST document into some output file. | |
3306 | Attempts to find configuration file, if it can, overrides the | |
3307 | options. There are two commands to choose from, with a prefix | |
3308 | argument, select the alternative toolset." | |
3309 | (interactive "P") | |
3310 | ;; Note: maybe we want to check if there is a Makefile too and not do anything | |
3311 | ;; if that is the case. I dunno. | |
3312 | (let* ((toolset (cdr (assq (if pfxarg | |
3313 | rst-compile-secondary-toolset | |
3314 | rst-compile-primary-toolset) | |
3315 | rst-compile-toolsets))) | |
3316 | (command (car toolset)) | |
3317 | (extension (cadr toolset)) | |
3318 | (options (caddr toolset)) | |
3319 | (conffile (rst-compile-find-conf)) | |
3320 | (bufname (file-name-nondirectory buffer-file-name)) | |
3321 | (outname (file-name-sans-extension bufname))) | |
3322 | ||
3323 | ;; Set compile-command before invocation of compile. | |
3324 | (set (make-local-variable 'compile-command) | |
3325 | (mapconcat 'identity | |
3326 | (list command | |
3327 | (or options "") | |
3328 | (if conffile | |
3329 | (concat "--config=\"" conffile "\"") | |
3330 | "") | |
3331 | bufname | |
3332 | (concat outname extension)) | |
3333 | " ")) | |
3334 | ||
3335 | ;; Invoke the compile command. | |
3336 | (if (or compilation-read-command current-prefix-arg) | |
3337 | (call-interactively 'compile) | |
3338 | (compile compile-command)) | |
3339 | )) | |
3340 | ||
3341 | (defun rst-compile-alt-toolset () | |
3342 | "Compile command with the alternative toolset." | |
3343 | (interactive) | |
3344 | (rst-compile 't)) | |
3345 | ||
3346 | (defun rst-compile-pseudo-region () | |
b4747519 SM |
3347 | "Show the pseudo-XML rendering of the current active region, |
3348 | or of the entire buffer, if the region is not selected." | |
94e9c286 SM |
3349 | (interactive) |
3350 | (with-output-to-temp-buffer "*pseudoxml*" | |
3351 | (shell-command-on-region | |
3352 | (if mark-active (region-beginning) (point-min)) | |
3353 | (if mark-active (region-end) (point-max)) | |
ef4271fe | 3354 | (cadr (assq 'pseudoxml rst-compile-toolsets)) |
94e9c286 SM |
3355 | standard-output))) |
3356 | ||
3357 | (defvar rst-pdf-program "xpdf" | |
3358 | "Program used to preview PDF files.") | |
3359 | ||
3360 | (defun rst-compile-pdf-preview () | |
3361 | "Convert the document to a PDF file and launch a preview program." | |
3362 | (interactive) | |
c1cf05f4 | 3363 | (let* ((tmp-filename (make-temp-file "rst-out" nil ".pdf")) |
ef4271fe GM |
3364 | (command (format "%s %s %s && %s %s" |
3365 | (cadr (assq 'pdf rst-compile-toolsets)) | |
94e9c286 SM |
3366 | buffer-file-name tmp-filename |
3367 | rst-pdf-program tmp-filename))) | |
3368 | (start-process-shell-command "rst-pdf-preview" nil command) | |
3369 | ;; Note: you could also use (compile command) to view the compilation | |
3370 | ;; output. | |
3371 | )) | |
3372 | ||
3373 | (defvar rst-slides-program "firefox" | |
3374 | "Program used to preview S5 slides.") | |
3375 | ||
3376 | (defun rst-compile-slides-preview () | |
3377 | "Convert the document to an S5 slide presentation and launch a preview program." | |
3378 | (interactive) | |
c1cf05f4 | 3379 | (let* ((tmp-filename (make-temp-file "rst-slides" nil ".html")) |
ef4271fe GM |
3380 | (command (format "%s %s %s && %s %s" |
3381 | (cadr (assq 's5 rst-compile-toolsets)) | |
94e9c286 SM |
3382 | buffer-file-name tmp-filename |
3383 | rst-slides-program tmp-filename))) | |
3384 | (start-process-shell-command "rst-slides-preview" nil command) | |
3385 | ;; Note: you could also use (compile command) to view the compilation | |
3386 | ;; output. | |
3387 | )) | |
3388 | ||
3389 | ||
3390 | \f | |
3391 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3392 | ;; | |
3393 | ;; Generic text functions that are more convenient than the defaults. | |
3394 | ;; | |
3395 | ||
3396 | (defun rst-replace-lines (fromchar tochar) | |
3397 | "Replace flush-left lines, consisting of multiple FROMCHAR characters, | |
3398 | with equal-length lines of TOCHAR." | |
3399 | (interactive "\ | |
3400 | cSearch for flush-left lines of char: | |
3401 | cand replace with char: ") | |
3402 | (save-excursion | |
b4747519 SM |
3403 | (let ((searchre (concat "^" (regexp-quote (string fromchar)) "+\\( *\\)$")) |
3404 | (found 0)) | |
3405 | (while (search-forward-regexp searchre nil t) | |
3406 | (setq found (1+ found)) | |
3407 | (goto-char (match-beginning 1)) | |
3408 | (let ((width (current-column))) | |
3409 | (rst-delete-entire-line) | |
3410 | (insert-char tochar width))) | |
3411 | (message (format "%d lines replaced." found))))) | |
94e9c286 SM |
3412 | |
3413 | (defun rst-join-paragraph () | |
3414 | "Join lines in current paragraph into one line, removing end-of-lines." | |
3415 | (interactive) | |
3416 | (let ((fill-column 65000)) ; some big number | |
3417 | (call-interactively 'fill-paragraph))) | |
3418 | ||
3419 | (defun rst-force-fill-paragraph () | |
3420 | "Fill paragraph at point, first joining the paragraph's lines into one. | |
3421 | This is useful for filling list item paragraphs." | |
3422 | (interactive) | |
3423 | (rst-join-paragraph) | |
3424 | (fill-paragraph nil)) | |
3425 | ||
3426 | ||
3427 | ;; Generic character repeater function. | |
3428 | ;; For sections, better to use the specialized function above, but this can | |
3429 | ;; be useful for creating separators. | |
3430 | (defun rst-repeat-last-character (&optional tofill) | |
e6438428 | 3431 | "Fill the current line up to the length of the preceding line (if not |
94e9c286 | 3432 | empty), using the last character on the current line. If the preceding line is |
b4747519 | 3433 | empty, we use the `fill-column'. |
94e9c286 SM |
3434 | |
3435 | If a prefix argument is provided, use the next line rather than the preceding | |
3436 | line. | |
3437 | ||
3438 | If the current line is longer than the desired length, shave the characters off | |
3439 | the current line to fit the desired length. | |
3440 | ||
3441 | As an added convenience, if the command is repeated immediately, the alternative | |
3442 | column is used (fill-column vs. end of previous/next line)." | |
3443 | (interactive) | |
3444 | (let* ((curcol (current-column)) | |
3445 | (curline (+ (count-lines (point-min) (point)) | |
3446 | (if (eq curcol 0) 1 0))) | |
3447 | (lbp (line-beginning-position 0)) | |
3448 | (prevcol (if (and (= curline 1) (not current-prefix-arg)) | |
3449 | fill-column | |
3450 | (save-excursion | |
3451 | (forward-line (if current-prefix-arg 1 -1)) | |
3452 | (end-of-line) | |
3453 | (skip-chars-backward " \t" lbp) | |
3454 | (let ((cc (current-column))) | |
3455 | (if (= cc 0) fill-column cc))))) | |
3456 | (rightmost-column | |
3457 | (cond (tofill fill-column) | |
3458 | ((equal last-command 'rst-repeat-last-character) | |
3459 | (if (= curcol fill-column) prevcol fill-column)) | |
3460 | (t (save-excursion | |
3461 | (if (= prevcol 0) fill-column prevcol))) | |
3462 | )) ) | |
3463 | (end-of-line) | |
3464 | (if (> (current-column) rightmost-column) | |
3465 | ;; shave characters off the end | |
3466 | (delete-region (- (point) | |
3467 | (- (current-column) rightmost-column)) | |
3468 | (point)) | |
3469 | ;; fill with last characters | |
3470 | (insert-char (preceding-char) | |
3471 | (- rightmost-column (current-column)))) | |
3472 | )) | |
3473 | ||
3474 | ||
3475 | (defun rst-portable-mark-active-p () | |
3476 | "A portable function that returns non-nil if the mark is active." | |
3477 | (cond | |
3478 | ((fboundp 'region-active-p) (region-active-p)) | |
ef4271fe GM |
3479 | ((boundp 'transient-mark-mode) (and transient-mark-mode mark-active)) |
3480 | (t mark-active))) | |
94e9c286 SM |
3481 | |
3482 | \f | |
3483 | (provide 'rst) | |
17150f48 | 3484 | |
94e9c286 | 3485 | ;;; rst.el ends here |