rst.el 118 KB
Newer Older
Stefan Monnier's avatar
Stefan Monnier committed
1 2
;;; rst.el --- Mode for viewing and editing reStructuredText-documents.

3
;; Copyright (C) 2003-2011  Free Software Foundation, Inc.
Stefan Monnier's avatar
Stefan Monnier committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

;; Authors: Martin Blais <blais@furius.ca>,
;;          Stefan Merten <smerten@oekonux.de>,
;;          David Goodger <goodger@python.org>

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This package provides major mode rst-mode, which supports documents marked up
Stefan Monnier's avatar
Stefan Monnier committed
27 28 29 30
;; using the reStructuredText format.  Support includes font locking as well as
;; some convenience functions for editing.  It does this by defining a Emacs
;; major mode: rst-mode (ReST).  This mode is derived from text-mode (and
;; inherits much of it).  This package also contains:
Stefan Monnier's avatar
Stefan Monnier committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
;;
;; - Functions to automatically adjust and cycle the section underline
;;   decorations;
;; - A mode that displays the table of contents and allows you to jump anywhere
;;   from it;
;; - Functions to insert and automatically update a TOC in your source
;;   document;
;; - Font-lock highlighting of notable reStructuredText structures;
;; - Some other convenience functions.
;;
;; See the accompanying document in the docutils documentation about
;; the contents of this package and how to use it.
;;
;; For more information about reStructuredText, see
;; http://docutils.sourceforge.net/rst.html
;;
;; For full details on how to use the contents of this file, see
;; http://docutils.sourceforge.net/docs/user/emacs.html
;;
;;
Stefan Monnier's avatar
Stefan Monnier committed
51 52
;; There are a number of convenient keybindings provided by rst-mode.
;; The main one is
Stefan Monnier's avatar
Stefan Monnier committed
53 54 55 56
;;
;;    C-c C-a (also C-=): rst-adjust
;;
;; Updates or rotates the section title around point or promotes/demotes the
Stefan Monnier's avatar
Stefan Monnier committed
57
;; decorations within the region (see full details below).  Note that C-= is a
Stefan Monnier's avatar
Stefan Monnier committed
58 59 60
;; good binding, since it allows you to specify a negative arg easily with C--
;; C-= (easy to type), as well as ordinary prefix arg with C-u C-=.
;;
Stefan Monnier's avatar
Stefan Monnier committed
61
;; For more on bindings, see rst-mode-map below.  There are also many variables
Stefan Monnier's avatar
Stefan Monnier committed
62 63 64 65 66 67 68
;; that can be customized, look for defcustom and defvar in this file.
;;
;; If you use the table-of-contents feature, you may want to add a hook to
;; update the TOC automatically everytime you adjust a section title::
;;
;;   (add-hook 'rst-adjust-hook 'rst-toc-update)
;;
Stefan Monnier's avatar
Stefan Monnier committed
69 70
;; Syntax highlighting: font-lock is enabled by default.  If you want to turn
;; off syntax highlighting to rst-mode, you can use the following::
Stefan Monnier's avatar
Stefan Monnier committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
;;
;;   (setq font-lock-global-modes '(not rst-mode ...))
;;


;; CUSTOMIZATION
;;
;; rst
;; ---
;; This group contains some general customizable features.
;;
;; The group is contained in the wp group.
;;
;; rst-faces
;; ---------
Stefan Monnier's avatar
Stefan Monnier committed
86
;; This group contains all necessary for customizing fonts.  The default
Stefan Monnier's avatar
Stefan Monnier committed
87 88 89 90 91 92 93 94 95 96 97
;; settings use standard font-lock-*-face's so if you set these to your
;; liking they are probably good in rst-mode also.
;;
;; The group is contained in the faces group as well as in the rst group.
;;
;; rst-faces-defaults
;; ------------------
;; This group contains all necessary for customizing the default fonts used for
;; section title faces.
;;
;; The general idea for section title faces is to have a non-default background
Stefan Monnier's avatar
Stefan Monnier committed
98 99
;; but do not change the background.  The section level is shown by the
;; lightness of the background color.  If you like this general idea of
Stefan Monnier's avatar
Stefan Monnier committed
100
;; generating faces for section titles but do not like the details this group
Stefan Monnier's avatar
Stefan Monnier committed
101
;; is the point where you can customize the details.  If you do not like the
Stefan Monnier's avatar
Stefan Monnier committed
102 103 104 105
;; general idea, however, you should customize the faces used in
;; rst-adornment-faces-alist.
;;
;; Note: If you are using a dark background please make sure the variable
Stefan Monnier's avatar
Stefan Monnier committed
106
;; frame-background-mode is set to the symbol dark.  This triggers
Stefan Monnier's avatar
Stefan Monnier committed
107 108 109 110
;; some default values which are probably right for you.
;;
;; The group is contained in the rst-faces group.
;;
Stefan Monnier's avatar
Stefan Monnier committed
111 112
;; All customizable features have a comment explaining their meaning.
;; Refer to the customization of your Emacs (try ``M-x customize``).
Stefan Monnier's avatar
Stefan Monnier committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129


;;; DOWNLOAD

;; The latest version of this file lies in the docutils source code repository:
;;   http://svn.berlios.de/svnroot/repos/docutils/trunk/docutils/tools/editors/emacs/rst.el


;;; INSTALLATION

;; Add the following lines to your `.emacs' file:
;;
;;   (require 'rst)
;;
;; If you are using `.txt' as a standard extension for reST files as
;; http://docutils.sourceforge.net/FAQ.html#what-s-the-standard-filename-extension-for-a-restructuredtext-file
;; suggests you may use one of the `Local Variables in Files' mechanism Emacs
Stefan Monnier's avatar
Stefan Monnier committed
130
;; provides to set the major mode automatically.  For instance you may use::
Stefan Monnier's avatar
Stefan Monnier committed
131 132 133
;;
;;    .. -*- mode: rst -*-
;;
Stefan Monnier's avatar
Stefan Monnier committed
134 135
;; in the very first line of your file.  The following code is useful if you
;; want automatically enter rst-mode from any file with compatible extensions:
Stefan Monnier's avatar
Stefan Monnier committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
;;
;; (setq auto-mode-alist
;;       (append '(("\\.txt$" . rst-mode)
;;                 ("\\.rst$" . rst-mode)
;;                 ("\\.rest$" . rst-mode)) auto-mode-alist))
;;

;;; BUGS

;; - rst-enumeration-region: Select a single paragraph, with the top at one
;;   blank line before the beginning, and it will fail.
;; - The active region goes away when we shift it left or right, and this
;;   prevents us from refilling it automatically when shifting many times.
;; - The suggested decorations when adjusting should not have to cycle
;;   below one below the last section decoration level preceding the
;;   cursor.  We need to fix that.

;;; TODO LIST

;; rst-toc-insert features
;; ------------------------
;; - rst-toc-insert: We should parse the contents:: options to figure out how
;;   deep to render the inserted TOC.
;; - On load, detect any existing TOCs and set the properties for links.
;; - TOC insertion should have an option to add empty lines.
;; - TOC insertion should deal with multiple lines.
;; - There is a bug on redo after undo of adjust when rst-adjust-hook uses the
;;   automatic toc update.  The cursor ends up in the TOC and this is
;;   annoying.  Gotta fix that.
;; - numbering: automatically detect if we have a section-numbering directive in
;;   the corresponding section, to render the toc.
;;
;; bulleted and enumerated list items
;; ----------------------------------
;; - We need to provide way to rebullet bulleted lists, and that would include
;;   automatic enumeration as well.
;;
;; Other
;; -----
;; - It would be nice to differentiate between text files using
;;   reStructuredText_ and other general text files.  If we had a
;;   function to automatically guess whether a .txt file is following the
;;   reStructuredText_ conventions, we could trigger rst-mode without
;;   having to hard-code this in every text file, nor forcing the user to
;;   add a local mode variable at the top of the file.
;;   We could perform this guessing by searching for a valid decoration
;;   at the top of the document or searching for reStructuredText_
;;   directives further on.
;;
;; - We should support imenu in our major mode, with the menu filled with the
;;   section titles (this should be really easy).
;;
;; - We should rename "adornment" to "decoration" or vice-versa in this
;;   document (Stefan's code ("adornment") vs Martin ("decoration")), maybe some
;;   functions even overlap.
;;
;; - We need to automatically recenter on rst-forward-section movement commands.


;;; HISTORY
;;

Stefan Monnier's avatar
Stefan Monnier committed
198
;;; Code:
Stefan Monnier's avatar
Stefan Monnier committed
199 200


Juanma Barranquero's avatar
Juanma Barranquero committed
201
(defgroup rst nil "Support for reStructuredText documents."
Stefan Monnier's avatar
Stefan Monnier committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
  :group 'wp
  :version "23.1"
  :link '(url-link "http://docutils.sourceforge.net/rst.html"))




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Define some generic support functions.

(eval-when-compile (require 'cl)) ;; We need this for destructuring-bind below.


;; From Emacs-22
(unless (fboundp 'line-number-at-pos)
  (defun line-number-at-pos (&optional pos)
    "Return (narrowed) buffer line number at position POS.
    If POS is nil, use current buffer location."
    (let ((opoint (or pos (point))) start)
      (save-excursion
	(goto-char (point-min))
	(setq start (point))
	(goto-char opoint)
	(forward-line 0)
	(1+ (count-lines start (point)))))) )



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Mode definition.

;; Key bindings.
(defvar rst-mode-map
  (let ((map (make-sparse-keymap)))

    ;;
    ;; Section Decorations.
    ;;
    ;; The adjustment function that decorates or rotates a section title.
    (define-key map [(control c) (control a)] 'rst-adjust)
    (define-key map [(control c) (control ?=)] 'rst-adjust)
    (define-key map [(control ?=)] 'rst-adjust) ;; (Does not work on the Mac OSX.)
    ;; Display the hierarchy of decorations implied by the current document contents.
    (define-key map [(control c) (control h)] 'rst-display-decorations-hierarchy)
Paul Eggert's avatar
Paul Eggert committed
246
    ;; Homogenize the decorations in the document.
Stefan Monnier's avatar
Stefan Monnier committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    (define-key map [(control c) (control s)] 'rst-straighten-decorations)
;;    (define-key map [(control c) (control s)] 'rst-straighten-deco-spacing)

    ;;
    ;; Section Movement and Selection.
    ;;
    ;; Mark the subsection where the cursor is.
    (define-key map [(control c) (control m)] 'rst-mark-section)
    ;; Move forward/backward between section titles.
    (define-key map [(control c) (control n)] 'rst-forward-section)
    (define-key map [(control c) (control p)] 'rst-backward-section)

    ;;
    ;; Operating on Blocks of Text.
    ;;
    ;; Makes paragraphs in region as a bullet list.
    (define-key map [(control c) (control b)] 'rst-bullet-list-region)
    ;; Makes paragraphs in region as a enumeration.
    (define-key map [(control c) (control e)] 'rst-enumerate-region)
    ;; Converts bullets to an enumeration.
    (define-key map [(control c) (control v)] 'rst-convert-bullets-to-enumeration)
    ;; Makes region a line-block.
    (define-key map [(control c) (control d)] 'rst-line-block-region)
    ;; Make sure that all the bullets in the region are consistent.
    (define-key map [(control c) (control w)] 'rst-straighten-bullets-region)
    ;; Shift region left or right (taking into account of enumerations/bullets, etc.).
    (define-key map [(control c) (control l)] 'rst-shift-region-left)
    (define-key map [(control c) (control r)] 'rst-shift-region-right)
    ;; Comment/uncomment the active region.
    (define-key map [(control c) (control c)] 'comment-region)

    ;;
    ;; Table-of-Contents Features.
    ;;
    ;; Enter a TOC buffer to view and move to a specific section.
    (define-key map [(control c) (control t)] 'rst-toc)
    ;; Insert a TOC here.
    (define-key map [(control c) (control i)] 'rst-toc-insert)
    ;; Update the document's TOC (without changing the cursor position).
    (define-key map [(control c) (control u)] 'rst-toc-update)
    ;; Got to the section under the cursor (cursor must be in TOC).
    (define-key map [(control c) (control f)] 'rst-goto-section)

    ;;
    ;; Converting Documents from Emacs.
    ;;
    ;; Run one of two pre-configured toolset commands on the document.
    (define-key map [(control c) (?1)] 'rst-compile)
    (define-key map [(control c) (?2)] 'rst-compile-alt-toolset)
    ;; Convert the active region to pseudo-xml using the docutils tools.
    (define-key map [(control c) (?3)] 'rst-compile-pseudo-region)
    ;; Convert the current document to PDF and launch a viewer on the results.
    (define-key map [(control c) (?4)] 'rst-compile-pdf-preview)
    ;; Convert the current document to S5 slides and view in a web browser.
    (define-key map [(control c) (?5)] 'rst-compile-slides-preview)

    map)
304
  "Keymap for reStructuredText mode commands.
Stefan Monnier's avatar
Stefan Monnier committed
305
This inherits from Text mode.")
Stefan Monnier's avatar
Stefan Monnier committed
306 307 308 309


;; Abbrevs.
(defvar rst-mode-abbrev-table nil
Juanma Barranquero's avatar
Juanma Barranquero committed
310
  "Abbrev table used while in Rst mode.")
Stefan Monnier's avatar
Stefan Monnier committed
311
(define-abbrev-table 'rst-mode-abbrev-table
312 313 314 315 316 317 318 319
  (mapcar (lambda (x) (append x '(nil 0 system)))
          '(("contents" ".. contents::\n..\n   ")
            ("con" ".. contents::\n..\n   ")
            ("cont" "[...]")
            ("skip" "\n\n[...]\n\n  ")
            ("seq" "\n\n[...]\n\n  ")
            ;; FIXME: Add footnotes, links, and more.
            )))
Stefan Monnier's avatar
Stefan Monnier committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345


;; Syntax table.
(defvar rst-mode-syntax-table
  (let ((st (copy-syntax-table text-mode-syntax-table)))

    (modify-syntax-entry ?$ "." st)
    (modify-syntax-entry ?% "." st)
    (modify-syntax-entry ?& "." st)
    (modify-syntax-entry ?' "." st)
    (modify-syntax-entry ?* "." st)
    (modify-syntax-entry ?+ "." st)
    (modify-syntax-entry ?. "_" st)
    (modify-syntax-entry ?/ "." st)
    (modify-syntax-entry ?< "." st)
    (modify-syntax-entry ?= "." st)
    (modify-syntax-entry ?> "." st)
    (modify-syntax-entry ?\\ "\\" st)
    (modify-syntax-entry ?| "." st)
    (modify-syntax-entry ?_ "." st)

    st)
  "Syntax table used while in `rst-mode'.")


(defcustom rst-mode-hook nil
346 347
  "Hook run when Rst mode is turned on.
The hook for Text mode is run before this one."
Stefan Monnier's avatar
Stefan Monnier committed
348 349 350 351 352
  :group 'rst
  :type '(hook))


(defcustom rst-mode-lazy t
Juanma Barranquero's avatar
Juanma Barranquero committed
353
  "If non-nil Rst mode tries to font-lock multi-line elements correctly.
Stefan Monnier's avatar
Stefan Monnier committed
354 355
Because this is really slow it should be set to nil if neither `jit-lock-mode'
not `lazy-lock-mode' and activated.
Stefan Monnier's avatar
Stefan Monnier committed
356

Stefan Monnier's avatar
Stefan Monnier committed
357
If nil, comments and literal blocks are font-locked only on the line they start.
Stefan Monnier's avatar
Stefan Monnier committed
358

Juanma Barranquero's avatar
Juanma Barranquero committed
359
The value of this variable is used when Rst mode is turned on."
Stefan Monnier's avatar
Stefan Monnier committed
360 361 362
  :group 'rst
  :type '(boolean))

363 364
;; Use rst-mode for *.rst and *.rest files.  Many ReStructured-Text files
;; use *.txt, but this is too generic to be set as a default.
365
;;;###autoload (add-to-list 'auto-mode-alist (purecopy '("\\.re?st\\'" . rst-mode)))
Stefan Monnier's avatar
Stefan Monnier committed
366 367 368
;;;###autoload
(define-derived-mode rst-mode text-mode "ReST"
  "Major mode for editing reStructuredText documents.
369
\\<rst-mode-map>
Stefan Monnier's avatar
Stefan Monnier committed
370
There are a number of convenient keybindings provided by
371
Rst mode.  The main one is \\[rst-adjust], it updates or rotates
Stefan Monnier's avatar
Stefan Monnier committed
372
the section title around point or promotes/demotes the
Juanma Barranquero's avatar
Juanma Barranquero committed
373 374
decorations within the region (see full details below).
Use negative prefix arg to rotate in the other direction.
Stefan Monnier's avatar
Stefan Monnier committed
375

376 377 378 379 380 381
Turning on `rst-mode' calls the normal hooks `text-mode-hook'
and `rst-mode-hook'.  This mode also supports font-lock
highlighting.  You may customize `rst-mode-lazy' to toggle
font-locking of blocks.

\\{rst-mode-map}"
382 383 384
  :abbrev-table rst-mode-abbrev-table
  :syntax-table rst-mode-syntax-table
  :group 'rst
Stefan Monnier's avatar
Stefan Monnier committed
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

  (set (make-local-variable 'paragraph-separate) paragraph-start)
  (set (make-local-variable 'indent-line-function) 'indent-relative-maybe)
  (set (make-local-variable 'paragraph-start)
       "\f\\|>*[ \t]*$\\|>*[ \t]*[-+*] \\|>*[ \t]*[0-9#]+\\. ")
  (set (make-local-variable 'adaptive-fill-mode) t)

  ;; FIXME: No need to reset this.
  ;; (set (make-local-variable 'indent-line-function) 'indent-relative)

  ;; The details of the following comment setup is important because it affects
  ;; auto-fill, and it is pretty common in running text to have an ellipsis
  ;; ("...") which trips because of the rest comment syntax (".. ").
  (set (make-local-variable 'comment-start) ".. ")
  (set (make-local-variable 'comment-start-skip) "^\\.\\. ")
  (set (make-local-variable 'comment-multi-line) nil)

  ;; Special variables
  (make-local-variable 'rst-adornment-level-alist)

  ;; Font lock
  (set (make-local-variable 'font-lock-defaults)
       '(rst-font-lock-keywords-function
	 t nil nil nil
	 (font-lock-mark-block-function . mark-paragraph)))
Stefan Monnier's avatar
Stefan Monnier committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
  ;; `jit-lock-mode' has been the default since Emacs-21.1, so there's no
  ;; point messing around with font-lock-support-mode any more.
  ;; (when (boundp 'font-lock-support-mode)
  ;;   ;; rst-mode has its own mind about font-lock-support-mode
  ;;   (make-local-variable 'font-lock-support-mode)
  ;;   ;; jit-lock-mode replaced lazy-lock-mode in GNU Emacs 21.
  ;;   (let ((jit-or-lazy-lock-mode
  ;;          (cond
  ;;           ((fboundp 'lazy-lock-mode) 'lazy-lock-mode)
  ;;           ((fboundp 'jit-lock-mode) 'jit-lock-mode)
  ;;           ;; if neither lazy-lock nor jit-lock is supported,
  ;;           ;; tell user and disable rst-mode-lazy
  ;;           (t (when rst-mode-lazy
  ;;                (message "Disabled lazy fontification, because no known support mode found.")
  ;;                (setq rst-mode-lazy nil))))))
  ;;     (cond
  ;;      ((and (not rst-mode-lazy) (not font-lock-support-mode)))
  ;;      ;; No support mode set and none required - leave it alone
  ;;      ((or (not font-lock-support-mode) ;; No support mode set (but required)
  ;;           (symbolp font-lock-support-mode)) ;; or a fixed mode for all
  ;;       (setq font-lock-support-mode
  ;;             (list (cons 'rst-mode (and rst-mode-lazy jit-or-lazy-lock-mode))
  ;;       	    (cons t font-lock-support-mode))))
  ;;      ((and (listp font-lock-support-mode)
  ;;            (not (assoc 'rst-mode font-lock-support-mode)))
  ;;       ;; A list of modes missing rst-mode
  ;;       (setq font-lock-support-mode
  ;;             (cons (cons 'rst-mode (and rst-mode-lazy jit-or-lazy-lock-mode))
  ;;       	    font-lock-support-mode))))))
Stefan Monnier's avatar
Stefan Monnier committed
439 440 441 442 443 444

  )


;;;###autoload
(define-minor-mode rst-minor-mode
Chong Yidong's avatar
Chong Yidong committed
445 446 447 448
  "Toggle ReST minor mode.
With a prefix argument ARG, enable ReST minor mode if ARG is
positive, and disable it otherwise.  If called from Lisp, enable
the mode if ARG is omitted or nil.
Stefan Monnier's avatar
Stefan Monnier committed
449

Juanma Barranquero's avatar
Juanma Barranquero committed
450 451 452
When ReST minor mode is enabled, the ReST mode keybindings
are installed on top of the major mode bindings.  Use this
for modes derived from Text mode, like Mail mode."
Stefan Monnier's avatar
Stefan Monnier committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
 ;; The initial value.
 nil
 ;; The indicator for the mode line.
 " ReST"
 ;; The minor mode bindings.
 rst-mode-map
 :group 'rst)

;; FIXME: can I somehow install these too?
;;  :abbrev-table rst-mode-abbrev-table
;;  :syntax-table rst-mode-syntax-table





;; Bulleted item lists.
(defcustom rst-bullets
  '(?- ?* ?+)
  "List of all possible bullet characters for bulleted lists."
  :group 'rst)




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Paul Eggert's avatar
Paul Eggert committed
479 480
;; Section Decoration Adjustment
;; =============================
Stefan Monnier's avatar
Stefan Monnier committed
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
;;
;; The following functions implement a smart automatic title sectioning feature.
;; The idea is that with the cursor sitting on a section title, we try to get as
;; much information from context and try to do the best thing automatically.
;; This function can be invoked many times and/or with prefix argument to rotate
;; between the various sectioning decorations.
;;
;; Definitions: the two forms of sectioning define semantically separate section
;; levels.  A sectioning DECORATION consists in:
;;
;;   - a CHARACTER
;;
;;   - a STYLE which can be either of 'simple' or 'over-and-under'.
;;
;;   - an INDENT (meaningful for the over-and-under style only) which determines
;;     how many characters and over-and-under style is hanging outside of the
;;     title at the beginning and ending.
;;
;; Important note: an existing decoration must be formed by at least two
;; characters to be recognized.
;;
;; Here are two examples of decorations (| represents the window border, column
;; 0):
;;
;;                                  |
;; 1. char: '-'   e                 |Some Title
;;    style: simple                 |----------
;;                                  |
;; 2. char: '='                     |==============
;;    style: over-and-under         |  Some Title
;;    indent: 2                     |==============
;;                                  |
;;
;; Some notes:
;;
;; - The underlining character that is used depends on context. The file is
;;   scanned to find other sections and an appropriate character is selected.
;;   If the function is invoked on a section that is complete, the character is
;;   rotated among the existing section decorations.
;;
;;   Note that when rotating the characters, if we come to the end of the
;;   hierarchy of decorations, the variable rst-preferred-decorations is
;;   consulted to propose a new underline decoration, and if continued, we cycle
;;   the decorations all over again.  Set this variable to nil if you want to
;;   limit the underlining character propositions to the existing decorations in
;;   the file.
;;
;; - A prefix argument can be used to alternate the style.
;;
;; - An underline/overline that is not extended to the column at which it should
;;   be hanging is dubbed INCOMPLETE.  For example::
;;
;;      |Some Title
;;      |-------
;;
;; Examples of default invocation:
;;
;;   |Some Title       --->    |Some Title
;;   |                         |----------
;;
;;   |Some Title       --->    |Some Title
;;   |-----                    |----------
;;
;;   |                         |------------
;;   | Some Title      --->    | Some Title
;;   |                         |------------
;;
;; In over-and-under style, when alternating the style, a variable is
;; available to select how much default indent to use (it can be zero).  Note
;; that if the current section decoration already has an indent, we don't
;; adjust it to the default, we rather use the current indent that is already
;; there for adjustment (unless we cycle, in which case we use the indent
;; that has been found previously).

(defgroup rst-adjust nil
Juanma Barranquero's avatar
Juanma Barranquero committed
556
  "Settings for adjustment and cycling of section title decorations."
Stefan Monnier's avatar
Stefan Monnier committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
  :group 'rst
  :version "21.1")

(defcustom rst-preferred-decorations '( (?= over-and-under 1)
                                         (?= simple 0)
                                         (?- simple 0)
                                         (?~ simple 0)
                                         (?+ simple 0)
                                         (?` simple 0)
                                         (?# simple 0)
                                         (?@ simple 0) )
  "Preferred ordering of section title decorations.

This sequence is consulted to offer a new decoration suggestion
when we rotate the underlines at the end of the existing
hierarchy of characters, or when there is no existing section
title in the file."
  :group 'rst-adjust)


(defcustom rst-default-indent 1
  "Number of characters to indent the section title.

Juanma Barranquero's avatar
Juanma Barranquero committed
580
This is used for when toggling decoration styles, when switching
Stefan Monnier's avatar
Stefan Monnier committed
581 582 583 584 585 586 587 588 589 590 591 592 593
from a simple decoration style to a over-and-under decoration
style."
  :group 'rst-adjust)


(defvar rst-section-text-regexp "^[ \t]*\\S-*\\w\\S-*"
  "Regular expression for valid section title text.")


(defun rst-line-homogeneous-p (&optional accept-special)
  "Return true if the line is homogeneous.

Predicate that returns the unique char if the current line is
Juanma Barranquero's avatar
Juanma Barranquero committed
594 595 596
composed only of a single repeated non-whitespace character.
This returns the char even if there is whitespace at the
beginning of the line.
Stefan Monnier's avatar
Stefan Monnier committed
597 598 599

If ACCEPT-SPECIAL is specified we do not ignore special sequences
which normally we would ignore when doing a search on many lines.
Juanma Barranquero's avatar
Juanma Barranquero committed
600
For example, normally we have cases to ignore commonly occurring
Stefan Monnier's avatar
Stefan Monnier committed
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
patterns, such as :: or ...; with the flag do not ignore them."
  (save-excursion
    (back-to-indentation)
    (unless (looking-at "\n")
      (let ((c (thing-at-point 'char)))
	(if (and (looking-at (format "[%s]+[ \t]*$" c))
		 (or accept-special
		     (and
		      ;; Common patterns.
		      (not (looking-at "::[ \t]*$"))
		      (not (looking-at "\\.\\.\\.[ \t]*$"))
		      ;; Discard one char line
		      (not (looking-at ".[ \t]*$"))
		      )))
	    (string-to-char c))
	))
    ))

(defun rst-line-homogeneous-nodent-p (&optional accept-special)
  "Return true if the line is homogeneous with no indent.
See `rst-line-homogeneous-p' about ACCEPT-SPECIAL."
  (save-excursion
    (beginning-of-line)
    (if (looking-at "^[ \t]+")
        nil
      (rst-line-homogeneous-p accept-special)
      )))


(defun rst-compare-decorations (deco1 deco2)
  "Compare decorations.
Juanma Barranquero's avatar
Juanma Barranquero committed
632
Return true if both DECO1 and DECO2 decorations are equal,
Stefan Monnier's avatar
Stefan Monnier committed
633
according to restructured text semantics (only the character and
Juanma Barranquero's avatar
Juanma Barranquero committed
634
the style are compared, the indentation does not matter)."
Stefan Monnier's avatar
Stefan Monnier committed
635 636 637 638 639 640 641
  (and (eq (car deco1) (car deco2))
       (eq (cadr deco1) (cadr deco2))))


(defun rst-get-decoration-match (hier deco)
  "Return the index (level) in hierarchy HIER of decoration DECO.
This basically just searches for the item using the appropriate
Juanma Barranquero's avatar
Juanma Barranquero committed
642
comparison and returns the index.  Return nil if the item is
Stefan Monnier's avatar
Stefan Monnier committed
643 644 645 646 647 648 649 650 651 652
not found."
  (let ((cur hier))
    (while (and cur (not (rst-compare-decorations (car cur) deco)))
      (setq cur (cdr cur)))
    cur))


(defun rst-suggest-new-decoration (alldecos &optional prev)
  "Suggest a new, different decoration from all that have been seen.

Juanma Barranquero's avatar
Juanma Barranquero committed
653 654 655
ALLDECOS is the set of all decorations, including the line numbers.
PREV is the optional previous decoration, in order to suggest a
better match."
Stefan Monnier's avatar
Stefan Monnier committed
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

  ;; For all the preferred decorations...
  (let* (
         ;; If 'prev' is given, reorder the list to start searching after the
         ;; match.
         (fplist
          (cdr (rst-get-decoration-match rst-preferred-decorations prev)))

         ;; List of candidates to search.
         (curpotential (append fplist rst-preferred-decorations)))
    (while
        ;; For all the decorations...
        (let ((cur alldecos)
              found)
          (while (and cur (not found))
            (if (rst-compare-decorations (car cur) (car curpotential))
                ;; Found it!
                (setq found (car curpotential))
              (setq cur (cdr cur))))
          found)

      (setq curpotential (cdr curpotential)))

Stefan Monnier's avatar
Stefan Monnier committed
679
    (copy-sequence (car curpotential))))
Stefan Monnier's avatar
Stefan Monnier committed
680 681 682

(defun rst-delete-entire-line ()
  "Delete the entire current line without using the `kill-ring'."
Stefan Monnier's avatar
Stefan Monnier committed
683 684
  (delete-region (line-beginning-position)
                 (line-beginning-position 2)))
Stefan Monnier's avatar
Stefan Monnier committed
685 686 687 688

(defun rst-update-section (char style &optional indent)
  "Unconditionally update the style of a section decoration.

Juanma Barranquero's avatar
Juanma Barranquero committed
689 690 691 692
Do this using the given character CHAR, with STYLE 'simple
or 'over-and-under, and with indent INDENT.  If the STYLE
is 'simple, whitespace before the title is removed (indent
is always assumed to be 0).
Stefan Monnier's avatar
Stefan Monnier committed
693 694 695 696 697 698 699

If there are existing overline and/or underline from the
existing decoration, they are removed before adding the
requested decoration."

  (interactive)
      (end-of-line)
700 701
  (let ((marker (point-marker))
        len)
Stefan Monnier's avatar
Stefan Monnier committed
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787

      ;; Fixup whitespace at the beginning and end of the line
      (if (or (null indent) (eq style 'simple))
          (setq indent 0))
      (beginning-of-line)
      (delete-horizontal-space)
      (insert (make-string indent ? ))

      (end-of-line)
      (delete-horizontal-space)

      ;; Set the current column, we're at the end of the title line
      (setq len (+ (current-column) indent))

      ;; Remove previous line if it consists only of a single repeated character
      (save-excursion
        (forward-line -1)
        (and (rst-line-homogeneous-p 1)
             ;; Avoid removing the underline of a title right above us.
             (save-excursion (forward-line -1)
                             (not (looking-at rst-section-text-regexp)))
             (rst-delete-entire-line)))

      ;; Remove following line if it consists only of a single repeated
      ;; character
      (save-excursion
        (forward-line +1)
        (and (rst-line-homogeneous-p 1)
             (rst-delete-entire-line))
        ;; Add a newline if we're at the end of the buffer, for the subsequence
        ;; inserting of the underline
        (if (= (point) (buffer-end 1))
            (newline 1)))

      ;; Insert overline
      (if (eq style 'over-and-under)
          (save-excursion
            (beginning-of-line)
            (open-line 1)
            (insert (make-string len char))))

      ;; Insert underline
      (forward-line +1)
      (open-line 1)
      (insert (make-string len char))

      (forward-line +1)
      (goto-char marker)
      ))


(defun rst-normalize-cursor-position ()
  "Normalize the cursor position.
If the cursor is on a decoration line or an empty line , place it
on the section title line (at the end).  Returns the line offset
by which the cursor was moved.  This works both over or under a
line."
  (if (save-excursion (beginning-of-line)
                      (or (rst-line-homogeneous-p 1)
                          (looking-at "^[ \t]*$")))
      (progn
        (beginning-of-line)
        (cond
         ((save-excursion (forward-line -1)
                          (beginning-of-line)
                          (and (looking-at rst-section-text-regexp)
                               (not (rst-line-homogeneous-p 1))))
          (progn (forward-line -1) -1))
         ((save-excursion (forward-line +1)
                          (beginning-of-line)
                          (and (looking-at rst-section-text-regexp)
                               (not (rst-line-homogeneous-p 1))))
          (progn (forward-line +1) +1))
         (t 0)))
    0 ))


(defun rst-find-all-decorations ()
  "Find all the decorations in the file.
Return a list of (line, decoration) pairs.  Each decoration
consists in a (char, style, indent) triple.

This function does not detect the hierarchy of decorations, it
just finds all of them in a file.  You can then invoke another
function to remove redundancies and inconsistencies."

788
  (let ((positions ())
Stefan Monnier's avatar
Stefan Monnier committed
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
        (curline 1))
    ;; Iterate over all the section titles/decorations in the file.
    (save-excursion
      (goto-char (point-min))
      (while (< (point) (buffer-end 1))
        (if (rst-line-homogeneous-nodent-p)
            (progn
              (setq curline (+ curline (rst-normalize-cursor-position)))

              ;; Here we have found a potential site for a decoration,
              ;; characterize it.
              (let ((deco (rst-get-decoration)))
                (if (cadr deco) ;; Style is existing.
                    ;; Found a real decoration site.
                    (progn
                      (push (cons curline deco) positions)
                      ;; Push beyond the underline.
                      (forward-line 1)
                      (setq curline (+ curline 1))
                      )))
              ))
        (forward-line 1)
        (setq curline (+ curline 1))
        ))
    (reverse positions)))


(defun rst-infer-hierarchy (decorations)
  "Build a hierarchy of decorations using the list of given DECORATIONS.

This function expects a list of (char, style, indent) decoration
specifications, in order that they appear in a file, and will
infer a hierarchy of section levels by removing decorations that
have already been seen in a forward traversal of the decorations,
comparing just the character and style.

Similarly returns a list of (char, style, indent), where each
list element should be unique."

  (let ((hierarchy-alist (list)))
    (dolist (x decorations)
      (let ((char (car x))
            (style (cadr x)))
        (unless (assoc (cons char style) hierarchy-alist)
	  (push (cons (cons char style) x) hierarchy-alist))
        ))

    (mapcar 'cdr (nreverse hierarchy-alist))
    ))


(defun rst-get-hierarchy (&optional alldecos ignore)
  "Return the hierarchy of section titles in the file.

Return a list of decorations that represents the hierarchy of
section titles in the file.  Reuse the list of decorations
already computed in ALLDECOS if present.  If the line number in
IGNORE is specified, the decoration found on that line (if there
is one) is not taken into account when building the hierarchy."
  (let ((all (or alldecos (rst-find-all-decorations))))
    (setq all (assq-delete-all ignore all))
    (rst-infer-hierarchy (mapcar 'cdr all))))


(defun rst-get-decoration (&optional point)
  "Get the decoration at POINT.

Looks around point and finds the characteristics of the
Juanma Barranquero's avatar
Juanma Barranquero committed
857
decoration that is found there.  Assumes that the cursor is
Stefan Monnier's avatar
Stefan Monnier committed
858 859 860 861
already placed on the title line (and not on the overline or
underline).

This function returns a (char, style, indent) triple.  If the
Juanma Barranquero's avatar
Juanma Barranquero committed
862 863 864
characters of overline and underline are different, return
the underline character.  The indent is always calculated.
A decoration can be said to exist if the style is not nil.
Stefan Monnier's avatar
Stefan Monnier committed
865 866 867 868

A point can be specified to go to the given location before
extracting the decoration."

869
  (let (char style)
Stefan Monnier's avatar
Stefan Monnier committed
870 871 872 873 874 875 876 877
    (save-excursion
      (if point (goto-char point))
      (beginning-of-line)
      (if (looking-at rst-section-text-regexp)
          (let* ((over (save-excursion
                         (forward-line -1)
                         (rst-line-homogeneous-nodent-p)))

878 879 880 881
                 (under (save-excursion
                          (forward-line +1)
                          (rst-line-homogeneous-nodent-p)))
                 )
Stefan Monnier's avatar
Stefan Monnier committed
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908

            ;; Check that the line above the overline is not part of a title
            ;; above it.
            (if (and over
                     (save-excursion
                       (and (equal (forward-line -2) 0)
                            (looking-at rst-section-text-regexp))))
                (setq over nil))

            (cond
             ;; No decoration found, leave all return values nil.
             ((and (eq over nil) (eq under nil)))

             ;; Overline only, leave all return values nil.
             ;;
             ;; Note: we don't return the overline character, but it could
             ;; perhaps in some cases be used to do something.
             ((and over (eq under nil)))

             ;; Underline only.
             ((and under (eq over nil))
              (setq char under
                    style 'simple))

             ;; Both overline and underline.
             (t
              (setq char under
909 910 911 912 913
                    style 'over-and-under)))))
      ;; Return values.
      (list char style
            ;; Find indentation.
            (save-excursion (back-to-indentation) (current-column))))))
Stefan Monnier's avatar
Stefan Monnier committed
914 915 916 917 918


(defun rst-get-decorations-around (&optional alldecos)
  "Return the decorations around point.

Juanma Barranquero's avatar
Juanma Barranquero committed
919 920 921
Given the list of all decorations ALLDECOS (with positions),
find the decorations before and after the given point.
A list of the previous and next decorations is returned."
Stefan Monnier's avatar
Stefan Monnier committed
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
  (let* ((all (or alldecos (rst-find-all-decorations)))
         (curline (line-number-at-pos))
         prev next
         (cur all))

    ;; Search for the decorations around the current line.
    (while (and cur (< (caar cur) curline))
      (setq prev cur
            cur (cdr cur)))
    ;; 'cur' is the following decoration.

    (if (and cur (caar cur))
        (setq next (if (= curline (caar cur)) (cdr cur) cur)))

    (mapcar 'cdar (list prev next))
    ))


(defun rst-decoration-complete-p (deco)
Juanma Barranquero's avatar
Juanma Barranquero committed
941
  "Return true if the decoration DECO around point is complete."
Stefan Monnier's avatar
Stefan Monnier committed
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
  ;; Note: we assume that the detection of the overline as being the underline
  ;; of a preceding title has already been detected, and has been eliminated
  ;; from the decoration that is given to us.

  ;; There is some sectioning already present, so check if the current
  ;; sectioning is complete and correct.
  (let* ((char (car deco))
         (style (cadr deco))
         (indent (caddr deco))
         (endcol (save-excursion (end-of-line) (current-column)))
         )
    (if char
        (let ((exps (concat "^"
                            (regexp-quote (make-string (+ endcol indent) char))
                            "$")))
          (and
           (save-excursion (forward-line +1)
                           (beginning-of-line)
                           (looking-at exps))
           (or (not (eq style 'over-and-under))
               (save-excursion (forward-line -1)
                               (beginning-of-line)
                               (looking-at exps))))
          ))
    ))


(defun rst-get-next-decoration
  (curdeco hier &optional suggestion reverse-direction)
  "Get the next decoration for CURDECO, in given hierarchy HIER.
If suggesting, suggest for new decoration SUGGESTION.
REVERSE-DIRECTION is used to reverse the cycling order."

  (let* (
         (char (car curdeco))
         (style (cadr curdeco))

         ;; Build a new list of decorations for the rotation.
         (rotdecos
          (append hier
                  ;; Suggest a new decoration.
                  (list suggestion
                        ;; If nothing to suggest, use first decoration.
                        (car hier)))) )
    (or
     ;; Search for next decoration.
     (cadr
      (let ((cur (if reverse-direction rotdecos
                   (reverse rotdecos))))
        (while (and cur
                    (not (and (eq char (caar cur))
                              (eq style (cadar cur)))))
          (setq cur (cdr cur)))
        cur))

     ;; If not found, take the first of all decorations.
     suggestion
     )))


(defun rst-adjust ()
  "Auto-adjust the decoration around point.

Adjust/rotate the section decoration for the section title
around point or promote/demote the decorations inside the region,
depending on if the region is active.  This function is meant to
Glenn Morris's avatar
Glenn Morris committed
1008
be invoked possibly multiple times, and can vary its behavior
Stefan Monnier's avatar
Stefan Monnier committed
1009
with a positive prefix argument (toggle style), or with a
Glenn Morris's avatar
Glenn Morris committed
1010
negative prefix argument (alternate behavior).
Stefan Monnier's avatar
Stefan Monnier committed
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

This function is the main focus of this module and is a bit of a
swiss knife.  It is meant as the single most essential function
to be bound to invoke to adjust the decorations of a section
title in restructuredtext.  It tries to deal with all the
possible cases gracefully and to do `the right thing' in all
cases.

See the documentations of `rst-adjust-decoration' and
`rst-promote-region' for full details.

Prefix Arguments
================

The method can take either (but not both) of

a. a (non-negative) prefix argument, which means to toggle the
   decoration style.  Invoke with a prefix arg for example;

b. a negative numerical argument, which generally inverts the
   direction of search in the file or hierarchy.  Invoke with C--
   prefix for example."
  (interactive)

  (let* (;; Save our original position on the current line.
1036
	 (origpt (point-marker))
Stefan Monnier's avatar
Stefan Monnier committed
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062

	 ;; Parse the positive and negative prefix arguments.
         (reverse-direction
          (and current-prefix-arg
               (< (prefix-numeric-value current-prefix-arg) 0)))
         (toggle-style
          (and current-prefix-arg (not reverse-direction))))

    (if (rst-portable-mark-active-p)
        ;; Adjust decorations within region.
        (rst-promote-region current-prefix-arg)
      ;; Adjust decoration around point.
      (rst-adjust-decoration toggle-style reverse-direction))

    ;; Run the hooks to run after adjusting.
    (run-hooks 'rst-adjust-hook)

    ;; Make sure to reset the cursor position properly after we're done.
    (goto-char origpt)

    ))

(defvar rst-adjust-hook nil
  "Hooks to be run after running `rst-adjust'.")

(defvar rst-new-decoration-down nil
Stefan Monnier's avatar
Stefan Monnier committed
1063 1064 1065 1066 1067
  "Non-nil if new decoration is added deeper.
If non-nil, a new decoration being added will be initialized to
be one level down from the previous decoration.  If nil, a new
decoration will be equal to the level of the previous
decoration.")
Stefan Monnier's avatar
Stefan Monnier committed
1068 1069 1070 1071 1072

(defun rst-adjust-decoration (&optional toggle-style reverse-direction)
"Adjust/rotate the section decoration for the section title around point.

This function is meant to be invoked possibly multiple times, and
Glenn Morris's avatar
Glenn Morris committed
1073
can vary its behavior with a true TOGGLE-STYLE argument, or with
Stefan Monnier's avatar
Stefan Monnier committed
1074 1075
a REVERSE-DIRECTION argument.

Juanma Barranquero's avatar
Juanma Barranquero committed
1076 1077
General Behavior
================
Stefan Monnier's avatar
Stefan Monnier committed
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114

The next action it takes depends on context around the point, and
it is meant to be invoked possibly more than once to rotate among
the various possibilities.  Basically, this function deals with:

- adding a decoration if the title does not have one;

- adjusting the length of the underline characters to fit a
  modified title;

- rotating the decoration in the set of already existing
  sectioning decorations used in the file;

- switching between simple and over-and-under styles.

You should normally not have to read all the following, just
invoke the method and it will do the most obvious thing that you
would expect.


Decoration Definitions
======================

The decorations consist in

1. a CHARACTER

2. a STYLE which can be either of 'simple' or 'over-and-under'.

3. an INDENT (meaningful for the over-and-under style only)
   which determines how many characters and over-and-under
   style is hanging outside of the title at the beginning and
   ending.

See source code for mode details.


Juanma Barranquero's avatar
Juanma Barranquero committed
1115 1116
Detailed Behavior Description
=============================
Stefan Monnier's avatar
Stefan Monnier committed
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176

Here are the gory details of the algorithm (it seems quite
complicated, but really, it does the most obvious thing in all
the particular cases):

Before applying the decoration change, the cursor is placed on
the closest line that could contain a section title.

Case 1: No Decoration
---------------------

If the current line has no decoration around it,

- search backwards for the last previous decoration, and apply
  the decoration one level lower to the current line.  If there
  is no defined level below this previous decoration, we suggest
  the most appropriate of the `rst-preferred-decorations'.

  If REVERSE-DIRECTION is true, we simply use the previous
  decoration found directly.

- if there is no decoration found in the given direction, we use
  the first of `rst-preferred-decorations'.

The prefix argument forces a toggle of the prescribed decoration
style.

Case 2: Incomplete Decoration
-----------------------------

If the current line does have an existing decoration, but the
decoration is incomplete, that is, the underline/overline does
not extend to exactly the end of the title line (it is either too
short or too long), we simply extend the length of the
underlines/overlines to fit exactly the section title.

If the prefix argument is given, we toggle the style of the
decoration as well.

REVERSE-DIRECTION has no effect in this case.

Case 3: Complete Existing Decoration
------------------------------------

If the decoration is complete (i.e. the underline (overline)
length is already adjusted to the end of the title line), we
search/parse the file to establish the hierarchy of all the
decorations (making sure not to include the decoration around
point), and we rotate the current title's decoration from within
that list (by default, going *down* the hierarchy that is present
in the file, i.e. to a lower section level).  This is meant to be
used potentially multiple times, until the desired decoration is
found around the title.

If we hit the boundary of the hierarchy, exactly one choice from
the list of preferred decorations is suggested/chosen, the first
of those decoration that has not been seen in the file yet (and
not including the decoration around point), and the next
invocation rolls over to the other end of the hierarchy (i.e. it
cycles).  This allows you to avoid having to set which character
Juanma Barranquero's avatar
Juanma Barranquero committed
1177
to use.
Stefan Monnier's avatar
Stefan Monnier committed
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205

If REVERSE-DIRECTION is true, the effect is to change the
direction of rotation in the hierarchy of decorations, thus
instead going *up* the hierarchy.

However, if there is a non-negative prefix argument, we do not
rotate the decoration, but instead simply toggle the style of the
current decoration (this should be the most common way to toggle
the style of an existing complete decoration).


Point Location
==============

The invocation of this function can be carried out anywhere
within the section title line, on an existing underline or
overline, as well as on an empty line following a section title.
This is meant to be as convenient as possible.


Indented Sections
=================

Indented section titles such as ::

   My Title
   --------

Juanma Barranquero's avatar
Juanma Barranquero committed
1206
are invalid in restructuredtext and thus not recognized by the
Stefan Monnier's avatar
Stefan Monnier committed
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
parser.  This code will thus not work in a way that would support
indented sections (it would be ambiguous anyway).


Joint Sections
==============

Section titles that are right next to each other may not be
treated well.  More work might be needed to support those, and
special conditions on the completeness of existing decorations
might be required to make it non-ambiguous.

For now we assume that the decorations are disjoint, that is,
there is at least a single line between the titles/decoration
lines.


Suggested Binding
=================

We suggest that you bind this function on C-=.  It is close to
C-- so a negative argument can be easily specified with a flick
of the right hand fingers and the binding is unused in `text-mode'."
  (interactive)

  ;; If we were invoked directly, parse the prefix arguments into the
  ;; arguments of the function.
  (if current-prefix-arg
      (setq reverse-direction
            (and current-prefix-arg
                 (< (prefix-numeric-value current-prefix-arg) 0))

            toggle-style
            (and current-prefix-arg (not reverse-direction))))

  (let* (;; Check if we're on an underline around a section title, and move the
         ;; cursor to the title if this is the case.
         (moved (rst-normalize-cursor-position))

         ;; Find the decoration and completeness around point.
         (curdeco (rst-get-decoration))
         (char (car curdeco))
         (style (cadr curdeco))
         (indent (caddr curdeco))

         ;; New values to be computed.
         char-new style-new indent-new
         )

    ;; We've moved the cursor... if we're not looking at some text, we have
    ;; nothing to do.
    (if (save-excursion (beginning-of-line)
                        (looking-at rst-section-text-regexp))
        (progn
          (cond
           ;;-------------------------------------------------------------------
           ;; Case 1: No Decoration
           ((and (eq char nil) (eq style nil))

            (let* ((alldecos (rst-find-all-decorations))

                   (around (rst-get-decorations-around alldecos))
                   (prev (car around))
                   cur

                   (hier (rst-get-hierarchy alldecos))
                   )

              ;; Advance one level down.
              (setq cur
                    (if prev
                        (if (not reverse-direction)
                            (or (funcall (if rst-new-decoration-down 'cadr 'car)
					 (rst-get-decoration-match hier prev))
                                (rst-suggest-new-decoration hier prev))
                          prev)
Stefan Monnier's avatar
Stefan Monnier committed
1283
                      (copy-sequence (car rst-preferred-decorations))))
Stefan Monnier's avatar
Stefan Monnier committed
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364

              ;; Invert the style if requested.
              (if toggle-style
                  (setcar (cdr cur) (if (eq (cadr cur) 'simple)
                                        'over-and-under 'simple)) )

              (setq char-new (car cur)
                    style-new (cadr cur)
                    indent-new (caddr cur))
              ))

           ;;-------------------------------------------------------------------
           ;; Case 2: Incomplete Decoration
           ((not (rst-decoration-complete-p curdeco))

            ;; Invert the style if requested.
            (if toggle-style
                (setq style (if (eq style 'simple) 'over-and-under 'simple)))

            (setq char-new char
                  style-new style
                  indent-new indent))

           ;;-------------------------------------------------------------------
           ;; Case 3: Complete Existing Decoration
           (t
            (if toggle-style

                ;; Simply switch the style of the current decoration.
                (setq char-new char
                      style-new (if (eq style 'simple) 'over-and-under 'simple)
                      indent-new rst-default-indent)

              ;; Else, we rotate, ignoring the decoration around the current
              ;; line...
              (let* ((alldecos (rst-find-all-decorations))

                     (hier (rst-get-hierarchy alldecos (line-number-at-pos)))

                     ;; Suggestion, in case we need to come up with something
                     ;; new
                     (suggestion (rst-suggest-new-decoration
                                  hier
                                  (car (rst-get-decorations-around alldecos))))

                     (nextdeco (rst-get-next-decoration
                                curdeco hier suggestion reverse-direction))

                     )

                ;; Indent, if present, always overrides the prescribed indent.
                (setq char-new (car nextdeco)
                      style-new (cadr nextdeco)
                      indent-new (caddr nextdeco))

                )))
           )

          ;; Override indent with present indent!
          (setq indent-new (if (> indent 0) indent indent-new))

          (if (and char-new style-new)
              (rst-update-section char-new style-new indent-new))
          ))


    ;; Correct the position of the cursor to more accurately reflect where it
    ;; was located when the function was invoked.
    (unless (= moved 0)
      (forward-line (- moved))
      (end-of-line))

    ))

;; Maintain an alias for compatibility.
(defalias 'rst-adjust-section-title 'rst-adjust)


(defun rst-promote-region (&optional demote)
  "Promote the section titles within the region.

1365 1366 1367
With argument DEMOTE or a prefix argument, demote the section
titles instead.  The algorithm used at the boundaries of the
hierarchy is similar to that used by `rst-adjust-decoration'."
Stefan Monnier's avatar
Stefan Monnier committed
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
  (interactive)

  (let* ((demote (or current-prefix-arg demote))
         (alldecos (rst-find-all-decorations))
         (cur alldecos)

         (hier (rst-get-hierarchy alldecos))
         (suggestion (rst-suggest-new-decoration hier))

         (region-begin-line (line-number-at-pos (region-beginning)))
         (region-end-line (line-number-at-pos (region-end)))

         marker-list
         )

    ;; Skip the markers that come before the region beginning
    (while (and cur (< (caar cur) region-begin-line))
      (setq cur (cdr cur)))

    ;; Create a list of markers for all the decorations which are found within
    ;; the region.
    (save-excursion
1390
      (let (line)
Stefan Monnier's avatar
Stefan Monnier committed
1391
        (while (and cur (< (setq line (caar cur)) region-end-line))
1392 1393
          (goto-char (point-min))
          (forward-line (1- line))
1394
          (push (list (point-marker) (cdar cur)) marker-list)
Stefan Monnier's avatar
Stefan Monnier committed
1395 1396 1397
          (setq cur (cdr cur)) ))

      ;; Apply modifications.
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409
      (dolist (p marker-list)
        ;; Go to the decoration to promote.
        (goto-char (car p))

        ;; Update the decoration.
        (apply 'rst-update-section
               ;; Rotate the next decoration.
               (rst-get-next-decoration
                (cadr p) hier suggestion demote))

        ;; Clear marker to avoid slowing down the editing after we're done.
        (set-marker (car p) nil))
Stefan Monnier's avatar
Stefan Monnier committed
1410
      (setq deactivate-mark nil)
1411
      )))
Stefan Monnier's avatar
Stefan Monnier committed
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434



(defun rst-display-decorations-hierarchy (&optional decorations)
  "Display the current file's section title decorations hierarchy.
This function expects a list of (char, style, indent) triples in
DECORATIONS."
  (interactive)

  (if (not decorations)
      (setq decorations (rst-get-hierarchy)))
  (with-output-to-temp-buffer "*rest section hierarchy*"
    (let ((level 1))
      (with-current-buffer standard-output
        (dolist (x decorations)
          (insert (format "\nSection Level %d" level))
          (apply 'rst-update-section x)
          (goto-char (point-max))
          (insert "\n")
          (incf level)
          ))
    )))

Stefan Monnier's avatar
Stefan Monnier committed
1435 1436 1437 1438 1439
(defun rst-position (elem list)
  "Return position of ELEM in LIST or nil."
  (let ((tail (member elem list)))
    (if tail (- (length list) (length tail)))))

Stefan Monnier's avatar
Stefan Monnier committed
1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
(defun rst-straighten-decorations ()
  "Redo all the decorations in the current buffer.
This is done using our preferred set of decorations.  This can be
used, for example, when using somebody else's copy of a document,
in order to adapt it to our preferred style."
  (interactive)
  (save-excursion
    (let* ((alldecos (rst-find-all-decorations))
	   (hier (rst-get-hierarchy alldecos))

	   ;; Get a list of pairs of (level . marker)
	   (levels-and-markers (mapcar
				(lambda (deco)
Stefan Monnier's avatar
Stefan Monnier committed
1453
				  (cons (rst-position (cdr deco) hier)
1454
					(progn
1455 1456
					  (goto-char (point-min))
					  (forward-line (1- (car deco)))
1457
                                          (point-marker))))
Stefan Monnier's avatar
Stefan Monnier committed
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
				alldecos))
	   )
      (dolist (lm levels-and-markers)
	;; Go to the appropriate position
	(goto-char (cdr lm))

	;; Apply the new styule
	(apply 'rst-update-section (nth (car lm) rst-preferred-decorations))

	;; Reset the market to avoid slowing down editing until it gets GC'ed
	(set-marker (cdr lm) nil)
	)
    )))




(defun rst-straighten-deco-spacing ()
  "Adjust the spacing before and after decorations in the entire document.
The spacing will be set to two blank lines before the first two
section levels, and one blank line before any of the other
section levels."
;; FIXME: we need to take care of subtitle at some point.
  (interactive)
  (save-excursion
    (let* ((alldecos (rst-find-all-decorations)))

      ;; Work the list from the end, so that we don't have to use markers to
      ;; adjust for the changes in the document.
      (dolist (deco (nreverse alldecos))
	;; Go to the appropriate position.
1489 1490
	(goto-char (point-min))
	(forward-line (1- (car deco)))
Stefan Monnier's avatar
Stefan Monnier committed
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500
	(insert "@\n")
;; FIXME: todo, we
	)
    )))


(defun rst-find-pfx-in-region (beg end pfx-re)
  "Find all the positions of prefixes in region between BEG and END.
This is used to find bullets and enumerated list items.  PFX-RE
is a regular expression for matching the lines with items."
1501
  (let ((pfx ()))
Stefan Monnier's avatar
Stefan Monnier committed
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515
    (save-excursion
      (goto-char beg)
      (while (< (point) end)
	(back-to-indentation)
	(when (and
	       (looking-at pfx-re)
	       (let ((pfx-col (current-column)))
		 (save-excursion
		   (forward-line -1)
		   (back-to-indentation)
		   (or (looking-at "^[ \t]*$")
		       (> (current-column) pfx-col)
		       (and (= (current-column) pfx-col)
			    (looking-at pfx-re))))))
Stefan Monnier's avatar
Stefan Monnier committed
1516 1517
	  (push (cons (point) (current-column))
                pfx))
Stefan Monnier's avatar
Stefan Monnier committed
1518 1519 1520 1521 1522 1523 1524
	(forward-line 1)) )
    (nreverse pfx)))

(defvar rst-re-bullets
  (format "\\([%s][ \t]\\)[^ \t]" (regexp-quote (concat rst-bullets)))
  "Regexp for finding bullets.")

Juanma Barranquero's avatar
Juanma Barranquero committed
1525 1526 1527
;; (defvar rst-re-enumerations
;;   "\\(\\(#\\|[0-9]+\\)\\.[ \t]\\)[^ \t]"
;;   "Regexp for finding bullets.")
Stefan Monnier's avatar
Stefan Monnier committed
1528 1529 1530 1531 1532 1533 1534 1535 1536

(defvar rst-re-items
  (format "\\(%s\\|%s\\)[^ \t]"
	  (format "[%s][ \t]" (regexp-quote (concat rst-bullets)))
	  "\\(#\\|[0-9]+\\)\\.[ \t]")
  "Regexp for finding bullets.")

(defvar rst-preferred-bullets
  '(?- ?* ?+)
Paul Eggert's avatar
Paul Eggert committed
1537
  "List of favorite bullets to set for straightening bullets.")
Stefan Monnier's avatar
Stefan Monnier committed
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562

(defun rst-straighten-bullets-region (beg end)
  "Make all the bulleted list items in the region consistent.
The region is specified between BEG and END.  You can use this
after you have merged multiple bulleted lists to make them use
the same/correct/consistent bullet characters.

See variable `rst-preferred-bullets' for the list of bullets to
adjust.  If bullets are found on levels beyond the
`rst-preferred-bullets' list, they are not modified."
  (interactive "r")

  (let ((bullets (rst-find-pfx-in-region beg end
					 rst-re-bullets))
	(levtable (make-hash-table :size 4)))

    ;; Create a map of levels to list of positions.
    (dolist (x bullets)
      (let ((key (cdr x)))
	(puthash key
		  (append (gethash key levtable (list))
			  (list (car x)))
		  levtable)))

    ;; Sort this map and create a new map of prefix char and list of positions.
Stefan Monnier's avatar
Stefan Monnier committed
1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574
    (let ((poslist ()))                 ; List of (indent . positions).
      (maphash (lambda (x y) (push (cons x y) poslist)) levtable)

      (let ((bullets rst-preferred-bullets))
        (dolist (x (sort poslist 'car-less-than-car))
          (when bullets
            ;; Apply the characters.
            (dolist (pos (cdr x))
              (goto-char pos)
              (delete-char 1)
              (insert (string (car bullets))))
            (setq bullets (cdr bullets))))))))
Stefan Monnier's avatar
Stefan Monnier committed
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601

(defun rst-rstrip (str)
  "Strips the whitespace at the end of string STR."
  (string-match "[ \t\n]*\\'" str)
  (substring str 0 (match-beginning 0)))

(defun rst-get-stripped-line ()
  "Return the line at cursor, stripped from whitespace."
  (re-search-forward "\\S-.*\\S-" (line-end-position))
  (buffer-substring-no-properties (match-beginning 0)
                                  (match-end 0)) )

(defun rst-section-tree (alldecos)
  "Get the hierarchical tree of section titles.

Returns a hierarchical tree of the sections titles in the
document, for decorations ALLDECOS.  This can be used to generate
a table of contents for the document.  The top node will always
be a nil node, with the top level titles as children (there may
potentially be more than one).

Each section title consists in a cons of the stripped title
string and a marker to the section in the original text document.

If there are missing section levels, the section titles are
inserted automatically, and the title string is set to nil, and
the marker set to the first non-nil child of itself.
Juanma Barranquero's avatar
Juanma Barranquero committed
1602
Conceptually, the nil nodes--i.e. those which have no title--are
Stefan Monnier's avatar
Stefan Monnier committed
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620
to be considered as being the same line as their first non-nil
child.  This has advantages later in processing the graph."

  (let* ((hier (rst-get-hierarchy alldecos))
         (levels (make-hash-table :test 'equal :size 10))
         lines)

    (let ((lev 0))
      (dolist (deco hier)
	;; Compare just the character and indent in the hash table.
        (puthash (cons (car deco) (cadr deco)) lev levels)
        (incf lev)))

    ;; Create a list of lines that contains (text, level, marker) for each
    ;; decoration.
    (save-excursion
      (setq lines
            (mapcar (lambda (deco)
1621 1622
                      (goto-char (point-min))
                      (forward-line (1- (car deco)))
Stefan Monnier's avatar
Stefan Monnier committed
1623 1624
                      (list (gethash (cons (cadr deco) (caddr deco)) levels)
                            (rst-get-stripped-line)
1625
                            (progn
Stefan Monnier's avatar
Stefan Monnier committed
1626
                              (beginning-of-line 1)
1627
                              (point-marker))))
Stefan Monnier's avatar
Stefan Monnier committed
1628 1629 1630 1631 1632 1633 1634 1635 1636
                    alldecos)))

    (let ((lcontnr (cons nil lines)))
      (rst-section-tree-rec lcontnr -1))))


(defun rst-section-tree-rec (decos lev)
  "Recursive guts of the section tree construction.
DECOS is a cons cell whose cdr is the remaining list of
1637 1638 1639 1640
decorations, and we change it as we consume them.  LEV is
the current level of that node.  This function returns a
pair of the subtree that was built.  This treats the DECOS
list destructively."
Stefan Monnier's avatar
Stefan Monnier committed
1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675

  (let ((ndeco (cadr decos))
        node
        children)

    ;; If the next decoration matches our level
    (when (and ndeco (= (car ndeco) lev))
      ;; Pop the next decoration and create the current node with it
      (setcdr decos (cddr decos))
      (setq node (cdr ndeco)) )
    ;; Else we let the node title/marker be unset.

    ;; Build the child nodes
    (while (and (cdr decos) (> (caadr decos) lev))
      (setq children
            (cons (rst-section-tree-rec decos (1+ lev))
                  children)))
    (setq children (reverse children))

    ;; If node is still unset, we use the marker of the first child.
    (when (eq node nil)
      (setq node (cons nil (cdaar children))))

    ;; Return this node with its children.
    (cons node children)
    ))


(defun rst-section-tree-point (node &optional point)
  "Find tree node at point.
Given a computed and valid section tree in NODE and a point
POINT (default being the current point in the current buffer),
find and return the node within the sectree where the cursor
lives.

Juanma Barranquero's avatar
Juanma Barranquero committed
1676 1677 1678
Return values: a pair of (parent path, container subtree).
The parent path is simply a list of the nodes above the
container subtree node that we're returning."
Stefan Monnier's avatar
Stefan Monnier committed
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706

  (let (path outtree)

    (let* ((curpoint (or point (point))))

      ;; Check if we are before the current node.
      (if (and (cadar node) (>= curpoint (cadar node)))

	  ;; Iterate all the children, looking for one that might contain the
	  ;; current section.
	  (let ((curnode (cdr node))
		last)

	    (while (and curnode (>= curpoint (cadaar curnode)))
	      (setq last curnode
		    curnode (cdr curnode)))

	    (if last
		(let ((sub (rst-section-tree-point (car last) curpoint)))
		  (setq path (car sub)
			outtree (cdr sub)))
	      (setq outtree node))

	    )))
    (cons (cons (car node) path) outtree)
    ))


Stefan Monnier's avatar
Stefan Monnier committed
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742
(defgroup rst-toc nil
  "Settings for reStructuredText table of contents."
  :group 'rst
  :version "21.1")

(defcustom rst-toc-indent 2
  "Indentation for table-of-contents display.
Also used for formatting insertion, when numbering is disabled."
  :group 'rst-toc)

(defcustom rst-toc-insert-style 'fixed
  "Insertion style for table-of-contents.
Set this to one of the following values to determine numbering and
indentation style:
- plain: no numbering (fixed indentation)
- fixed: numbering, but fixed indentation
- aligned: numbering, titles aligned under each other
- listed: numbering, with dashes like list items (EXPERIMENTAL)"
  :group 'rst-toc)

(defcustom rst-toc-insert-number-separator "  "
  "Separator that goes between the TOC number and the title."
  :group 'rst-toc)

;; This is used to avoid having to change the user's mode.
(defvar rst-toc-insert-click-keymap
  (let ((map (make-sparse-keymap)))
       (define-key map [mouse-1] 'rst-toc-mode-mouse-goto)
       map)
  "(Internal) What happens when you click on propertized text in the TOC.")

(defcustom rst-toc-insert-max-level nil
  "If non-nil, maximum depth of the inserted TOC."
  :group 'rst-toc)


Stefan Monnier's avatar
Stefan Monnier committed
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775
(defun rst-toc-insert (&optional pfxarg)
  "Insert a simple text rendering of the table of contents.
By default the top level is ignored if there is only one, because
we assume that the document will have a single title.

If a numeric prefix argument PFXARG is given, insert the TOC up
to the specified level.

The TOC is inserted indented at the current column."

  (interactive "P")

  (let* (;; Check maximum level override
         (rst-toc-insert-max-level
          (if (and (integerp pfxarg) (> (prefix-numeric-value pfxarg) 0))
              (prefix-numeric-value pfxarg) rst-toc-insert-max-level))

         ;; Get the section tree for the current cursor point.
         (sectree-pair
	  (rst-section-tree-point
	   (rst-section-tree (rst-find-all-decorations))))

         ;; Figure out initial indent.
         (initial-indent (make-string (current-column) ? ))
         (init-point (point)))

    (when (cddr sectree-pair)
      (rst-toc-insert-node (cdr sectree-pair) 0 initial-indent "")

      ;; Fixup for the first line.
      (delete-region init-point (+ init-point (length initial-indent)))

      ;; Delete the last newline added.
1776
      (delete-char -1)
Stefan Monnier's avatar
Stefan Monnier committed
1777 1778 1779 1780
    )))

(defun rst-toc-insert-node (node level indent pfx)
  "Insert tree node NODE in table-of-contents.
Juanma Barranquero's avatar
Juanma Barranquero committed
1781 1782 1783 1784 1785
Recursive function that does printing of the inserted toc.
LEVEL is the depth level of the sections in the tree.
INDENT is the indentation string.  PFX is the prefix numbering,
that includes the alignment necessary for all the children of
level to align."
Stefan Monnier's avatar
Stefan Monnier committed
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855

  ;; Note: we do child numbering from the parent, so we start number the
  ;; children one level before we print them.
  (let ((do-print (> level 0))
        (count 1))
    (when do-print
      (insert indent)
      (let ((b (point)))
	(unless (equal rst-toc-insert-style 'plain)
	  (insert pfx rst-toc-insert-number-separator))
	(insert (or (caar node) "[missing node]"))
	;; Add properties to the text, even though in normal text mode it
	;; won't be doing anything for now.  Not sure that I want to change
	;; mode stuff.  At least the highlighting gives the idea that this
	;; is generated automatically.
	(put-text-property b (point) 'mouse-face 'highlight)
	(put-text-property b (point) 'rst-toc-target (cadar node))
	(put-text-property b (point) 'keymap rst-toc-insert-click-keymap)

	)
      (insert "\n")

      ;; Prepare indent for children.
      (setq indent
	    (cond
	     ((eq rst-toc-insert-style 'plain)
              (concat indent (make-string rst-toc-indent ? )))

	     ((eq rst-toc-insert-style 'fixed)
	      (concat indent (make-string rst-toc-indent ? )))

	     ((eq rst-toc-insert-style 'aligned)
	      (concat indent (make-string (+ (length pfx) 2) ? )))

	     ((eq rst-toc-insert-style 'listed)
	      (concat (substring indent 0 -3)
		      (concat (make-string (+ (length pfx) 2) ? ) " - ")))
	     ))
      )

    (if (or (eq rst-toc-insert-max-level nil)
            (< level rst-toc-insert-max-level))
        (let ((do-child-numbering (>= level 0))
              fmt)
          (if do-child-numbering
              (progn
                ;; Add a separating dot if there is already a prefix
                (if (> (length pfx) 0)
                    (setq pfx (concat (rst-rstrip pfx) ".")))

                ;; Calculate the amount of space that the prefix will require
                ;; for the numbers.
                (if (cdr node)
                    (setq fmt (format "%%-%dd"
                                      (1+ (floor (log10 (length
							 (cdr node))))))))
                ))

          (dolist (child (cdr node))
            (rst-toc-insert-node child
				 (1+ level)
				 indent
				 (if do-child-numbering
				     (concat pfx (format fmt count)) pfx))
            (incf count)))

      )))


(defun rst-toc-insert-find-delete-contents ()
Juanma Barranquero's avatar
Juanma Barranquero committed
1856
  "Find and delete an existing comment after the first contents directive.
Stefan Monnier's avatar
Stefan Monnier committed
1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938