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