A simple first stab at a series of regression
[docutils.git] / tools / editors / emacs / restructuredtext.el
blobc7c431f0546e1ecacd972698cdb5247630833ecd
1 ;; Authors: David Goodger <goodger@python.org>,
2 ;; Martin Blais <blais@furius.ca>
3 ;; Date: $Date$
4 ;; Copyright: This module has been placed in the public domain.
5 ;;
6 ;; Support code for editing reStructuredText with Emacs indented-text mode.
7 ;; The goal is to create an integrated reStructuredText editing mode.
8 ;;
9 ;; Installation instructions
10 ;; -------------------------
12 ;; You should bind the versatile sectioning command to some key in the text-mode
13 ;; hook. Something like this::
15 ;; (defun user-rst-mode-hook ()
16 ;; (local-set-key [(control ?=)] 'rest-adjust-section-title)
17 ;; )
18 ;; (add-hook 'text-mode-hook 'user-rst-mode-hook)
20 ;; Other specialized and more generic functions are also available.
21 ;; Note that C-= is a good binding, since it allows you to specif a negative arg
22 ;; easily with C-- C-= (easy to type), as well as ordinary prefix arg with
23 ;; C-u C-=.
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 ;; Generic text functions that are more convenient than the defaults.
30 (defun replace-lines (fromchar tochar)
31 "Replace flush-left lines, consisting of multiple FROMCHAR characters,
32 with equal-length lines of TOCHAR."
33 (interactive "\
34 cSearch for flush-left lines of char:
35 cand replace with char: ")
36 (save-excursion
37 (let* ((fromstr (string fromchar))
38 (searchre (concat "^" (regexp-quote fromstr) "+ *$"))
39 (found 0))
40 (condition-case err
41 (while t
42 (search-forward-regexp searchre)
43 (setq found (1+ found))
44 (search-backward fromstr) ;; point will be *before* last char
45 (setq p (1+ (point)))
46 (beginning-of-line)
47 (setq l (- p (point)))
48 (kill-line)
49 (insert-char tochar l))
50 (search-failed
51 (message (format "%d lines replaced." found)))))))
53 (defun join-paragraph ()
54 "Join lines in current paragraph into one line, removing end-of-lines."
55 (interactive)
56 (let ((fill-column 65000)) ; some big number
57 (call-interactively 'fill-paragraph)))
59 (defun force-fill-paragraph ()
60 "Fill paragraph at point, first joining the paragraph's lines into one.
61 This is useful for filling list item paragraphs."
62 (interactive)
63 (join-paragraph)
64 (fill-paragraph nil))
67 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
69 ;; The following functions implement a smart automatic title sectioning feature.
70 ;; The idea is that with the cursor sitting on a section title, we try to get as
71 ;; much information from context and do the best thing. This function can be
72 ;; invoked many time and/or with prefix argument to rotate between the various
73 ;; options.
75 ;; There are two styles of sectioning:
77 ;; 1. simple-underline, e.g. |Some Title
78 ;; |----------
80 ;; 2. overline-and-underline, e.g. |------------
81 ;; | Some Title
82 ;; |------------
84 ;; Some notes:
86 ;; - the underlining character that is used depends on context. The file is
87 ;; scanned to find other sections and an appropriate character is selected.
88 ;; If the function is invoked on a section that is complete, the character
89 ;; is rotated among the existing ones.
91 ;; - prefix argument is used to alternate the sectioning style.
93 ;; Examples:
95 ;; |Some Title ---> |Some Title
96 ;; | |----------
98 ;; |Some Title ---> |Some Title
99 ;; |----- |----------
101 ;; | |------------
102 ;; | Some Title ---> | Some Title
103 ;; | |------------
105 ;; In overline-and-underline style, a variable is available to select how much
106 ;; space to leave before and after the title (it can be zero) when alternating
107 ;; the style. Note that if the title already has some whitespace in front of
108 ;; it, we don't adjust it to the variable setting, we use the whitespace that is
109 ;; already there for adjustment.
111 (defun rest-line-single-char-p (&optional accept-special)
112 "Predicate return the unique char if the current line is
113 composed only of a single repeated non-whitespace
114 character. This returns the char even if there is whitespace at
115 the beginning of the line.
117 If ACCEPT-SPECIAL is specified we do not ignore special sequences
118 which normally we would ignore when doing a search on many lines.
119 For example, normally we have cases to ignore commonly occuring
120 patterns, such as :: or ...; with the flag do not ignore them."
121 (save-excursion
122 (back-to-indentation)
123 (if (not (looking-at "\n"))
124 (let ((c (thing-at-point 'char)))
125 (if (and (looking-at (format "[%s]+\\s-*$" c))
126 (or accept-special
127 (and
128 ;; common patterns
129 (not (looking-at "::\\s-*$"))
130 (not (looking-at "\\.\\.\\.\\s-*$"))
131 ;; discard one char line
132 (not (looking-at ".\\s-*$"))
134 (string-to-char c))
138 (defun rest-find-last-section-char ()
139 "Looks backward for the last section char found in the file."
141 (let (c)
142 (save-excursion
143 (while (and (not c) (not (bobp)))
144 (forward-line -1)
145 (setq c (rest-line-single-char-p))
149 (defun rest-current-section-char (&optional point)
150 "Gets the section char around the current point."
151 (save-excursion
152 (if point (goto-char point))
153 (let ((offlist '(0 1 -2))
154 loff
155 rval
157 (while offlist
158 (forward-line (car offlist))
159 (setq c (rest-line-single-char-p 1))
160 (if c
161 (progn (setq offlist nil
162 rval c))
163 (setq offlist (cdr offlist)))
165 rval
168 (defun rest-initial-sectioning-style (&optional point)
169 "Looks around point and attempts to determine the sectioning
170 style, between simple-underline and overline-and-underline. If
171 there aren't any existing over/underlines, return nil."
172 (save-excursion
173 (if point (goto-char point))
174 (let (ou)
175 (save-excursion
176 (setq ou (mapcar
177 (lambda (x)
178 (forward-line x)
179 (rest-line-single-char-p))
180 '(-1 2))))
181 (beginning-of-line)
182 (cond
183 ((equal ou '(nil nil)) nil)
184 ((car ou) 'over-and-under) ;; we only need check the overline
185 (t 'simple)
189 (defun rest-all-section-chars (&optional ignore-lines)
190 "Finds all the section chars in the entire file and orders them
191 hierarchically, removing duplicates. Basically, returns a list
192 of the section underlining characters.
194 Optional parameters IGNORE-AROUND can be a list of lines to
195 ignore."
197 (let (chars
199 (curline 1))
200 (save-excursion
201 (beginning-of-buffer)
202 (while (< (point) (buffer-end 1))
203 (if (not (memq curline ignore-lines))
204 (progn
205 (setq c (rest-line-single-char-p))
206 (if c
207 (progn
208 (add-to-list 'chars c t)
209 ))) )
210 (forward-line 1) (setq curline (+ curline 1))
212 chars))
214 (defun rest-update-section (underlinechar style &optional indent)
215 "Unconditionally updates the overline/underline of a section
216 title using the given character CHAR, with STYLE 'simple or
217 'over-and-under, in which case with title whitespace separation
218 on each side with INDENT whitespaces. If the style is 'simple,
219 whitespace before the title is removed.
221 If there are existing overline and/or underline, they are
222 removed before adding the requested adornments."
224 (interactive)
225 (let (marker
228 (c ?-))
230 (end-of-line)
231 (setq marker (point-marker))
233 ;; Fixup whitespace at the beginning and end of the line
234 (if (or (null indent) (eq style 'simple))
235 (setq indent 0))
236 (beginning-of-line)
237 (delete-horizontal-space)
238 (insert (make-string indent ? ))
240 (end-of-line)
241 (delete-horizontal-space)
243 ;; Set the current column, we're at the end of the title line
244 (setq len (+ (current-column) indent))
246 ;; Remove previous line if it consists only of a single repeated character
247 (save-excursion
248 (forward-line -1)
249 (and (rest-line-single-char-p 1)
250 (kill-line 1)))
252 ;; Remove following line if it consists only of a single repeated character
253 (save-excursion
254 (forward-line +1)
255 (and (rest-line-single-char-p 1)
256 (kill-line 1))
257 ;; Add a newline if we're at the end of the buffer, for the subsequence
258 ;; inserting of the underline
259 (if (= (point) (buffer-end 1))
260 (newline 1)))
262 ;; Insert overline
263 (if (eq style 'over-and-under)
264 (save-excursion
265 (beginning-of-line)
266 (open-line 1)
267 (insert (make-string len underlinechar))))
269 ;; Insert underline
270 (forward-line +1)
271 (open-line 1)
272 (insert (make-string len underlinechar))
274 (forward-line +1)
275 (goto-char marker)
279 (defvar rest-default-section-char ?=
280 "Default section underlining character to use when there aren't
281 any others to be used in the file.")
283 (defvar rest-default-under-and-over-indent 1
284 "Number of characters to indent the section title when toggling
285 sectioning styles. This is used when switching from a simple
286 section style to a over-and-under style.")
288 (defun rest-adjust-section-title ()
289 "Adjust/rotate the section underlining for the section around
290 point.
292 This function is the main entry point of this module and is a
293 bit of a swiss knife. It is meant as the single function to
294 invoke to adjust the underlines (and possibly overlines) of a
295 section title in restructuredtext. The next action it takes
296 depends on context around the point, and it is meant to be
297 invoked possibly more than once. Basically, this function deals
298 with:
300 - underlining a title if it does not have an underline;
301 - adjusting the length of the underline characters to fit a
302 modified title;
303 - rotating the underlines/overlines in the set of already
304 existing underline chars used in the file;
305 - switching between simple underline and over-and-under style
306 sectioning (or box style).
308 Here are the gory details:
310 - If the current line has no underline character around it,
311 search backwards for a previously used underlining character,
312 and underline the current line as a section title (also see
313 prefix argument below).
315 If no pre-existing underlining character is found in the
316 file, we use the last seen underline char or
317 rest-default-section-char if this is the first title in the
318 entire file.
320 - If the current line does have an underline or overline, and
323 - the underline do not extend to exactly the end of the
324 title line, this changes the length of the under(over)lines
325 to fit exactly the section title;
327 - the underline length is already adjusted to the end of the
328 title line, we search the file for the underline chars, and
329 we rotate the current title's underline character with that
330 list (going down the hierarchy that is present in the
331 file);
333 If there is a prefix argument, switch the style between the
334 initial sectioning style and the other sectioning style. The
335 two styles are overline-and-underline and simple-underline.
337 If however, you are on a complete section title and you
338 specify a negative argument, the effect of the prefix
339 argument is to change the direction of rotation of the
340 underline characters. Thus using a prefix argument and a
341 negative prefix argument achieves a different result in the
342 case of rotation.
344 Note that the initial style of underlining (simple underline
345 or box-style) depends on if there is whitespace at the start
346 of the line. If there are already underlines/overlines,
347 those are used to select the style, otherwise if there is
348 whitespace at the front of the title overline-and-underline
349 style is chosen, and otherwise simple underline.
351 Also, note that this should work on the section title line as
352 well as on a complete or incomplete underline for a
353 title (first thing we check for that case and move the cursor
354 up a line if needed)."
356 (interactive)
358 ;; check if we're on an underline under a title line, and move the cursor up
359 ;; if it is so.
360 (if (and (or (rest-line-single-char-p 1)
361 (looking-at "^\\s-*$"))
362 (save-excursion
363 (forward-line -1)
364 (beginning-of-line)
365 (looking-at "^.+$")))
366 (forward-line -1))
368 (let (
369 ;; find current sectioning character
370 (curchar (rest-current-section-char))
371 ;; find current sectioning style
372 (init-style (rest-initial-sectioning-style))
373 ;; find current indentation of title line
374 (curindent (save-excursion
375 (back-to-indentation)
376 (current-column)))
378 ;; ending column
379 (endcol (- (line-end-position) (line-beginning-position)))
382 ;; if there is no current style found...
383 (if (eq init-style nil)
384 ;; select based on the whitespace at the beginning of the line
385 (save-excursion
386 (beginning-of-line)
387 (setq init-style
388 (if (looking-at "^\\s-+") 'over-and-under 'simple))))
390 ;; if we're switching characters, we're going to simply change the
391 ;; sectioning style. this branch is also taken if there is no current
392 ;; sectioning around the title.
393 (if (or (and current-prefix-arg
394 (not (< (prefix-numeric-value current-prefix-arg) 0)))
395 (eq curchar nil))
397 ;; we're switching characters or there is currently no sectioning
398 (progn
399 (setq curchar
400 (or curchar
401 (rest-find-last-section-char)
402 (car (rest-all-section-chars))
403 rest-default-section-char))
405 (rest-update-section
406 (or curchar rest-default-section-char)
407 (if current-prefix-arg
408 (if (eq init-style 'over-and-under) 'simple 'over-and-under)
409 init-style)
410 rest-default-under-and-over-indent)
413 ;; else we're not switching characters, and there is some sectioning
414 ;; already present, so check if the current sectioning is complete and
415 ;; correct.
416 (let ((exps (concat "^"
417 (regexp-quote (make-string
418 (+ endcol curindent) curchar))
419 "$")))
420 (if (or
421 (not (save-excursion (forward-line +1)
422 (beginning-of-line)
423 (looking-at exps)))
424 (and (eq init-style 'over-and-under)
425 (not (save-excursion (forward-line -1)
426 (beginning-of-line)
427 (looking-at exps)))))
429 ;; the current sectioning needs to be fixed/updated!
430 (rest-update-section curchar init-style curindent)
432 ;; the current sectioning is complete, rotate characters
433 (let* ( (curline (+ (count-lines (point-min) (point))
434 (if (bolp) 1 0)))
435 (allchars (rest-all-section-chars
436 (list (- curline 1) curline (+ curline 1))))
437 (rotchars (append allchars (list (car allchars))))
438 (nextchar
439 (or (cadr (memq curchar
440 (if (< (prefix-numeric-value
441 current-prefix-arg) 0)
442 (reverse rotchars) rotchars)))
443 (car allchars)) ) )
445 (if nextchar
446 (rest-update-section nextchar init-style curindent))
451 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
453 ;; Generic character repeater function.
455 ;; For sections, better to use the specialized function above, but this can
456 ;; be useful for creating separators.
458 (defun repeat-last-character (&optional tofill)
459 "Fills the current line up to the length of the preceding line (if not
460 empty), using the last character on the current line. If the preceding line is
461 empty, we use the fill-column.
463 If a prefix argument is provided, use the next line rather than the preceding
464 line.
466 If the current line is longer than the desired length, shave the characters off
467 the current line to fit the desired length.
469 As an added convenience, if the command is repeated immediately, the alternative
470 column is used (fill-column vs. end of previous/next line)."
471 (interactive)
472 (let* ((curcol (current-column))
473 (curline (+ (count-lines (point-min) (point))
474 (if (eq curcol 0) 1 0)))
475 (lbp (line-beginning-position 0))
476 (prevcol (if (and (= curline 1) (not current-prefix-arg))
477 fill-column
478 (save-excursion
479 (forward-line (if current-prefix-arg 1 -1))
480 (end-of-line)
481 (skip-chars-backward " \t" lbp)
482 (let ((cc (current-column)))
483 (if (= cc 0) fill-column cc)))))
484 (rightmost-column
485 (cond (tofill fill-column)
486 ((equal last-command 'repeat-last-character)
487 (if (= curcol fill-column) prevcol fill-column))
488 (t (save-excursion
489 (if (= prevcol 0) fill-column prevcol)))
490 )) )
491 (end-of-line)
492 (if (> (current-column) rightmost-column)
493 ;; shave characters off the end
494 (delete-region (- (point)
495 (- (current-column) rightmost-column))
496 (point))
497 ;; fill with last characters
498 (insert-char (preceding-char)
499 (- rightmost-column (current-column))))
504 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
506 ;; Section movement commands.
509 ;; Note: this is not quite correct, the definition is any non alpha-numeric
510 ;; character.
511 (defun rest-title-char-p (c)
512 "Returns true if the given character is a valid title char."
513 (and (string-match "[-=`:\\.'\"~^_*+#<>!$%&(),/;?@\\\|]"
514 (char-to-string c)) t))
516 (defun rest-forward-section ()
517 "Skip to the next restructured text section title."
518 (interactive)
519 (let* ( (newpoint
520 (save-excursion
521 (forward-char) ;; in case we're right on a title
522 (while
523 (not
524 (and (re-search-forward "^[A-Za-z0-9].*[ \t]*$" nil t)
525 (reST-title-char-p (char-after (+ (point) 1)))
526 (looking-at (format "\n%c\\{%d,\\}[ \t]*$"
527 (char-after (+ (point) 1))
528 (current-column))))))
529 (beginning-of-line)
530 (point))) )
531 (if newpoint (goto-char newpoint)) ))
533 (defun rest-backward-section ()
534 "Skip to the previous restructured text section title."
535 (interactive)
536 (let* ( (newpoint
537 (save-excursion
538 ;;(forward-char) ;; in case we're right on a title
539 (while
540 (not
541 (and (or (backward-char) t)
542 (re-search-backward "^[A-Za-z0-9].*[ \t]*$" nil t)
543 (or (end-of-line) t)
544 (reST-title-char-p (char-after (+ (point) 1)))
545 (looking-at (format "\n%c\\{%d,\\}[ \t]*$"
546 (char-after (+ (point) 1))
547 (current-column))))))
548 (beginning-of-line)
549 (point))) )
550 (if newpoint (goto-char newpoint)) ))
553 ;;------------------------------------------------------------------------------
554 ;; For backwards compatibility. Remove at some point.
555 (defalias 'reST-title-char-p 'rest-title-char-p)
556 (defalias 'reST-forward-title 'rest-forward-section)
557 (defalias 'reST-backward-title 'rest-backward-section)