├── .gitignore ├── adoc-mode-test.el └── adoc-mode.el /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.elc 3 | #*# 4 | TAGS 5 | -------------------------------------------------------------------------------- /adoc-mode-test.el: -------------------------------------------------------------------------------- 1 | ;;; adoc-mode-test.el --- test suite for adoc-mode.el 2 | ;;; 3 | ;;; Commentary: 4 | ;; 5 | ;; Call adoc-test-run to run the test suite 6 | ;; 7 | ;;; Todo: 8 | ;; - test for font lock multiline property 9 | ;; - test for presence of adoc-reserved (we do white-box testing here) 10 | ;; - test also with multiple versions of (X)Emacs 11 | ;; - compare adoc-mode fontification with actuall output from AsciiDoc, being 12 | ;; almost the ultimative test for correctness 13 | ;; 14 | 15 | ;;; Code: 16 | (require 'ert) 17 | (require 'adoc-mode) 18 | 19 | ;; todo: 20 | ;; - auto-create different contexts like 21 | ;; - beginning/end of buffer 22 | ;; - beginning/end of paragraph 23 | ;; - side-to-side yes/no with next same construct 24 | (defun adoctest-faces (name &rest args) 25 | (let ((not-done t) 26 | (font-lock-support-mode)) 27 | (with-temp-buffer 28 | ;; setup 29 | (while args 30 | (insert (propertize (car args) 'adoctest (cadr args))) 31 | (setq args (cddr args))) 32 | 33 | ;; exercise 34 | (adoc-mode) 35 | (font-lock-fontify-buffer) 36 | 37 | ;; verify 38 | (goto-char (point-min)) 39 | (while not-done 40 | (let* ((tmp (get-text-property (point) 'adoctest)) 41 | (tmp2 (get-text-property (point) 'face))) 42 | (cond 43 | ((null tmp)) ; nop 44 | ((eq tmp 'no-face) 45 | (should (null tmp2))) 46 | (t 47 | (if (and (listp tmp2) (not (listp tmp))) 48 | (should (and (= 1 (length tmp2)) (equal tmp (car tmp2)))) 49 | (should (equal tmp tmp2))))) 50 | (if (< (point) (point-max)) 51 | (forward-char 1) 52 | (setq not-done nil))))))) 53 | 54 | (defun adoctest-trans (original-text expected-text transform) 55 | "Calling TRANSFORM on EXPECTED-TEXT, ORIGINAL-TEXT `should' result. 56 | ORIGINAL-TEXT is put in an temporary buffer and TRANSFORM is 57 | evaluated using `eval'. The resulting buffer content is compared 58 | to EXPECTED-TEXT. 59 | 60 | ORIGINAL-TEXT optionaly may contain the following special 61 | charachters. Escaping them is not (yet) supported. They are 62 | removed before TRANSFORM is evaluated. 63 | 64 | ! Position of point before TRANSFORM is evaluated 65 | 66 | <> Position of mark (<) and point (>) before TRANSFORM is 67 | evaluatred" 68 | (if (string-match "[!<>]" original-text) 69 | ;; original-text has ! markers 70 | (let ((pos 0) ; pos in original-text 71 | (pos-old 0) ; pos of the last iteration 72 | (pos-in-new-region-start 0) 73 | (pos-new-list) ; list of positions in new-original-text 74 | (new-original-text "")) ; as original-text, but with < > ! stripped 75 | ;; original-text -> new-original-text by removing ! and remembering their positions 76 | (while (and (< pos (length original-text)) 77 | (setq pos (string-match "[!<>]" original-text pos))) 78 | (setq new-original-text (concat new-original-text (substring original-text pos-old pos))) 79 | (cond 80 | ((eq (aref original-text pos) ?<) 81 | (setq pos-in-new-region-start (length new-original-text))) 82 | ((eq (aref original-text pos) ?>) 83 | (setq pos-new-list (cons (cons pos-in-new-region-start (length new-original-text)) pos-new-list))) 84 | (t 85 | (setq pos-new-list (cons (length new-original-text) pos-new-list)))) 86 | (setq pos (1+ pos)) 87 | (setq pos-old pos)) 88 | (setq new-original-text (concat new-original-text (substring original-text pos-old pos))) 89 | ;; run adoctest-trans-inner for each remembered pos 90 | (while pos-new-list 91 | (adoctest-trans-inner new-original-text expected-text transform (car pos-new-list)) 92 | (setq pos-new-list (cdr pos-new-list)))) 93 | ;; original-text has no ! markers 94 | (adoctest-trans-inner original-text expected-text transform))) 95 | 96 | (defun adoctest-trans-inner (original-text expected-text transform &optional pos) 97 | (let ((not-done t) 98 | (font-lock-support-mode)) 99 | (with-temp-buffer 100 | ;; setup 101 | (adoc-mode) 102 | (insert original-text) 103 | (cond ; 1+: buffer pos starts at 1, but string pos at 0 104 | ((consp pos) 105 | (goto-char (1+ (car pos))) 106 | (set-mark (1+ (cdr pos)))) 107 | (pos 108 | (goto-char (1+ pos)))) 109 | ;; exercise 110 | (eval transform) 111 | ;; verify 112 | (should (string-equal (buffer-substring (point-min) (point-max)) expected-text))))) 113 | 114 | (ert-deftest adoctest-test-titles-simple-one-line-before () 115 | (adoctest-faces "titles-simple-one-line-before" 116 | "= " markup-meta-hide-face "document title" markup-title-0-face "\n" nil 117 | "\n" nil 118 | "== " markup-meta-hide-face "chapter 1" markup-title-1-face "\n" nil 119 | "\n" nil 120 | "=== " markup-meta-hide-face "chapter 2" markup-title-2-face "\n" nil 121 | "\n" nil 122 | "==== " markup-meta-hide-face "chapter 3" markup-title-3-face "\n" nil 123 | "\n" nil 124 | "===== " markup-meta-hide-face "chapter 4" markup-title-4-face)) 125 | 126 | (ert-deftest adoctest-test-titles-simple-one-line-enclosed () 127 | (adoctest-faces "titles-simple-one-line-enclosed" 128 | "= " markup-meta-hide-face "document title" markup-title-0-face " =" markup-meta-hide-face "\n" nil 129 | "\n" nil 130 | "== " markup-meta-hide-face "chapter 1" markup-title-1-face " ==" markup-meta-hide-face "\n" nil 131 | "\n" nil 132 | "=== " markup-meta-hide-face "chapter 2" markup-title-2-face " ===" markup-meta-hide-face "\n" nil 133 | "\n" nil 134 | "==== " markup-meta-hide-face "chapter 3" markup-title-3-face " ====" markup-meta-hide-face "\n" nil 135 | "\n" nil 136 | "===== " markup-meta-hide-face "chapter 4" markup-title-4-face " =====" markup-meta-hide-face)) 137 | 138 | (ert-deftest adoctest-test-titles-simple-two-line () 139 | (adoctest-faces "titles-simple-two-line" 140 | ;; todo 141 | ;; ensure somehow adoc-enable-two-line-title is t 142 | "document title" markup-title-0-face "\n" nil 143 | "==============" markup-meta-hide-face "\n" nil 144 | "\n" nil 145 | "chapter 1" markup-title-1-face "\n" nil 146 | "---------" markup-meta-hide-face "\n" nil 147 | "\n" nil 148 | "chapter 2" markup-title-2-face "\n" nil 149 | "~~~~~~~~~" markup-meta-hide-face "\n" nil 150 | "\n" nil 151 | "chapter 3" markup-title-3-face "\n" nil 152 | "^^^^^^^^^" markup-meta-hide-face "\n" nil 153 | "\n" nil 154 | "chapter 4" markup-title-4-face "\n" nil 155 | "+++++++++" markup-meta-hide-face)) 156 | 157 | (ert-deftest adoctest-test-titles-simple-block-title () 158 | (adoctest-faces "titles-simple-block-title" 159 | "." markup-meta-face "Block title" markup-gen-face)) 160 | 161 | (ert-deftest adoctest-test-delimited-blocks-simple () 162 | (adoctest-faces "delimited-blocks-simple" 163 | 164 | ;; note that the leading spaces are NOT allowed to have adoc-align face 165 | "////////" markup-meta-hide-face "\n" nil 166 | " comment line 1\n comment line 2" markup-comment-face "\n" nil 167 | "////////" markup-meta-hide-face "\n" nil 168 | "\n" nil 169 | "++++++++" markup-meta-hide-face "\n" nil 170 | " passthrouh line 1\n passthrouh line 2" markup-passthrough-face "\n" nil 171 | "++++++++" markup-meta-hide-face "\n" nil 172 | "\n" nil 173 | "--------" markup-meta-hide-face "\n" nil 174 | " listing line 1\n listing line 2" markup-code-face "\n" nil 175 | "--------" markup-meta-hide-face "\n" nil 176 | "\n" nil 177 | "........" markup-meta-hide-face "\n" nil 178 | " literal line 1\n literal line 2" markup-verbatim-face "\n" nil 179 | "........" markup-meta-hide-face "\n" nil 180 | "\n" nil 181 | 182 | "________" markup-meta-hide-face "\n" nil 183 | "quote line 1\nquote line 2" nil "\n" nil 184 | "________" markup-meta-hide-face "\n" nil 185 | "\n" nil 186 | "========" markup-meta-hide-face "\n" nil 187 | "example line 1\nexample line 2" nil "\n" nil 188 | "========" markup-meta-hide-face "\n" nil 189 | "\n" nil 190 | "********" markup-meta-hide-face "\n" nil 191 | "sidebar line 1\nsidebar line 2" markup-secondary-text-face "\n" nil 192 | "********" markup-meta-hide-face "\n")) 193 | 194 | ;; Don't mistake text between two same delimited blocks as a delimited block, 195 | ;; i.e. wrongly treating the end of block 1 as a beginning and wrongly 196 | ;; treating the beginning of block 2 as ending. 197 | (ert-deftest adoctest-test-delimited-blocks-simple () 198 | (adoctest-faces "delimited-blocks-special-case" 199 | 200 | "--------" markup-meta-hide-face "\n" nil 201 | "11\n12\n13\n14" markup-code-face "\n" nil 202 | "--------" markup-meta-hide-face "\n" nil 203 | "\n" nil 204 | "lorem" 'no-face "\n" nil 205 | "\n" nil 206 | "--------" markup-meta-hide-face "\n" nil 207 | "21\n22\n23\n24" markup-code-face "\n" nil 208 | "--------" markup-meta-hide-face "\n" nil 209 | "\n" nil 210 | "ipsum" 'no-face "\n" nil)) 211 | 212 | (ert-deftest adoctest-test-delimited-blocks-simple () 213 | (adoctest-faces "delimited-blocks-empty" 214 | "////////" markup-meta-hide-face "\n" nil 215 | "////////" markup-meta-hide-face "\n" nil 216 | "\n" nil 217 | "++++++++" markup-meta-hide-face "\n" nil 218 | "++++++++" markup-meta-hide-face "\n" nil 219 | "\n" nil 220 | "--------" markup-meta-hide-face "\n" nil 221 | "--------" markup-meta-hide-face "\n" nil 222 | "\n" nil 223 | "........" markup-meta-hide-face "\n" nil 224 | "........" markup-meta-hide-face "\n" nil 225 | "\n" nil 226 | "________" markup-meta-hide-face "\n" nil 227 | "________" markup-meta-hide-face "\n" nil 228 | "\n" nil 229 | "========" markup-meta-hide-face "\n" nil 230 | "========" markup-meta-hide-face "\n" nil 231 | "\n" nil 232 | "********" markup-meta-hide-face "\n" nil 233 | "********" markup-meta-hide-face "\n")) 234 | 235 | (ert-deftest adoctest-test-open-block () 236 | (adoctest-faces "open-block" 237 | "--" markup-meta-hide-face "\n" nil 238 | "open block line 1\nopen block line 2" nil "\n" nil 239 | "--" markup-meta-hide-face)) 240 | 241 | (ert-deftest adoctest-test-comments () 242 | (adoctest-faces "comments" 243 | ;; as block macro 244 | "// lorem ipsum\n" markup-comment-face 245 | "\n" nil 246 | ;; as inline macro 247 | "lorem ipsum\n" 'no-face 248 | "// dolor sit\n" markup-comment-face 249 | "amen\n" 'no-face 250 | "\n" nil 251 | ;; block macro and end of buffer 252 | "// lorem ipsum" markup-comment-face 253 | ;; as delimited block it's tested in delimited-blocks-simple 254 | )) 255 | 256 | (ert-deftest adoctest-test-anchors () 257 | (adoctest-faces "anchors" 258 | ;; block id 259 | "[[" markup-meta-face "foo" markup-anchor-face "]]" markup-meta-face "\n" nil 260 | "[[" markup-meta-face "foo" markup-anchor-face "," markup-meta-face 261 | "bar" markup-secondary-text-face "]]" markup-meta-face "\n" nil 262 | 263 | ;; special inline syntax: [[id]] [[id,xreftext]] 264 | "lorem " 'no-face "[[" markup-meta-face "foo" markup-anchor-face "]]" 265 | markup-meta-face "ipsum" 'no-face "\n" nil 266 | "lorem " 'no-face "[[" markup-meta-face "foo" markup-anchor-face "," markup-meta-face 267 | "bla bli bla blu" markup-secondary-text-face "]]" markup-meta-face "ipsum" 'no-face "\n" nil 268 | 269 | ;; general inline macro syntax 270 | "lorem " 'no-face "anchor" markup-command-face ":" markup-meta-face 271 | "foo" markup-anchor-face 272 | "[]" markup-meta-face "ipsum" 'no-face "\n" nil 273 | "lorem " 'no-face "anchor" markup-command-face ":" markup-meta-face 274 | "foo" markup-anchor-face 275 | "[" markup-meta-face "bla bli bla blu" markup-secondary-text-face "]" markup-meta-face 276 | "ipsum" 'no-face "\n" nil 277 | 278 | ;; biblio 279 | "lorem " 'no-face "[[" markup-meta-face "[foo]" markup-gen-face "]]" markup-meta-face 280 | " ipsum" 'no-face 281 | )) 282 | 283 | (ert-deftest adoctest-test-references () 284 | (adoctest-faces "references" 285 | "lorem " 'no-face "xref" markup-command-face ":" markup-meta-face 286 | "foo" markup-reference-face "[]" markup-meta-face "\n" nil 287 | "lorem " 'no-face "xref" markup-command-face ":" markup-meta-face 288 | "foo" markup-internal-reference-face "[" markup-meta-face 289 | "bla bli bla blu" markup-reference-face "]" markup-meta-face "\n" nil 290 | 291 | ;; caption spawns multiple lines 292 | "xref" markup-command-face ":" markup-meta-face 293 | "foo" markup-internal-reference-face "[" markup-meta-face 294 | "bla\nbli\nbla\nblu" markup-reference-face "]" markup-meta-face "\n" nil 295 | )) 296 | 297 | (ert-deftest adoctest-test-footnotes () 298 | (adoctest-faces "footnotes" 299 | ;; simple example 300 | "footnote" markup-command-face ":" markup-meta-face 301 | "[" markup-meta-face "lorem ipsum" markup-secondary-text-face 302 | "]" markup-meta-face "\n" nil 303 | 304 | ;; footnote can be hard up against the preceding word 305 | "lorem" 'no-face "footnote" markup-command-face ":" markup-meta-face 306 | "[" markup-meta-face "ipsum" markup-secondary-text-face 307 | "]" markup-meta-face "\n" nil 308 | 309 | ;; attribute-list is not really an attribute list but normal text, 310 | ;; i.e. comma, equal, double quotes are not fontified as meta characters 311 | "footnote" markup-command-face ":" markup-meta-face 312 | "[" markup-meta-face 313 | "lorem, ipsum=dolor, sit=\"amen\"" markup-secondary-text-face 314 | "]" markup-meta-face "\n" nil 315 | 316 | ;; multiline attribute list 317 | "footnote" markup-command-face ":" markup-meta-face 318 | "[" markup-meta-face 319 | "lorem\nipsum\ndolor\nsit\namen" markup-secondary-text-face 320 | "]" markup-meta-face "\n" nil 321 | )) 322 | 323 | (ert-deftest adoctest-test-footnoterefs () 324 | (adoctest-faces "footnoterefs" 325 | ;; simple example 326 | "footnoteref" markup-command-face ":" markup-meta-face 327 | "[" markup-meta-face 328 | "myid" markup-internal-reference-face 329 | "]" markup-meta-face "\n" nil 330 | 331 | "footnoteref" markup-command-face ":" markup-meta-face 332 | "[" markup-meta-face 333 | "myid" markup-anchor-face 334 | "," markup-meta-face 335 | "lorem ipsum" markup-secondary-text-face 336 | "]" markup-meta-face "\n" nil 337 | 338 | ;; footnoteref can be hard up against the preceding word 339 | "lorem" 'no-face 340 | "footnoteref" markup-command-face ":" markup-meta-face 341 | "[" markup-meta-face 342 | "myid" markup-internal-reference-face 343 | "]" markup-meta-face "\n" nil 344 | 345 | "lorem" 'no-face 346 | "footnoteref" markup-command-face ":" markup-meta-face 347 | "[" markup-meta-face 348 | "myid" markup-anchor-face 349 | "," markup-meta-face 350 | "lorem ipsum" markup-secondary-text-face 351 | "]" markup-meta-face "\n" nil 352 | 353 | ;; multiline text 354 | "lorem" 'no-face 355 | "footnoteref" markup-command-face ":" markup-meta-face 356 | "[" markup-meta-face 357 | "myid" markup-anchor-face 358 | "," markup-meta-face 359 | "lorem\nipsum\ndolor\nsit" markup-secondary-text-face 360 | "]" markup-meta-face "\n" nil 361 | )) 362 | 363 | (ert-deftest adoctest-test-images () 364 | (adoctest-faces "images" 365 | ;; block macros 366 | ;; empty arglist 367 | "image" markup-complex-replacement-face "::" markup-meta-face 368 | "./foo/bar.png" markup-internal-reference-face 369 | "[]" markup-meta-face "\n" nil 370 | ;; pos attribute 0 = alternate text 371 | "image" markup-complex-replacement-face "::" markup-meta-face 372 | "./foo/bar.png" markup-internal-reference-face 373 | "[" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "\n" nil 374 | ;; keyword title 375 | "image" markup-complex-replacement-face "::" markup-meta-face 376 | "./foo/bar.png" markup-internal-reference-face 377 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "\n" nil 378 | ;; keyword alt and title 379 | "image" markup-complex-replacement-face "::" markup-meta-face 380 | "./foo/bar.png" markup-internal-reference-face 381 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "," markup-meta-face 382 | "title" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "\n" nil 383 | ;; multiline alt and title 384 | "image" markup-complex-replacement-face "::" markup-meta-face 385 | "./foo/bar.png" markup-internal-reference-face 386 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face 387 | "lorem\nipsum\nsit" markup-secondary-text-face "," markup-meta-face 388 | "title" markup-attribute-face "=" markup-meta-face 389 | "lorem\nipsum\nsit" markup-secondary-text-face "]" markup-meta-face "\n" nil 390 | 391 | ;; no everything again with inline macros 392 | "foo " 'no-face "image" markup-complex-replacement-face ":" markup-meta-face 393 | "./foo/bar.png" markup-internal-reference-face 394 | "[]" markup-meta-face "bar" 'no-face "\n" nil 395 | 396 | "foo " 'no-face "image" markup-complex-replacement-face ":" markup-meta-face 397 | "./foo/bar.png" markup-internal-reference-face 398 | "[" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "bar" 'no-face "\n" nil 399 | 400 | "foo " 'no-face "image" markup-complex-replacement-face ":" markup-meta-face 401 | "./foo/bar.png" markup-internal-reference-face 402 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "bar" 'no-face "\n" nil 403 | 404 | "foo " 'no-face "image" markup-complex-replacement-face ":" markup-meta-face 405 | "./foo/bar.png" markup-internal-reference-face 406 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "," markup-meta-face 407 | "title" markup-attribute-face "=" markup-meta-face "lorem ipsum" markup-secondary-text-face "]" markup-meta-face "bar" 'no-face "\n" nil 408 | 409 | "image" markup-complex-replacement-face ":" markup-meta-face 410 | "./foo/bar.png" markup-internal-reference-face 411 | "[" markup-meta-face "alt" markup-attribute-face "=" markup-meta-face 412 | "lorem\nipsum\nsit" markup-secondary-text-face "," markup-meta-face 413 | "title" markup-attribute-face "=" markup-meta-face 414 | "lorem\nipsum\nsit" markup-secondary-text-face "]" markup-meta-face "\n" nil)) 415 | 416 | (ert-deftest adoctest-test-attribute-list () 417 | (adoctest-faces "attribute-list" 418 | ;; positional attribute 419 | "[" markup-meta-face "hello" markup-value-face "]" markup-meta-face "\n" nil 420 | ;; positional attribute containing spaces 421 | "[" markup-meta-face "hello world" markup-value-face "]" markup-meta-face "\n" nil 422 | ;; positional attribute as string 423 | "[\"" markup-meta-face "hello world" markup-value-face "\"]" markup-meta-face "\n" nil 424 | 425 | ;; multiple positional attributes 426 | "[" markup-meta-face "hello" markup-value-face "," markup-meta-face "world" markup-value-face "]" markup-meta-face "\n" nil 427 | 428 | ;; multiple positional attributes, however one or both are empty (really empty or only one space) 429 | "[" markup-meta-face "hello" markup-value-face ",]" markup-meta-face "\n" nil 430 | "[" markup-meta-face "hello" markup-value-face "," markup-meta-face " " markup-value-face "]" markup-meta-face "\n" nil 431 | "[," markup-meta-face "hello" markup-value-face "]" markup-meta-face "\n" nil 432 | "[" markup-meta-face " " markup-value-face "," markup-meta-face "hello" markup-value-face "]" markup-meta-face "\n" nil 433 | "[,]" markup-meta-face "\n" nil 434 | "[," markup-meta-face " " markup-value-face "]" markup-meta-face "\n" nil 435 | "[" markup-meta-face " " markup-value-face ",]" markup-meta-face "\n" nil 436 | "[" markup-meta-face " " markup-value-face "," markup-meta-face " " markup-value-face "]" markup-meta-face "\n" nil 437 | 438 | ;; zero positional attributes 439 | "[]" markup-meta-face "\n" nil 440 | "[" markup-meta-face " " markup-value-face "]" markup-meta-face "\n" nil 441 | 442 | ;; keyword attribute 443 | "[" markup-meta-face "salute" markup-attribute-face "=" markup-meta-face "hello" markup-value-face "]" markup-meta-face "\n" nil 444 | ;; keyword attribute where value is a string 445 | "[" markup-meta-face "salute" markup-attribute-face "=\"" markup-meta-face "hello world" markup-value-face "\"]" markup-meta-face "\n" nil 446 | 447 | ;; multiple positional attributes, multiple keyword attributes 448 | "[" markup-meta-face "lorem" markup-value-face "," markup-meta-face "ipsum" markup-value-face "," markup-meta-face 449 | "dolor" markup-attribute-face "=" markup-meta-face "sit" markup-value-face "," markup-meta-face 450 | "dolor" markup-attribute-face "=" markup-meta-face "sit" markup-value-face "]" markup-meta-face "\n" nil 451 | 452 | ;; is , within strings really part of the string and not mistaken as element separator 453 | "[\"" markup-meta-face "lorem,ipsum=dolor" markup-value-face "\"]" markup-meta-face "\n" nil 454 | ;; does escaping " in strings work 455 | "[\"" markup-meta-face "lorem \\\"ipsum\\\" dolor" markup-value-face "\"]" markup-meta-face 456 | )) 457 | 458 | (ert-deftest adoctest-test-block-macro () 459 | (adoctest-faces "block-macro" 460 | "lorem" markup-command-face "::" markup-meta-face "ipsum[]" markup-meta-face)) 461 | 462 | (ert-deftest adoctest-test-quotes-simple () 463 | (adoctest-faces "test-quotes-simple" 464 | ;; note that in unconstraned quotes cases " ipsum " has spaces around, in 465 | ;; constrained quotes case it doesn't 466 | "Lorem " nil "`" markup-meta-hide-face "ipsum" '(markup-typewriter-face markup-verbatim-face) "`" markup-meta-hide-face " dolor\n" nil 467 | "Lorem " nil "+++" markup-meta-hide-face " ipsum " '(markup-typewriter-face markup-verbatim-face) "+++" markup-meta-hide-face " dolor\n" nil 468 | "Lorem " nil "$$" markup-meta-hide-face " ipsum " '(markup-typewriter-face markup-verbatim-face) "$$" markup-meta-hide-face " dolor\n" nil 469 | "Lorem " nil "**" markup-meta-hide-face " ipsum " markup-strong-face "**" markup-meta-hide-face " dolor\n" nil 470 | "Lorem " nil "*" markup-meta-hide-face "ipsum" markup-strong-face "*" markup-meta-hide-face " dolor\n" nil 471 | "Lorem " nil "``" markup-replacement-face "ipsum" nil "''" markup-replacement-face " dolor\n" nil 472 | "Lorem " nil "'" markup-meta-hide-face "ipsum" markup-emphasis-face "'" markup-meta-hide-face " dolor\n" nil 473 | "Lorem " nil "`" markup-replacement-face "ipsum" nil "'" markup-replacement-face " dolor\n" nil 474 | "Lorem " nil "++" markup-meta-hide-face " ipsum " markup-typewriter-face "++" markup-meta-hide-face " dolor\n" nil 475 | "Lorem " nil "+" markup-meta-hide-face "ipsum" markup-typewriter-face "+" markup-meta-hide-face " dolor\n" nil 476 | "Lorem " nil "__" markup-meta-hide-face " ipsum " markup-emphasis-face "__" markup-meta-hide-face " dolor\n" nil 477 | "Lorem " nil "_" markup-meta-hide-face "ipsum" markup-emphasis-face "_" markup-meta-hide-face " dolor\n" nil 478 | "Lorem " nil "##" markup-meta-hide-face " ipsum " markup-gen-face "##" markup-meta-hide-face " dolor\n" nil 479 | "Lorem " nil "#" markup-meta-hide-face "ipsum" markup-gen-face "#" markup-meta-hide-face " dolor\n" nil 480 | "Lorem " nil "~" markup-meta-hide-face " ipsum " markup-subscript-face "~" markup-meta-hide-face " dolor\n" nil 481 | "Lorem " nil "^" markup-meta-hide-face " ipsum " markup-superscript-face "^" markup-meta-hide-face " dolor")) 482 | 483 | (ert-deftest adoctest-test-quotes-medium () 484 | (adoctest-faces "test-quotes-medium" 485 | ;; test wheter constrained/unconstrained quotes can spawn multiple lines 486 | "Lorem " 'no-face "*" markup-meta-hide-face "ipsum" markup-strong-face 487 | "\n" nil "dolor" markup-strong-face "\n" nil "dolor" markup-strong-face 488 | "\n" nil "dolor" markup-strong-face "\n" nil "dolor" markup-strong-face 489 | "*" markup-meta-hide-face 490 | " sit" 'no-face "\n" nil 491 | 492 | "Lorem " 'no-face "__" markup-meta-hide-face "ipsum" markup-emphasis-face 493 | "\n" nil "dolor" markup-emphasis-face "\n" nil "dolor" markup-emphasis-face 494 | "\n" nil "dolor" markup-emphasis-face "\n" nil "dolor" markup-emphasis-face 495 | "__" markup-meta-hide-face 496 | " sit" 'no-face "\n" nil 497 | 498 | ;; tests border case that delimiter is at the beginnin/end of an paragraph/line 499 | ;; constrained at beginning 500 | "*" markup-meta-hide-face "lorem" 'markup-strong-face "*" markup-meta-hide-face " ipsum\n" 'no-face 501 | "\n" nil 502 | ;; constrained at end 503 | "lorem " 'no-face "*" markup-meta-hide-face "ipsum" markup-strong-face "*" markup-meta-hide-face "\n" nil 504 | "\n" nil 505 | ;; constrained from beginning to end 506 | "*" markup-meta-hide-face "lorem" markup-strong-face "*" markup-meta-hide-face "\n" nil 507 | "\n" nil 508 | ;; unconstrained at beginning. Note that "** " at the beginning of a line would be a list item. 509 | "__" markup-meta-hide-face " lorem " 'markup-emphasis-face "__" markup-meta-hide-face " ipsum\n" 'no-face 510 | "\n" nil 511 | ;; unconstrained at end 512 | "lorem " 'no-face "__" markup-meta-hide-face " ipsum " markup-emphasis-face "__" markup-meta-hide-face "\n" nil 513 | "\n" nil 514 | ;; unconstrained from beginning to end 515 | "__" markup-meta-hide-face " lorem " markup-emphasis-face "__" markup-meta-hide-face "\n" nil 516 | "\n" nil 517 | 518 | ;; test wheter quotes can nest 519 | ;; done by meta-face-cleanup 520 | 521 | ;; tests that quotes work within titles / labeled lists 522 | "== " markup-meta-hide-face "chapter " markup-title-1-face "*" markup-meta-hide-face "1" '(markup-title-1-face markup-strong-face) "*" markup-meta-hide-face " ==" markup-meta-hide-face "\n" nil 523 | "\n" nil 524 | "chapter " markup-title-2-face "_" markup-meta-hide-face "2" '(markup-title-2-face markup-emphasis-face) "_" markup-meta-hide-face "\n" nil 525 | "~~~~~~~~~~~" markup-meta-hide-face "\n" nil 526 | "." markup-meta-face "lorem " 'markup-gen-face "_" markup-meta-hide-face "ipsum" '(markup-gen-face markup-emphasis-face) "_" markup-meta-hide-face "\n" nil 527 | "\n" nil 528 | "lorem " markup-gen-face "+" markup-meta-hide-face "ipsum" '(markup-gen-face markup-typewriter-face) "+" markup-meta-hide-face " sit" markup-gen-face "::" markup-list-face " " adoc-align 529 | )) 530 | 531 | ;; test border cases where the quote delimiter is at the beginning and/or the 532 | ;; end of the buffer 533 | (ert-deftest adoctest-test-quotes-medium-2 () 534 | (adoctest-faces "test-quotes-medium-2" 535 | "*" markup-meta-hide-face "lorem" markup-strong-face "*" markup-meta-hide-face " ipsum" 'no-face)) 536 | (ert-deftest adoctest-test-quotes-medium-3 () 537 | (adoctest-faces "test-quotes-medium-3" 538 | "lorem " 'no-face "*" markup-meta-hide-face "ipsum" markup-strong-face "*" markup-meta-hide-face)) 539 | (ert-deftest adoctest-test-quotes-medium-4 () 540 | (adoctest-faces "test-quotes-medium-4" 541 | "*" markup-meta-hide-face "lorem" markup-strong-face "*" markup-meta-hide-face)) 542 | 543 | (ert-deftest adoctest-test-lists-simple () 544 | (adoctest-faces "test-lists-simple" 545 | " " adoc-align "-" markup-list-face " " adoc-align "uo list item\n" nil 546 | " " adoc-align "*" markup-list-face " " adoc-align "uo list item\n" nil 547 | " " adoc-align "**" markup-list-face " " adoc-align "uo list item\n" nil 548 | " " adoc-align "***" markup-list-face " " adoc-align "uo list item\n" nil 549 | " " adoc-align "****" markup-list-face " " adoc-align "uo list item\n" nil 550 | " " adoc-align "*****" markup-list-face " " adoc-align "uo list item\n" nil 551 | "+" markup-list-face " " adoc-align "uo list item\n" nil 552 | 553 | " " adoc-align "1." markup-list-face " " adoc-align "o list item\n" nil 554 | " " adoc-align "a." markup-list-face " " adoc-align "o list item\n" nil 555 | " " adoc-align "B." markup-list-face " " adoc-align "o list item\n" nil 556 | " " adoc-align "ii)" markup-list-face " " adoc-align "o list item\n" nil 557 | " " adoc-align "II)" markup-list-face " " adoc-align "o list item\n" nil 558 | 559 | " " adoc-align "." markup-list-face " " adoc-align "implicitly numbered list item\n" nil 560 | " " adoc-align ".." markup-list-face " " adoc-align "implicitly numbered list item\n" nil 561 | " " adoc-align "..." markup-list-face " " adoc-align "implicitly numbered list item\n" nil 562 | " " adoc-align "...." markup-list-face " " adoc-align "implicitly numbered list item\n" nil 563 | " " adoc-align "....." markup-list-face " " adoc-align "implicitly numbered list item\n" nil 564 | "<1>" markup-list-face " " adoc-align "callout\n" nil 565 | "1>" markup-list-face " " adoc-align "callout\n" nil 566 | 567 | " " adoc-align "term" markup-gen-face "::" markup-list-face " " adoc-align "lorem ipsum\n" nil 568 | " " adoc-align "term" markup-gen-face ";;" markup-list-face " " adoc-align "lorem ipsum\n" nil 569 | " " adoc-align "term" markup-gen-face ":::" markup-list-face " " adoc-align "lorem ipsum\n" nil 570 | " " adoc-align "term" markup-gen-face "::::" markup-list-face " " adoc-align "lorem ipsum\n" nil 571 | " " adoc-align "question" markup-gen-face "??" markup-list-face "\n" nil 572 | "glossary" markup-gen-face ":-" markup-list-face "\n" nil 573 | 574 | "-" markup-list-face " " adoc-align "uo list item\n" nil 575 | "+" markup-meta-face "\n" nil 576 | "2nd list paragraph\n" nil )) 577 | 578 | (ert-deftest adoctest-test-lists-medium () 579 | (adoctest-faces "test-lists-medium" 580 | ;; white box test: labeled list item font lock keyword is implemented 581 | ;; specially in that it puts adoc-reserved text-property on the preceding 582 | ;; newline. However it shall deal with the situation that there is no 583 | ;; preceding newline, because we're at the beginning of the buffer 584 | "lorem" markup-gen-face "::" markup-list-face " " nil "ipsum" 'no-face)) 585 | 586 | (ert-deftest adoctest-test-inline-macros () 587 | (adoctest-faces "inline-macros" 588 | "commandname" markup-command-face ":target[" markup-meta-face "attribute list" markup-value-face "]" markup-meta-face)) 589 | 590 | (ert-deftest adoctest-test-meta-face-cleanup () 591 | ;; begin with a few simple explicit cases which are easier to debug in case of troubles 592 | 593 | ;; 1) test that meta characters always only have a single meta and don't get 594 | ;; markup-bold/-emphasis/... face just because they are within a 595 | ;; strongh/emphasis/... construct. 596 | ;; 2) test that nested quotes really apply the faces of both quotes to the inner text 597 | (adoctest-faces "meta-face-cleanup-1" 598 | "*" markup-meta-hide-face "lorem " markup-strong-face 599 | "_" markup-meta-hide-face "ipsum" '(markup-strong-face markup-emphasis-face) "_" markup-meta-hide-face 600 | " dolor" markup-strong-face "*" markup-meta-hide-face "\n" nil) 601 | (adoctest-faces "meta-face-cleanup-2" 602 | "_" markup-meta-hide-face "lorem " markup-emphasis-face 603 | "*" markup-meta-hide-face "ipsum" '(markup-strong-face markup-emphasis-face) "*" markup-meta-hide-face 604 | " dolor" markup-emphasis-face "_" markup-meta-hide-face) 605 | 606 | ;; now test all possible cases 607 | ;; mmm, that is all possible cases inbetween constrained/unconstrained quotes 608 | 609 | ;; .... todo 610 | ) 611 | 612 | (ert-deftest adoctest-test-url () 613 | (adoctest-faces "url" 614 | ;; url inline macro with attriblist 615 | "foo " nil 616 | "http://www.lorem.com/ipsum.html" markup-internal-reference-face 617 | "[" markup-meta-face "sit amet" markup-reference-face "]" markup-meta-face 618 | " bar \n" nil 619 | ;; link text contains newlines and commas 620 | "http://www.lorem.com/ipsum.html" markup-internal-reference-face 621 | "[" markup-meta-face 622 | "sit,\namet,\nconsectetur" markup-reference-face 623 | "]" markup-meta-face 624 | " bar \n" nil 625 | ;; url inline macro withOUT attriblist 626 | "http://www.lorem.com/ipsum.html" markup-reference-face 627 | "[]" markup-meta-face 628 | " bar \n" nil 629 | ;; plain url 630 | "http://www.lorem.com/ipsum.html" markup-reference-face 631 | " foo " nil "joe.bloggs@foobar.com" markup-reference-face )) 632 | 633 | (ert-deftest adoctest-test-url-enclosing-quote () 634 | (adoctest-faces "url-enclosing-quote" 635 | ;; spaces between __ and url seem really to be needed also in asciidoc 636 | "foo " nil "__" markup-meta-hide-face " " nil 637 | "http://www.lorem.com/ipsum.html" '(markup-emphasis-face markup-reference-face) 638 | " " nil "__" markup-meta-hide-face 639 | 640 | "\nfoo " nil 641 | "**" markup-meta-hide-face " " nil 642 | "joe.bloggs@foobar.com" '(markup-strong-face markup-reference-face) 643 | " " nil "**" markup-meta-hide-face )) 644 | 645 | ;; inline substitutions only within the block they belong to. I.e. don't cross 646 | ;; block boundaries. 647 | (ert-deftest adoctest-test-inline-subst-boundaries () 648 | (adoctest-faces "inline-subst-boundaries" 649 | 650 | ;; 1) don't cross title boundaries. 651 | ;; 2) don't cross paragraph boundaries. 652 | ;; 3) verify that the (un)constrained quotes would work however 653 | "== " markup-meta-hide-face "chapter ** 1" markup-title-1-face "\n" nil 654 | "lorem ** ipsum\n" 'no-face 655 | "\n" nil 656 | "lorem " 'no-face "**" markup-meta-hide-face " ipsum " markup-strong-face "**" markup-meta-hide-face "\n" nil 657 | "\n" nil 658 | 659 | "== " markup-meta-hide-face "chapter __ 1" markup-title-1-face " ==" markup-meta-hide-face "\n" nil 660 | "lorem __ ipsum\n" 'no-face 661 | "\n" nil 662 | "lorem " 'no-face "__" markup-meta-hide-face " ipsum " markup-emphasis-face "__" markup-meta-hide-face "\n" nil 663 | "\n" nil 664 | 665 | "chapter ++ 1" markup-title-1-face "\n" nil 666 | "------------" markup-meta-hide-face "\n" nil 667 | "lorem ++ ipsum\n" 'no-face 668 | "\n" nil 669 | "lorem " 'no-face "++" markup-meta-hide-face " ipsum " markup-typewriter-face "++" markup-meta-hide-face "\n" nil 670 | "\n" nil 671 | 672 | "." markup-meta-face "block ^title" markup-gen-face "\n" nil 673 | "lorem^ ipsum\n" 'no-face 674 | "\n" nil 675 | "lorem " 'no-face "^" markup-meta-hide-face " ipsum " markup-superscript-face "^" markup-meta-hide-face "\n" nil 676 | "\n" nil 677 | 678 | ;; Being able to use a ** that potentially could be mistaken as an end 679 | ;; delimiter as start delimiter 680 | "== " markup-meta-hide-face "chapter ** 1" markup-title-1-face "\n" nil 681 | "lorem " 'no-face "**" markup-meta-hide-face " ipsum " markup-strong-face "**" markup-meta-hide-face "\n" nil 682 | "\n" nil 683 | 684 | ;; don't cross list item boundaries 685 | "-" markup-list-face " " nil "lorem ** ipsum\n" 'no-face 686 | "-" markup-list-face " " nil "dolor ** sit\n" 'no-face 687 | ;; test that a quote within the list element works 688 | "-" markup-list-face " " nil "dolor " 'no-face "**" markup-meta-hide-face " sit " markup-strong-face "**" markup-meta-hide-face "\n" nil 689 | ;; dont mistake '**' list elements for quote starters/enders 690 | "**" markup-list-face " " nil "lorem ** ipsum\n" 'no-face 691 | "**" markup-list-face " " nil "dolor ** sit\n" 'no-face 692 | "**" markup-list-face " " nil "dolor ** sit\n" 'no-face 693 | ;; don't cross list item boundaries in the case of labeled lists 694 | "lorem ** ipsum " markup-gen-face "::" markup-list-face " " nil "sit ** dolor\n" 'no-face 695 | "lorem ** ipsum " markup-gen-face "::" markup-list-face " " nil "sit ** dolor" 'no-face)) 696 | 697 | (ert-deftest adoctest-test-promote-title () 698 | (adoctest-trans "= foo" "== foo" '(adoc-promote-title 1)) 699 | (adoctest-trans "===== foo" "= foo" '(adoc-promote-title 1)) 700 | (adoctest-trans "== foo" "==== foo" '(adoc-promote-title 2)) 701 | 702 | (adoctest-trans "= foo =" "== foo ==" '(adoc-promote-title 1)) 703 | (adoctest-trans "===== foo =====" "= foo =" '(adoc-promote-title 1)) 704 | (adoctest-trans "== foo ==" "==== foo ====" '(adoc-promote-title 2)) 705 | 706 | (adoctest-trans "foo!\n===!" "foo\n---" '(adoc-promote-title 1)) 707 | (adoctest-trans "foo!\n+++!" "foo\n===" '(adoc-promote-title 1)) 708 | (adoctest-trans "foo!\n---!" "foo\n^^^" '(adoc-promote-title 2))) 709 | 710 | ;; since it's a whitebox test we know demote and promote only differ by inverse 711 | ;; arg. So demote doesn't need to be throuhly tested again 712 | (ert-deftest adoctest-test-demote-title () 713 | (adoctest-trans "= foo" "===== foo" '(adoc-demote-title 1)) 714 | (adoctest-trans "= foo =" "===== foo =====" '(adoc-demote-title 1)) 715 | (adoctest-trans "foo!\n===!" "foo\n+++" '(adoc-demote-title 1))) 716 | 717 | ;; todo: test after transition point is still on title lines 718 | (ert-deftest adoctest-test-toggle-title-type () 719 | (adoctest-trans "= one" "one\n===" '(adoc-toggle-title-type)) 720 | (adoctest-trans "two!\n===!" "= two" '(adoc-toggle-title-type)) 721 | (adoctest-trans "= three!\nbar" "three\n=====\nbar" '(adoc-toggle-title-type)) 722 | (adoctest-trans "four!\n====!\nbar" "= four\nbar" '(adoc-toggle-title-type)) 723 | (adoctest-trans "= five" "= five =" '(adoc-toggle-title-type t)) 724 | (adoctest-trans "= six =" "= six" '(adoc-toggle-title-type t))) 725 | 726 | (ert-deftest adoctest-test-adjust-title-del () 727 | (adoctest-trans "lorem!\n===!" "lorem\n=====" '(adoc-adjust-title-del)) 728 | (adoctest-trans "lorem!\n========!" "lorem\n=====" '(adoc-adjust-title-del)) 729 | (adoctest-trans "lorem!\n=====!" "lorem\n=====" '(adoc-adjust-title-del))) 730 | 731 | (ert-deftest adoctest-test-xref-at-point-1 () 732 | (unwind-protect 733 | (progn 734 | (set-buffer (get-buffer-create "adoc-test")) 735 | (insert "lorem xref:bogous1[] ipsum xref:foo[bla\nbli] dolor xref:bogous2[]") 736 | (re-search-backward "bli") ; move point within ref 737 | (should (equal (adoc-xref-id-at-point) "foo"))) 738 | (kill-buffer "adoc-test"))) 739 | 740 | (ert-deftest adoctest-test-xref-at-point-2 () 741 | (unwind-protect 742 | (progn 743 | (set-buffer (get-buffer-create "adoc-test")) 744 | (insert "lorem <> ipsum <> dolor <>") 745 | (re-search-backward "bli") ; move point within ref 746 | (should (equal (adoc-xref-id-at-point) "foo"))) 747 | (kill-buffer "adoc-test"))) 748 | 749 | (ert-deftest adoctest-test-goto-ref-label () 750 | (unwind-protect 751 | (progn 752 | (set-buffer (get-buffer-create "adoc-test")) 753 | (insert "[[foo]]\n" ;1 754 | "lorem ipsum\n" ;2 755 | "[[bar]]\n" ;3 756 | "dolor [[geil]]sit amen\n" ;4 757 | "anchor:cool[]\n") ;5 758 | (adoc-goto-ref-label "cool") 759 | (should (equal (line-number-at-pos) 5)) 760 | (adoc-goto-ref-label "geil") 761 | (should (equal (line-number-at-pos) 4)) 762 | (adoc-goto-ref-label "bar") 763 | (should (equal (line-number-at-pos) 3))) 764 | (kill-buffer "adoc-test"))) 765 | 766 | (defun adoctest-template (template expected) 767 | (let ((buf-name (concat "adoctest-" (symbol-name template)))) 768 | (unwind-protect 769 | (progn 770 | ;; setup 771 | (set-buffer (get-buffer-create buf-name)) 772 | (delete-region (point-min) (point-max)) 773 | (funcall template) 774 | (should (equal (buffer-substring-no-properties (point-min) (point-max)) expected))) 775 | ;; tear-down 776 | (kill-buffer buf-name)))) 777 | 778 | (defun adoctest-quotes (start-del end-del transform) 779 | (adoctest-trans "lorem ! ipsum" 780 | (concat "lorem " start-del end-del " ipsum") transform) 781 | (adoctest-trans "lorem dolor" 782 | (concat "lorem " start-del "ipsum" end-del " dolor") transform)) 783 | 784 | ;; todo: test templates also with tempo-snippets 785 | (ert-deftest adoctest-test-tempo-quotes () 786 | (adoctest-quotes "_" "_" '(tempo-template-adoc-emphasis)) 787 | (adoctest-quotes "*" "*" '(tempo-template-adoc-strong)) 788 | (adoctest-quotes "+" "+" '(tempo-template-adoc-monospace)) 789 | (adoctest-quotes "`" "`" '(tempo-template-adoc-monospace-literal)) 790 | (adoctest-quotes "`" "'" '(tempo-template-adoc-single-quote)) 791 | (adoctest-quotes "``" "''" '(tempo-template-adoc-double-quote)) 792 | (adoctest-quotes "[]#" "#" '(tempo-template-adoc-attributed)) 793 | (adoctest-quotes "__" "__" '(tempo-template-adoc-emphasis-uc)) 794 | (adoctest-quotes "**" "**" '(tempo-template-adoc-strong-uc)) 795 | (adoctest-quotes "++" "++" '(tempo-template-adoc-monospace-uc)) 796 | (adoctest-quotes "^" "^" '(tempo-template-adoc-superscript)) 797 | (adoctest-quotes "~" "~" '(tempo-template-adoc-subscript))) 798 | 799 | (ert-deftest adoctest-test-tempo-formatting-misc () 800 | 801 | (adoctest-trans "" " +" '(tempo-template-adoc-line-break)) 802 | (adoctest-trans "lor!em" "lor +\nem" '(tempo-template-adoc-line-break)) 803 | ;; dont change only white sequence between point and end of line 804 | (adoctest-trans "lorem! \nipsum" "lorem + \nipsum" '(tempo-template-adoc-line-break)) 805 | ;; if befor point already a space is, dont insert a new one 806 | (adoctest-trans "lorem !\nipsum" "lorem +\nipsum" '(tempo-template-adoc-line-break)) 807 | 808 | (adoctest-trans "" "<<<" '(tempo-template-adoc-page-break)) 809 | (adoctest-trans "lorem\n!\nipsum" "lorem\n<<<\nipsum" '(tempo-template-adoc-page-break)) 810 | (adoctest-trans "lor!em\nipsum" "lor\n<<<\nem\nipsum" '(tempo-template-adoc-page-break)) 811 | 812 | (adoctest-trans "" "---" '(tempo-template-adoc-ruler-line)) 813 | (adoctest-trans "lorem\n!\nipsum" "lorem\n---\nipsum" '(tempo-template-adoc-ruler-line)) 814 | (adoctest-trans "lor!em\nipsum" "lor\n---\nem\nipsum" '(tempo-template-adoc-ruler-line))) 815 | 816 | ;; todo: check buffer position after insertion (aka transionsion). Probably 817 | ;; factor out from adoctest-trans a defun which translates a string containing 818 | ;; !s into one with the ! stripped and a buffer-position-list 819 | (ert-deftest adoctest-test-tempo-title () 820 | (let ((adoc-title-style 'adoc-title-style-one-line)) 821 | ;; BUG!! somehow I need to load adoc-mode twice until it works!!! 822 | (adoctest-trans "" "= " '(tempo-template-adoc-title-1)) 823 | (adoctest-trans "" "=== " '(tempo-template-adoc-title-3)) 824 | (adoctest-trans "lorem\n!\nipsum" "lorem\n= \nipsum" '(tempo-template-adoc-title-1))) 825 | 826 | (let ((adoc-title-style 'adoc-title-style-one-line-enclosed)) 827 | (adoctest-trans "" "= =" '(tempo-template-adoc-title-1)) 828 | (adoctest-trans "" "=== ===" '(tempo-template-adoc-title-3)) 829 | (adoctest-trans "lorem\n!\nipsum" "lorem\n= =\nipsum" '(tempo-template-adoc-title-1))) 830 | 831 | (let ((adoc-title-style 'adoc-title-style-two-line)) 832 | (adoctest-trans "" "\n====" '(tempo-template-adoc-title-1)) 833 | (adoctest-trans "" "\n~~~~" '(tempo-template-adoc-title-3)) 834 | (adoctest-trans "lorem\n!\nipsum" "lorem\n\n====\nipsum" '(tempo-template-adoc-title-1)))) 835 | 836 | (ert-deftest adoctest-test-tempo-paragraphs () 837 | (adoctest-trans "" " " '(tempo-template-adoc-literal-paragraph)) 838 | (adoctest-trans "lorem" "lorem\n ipsum" '(tempo-template-adoc-literal-paragraph)) 839 | (adoctest-trans "" "TIP: " '(tempo-template-adoc-paragraph-tip)) 840 | (adoctest-trans "lorem" "lorem\nTIP: ipsum" '(tempo-template-adoc-paragraph-tip))) 841 | 842 | (defun adoctest-delimited-block (del transform) 843 | (let ((del-line (if (integerp del) (make-string 50 del) del))) 844 | (adoctest-trans 845 | "" (concat del-line "\n\n" del-line) transform) 846 | (adoctest-trans 847 | "lorem\n!\nipsum" (concat "lorem\n" del-line "\n\n" del-line "\nipsum") transform) 848 | (adoctest-trans 849 | "lorem\n\ndolor" (concat "lorem\n" del-line "\nipsum\n" del-line "\ndolor") transform) 850 | (adoctest-trans 851 | "lorem !dolor" (concat "lorem \n" del-line "\n\n" del-line "\ndolor") transform) 852 | (adoctest-trans 853 | "lorem dolor" (concat "lorem \n" del-line "\nipsum \n" del-line "\ndolor") transform))) 854 | 855 | (ert-deftest adoctest-test-tempo-delimited-blocks () 856 | (adoctest-delimited-block ?/ '(tempo-template-adoc-delimited-block-comment)) 857 | (adoctest-delimited-block ?+ '(tempo-template-adoc-delimited-block-passthrough)) 858 | (adoctest-delimited-block ?- '(tempo-template-adoc-delimited-block-listing)) 859 | (adoctest-delimited-block ?. '(tempo-template-adoc-delimited-block-literal)) 860 | (adoctest-delimited-block ?_ '(tempo-template-adoc-delimited-block-quote)) 861 | (adoctest-delimited-block ?= '(tempo-template-adoc-delimited-block-example)) 862 | (adoctest-delimited-block ?* '(tempo-template-adoc-delimited-block-sidebar)) 863 | (adoctest-delimited-block "--" '(tempo-template-adoc-delimited-block-open-block))) 864 | 865 | (ert-deftest adoctest-test-tempo-lists () 866 | (let ((tab-width 2) 867 | (indent-tabs-mode nil)) 868 | (adoctest-trans "" "- " '(tempo-template-adoc-bulleted-list-item-1)) 869 | (adoctest-trans "" " ** " '(tempo-template-adoc-bulleted-list-item-2)) 870 | (adoctest-trans "" "- foo" '(tempo-template-adoc-bulleted-list-item-1)) 871 | (adoctest-trans "" ":: " '(tempo-template-adoc-labeled-list-item)) 872 | (adoctest-trans "" ":: foo" '(tempo-template-adoc-labeled-list-item)))) 873 | 874 | (ert-deftest adoctest-test-tempo-macros () 875 | (adoctest-trans "" "http://foo.com[]" '(tempo-template-adoc-url-caption)) 876 | (adoctest-trans "see for" "see http://foo.com[here] for" '(tempo-template-adoc-url-caption)) 877 | (adoctest-trans "" "mailto:[]" '(tempo-template-adoc-email-caption)) 878 | (adoctest-trans "ask for" "ask mailto:[bob] for" '(tempo-template-adoc-email-caption)) 879 | (adoctest-trans "" "[[]]" '(tempo-template-adoc-anchor)) 880 | (adoctest-trans "lorem dolor" "lorem [[ipsum]] dolor" '(tempo-template-adoc-anchor)) 881 | (adoctest-trans "" "anchor:[]" '(tempo-template-adoc-anchor-default-syntax)) 882 | (adoctest-trans "lorem dolor" "lorem anchor:ipsum[] dolor" '(tempo-template-adoc-anchor-default-syntax)) 883 | (adoctest-trans "" "<<,>>" '(tempo-template-adoc-xref)) 884 | (adoctest-trans "see for" "see <<,here>> for" '(tempo-template-adoc-xref)) 885 | (adoctest-trans "" "xref:[]" '(tempo-template-adoc-xref-default-syntax)) 886 | (adoctest-trans "see for" "see xref:[here] for" '(tempo-template-adoc-xref-default-syntax)) 887 | (adoctest-trans "" "image:[]" '(tempo-template-adoc-image))) 888 | 889 | (ert-deftest adoctest-test-tempo-passthroug-macros () 890 | ;; backticks are tested in adoctest-test-tempo-quotes 891 | (adoctest-trans "" "pass:[]" '(tempo-template-adoc-pass)) 892 | (adoctest-trans "lorem dolor" "lorem pass:[ipsum] dolor" '(tempo-template-adoc-pass)) 893 | (adoctest-trans "" "asciimath:[]" '(tempo-template-adoc-asciimath)) 894 | (adoctest-trans "lorem dolor" "lorem asciimath:[ipsum] dolor" '(tempo-template-adoc-asciimath)) 895 | (adoctest-trans "" "latexmath:[]" '(tempo-template-adoc-latexmath)) 896 | (adoctest-trans "lorem dolor" "lorem latexmath:[ipsum] dolor" '(tempo-template-adoc-latexmath)) 897 | (adoctest-trans "" "++++++" '(tempo-template-adoc-pass-+++)) 898 | (adoctest-trans "lorem dolor" "lorem +++ipsum+++ dolor" '(tempo-template-adoc-pass-+++)) 899 | (adoctest-trans "" "$$$$" '(tempo-template-adoc-pass-$$)) 900 | (adoctest-trans "lorem dolor" "lorem $$ipsum$$ dolor" '(tempo-template-adoc-pass-$$))) 901 | 902 | (ert-deftest adoctest-test-make-two-line-title-underline () 903 | (should (equal (adoc-make-two-line-title-underline 0 6) 904 | "======")) 905 | (should (equal (adoc-make-two-line-title-underline 2) 906 | "~~~~"))) 907 | 908 | (ert-deftest adoctest-test-repeat-string () 909 | (should (equal (adoc-repeat-string "lorem" 0) "")) 910 | (should (equal (adoc-repeat-string "lorem" 1) "lorem")) 911 | (should (equal (adoc-repeat-string "lorem" 2) "loremlorem"))) 912 | 913 | (ert-deftest adoctest-test-indent-by-example () 914 | (let ((tab-width 2) 915 | (indent-tabs-mode nil)) 916 | (adoctest-trans "" " x" '(adoc-insert-indented "x" 1)) 917 | (adoctest-trans "" " x" '(adoc-insert-indented "x" 2))) 918 | 919 | (let ((tab-width 3) 920 | (indent-tabs-mode t)) 921 | (adoctest-trans "" " x" '(adoc-insert-indented "x" 1)) 922 | (adoctest-trans "" "\t x" '(adoc-insert-indented "x" 2)))) 923 | 924 | (ert-deftest adoctest-test-imenu-create-index () 925 | (unwind-protect 926 | (progn 927 | (set-buffer (get-buffer-create "adoc-test")) 928 | (insert "= document title\n" 929 | "== chapter 1\n" 930 | "=== sub chapter 1.1\n" 931 | "chapter 2\n" 932 | "----------\n" 933 | "sub chapter 2.1\n" 934 | "~~~~~~~~~~~~~~\n") 935 | (should 936 | (equal 937 | (adoc-imenu-create-index) 938 | (list 939 | (cons "document title" 1) 940 | (cons "chapter 1" 18) 941 | (cons "sub chapter 1.1" 31) 942 | (cons "chapter 2" 51) 943 | (cons "sub chapter 2.1" 72))))) 944 | (kill-buffer "adoc-test"))) 945 | 946 | ;; purpose 947 | ;; - ensure that the latest version, i.e. the one currently in buffer(s), of 948 | ;; adoc-mode and adoc-mode-test is used for the test 949 | ;; - test that adoc-mode and adoc-mode-test are compileble & loadable 950 | ;; - ensure no *.elc are lying around because they are 'dangerous' when the 951 | ;; corresponding .el is edited regulary; dangerous because it's not unlikely 952 | ;; that the .el is newer than the .elc, but load-library takes the outdated 953 | ;; .elc. 954 | ;; 955 | ;; todo: also test for warnings 956 | (defun adoctest-save-compile-load () 957 | (unwind-protect 958 | (progn 959 | (let ((buf-adoc-mode (find-buffer-visiting "adoc-mode.el")) 960 | (buf-adoc-mode-test (find-buffer-visiting "adoc-mode-test.el"))) 961 | 962 | ;; adoc-mode 963 | (cond 964 | ((null buf-adoc-mode)) ;nop 965 | ((bufferp buf-adoc-mode) (save-buffer buf-adoc-mode)) 966 | (t (error "Multiple buffer are visiting adoc-mode.el. Save them first"))) 967 | (or (byte-compile-file (locate-library "adoc-mode.el" t)) (error "compile error")) 968 | (or (load "adoc-mode.el" nil nil t) (error "load error")) 969 | 970 | ;; adoc-mode-test 971 | (cond 972 | ((null buf-adoc-mode-test)) ;nop 973 | ((bufferp buf-adoc-mode-test) (save-buffer buf-adoc-mode-test)) 974 | (t (error "Multiple buffer are visiting adoc-mode-test.el. Save them first"))) 975 | (or (byte-compile-file (locate-library "adoc-mode-test.el" t)) (error "compile error")) 976 | (or (load "adoc-mode-test.el" nil nil t) (error "load error")))) 977 | 978 | (when (file-exists-p "adoc-mode.elc") 979 | (delete-file "adoc-mode.elc")) 980 | (when (file-exists-p "adoc-mode-test.elc") 981 | (delete-file "adoc-mode-test.elc")))) 982 | 983 | (defun adoc-test-run() 984 | (interactive) 985 | 986 | ;; ensure that a failed test can be re-run 987 | (when (get-buffer "*ert*") 988 | (kill-buffer "*ert*")) 989 | 990 | ;; ensure no no-longer test defuns exist, which would otherwise be executed 991 | (mapatoms 992 | (lambda (x) (if (string-match "^adoctest-test-" (symbol-name x)) 993 | (unintern x nil)))) 994 | 995 | (adoctest-save-compile-load) 996 | 997 | ;; todo: execute tests in an smart order: the basic/simple tests first, so 998 | ;; when a complicated test fails one knows that the simple things do work 999 | (ert-run-tests-interactively "^adoctest-test-")) 1000 | 1001 | ;;; adoc-mode-test.el ends here 1002 | -------------------------------------------------------------------------------- /adoc-mode.el: -------------------------------------------------------------------------------- 1 | ;;; adoc-mode.el --- a major-mode for editing AsciiDoc files in Emacs 2 | ;; 3 | ;; Copyright 2010-2013 Florian Kaufmann 4 | ;; 5 | ;; Author: Florian Kaufmann 6 | ;; URL: https://github.com/sensorflo/adoc-mode/wiki 7 | ;; Created: 2009 8 | ;; Version: 0.6.6 9 | ;; Package-Requires: ((markup-faces "1.0.0")) 10 | ;; Keywords: wp AsciiDoc 11 | ;; 12 | ;; This file is not part of GNU Emacs. 13 | ;; 14 | ;; This program is free software; you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation; either version 2, or (at your option) 17 | ;; any later version. 18 | ;; 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | ;; 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program; see the file COPYING. If not, write to 26 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 27 | ;; Floor, Boston, MA 02110-1301, USA. 28 | ;; 29 | ;; 30 | ;; The syntax of the following commentary section is Markdown, so the same text 31 | ;; can be used for the wiki page on GitHub. Also, each paragraph, including list 32 | ;; items, are separated by blank lines, so it also looks good on Marmalade. 33 | ;;; Commentary: 34 | ;; 35 | ;; # Introduction 36 | ;; 37 | ;; [AsciiDoc](http://www.methods.co.nz/asciidoc/) is a text document format for 38 | ;; writing short documents, articles, books and UNIX man pages. AsciiDoc files 39 | ;; can be translated to HTML and DocBook markups. 40 | ;; 41 | ;; adoc-mode is an Emacs major mode for editing AsciiDoc files. It emphasizes on 42 | ;; the idea that the document is highlighted so it pretty much looks like the 43 | ;; final output. What must be bold is bold, what must be italic is italic etc. 44 | ;; Meta characters are naturally still visible, but in a faint way, so they can 45 | ;; be easily ignored. 46 | ;; 47 | ;; 48 | ;; # Download 49 | ;; 50 | ;; The raw file (adoc-mode.el) can be found 51 | ;; [here](https://raw.github.com/sensorflo/adoc-mode/master/adoc-mode.el). 52 | ;; Optionally you can get the sources from the [git 53 | ;; repository](https://github.com/sensorflo/adoc-mode). 54 | ;; 55 | ;; You will also need to download the library 56 | ;; [markup-faces](https://github.com/sensorflo/markup-faces). If you install 57 | ;; adoc-mode via Emacs Lisp Packages, see below, markup-faces is installed 58 | ;; automatically if you don't have it yet. 59 | ;; 60 | ;; 61 | ;; # Installation 62 | ;; 63 | ;; Installation is as usual, so if you are proficient with Emacs you don't need 64 | ;; to read this. 65 | ;; 66 | ;; ## Install the traditional way 67 | ;; 68 | ;; 1. Copy the file adoc-mode.el to a directory in your load-path, e.g. 69 | ;; \~/.emacs.d. To add a specific directory to the load path, add this to our 70 | ;; initialization file (probably ~/.emacs): `(add-to-list 'load-path 71 | ;; "mypath")` 72 | ;; 73 | ;; 2. Add either of the two following lines to your initialization file. The 74 | ;; first only loads adoc mode when necessary, the 2nd always during startup 75 | ;; of Emacs. 76 | ;; 77 | ;; * `(autoload 'adoc-mode "adoc-mode" nil t)` 78 | ;; 79 | ;; * `(require 'adoc-mode)` 80 | ;; 81 | ;; 3. Optionally byte compile adoc-mode.el for faster startup: `M-x 82 | ;; byte-compile` 83 | ;; 84 | ;; 4. To use adoc mode, call adoc-mode after you opened an AsciiDoc file: `M-x 85 | ;; adoc-mode` 86 | ;; 87 | ;; 88 | ;; ## Install via Emacs Lisp Packages (on Marmalade) 89 | ;; 90 | ;; For this way you either need packages.el from 91 | ;; [here](https://github.com/technomancy/package.el) and or Emacs 24, where the 92 | ;; packages library is already included. adoc-mode is on the 93 | ;; [Marmalade](http://marmalade-repo.org/) package archive. 94 | ;; 95 | ;; * Type `M-x package-install RET adoc-mode RET`. 96 | ;; 97 | ;; 98 | ;; ## Possible steps after installation 99 | ;; 100 | ;; Each of the following is optional 101 | ;; 102 | ;; * According to AsciiDoc manual, .txt is the standard file extension of 103 | ;; AsciiDoc files. Add the following to your initialization file to open all 104 | ;; .txt files with adoc-mode as major mode automatically: `(add-to-list 105 | ;; 'auto-mode-alist (cons "\\.txt\\'" 'adoc-mode))` 106 | ;; 107 | ;; * If your default face is a fixed pitch (monospace) face, but in AsciiDoc 108 | ;; files you liked to have normal text with a variable pitch face, 109 | ;; buffer-face-mode is for you: `(add-hook 'adoc-mode-hook (lambda() 110 | ;; (buffer-face-mode t)))` 111 | ;; 112 | ;; 113 | ;; # Features 114 | ;; 115 | ;; - sophisticated highlighting 116 | ;; 117 | ;; - promote / demote title 118 | ;; 119 | ;; - toggle title type between one line title and two line title 120 | ;; 121 | ;; - adjust underline length of a two line title to match title text's length 122 | ;; 123 | ;; - goto anchor defining a given id, default reading from xref at point 124 | ;; 125 | ;; - support for outline (however only with the one-line title style) 126 | ;; 127 | ;; 128 | ;; ## Coming features 129 | ;; 130 | ;; The next features I plan to implement 131 | ;; 132 | ;; - Demote / promote for list items 133 | ;; - Outline support also for two line titles 134 | ;; - Correctly highlighting backslash escapes 135 | ;; 136 | ;; 137 | ;; # Screenshot 138 | ;; 139 | ;; The highlighting emphasizes on how the output will look like. _All_ 140 | ;; characters are visible, however meta characters are displayed in a faint way. 141 | ;; 142 | ;; ![screenshot](http://dl.dropbox.com/u/75789984/adoc-mode.png) 143 | ;; 144 | ;; 145 | ;;; Todo: 146 | ;; - Fontlock 147 | ;; - make font-lock regexps based upon AsciiDoc configuration file, or make 148 | ;; them configurable in a way similar to that configuration file 149 | ;; - respect font-lock-maximum-decoration 150 | ;; - delimited blocks are supported, but not well at all 151 | ;; - Most regexps for highlighting can spawn at most over two lines. 152 | ;; - font-lock's multi line capabilities are not used well enough. At least 2 153 | ;; line spawns should be covered - replace all .*? by .*?\\(?:\n.*?\\)?? 154 | ;; - backslash escapes are seldom highlighted correctly 155 | ;; - Other common Emacs functionality/features 156 | ;; - demote/promote/create/delete titles/list-items. Also put emphasis on a 157 | ;; convenient simple user interface. 158 | ;; - hideshow 159 | ;; - outline mode shall support two line titles 160 | ;; - tags tables for anchors, indixes, bibliography items, titles, ... 161 | ;; - spell check shall ignore meta characters 162 | ;; - supply a regexp for magic-mode-alist 163 | ;; - Is there something that would remove hard newlines within a paragraph, 164 | ;; but just for display, so the paragraph uses the whole buffer length. 165 | ;; - are there generic base packages to handle lists / tables? 166 | ;; - a read only view mode where commands for navigation are on short key 167 | ;; bindings like alphanum letters 168 | ;; - study what other markup modes like rst offer 169 | ;; - AsciiDoc related features 170 | ;; - Two (or gradually fading) display modes: one emphasises to see the 171 | ;; AsciiDoc source text, the other emphasises to see how the output will 172 | ;; look like. Or even hide meta characters all together 173 | 174 | ;;; Variables: 175 | 176 | (require 'markup-faces) ; https://github.com/sensorflo/markup-faces 177 | (require 'cl) ; I know, I should remove it, I will, eventually 178 | ;; tempo or tempo-snippet is required later below 179 | 180 | 181 | (defconst adoc-mode-version "0.6.6" 182 | "adoc mode version number. 183 | 184 | Based upon AsciiDoc version 8.5.2. I.e. regexeps and rules are 185 | taken from that version's asciidoc.conf / manual.") 186 | 187 | 188 | ;;;; customization 189 | (defgroup adoc nil 190 | "Major-mode for editing AsciiDoc files in Emacs. 191 | 192 | Most faces adoc-mode uses belong to the markup-faces 193 | customization group, see link below, and have to be customized 194 | there. adoc-mode has only a few faces of its own, which can be 195 | customized on this page." 196 | :group 'wp 197 | :link '(custom-group-link markup-faces)) 198 | 199 | (defcustom adoc-script-raise '(-0.3 0.3) 200 | "How much to lower and raise subscript and superscript content. 201 | 202 | This is a list of two floats. The first is negative and specifies 203 | how much subscript is lowered, the second is positive and 204 | specifies how much superscript is raised. Heights are measured 205 | relative to that of the normal text. The faces used are 206 | markup-superscript-face and markup-subscript-face respectively. 207 | 208 | You need to call `adoc-calc' after a change." 209 | :type '(list (float :tag "Subscript") 210 | (float :tag "Superscript")) 211 | :group 'adoc) 212 | 213 | ;; Interacts very badly with minor-modes using overlays because 214 | ;; adoc-unfontify-region-function removes ALL overlays, not only those which 215 | ;; where insered by adoc-mode. 216 | (defcustom adoc-insert-replacement nil 217 | "When non-nil the character/string a replacment/entity stands for is displayed. 218 | 219 | E.g. after '&' an '&' is displayed, after '(C)' the copy right 220 | sign is displayed. It's only about display, neither the file nor 221 | the buffer content is affected. 222 | 223 | You need to call `adoc-calc' after you change 224 | `adoc-insert-replacement'. For named character entities (e.g. 225 | '&', in contrast to '' or '(C)' ) to be displayed you 226 | need to set `adoc-unichar-name-resolver'. 227 | 228 | Setting it to non-nil interacts very badly with minor-modes using 229 | overlays." 230 | :type 'boolean 231 | :group 'adoc) 232 | 233 | (defcustom adoc-unichar-name-resolver nil 234 | "Function taking a unicode char name and returing it's codepoint. 235 | 236 | E.g. when given \"amp\" (as in the character entity reference 237 | \"&\"), it shall return 38 (#x26). Is used to insert the 238 | character a character entity reference is refering to after the 239 | entity. When adoc-unichar-name-resolver is nil, or when its 240 | function returns nil, nothing is done with named character 241 | entities. Note that if `adoc-insert-replacement' is nil, 242 | adoc-unichar-name-resolver is not used. 243 | 244 | You can set it to `adoc-unichar-by-name'; however it requires 245 | unichars.el (http://nwalsh.com/emacs/xmlchars/unichars.el). When 246 | you set adoc-unichar-name-resolver to adoc-unichar-by-name, you 247 | need to call `adoc-calc' for the change to take effect." 248 | :type '(choice (const nil) 249 | (const adoc-unichar-by-name) 250 | function) 251 | :group 'adoc) 252 | 253 | (defcustom adoc-two-line-title-del '("==" "--" "~~" "^^" "++") 254 | "Delimiter used for the underline of two line titles. 255 | Each string must be exactly 2 characters long. Corresponds to the 256 | underlines element in the titles section of the asciidoc 257 | configuration file." 258 | :type '(list 259 | (string :tag "level 0") 260 | (string :tag "level 1") 261 | (string :tag "level 2") 262 | (string :tag "level 3") 263 | (string :tag "level 4") ) 264 | :group 'adoc) 265 | 266 | (defcustom adoc-delimited-block-del 267 | '("^/\\{4,\\}" ; 0 comment 268 | "^\\+\\{4,\\}" ; 1 pass 269 | "^-\\{4,\\}" ; 2 listing 270 | "^\\.\\{4,\\}" ; 3 literal 271 | "^_\\{4,\\}" ; 4 quote 272 | "^=\\{4,\\}" ; 5 example 273 | "^\\*\\{4,\\}" ; 6 sidebar 274 | "^--") ; 7 open block 275 | "Regexp used for delimited blocks. 276 | 277 | WARNING: They should not contain a $. It is implied that they 278 | match up to end of the line; 279 | 280 | They correspond to delimiter variable blockdef-xxx sections in 281 | the AsciiDoc configuration file. 282 | 283 | However contrary to the AsciiDoc configuration file a separate 284 | regexp can be given for the start line and for the end line. You 285 | may want to do that because adoc-mode often can't properly 286 | distinguish between a) a two line tile b) start of a delimited 287 | block and c) end of a delimited block. If you start a listing 288 | delimited block with '>----' and end it with '<----', then all 289 | three cases can easily be distinguished. The regexp in your 290 | AsciiDoc config file would the probably be '^[<>]-{4,}$'" 291 | :type '(list 292 | (choice :tag "comment" 293 | (regexp :tag "start/end regexp") 294 | (list :tag "separate regexp" 295 | (regexp :tag "start regexp") 296 | (regexp :tag "end regexp"))) 297 | (choice :tag "pass" 298 | (regexp :tag "start/end regexp") 299 | (list :tag "separate regexp" 300 | (regexp :tag "start regexp") 301 | (regexp :tag "end regexp"))) 302 | (choice :tag "listing" 303 | (regexp :tag "start/end regexp") 304 | (list :tag "separate regexp" 305 | (regexp :tag "start regexp") 306 | (regexp :tag "end regexp"))) 307 | (choice :tag "literal" 308 | (regexp :tag "start/end regexp") 309 | (list :tag "separate regexp" 310 | (regexp :tag "start regexp") 311 | (regexp :tag "end regexp"))) 312 | (choice :tag "quote" 313 | (regexp :tag "start/end regexp") 314 | (list :tag "separate regexp" 315 | (regexp :tag "start regexp") 316 | (regexp :tag "end regexp"))) 317 | (choice :tag "example" 318 | (regexp :tag "start/end regexp") 319 | (list :tag "separate regexp" 320 | (regexp :tag "start regexp") 321 | (regexp :tag "end regexp"))) 322 | (choice :tag "sidebar" 323 | (regexp :tag "start/end regexp") 324 | (list :tag "separate regexp" 325 | (regexp :tag "start regexp") 326 | (regexp :tag "end regexp"))) 327 | (choice :tag "open" 328 | (regexp :tag "start/end regexp") 329 | (list :tag "separate regexp" 330 | (regexp :tag "start regexp") 331 | (regexp :tag "end regexp"))))) 332 | 333 | ;; todo: limit value range to 1 or 2 334 | (defcustom adoc-default-title-type 1 335 | "Default title type, see `adoc-title-descriptor'." 336 | :group 'adoc) 337 | 338 | ;; todo: limit value range to 1 or 2 339 | (defcustom adoc-default-title-sub-type 1 340 | "Default title sub type, see `adoc-title-descriptor'." 341 | :group 'adoc ) 342 | 343 | (defcustom adoc-enable-two-line-title t 344 | "Wether or not two line titles shall be fontified. 345 | 346 | nil means never fontify. t means always fontify. A number means 347 | only fontify if the line below has NOT the lenght of the given 348 | number. You could use a number for example when all your 349 | delimited block lines have a certain length. 350 | 351 | This is usefull because adoc-mode has troubles to properly 352 | distinguish between two line titles and a line of text before a 353 | delimited block. Note however that adoc-mode knows the AsciiDoc 354 | rule that the length of a two line title underline can differ at 355 | most 3 chars from the length of the title text." 356 | :type '(choice (const nil) 357 | (const t) 358 | number) 359 | :group 'adoc) 360 | 361 | (defcustom adoc-title-style 'adoc-title-style-one-line 362 | "Title style used for title tempo templates. 363 | 364 | See for example `tempo-template-adoc-title-1'." 365 | :type '(choice (const :tag "== one line" adoc-title-style-one-line) 366 | (const :tag "== one line enclosed ==" adoc-title-style-one-line-enclosed) 367 | (const :tag "two line\\n--------" adoc-title-style-two-line)) 368 | :group 'adoc) 369 | 370 | (defcustom adoc-tempo-frwk 'tempo-vanilla 371 | "Tempo framework to be used by adoc's templates. " 372 | :type '(choice (const :tag "tempo" tempo-vanilla) 373 | (const :tag "tempo-snippets" tempo-snippets)) 374 | :group 'adoc) 375 | 376 | 377 | ;;;; faces / font lock 378 | (define-obsolete-face-alias 'adoc-orig-default 'adoc-align "23.3") 379 | (defface adoc-align 380 | '((t (:inherit (markup-meta-face)))) 381 | "Face used so the text looks left aligned. 382 | 383 | Is applied to whitespaces at the beginning of a line. You want to 384 | set it to a fixed width face. This is useful if your default face 385 | is a variable with face. Because for e.g. in a variable with 386 | face, '- ' and ' ' (two spaces) don't have equal with, with 387 | `adoc-align' in the following example the item's text looks 388 | aligned. 389 | 390 | - lorem ipsum 391 | dolor ..." 392 | :group 'adoc) 393 | 394 | (define-obsolete-face-alias 'adoc-generic 'markup-gen-face "23.3") 395 | (define-obsolete-face-alias 'adoc-monospace 'markup-typewriter-face "23.3") 396 | (define-obsolete-face-alias 'adoc-strong 'markup-strong-face "23.3") 397 | (define-obsolete-face-alias 'adoc-emphasis 'markup-emphasis-face "23.3") 398 | (define-obsolete-face-alias 'adoc-superscript 'markup-superscript-face "23.3") 399 | (define-obsolete-face-alias 'adoc-subscript 'markup-subscript-face "23.3") 400 | (define-obsolete-face-alias 'adoc-secondary-text 'markup-secondary-text-face "23.3") 401 | (define-obsolete-face-alias 'adoc-replacement 'markup-replacement-face "23.3") 402 | (define-obsolete-face-alias 'adoc-complex-replacement 'markup-complex-replacement-face "23.3") 403 | (define-obsolete-face-alias 'adoc-list-item 'markup-list-face "23.3") 404 | (define-obsolete-face-alias 'adoc-table-del 'markup-table-face "23.3") 405 | (define-obsolete-face-alias 'adoc-reference 'markup-reference-face "23.3") 406 | (define-obsolete-face-alias 'adoc-delimiter 'markup-meta-face "23.3") 407 | (define-obsolete-face-alias 'adoc-hide-delimiter 'markup-hide-delimiter-face "23.3") 408 | (define-obsolete-face-alias 'adoc-anchor 'markup-anchor-face "23.3") 409 | (define-obsolete-face-alias 'adoc-comment 'markup-comment-face "23.3") 410 | (define-obsolete-face-alias 'adoc-warning 'markup-error-face "23.3") 411 | (define-obsolete-face-alias 'adoc-preprocessor 'markup-preprocessor-face "23.3") 412 | 413 | ;; Despite the comment in font-lock.el near 'defvar font-lock-comment-face', it 414 | ;; seems I still need variables to refer to faces in adoc-font-lock-keywords. 415 | ;; Not having variables and only referring to face names in 416 | ;; adoc-font-lock-keywords does not work. 417 | (defvar adoc-align 'adoc-align) 418 | (defvar adoc-generic 'markup-gen-face) 419 | (defvar adoc-monospace 'markup-typewriter-face) 420 | (defvar adoc-replacement 'markup-replacement-face) 421 | (defvar adoc-complex-replacement 'markup-complex-replacement-face) 422 | (defvar adoc-table-del 'markup-table-face) 423 | (defvar adoc-reference 'markup-reference-face) 424 | (defvar adoc-secondary-text 'markup-secondary-text-face) 425 | (defvar adoc-delimiter 'markup-meta-face) 426 | (defvar adoc-hide-delimiter 'markup-meta-hide-face) 427 | (defvar adoc-anchor 'markup-anchor-face) 428 | (defvar adoc-comment 'markup-comment-face) 429 | (defvar adoc-warning 'markup-error-face) 430 | (defvar adoc-preprocessor 'markup-preprocessor-face) 431 | 432 | 433 | ;;;; misc 434 | (defconst adoc-title-max-level 4 435 | "Max title level, counting starts at 0.") 436 | 437 | (defconst adoc-uolist-max-level 5 438 | "Max unordered (bulleted) list item nesting level, counting starts at 0.") 439 | 440 | ;; I think it's actually not worth the fuzz to try to sumarize regexps until 441 | ;; profiling profes otherwise. Nevertheless I can't stop doing it. 442 | (defconst adoc-summarize-re-uolisti t 443 | "When non-nil, sumarize regexps for unordered list items into one regexp. 444 | To become a customizable variable when regexps for list items become customizable.") 445 | 446 | (defconst adoc-summarize-re-olisti t 447 | "As `adoc-summarize-re-uolisti', but for ordered list items.") 448 | 449 | (defconst adoc-summarize-re-llisti t 450 | "As `adoc-summarize-re-uolisti', but for labeled list items.") 451 | 452 | (defvar adoc-unichar-alist nil 453 | "An alist, key=unicode character name as string, value=codepoint.") 454 | 455 | ;; altough currently always the same face is used, I prefer an alist over a 456 | ;; list. It is faster to find out wheter any attribute id is in the alist or 457 | ;; not. And maybe markup-faces splits up markup-secondary-text-face into more 458 | ;; specific faces. 459 | (defvar adoc-attribute-face-alist 460 | '(("id" . markup-anchor-face) 461 | ("caption" . markup-secondary-text-face) 462 | ("xreflabel" . markup-secondary-text-face) 463 | ("alt" . markup-secondary-text-face) 464 | ("title" . markup-secondary-text-face) 465 | ("attribution" . markup-secondary-text-face) 466 | ("citetitle" . markup-secondary-text-face) 467 | ("text" . markup-secondary-text-face)) 468 | "An alist, key=attribute id, value=face.") 469 | 470 | (defvar adoc-mode-abbrev-table nil 471 | "Abbrev table in use in adoc-mode buffers.") 472 | 473 | (defvar adoc-font-lock-keywords nil 474 | "Font lock keywords in adoc-mode buffers.") 475 | 476 | (defvar adoc-replacement-failed nil ) 477 | 478 | (define-abbrev-table 'adoc-mode-abbrev-table ()) 479 | 480 | (defvar adoc-mode-map 481 | (let ((map (make-sparse-keymap))) 482 | (define-key map "\C-c\C-d" 'adoc-demote) 483 | (define-key map "\C-c\C-p" 'adoc-promote) 484 | (define-key map "\C-c\C-t" 'adoc-toggle-title-type) 485 | (define-key map "\C-c\C-g" 'adoc-goto-ref-label) 486 | map) 487 | "Keymap used in adoc mode.") 488 | 489 | 490 | ;;;; help text copied from asciidoc manual 491 | (defconst adoc-help-constrained-quotes 492 | "Constrained quotes must be bounded by white space or commonly 493 | adjoining punctuation characters. These are the most commonly 494 | used type of quote.") 495 | (defconst adoc-help-emphasis 496 | "Usually rendered italic") 497 | (defconst adoc-help-strong 498 | "Usually rendered bold") 499 | (defconst adoc-help-monospace 500 | "Aka typewritter. This does _not_ mean verbatim / literal") 501 | (defconst adoc-help-single-quote 502 | "Single quotation marks around enclosed text.") 503 | (defconst adoc-help-double-quote 504 | "Quotation marks around enclosed text.") 505 | (defconst adoc-help-attributed 506 | "A mechanism to allow inline attributes to be applied to 507 | otherwise unformatted text.") 508 | (defconst adoc-help-unconstrained-quotes 509 | "Unconstrained quotes have no boundary constraints and can be 510 | placed anywhere within inline text.") 511 | (defconst adoc-help-line-break 512 | "A plus character preceded by at least one space character at 513 | the end of a non-blank line forces a line break. It generates a 514 | line break (`br`) tag for HTML outputs and a custom XML 515 | `asciidoc-br` processing instruction for DocBook outputs") 516 | (defconst adoc-help-page-break 517 | "A line of three or more less-than (`<<<`) characters will 518 | generate a hard page break in DocBook and printed HTML 519 | outputs.") 520 | (defconst adoc-help-ruler-line 521 | "A line of three or more apostrophe characters will generate a 522 | ruler line. It generates a ruler (`hr`) tag for HTML outputs 523 | and a custom XML `asciidoc-hr` processing instruction for 524 | DocBook outputs.") 525 | (defconst adoc-help-entity-reference 526 | "You can also include arbitrary character entity references in 527 | the AsciiDoc source. Example both `&` and `&` are 528 | replace by an & (ampersand).") 529 | (defconst adoc-help-literal-paragraph 530 | "Verbatim in a monospaced font. Applyied to paragraphs where 531 | the first line is indented by one or more space or tab 532 | characters") 533 | (defconst adoc-help-delimited-block 534 | "Delimited blocks are blocks of text enveloped by leading and 535 | trailing delimiter lines (normally a series of four or more 536 | repeated characters).") 537 | (defconst adoc-help-delimited-block-comment 538 | "The contents of 'CommentBlocks' are not processed; they are 539 | useful for annotations and for excluding new or outdated 540 | content that you don't want displayed. CommentBlocks are never 541 | written to output files.") 542 | (defconst adoc-help-delimited-block-passthrouh 543 | "By default the block contents is subject only to 'attributes' 544 | and 'macros' substitutions (use an explicit 'subs' attribute to 545 | apply different substitutions). PassthroughBlock content will 546 | often be backend specific. The following styles can be applied 547 | to passthrough blocks: pass:: No substitutions are performed. 548 | This is equivalent to `subs=\"none\"`. asciimath, latexmath:: 549 | By default no substitutions are performed, the contents are 550 | rendered as mathematical formulas.") 551 | (defconst adoc-help-delimited-block-listing 552 | "'ListingBlocks' are rendered verbatim in a monospaced font, 553 | they retain line and whitespace formatting and are often 554 | distinguished by a background or border. There is no text 555 | formatting or substitutions within Listing blocks apart from 556 | Special Characters and Callouts. Listing blocks are often used 557 | for computer output and file listings.") 558 | (defconst adoc-help-delimited-block-literal 559 | "'LiteralBlocks' are rendered just like literal paragraphs.") 560 | (defconst adoc-help-delimited-block-quote 561 | "'QuoteBlocks' are used for quoted passages of text. There are 562 | two styles: 'quote' and 'verse'. The style behavior is 563 | identical to quote and verse paragraphs except that blocks can 564 | contain multiple paragraphs and, in the case of the 'quote' 565 | style, other section elements. The first positional attribute 566 | sets the style, if no attributes are specified the 'quote' 567 | style is used. The optional 'attribution' and 'citetitle' 568 | attributes (positional attributes 2 and3) specify the quote's 569 | author and source.") 570 | (defconst adoc-help-delimited-block-example 571 | "'ExampleBlocks' encapsulate the DocBook Example element and 572 | are used for, well, examples. Example blocks can be titled by 573 | preceding them with a 'BlockTitle'. DocBook toolchains will 574 | normally automatically number examples and generate a 'List of 575 | Examples' backmatter section. 576 | 577 | Example blocks are delimited by lines of equals characters and 578 | can contain any block elements apart from Titles, BlockTitles 579 | and Sidebars) inside an example block.") 580 | (defconst adoc-help-delimited-block-sidebar 581 | "A sidebar is a short piece of text presented outside the 582 | narrative flow of the main text. The sidebar is normally 583 | presented inside a bordered box to set it apart from the main 584 | text.The sidebar body is treated like a normal section body.") 585 | (defconst adoc-help-delimited-block-open-block 586 | "An 'OpenBlock' groups a set of block elements.") 587 | (defconst adoc-help-list 588 | "Indentation is optional and does _not_ determine nesting.") 589 | (defconst adoc-help-bulleted-list 590 | "Aka itimized or unordered.") 591 | (defconst adoc-help-list-item-continuation 592 | "Another list or a literal paragraph immediately following a 593 | list item is implicitly appended to the list item; to append 594 | other block elements to a list item you need to explicitly join 595 | them to the list item with a 'list continuation' (a separator 596 | line containing a single plus character). Multiple block 597 | elements can be appended to a list item using list 598 | continuations (provided they are legal list item children in 599 | the backend markup).") 600 | (defconst adoc-help-table 601 | "The AsciiDoc table syntax looks and behaves like other 602 | delimited block types and supports standard block configuration 603 | entries.") 604 | (defconst adoc-help-macros 605 | "Inline Macros occur in an inline element context. A Block 606 | macro reference must be contained in a single line separated 607 | either side by a blank line or a block delimiter. Block macros 608 | behave just like Inline macros, with the following differences: 609 | 1) They occur in a block context. 2) The default syntax is 610 | ::[] (two colons, not one). 3) Markup 611 | template section names end in -blockmacro instead of 612 | -inlinemacro.") 613 | (defconst adoc-help-url 614 | "If you don’t need a custom link caption you can enter the 615 | http, https, ftp, file URLs and email addresses without any 616 | special macro syntax.") 617 | (defconst adoc-help-anchor 618 | "Used to specify hypertext link targets. The `` is a unique 619 | string that conforms to the output markup's anchor syntax. The 620 | optional `` is the text to be displayed by 621 | captionless 'xref' macros that refer to this anchor.") 622 | (defconst adoc-help-xref 623 | "Creates a hypertext link to a document anchor. The `` 624 | refers to an anchor ID. The optional `` is the link's 625 | displayed text.") 626 | (defconst adoc-help-local-doc-link 627 | "Hypertext links to files on the local file system are 628 | specified using the link inline macro.") 629 | (defconst adoc-help-local-doc-link 630 | "Hypertext links to files on the local file system are 631 | specified using the link inline macro.") 632 | (defconst adoc-help-comment 633 | "Single lines starting with two forward slashes hard up against 634 | the left margin are treated as comments. Comment lines do not 635 | appear in the output unless the showcomments attribute is 636 | defined. Comment lines have been implemented as both block and 637 | inline macros so a comment line can appear as a stand-alone 638 | block or within block elements that support inline macro 639 | expansion.") 640 | (defconst adoc-help-passthrough-macros 641 | "Passthrough macros are analogous to passthrough blocks and are 642 | used to pass text directly to the output. The substitution 643 | performed on the text is determined by the macro definition but 644 | can be overridden by the . Passthroughs, by 645 | definition, take precedence over all other text 646 | substitutions.") 647 | (defconst adoc-help-pass 648 | "Inline and block. Passes text unmodified (apart from 649 | explicitly specified substitutions).") 650 | (defconst adoc-help-asciimath 651 | "Inline and block. Passes text unmodified. A (backend 652 | dependent) mechanism for rendering mathematical formulas given 653 | using the ASCIIMath syntax.") 654 | (defconst adoc-help-latexmath 655 | "Inline and block. Passes text unmodified. A (backend 656 | dependent) mechanism for rendering mathematical formulas given 657 | using the LaTeX math syntax.") 658 | (defconst adoc-help-pass-+++ 659 | "Inline and block. The triple-plus passthrough is functionally 660 | identical to the pass macro but you don’t have to escape ] 661 | characters and you can prefix with quoted attributes in the 662 | inline version.") 663 | (defconst adoc-help-pass-$$ 664 | "Inline and block. The double-dollar passthrough is 665 | functionally identical to the triple-plus passthrough with one 666 | exception: special characters are escaped.") 667 | (defconst adoc-help-monospace-literal 668 | "Text quoted with single backtick characters constitutes an 669 | inline literal passthrough. The enclosed text is rendered in a 670 | monospaced font and is only subject to special character 671 | substitution. This makes sense since monospace text is usually 672 | intended to be rendered literally and often contains characters 673 | that would otherwise have to be escaped. If you need monospaced 674 | text containing inline substitutions use a plus character 675 | instead of a backtick.") 676 | 677 | 678 | ;;; Code: 679 | 680 | ;;;; regexps 681 | ;; from AsciiDoc manual: The attribute name/value syntax is a single line ... 682 | ;; from asciidoc.conf: 683 | ;; ^:(?P\w[^.]*?)(\.(?P.*?))?:(\s+(?P.*))?$ 684 | ;; asciidoc src code: AttributeEntry.isnext shows that above regexp is matched 685 | ;; against single line. 686 | (defun adoc-re-attribute-entry () 687 | (concat "^\\(:[a-zA-Z0-9_][^.\n]*?\\(?:\\..*?\\)?:[ \t]*\\)\\(.*?\\)$")) 688 | 689 | ;; from asciidoc.conf: ^= +(?P[\S].*?)( +=)?$ 690 | ;; asciidoc src code: Title.isnext reads two lines, which are then parsed by 691 | ;; Title.parse. The second line is only for the underline of two line titles. 692 | (defun adoc-re-one-line-title (level) 693 | "Returns a regex matching a one line title of the given LEVEL. 694 | When LEVEL is nil, a one line title of any level is matched. 695 | 696 | match-data has these sub groups: 697 | 1 leading delimiter inclusive whites between delimiter and title text 698 | 2 title's text exclusive leading/trailing whites 699 | 3 trailing delimiter with all whites 700 | 4 trailing delimiter only inclusive whites between title text and delimiter 701 | 0 only chars that belong to the title block element 702 | 703 | == my title == n 704 | ---12------23------ 705 | 4--4" 706 | (let* ((del (if level 707 | (make-string (+ level 1) ?=) 708 | (concat "=\\{1," (+ adoc-title-max-level 1) "\\}")))) 709 | (concat 710 | "^\\(" del "[ \t]+\\)" ; 1 711 | "\\([^ \t\n].*?\\)" ; 2 712 | ;; using \n instad $ is important so group 3 is guaranteed to be at least 1 713 | ;; char long (except when at the end of the buffer()). That is important to 714 | ;; to have a place to put the text property adoc-reserved on. 715 | "\\(\\([ \t]+" del "\\)?[ \t]*\\(?:\n\\|\\'\\)\\)" ))) ; 3 & 4 716 | 717 | (defun adoc-make-one-line-title (sub-type level text) 718 | "Returns a one line title of LEVEL and SUB-TYPE containing the given text." 719 | (let ((del (make-string (+ level 1) ?=))) 720 | (concat del " " text (when (eq sub-type 2) (concat " " del))))) 721 | 722 | ;; AsciiDoc handles that by source code, there is no regexp in AsciiDoc. See 723 | ;; also adoc-re-one-line-title. 724 | (defun adoc-re-two-line-title-undlerline (&optional del) 725 | "Returns a regexp matching the underline of a two line title. 726 | 727 | DEL is an element of `adoc-two-line-title-del' or nil. If nil, 728 | any del is matched. 729 | 730 | Note that even if this regexp matches it still doesn't mean it is 731 | a two line title underline, see also `adoc-re-two-line-title'." 732 | (concat 733 | "\\(" 734 | (mapconcat 735 | (lambda(x) 736 | (concat 737 | "\\(?:" 738 | "\\(?:" (regexp-quote x) "\\)+" 739 | (regexp-quote (substring x 0 1)) "?" 740 | "\\)")) 741 | (if del (list del) adoc-two-line-title-del) "\\|") 742 | ;; adoc-re-two-line-title shall have same behaviour als one line, thus 743 | ;; also here use \n instead $ 744 | "\\)[ \t]*\\(?:\n\\|\\'\\)")) 745 | 746 | ;; asciidoc.conf: regexp for _first_ line 747 | ;; ^(?P<title>.*?)$ additionally, must contain (?u)\w, see Title.parse 748 | (defun adoc-re-two-line-title (del) 749 | "Returns a regexps that matches a two line title. 750 | 751 | Note that even if this regexp matches it still doesn't mean it is 752 | a two line title. You additionaly have to test if the underline 753 | has the correct length. 754 | 755 | DEL is described in `adoc-re-two-line-title-undlerline'. 756 | 757 | match-data has these sub groups: 758 | 759 | 1 dummy, so that group 2 is the title's text as in 760 | adoc-re-one-line-title 761 | 2 title's text 762 | 3 delimiter 763 | 0 only chars that belong to the title block element" 764 | (when (not (eq (length del) 2)) 765 | (error "two line title delimiters must be 2 chars long")) 766 | (concat 767 | ;; 1st line: title text must contain at least one \w character, see 768 | ;; asciidoc src, Title.parse, 769 | "\\(\\)\\(^.*?[a-zA-Z0-9_].*?\\)[ \t]*\n" 770 | ;; 2nd line: underline 771 | (adoc-re-two-line-title-undlerline del))) 772 | 773 | (defun adoc-make-two-line-title (level text) 774 | "Returns a two line title of given LEVEL containing given TEXT. 775 | LEVEL starts at 1." 776 | (concat text "\n" (adoc-make-two-line-title-underline level (length text)))) 777 | 778 | (defun adoc-make-two-line-title-underline (level &optional length) 779 | "Returns a two line title underline. 780 | LEVEL is the level of the title, starting at 1. LENGTH is the 781 | line of the title's text. When nil it defaults to 4." 782 | (unless length 783 | (setq length 4)) 784 | (let* ((repetition-cnt (if (>= length 2) (/ length 2) 1)) 785 | (del (nth level adoc-two-line-title-del)) 786 | (result "")) 787 | (while (> repetition-cnt 0) 788 | (setq result (concat result del)) 789 | (setq repetition-cnt (- repetition-cnt 1))) 790 | (when (eq (% length 2) 1) 791 | (setq result (concat result (substring del 0 1)))) 792 | result)) 793 | 794 | (defun adoc-re-oulisti (type &optional level sub-type) 795 | "Returns a regexp matching an (un)ordered list item. 796 | 797 | match-data his this sub groups: 798 | 1 leading whites 799 | 2 delimiter 800 | 3 trailing whites between delimiter and item's text 801 | 0 only chars belonging to delimiter/whites. I.e. none of text. 802 | 803 | WARNING: See warning about list item nesting level in `adoc-list-descriptor'." 804 | (cond 805 | 806 | ;; ^\s*- +(?P<text>.+)$ normal 0 807 | ;; ^\s*\* +(?P<text>.+)$ normal 1 808 | ;; ... ... 809 | ;; ^\s*\*{5} +(?P<text>.+)$ normal 5 810 | ;; ^\+ +(?P<text>.+)$ bibliograpy(DEPRECATED) 811 | ((eq type 'adoc-unordered) 812 | (cond 813 | ((or (eq sub-type 'adoc-normal) (null sub-type)) 814 | (let ((r (cond ((numberp level) (if (eq level 0) "-" (make-string level ?\*))) 815 | ((or (null level) (eq level 'adoc-all-levels)) "-\\|\\*\\{1,5\\}") 816 | (t (error "adoc-unordered/adoc-normal: invalid level"))))) 817 | (concat "^\\([ \t]*\\)\\(" r "\\)\\([ \t]+\\)"))) 818 | ((and (eq sub-type 'adoc-bibliography) (null level)) 819 | "^\\(\\)\\(\\+\\)\\([ \t]+\\)") 820 | (t (error "adoc-unordered: invalid sub-type/level combination")))) 821 | 822 | ;; ^\s*(?P<index>\d+\.) +(?P<text>.+)$ decimal = 0 823 | ;; ^\s*(?P<index>[a-z]\.) +(?P<text>.+)$ lower alpha = 1 824 | ;; ^\s*(?P<index>[A-Z]\.) +(?P<text>.+)$ upper alpha = 2 825 | ;; ^\s*(?P<index>[ivx]+\)) +(?P<text>.+)$ lower roman = 3 826 | ;; ^\s*(?P<index>[IVX]+\)) +(?P<text>.+)$ upper roman = 4 827 | ((eq type 'adoc-explicitly-numbered) 828 | (when level (error "adoc-explicitly-numbered: invalid level")) 829 | (let* ((l '("[0-9]+\\." "[a-z]\\." "[A-Z]\\." "[ivx]+)" "[IVX]+)")) 830 | (r (cond ((numberp sub-type) (nth sub-type l)) 831 | ((or (null sub-type) (eq sub-type 'adoc-all-subtypes)) (mapconcat 'identity l "\\|")) 832 | (t (error "adoc-explicitly-numbered: invalid subtype"))))) 833 | (concat "^\\([ \t]*\\)\\(" r "\\)\\([ \t]+\\)"))) 834 | 835 | ;; ^\s*\. +(?P<text>.+)$ normal 0 836 | ;; ^\s*\.{2} +(?P<text>.+)$ normal 1 837 | ;; ... etc until 5 ... 838 | ((eq type 'adoc-implicitly-numbered) 839 | (let ((r (cond ((numberp level) (number-to-string (+ level 1))) 840 | ((or (null level) (eq level 'adoc-all-levels)) "1,5") 841 | (t (error "adoc-implicitly-numbered: invalid level"))))) 842 | (concat "^\\([ \t]*\\)\\(\\.\\{" r "\\}\\)\\([ \t]+\\)"))) 843 | 844 | ;; ^<?(?P<index>\d*>) +(?P<text>.+)$ callout 845 | ((eq type 'adoc-callout) 846 | (when (or level sub-type) (error "adoc-callout invalid level/sub-type")) 847 | "^\\(\\)\\(<?[0-9]*>\\)\\([ \t]+\\)") 848 | 849 | ;; invalid 850 | (t (error "invalid (un)ordered list type")))) 851 | 852 | (defun adoc-make-uolisti (level is-1st-line) 853 | "Returns a regexp matching a unordered list item." 854 | (let* ((del (if (eq level 0) "-" (make-string level ?\*))) 855 | (white-1st (if indent-tabs-mode 856 | (make-string (/ (* level standard-indent) tab-width) ?\t) 857 | (make-string (* level standard-indent) ?\ ))) 858 | (white-rest (make-string (+ (length del) 1) ?\ ))) 859 | (if is-1st-line 860 | (concat white-1st del " ") 861 | white-rest))) 862 | 863 | (defun adoc-re-llisti (type level) 864 | "Returns a regexp matching a labeled list item. 865 | Subgroups: 866 | 1 leading blanks 867 | 2 label text, incl whites before delimiter 868 | 3 delimiter incl trailing whites 869 | 4 delimiter only 870 | 871 | foo :: bar 872 | -12--23-3 873 | 44" 874 | (cond 875 | ;; ^\s*(?P<label>.*[^:])::(\s+(?P<text>.+))?$ normal 0 876 | ;; ^\s*(?P<label>.*[^;]);;(\s+(?P<text>.+))?$ normal 1 877 | ;; ^\s*(?P<label>.*[^:]):{3}(\s+(?P<text>.+))?$ normal 2 878 | ;; ^\s*(?P<label>.*[^:]):{4}(\s+(?P<text>.+))?$ normal 3 879 | ((eq type 'adoc-labeled-normal) 880 | (let* ((deluq (nth level '("::" ";;" ":::" "::::"))) ; unqutoed 881 | (del (regexp-quote deluq)) 882 | (del1st (substring deluq 0 1))) 883 | (concat 884 | "^\\([ \t]*\\)" ; 1 885 | "\\(.*[^" del1st "\n]\\)" ; 2 886 | "\\(\\(" del "\\)\\(?:[ \t]+\\|$\\)\\)"))) ; 3 & 4 887 | 888 | ;; glossary (DEPRECATED) 889 | ;; ^(?P<label>.*\S):-$ 890 | ((eq type 'adoc-labeled-qanda) 891 | (concat 892 | "^\\([ \t]*\\)" ; 1 893 | "\\(.*[^ \t\n]\\)" ; 2 894 | "\\(\\(\\?\\?\\)\\)$")) ; 3 & 4 895 | 896 | ;; qanda (DEPRECATED) 897 | ;; ^\s*(?P<label>.*\S)\?\?$ 898 | ((eq type 'adoc-labeled-glossary) 899 | (concat 900 | "^\\(\\)" ; 1 901 | "\\(.*[^ \t\n]\\)" ; 2 902 | "\\(\\(:-\\)\\)$")) ; 3 & 4 903 | (t (error "Unknown type/level")))) 904 | 905 | (defun adoc-re-delimited-block-line () 906 | (concat 907 | "\\(?:" 908 | (mapconcat 909 | (lambda (x) 910 | (concat "\\(?:" x "\\)[ \t]*$")) 911 | adoc-delimited-block-del "\\|") 912 | "\\)")) 913 | 914 | ;; KLUDGE: Contrary to what the AsciiDoc manual specifies, adoc-mode does not 915 | ;; allow that either the first or the last line within a delmited block is 916 | ;; blank. That shall help to prevent the case that adoc-mode wrongly 917 | ;; interprets the end of a delimited block as the beginning, and the beginning 918 | ;; of a following delimited block as the ending, thus wrongly interpreting the 919 | ;; text between two adjacent delimited blocks as delimited block. It is 920 | ;; expected that it is unlikely that one wants to begin or end a delimited 921 | ;; block with a blank line, and it is expected that it is likely that 922 | ;; delimited blocks are surrounded by blank lines. 923 | (defun adoc-re-delimited-block (del) 924 | (let* ((tmp (nth del adoc-delimited-block-del)) 925 | (start (if (consp tmp) (car tmp) tmp)) 926 | (end (if (consp tmp) (cdr tmp) tmp))) 927 | (concat 928 | "\\(" start "\\)[ \t]*\n" 929 | "\\(" 930 | ;; a single leading non-blank line 931 | "[ \t]*[^ \t\n].*\n" 932 | ;; optionally followed by 933 | "\\(?:" 934 | ;; any number of arbitrary lines followed by 935 | "\\(?:.*\n\\)*?" 936 | ;; a trailing non blank line 937 | "[ \t]*[^ \t\n].*\n" 938 | "\\)??" 939 | "\\)??" 940 | "\\(" end "\\)[ \t]*$"))) 941 | 942 | ;; TODO: since its multiline, it doesn't yet work properly. 943 | (defun adoc-re-verbatim-paragraph-sequence () 944 | (concat 945 | "\\(" 946 | ;; 1. paragraph in sequence delimited by blank line or list continuation 947 | "^\\+?[ \t]*\n" 948 | 949 | ;; sequence of verbatim paragraphs 950 | "\\(?:" 951 | ;; 1st line starts with blanks, but has also non blanks, i.e. is not empty 952 | "[ \t]+[^ \t\n].*" 953 | ;; 2nd+ line is neither a blank line nor a list continuation line 954 | "\\(?:\n\\(?:[^+ \t\n]\\|[ \t]+[^ \t\n]\\|\\+[ \t]*[^ \t\n]\\).*?\\)*?" 955 | ;; paragraph delimited by blank line or list continuation or end of buffer 956 | ;; NOTE: now list continuation belongs the the verbatim paragraph sequence, 957 | ;; but actually we want to highlight it differently. Thus the font lock 958 | ;; keywoard handling list continuation must come after verbatim paraphraph 959 | ;; sequence. 960 | "\\(?:\n[ \t]*\\(?:\n\\|\\'\\)\\|\n\\+[ \t]*\n\\|\\'\\)" 961 | "\\)+" 962 | 963 | "\\)" )) 964 | 965 | ;; ^\.(?P<title>([^.\s].*)|(\.[^.\s].*))$ 966 | ;; Isn't that asciidoc.conf regexp the same as: ^\.(?P<title>(.?[^.\s].*))$ 967 | ;; insertion: so that this whole regex doesn't mistake a line starting with a 968 | ;; cell specifier like .2+| as a block title 969 | (defun adoc-re-block-title () 970 | "Returns a regexp matching an block title 971 | 972 | Subgroups: 973 | 1 delimiter 974 | 2 title's text incl trailing whites 975 | 3 newline or end-of-buffer anchor 976 | 977 | .foo n 978 | 12--23" 979 | (concat 980 | "^\\(\\.\\)" 981 | "\\(\\.?\\(?:" 982 | "[0-9]+[^+*]" ; inserted part, see above 983 | "\\|[^. \t\n]\\).*\\)" 984 | "\\(\n\\|\\'\\)")) 985 | 986 | ;; (?u)^(?P<name>image|unfloat|toc)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$ 987 | ;; note that altough it hasn't got the s Python regular expression flag, it 988 | ;; still can spawn multiple lines. Probably asciidoc removes newlines before 989 | ;; it applies the regexp above 990 | (defun adoc-re-block-macro (&optional cmd-name) 991 | "Returns a regexp matching an attribute list elment. 992 | Subgroups: 993 | 1 cmd name 994 | 2 target 995 | 3 attribute list, exclusive brackets []" 996 | (concat 997 | "^\\(" (or cmd-name "[a-zA-Z0-9_]+") "\\)::" 998 | "\\([^ \t\n]*?\\)" 999 | "\\[" 1000 | "\\(" (adoc-re-content) "\\)" 1001 | "\\][ \t]*$")) 1002 | 1003 | ;; ?P<id>[\w\-_]+ 1004 | (defun adoc-re-id () 1005 | "Returns a regexp matching an id used by anchors/xrefs" 1006 | "\\(?:[-a-zA-Z0-9_]+\\)") 1007 | 1008 | (defun adoc-re-anchor (&optional type id) 1009 | "Returns a regexp matching an anchor. 1010 | 1011 | If TYPE is non-nil, only that type is matched. If TYPE is nil, 1012 | all types are matched. 1013 | 1014 | If ID is non-nil, the regexp matches an anchor defining exactly 1015 | this id. If ID is nil, the regexp matches any anchor." 1016 | (cond 1017 | ((eq type 'block-id) 1018 | ;; ^\[\[(?P<id>[\w\-_]+)(,(?P<reftext>.*?))?\]\]$ 1019 | (concat "^\\[\\[" 1020 | "\\(" (if id (regexp-quote id) (adoc-re-id)) "\\)" 1021 | "\\(?:,\\(.*?\\)\\)?" 1022 | "\\]\\][ \t]*$")) 1023 | 1024 | ((eq type 'inline-special) 1025 | ;; [\\]?\[\[(?P<attrlist>[\w"_:].*?)\]\] 1026 | (concat "\\(\\[\\[\\)" 1027 | "\\(" (if id (concat (regexp-quote id) "[ \t]*?") "[a-zA-Z0-9\"_:].*?") "\\)" 1028 | "\\(\\]\\]\\)")) 1029 | 1030 | ((eq type 'biblio) 1031 | ;; [\\]?\[\[\[(?P<attrlist>[\w_:][\w_:.-]*?)\]\]\] 1032 | (concat "\\(\\[\\[\\)" 1033 | "\\(\\[" (if id (regexp-quote id) "[a-zA-Z0-9_:][a-zA-Z0-9_:.-]*?") "\\]\\)" 1034 | "\\(\\]\\]\\)")) 1035 | 1036 | ((eq type 'inline-general) 1037 | (adoc-re-inline-macro "anchor" id)) 1038 | 1039 | ((null type) 1040 | (mapconcat 1041 | (lambda (x) (adoc-re-anchor x id)) 1042 | '(block-id inline-special biblio inline-general) 1043 | "\\|")) 1044 | 1045 | (t 1046 | (error "Unknown type")))) 1047 | 1048 | (defun adoc-re-xref (&optional type for-kw) 1049 | "Returns a regexp matching a reference. 1050 | 1051 | If TYPE is nil, any type is matched. If FOR-KW is true, the 1052 | regexp is intendet for a font lock keyword, which has to make 1053 | further tests to find a proper xref." 1054 | (cond 1055 | ((eq type 'inline-special-with-caption) 1056 | ;; (?su)[\\]?<<(?P<attrlist>[\w"].*?)>>=xref2 1057 | (if for-kw 1058 | "\\(<<\\)\\([a-zA-Z0-9\"].*?\\)\\(,\\)\\(.*?\\(?:\n.*?\\)??\\)\\(>>\\)" 1059 | (concat "\\(<<\\)\\(" (adoc-re-id) "[ \t\n]*\\)\\(,\\)\\([^>\n]*?\\(?:\n[^>\n]*?\\)??\\)\\(>>\\)"))) 1060 | 1061 | ((eq type 'inline-special-no-caption) 1062 | ;; asciidoc.conf uses the same regexp as for without caption 1063 | (if for-kw 1064 | "\\(<<\\)\\([a-zA-Z0-9\"].*?\\(?:\n.*?\\)??\\)\\(>>\\)" 1065 | (concat "\\(<<\\)\\(" (adoc-re-id) "[ \t\n]*\\)\\(>>\\)"))) 1066 | 1067 | ((eq type 'inline-general-macro) 1068 | (adoc-re-inline-macro "xref")) 1069 | 1070 | ((null type) 1071 | (mapconcat 1072 | (lambda (x) (adoc-re-xref x for-kw)) 1073 | '(inline-special-with-caption inline-special-no-caption inline-general-macro) 1074 | "\\|")) 1075 | 1076 | (t (error "unknown type")))) 1077 | 1078 | (defun adoc-re-attribute-list-elt () 1079 | "Returns a regexp matching an attribute list elment. 1080 | Subgroups: 1081 | 1 attribute name 1082 | 2 attribute value if given as string 1083 | 3 attribute value if not given as string" 1084 | (concat 1085 | ",?[ \t\n]*" 1086 | "\\(?:\\([a-zA-Z_]+\\)[ \t\n]*=[ \t\n]*\\)?" ; 1 1087 | "\\(?:" 1088 | ;; regexp for string: See 'Mastering Regular Expressions', chapter 'The 1089 | ;; Real "Unrolling-the-Loop" Pattern'. 1090 | "\"\\([^\"\\]*\\(?:\\\\.[^\"\\]*\\)*\\)\"[ \t\n]*" "\\|" ; 2 1091 | "\\([^,]+\\)" ; 3 1092 | "\\)")) 1093 | 1094 | (defun adoc-re-precond (&optional unwanted-chars backslash-allowed disallowed-at-bol) 1095 | (concat 1096 | (when disallowed-at-bol ".") 1097 | "\\(?:" 1098 | (unless disallowed-at-bol "^\\|") 1099 | "[^" 1100 | (if unwanted-chars unwanted-chars "") 1101 | (if backslash-allowed "" "\\") 1102 | "\n" 1103 | "]" 1104 | "\\)")) 1105 | 1106 | (defun adoc-re-quote-precondition (not-allowed-chars) 1107 | "Regexp that matches before a (un)constrained quote delimiter. 1108 | 1109 | NOT-ALLOWED-CHARS are chars not allowed before the quote." 1110 | (concat 1111 | "\\(?:" 1112 | "^" 1113 | "\\|" 1114 | "\\=" 1115 | "\\|" 1116 | ; or *not* after 1117 | ; - an backslash 1118 | ; - user defined chars 1119 | "[^" not-allowed-chars "\\\n]" 1120 | "\\)")) 1121 | 1122 | ;; AsciiDoc src: 1123 | ;; # Unconstrained quotes can appear anywhere. 1124 | ;; reo = re.compile(r'(?msu)(^|.)(\[(?P<attrlist>[^[\]]+?)\])?' \ 1125 | ;; + r'(?:' + re.escape(lq) + r')' \ 1126 | ;; + r'(?P<content>.+?)(?:'+re.escape(rq)+r')') 1127 | ;; 1128 | ;; BUG: Escaping ala \\**...** does not yet work. Probably adoc-mode should do 1129 | ;; it like this, which is more similar to how asciidoc does it: 'Allow' 1130 | ;; backslash as the first char. If the first char is ineed a backslash, it is 1131 | ;; 'removed' (-> adoc-hide-delimiter face), and the rest of the match is left 1132 | ;; unaffected. 1133 | (defun adoc-re-unconstrained-quote (ldel &optional rdel) 1134 | (unless rdel (setq rdel ldel)) 1135 | (let* ((qldel (regexp-quote ldel)) 1136 | (qrdel (regexp-quote rdel))) 1137 | (concat 1138 | (adoc-re-quote-precondition "") 1139 | "\\(\\[[^][]+?\\]\\)?" 1140 | "\\(" qldel "\\)" 1141 | "\\(" (adoc-re-content "+") "\\)" 1142 | "\\(" qrdel "\\)"))) 1143 | 1144 | ;; AsciiDoc src for constrained quotes 1145 | ;; # The text within constrained quotes must be bounded by white space. 1146 | ;; # Non-word (\W) characters are allowed at boundaries to accommodate 1147 | ;; # enveloping quotes. 1148 | ;; 1149 | ;; reo = re.compile(r'(?msu)(^|[^\w;:}])(\[(?P<attrlist>[^[\]]+?)\])?' \ 1150 | ;; + r'(?:' + re.escape(lq) + r')' \ 1151 | ;; + r'(?P<content>\S|\S.*?\S)(?:'+re.escape(rq)+r')(?=\W|$)') 1152 | (defun adoc-re-constrained-quote (ldel &optional rdel) 1153 | " 1154 | subgroups: 1155 | 1 attribute list [optional] 1156 | 2 starting del 1157 | 3 enclosed text 1158 | 4 closing del" 1159 | (unless rdel (setq rdel ldel)) 1160 | (let ((qldel (regexp-quote ldel)) 1161 | (qrdel (regexp-quote rdel))) 1162 | (concat 1163 | ;; added &<> because those are special chars which are substituted by a 1164 | ;; entity, which ends in ;, which is prohibited in the ascidoc.conf regexp 1165 | (adoc-re-quote-precondition "A-Za-z0-9;:}&<>") 1166 | "\\(\\[[^][]+?\\]\\)?" 1167 | "\\(" qldel "\\)" 1168 | "\\([^ \t\n]\\|[^ \t\n]" (adoc-re-content) "[^ \t\n]\\)" 1169 | "\\(" qrdel "\\)" 1170 | ;; BUG: now that Emacs doesn't has look-ahead, the match is too long, and 1171 | ;; adjancted quotes of the same type wouldn't be recognized. 1172 | "\\(?:[^A-Za-z0-9\n]\\|[ \t]*$\\)"))) 1173 | 1174 | (defun adoc-re-quote (type ldel &optional rdel) 1175 | (cond 1176 | ((eq type 'adoc-constrained) 1177 | (adoc-re-constrained-quote ldel rdel)) 1178 | ((eq type 'adoc-unconstrained) 1179 | (adoc-re-unconstrained-quote ldel rdel)) 1180 | (t 1181 | (error "Invalid type")))) 1182 | 1183 | ;; Macros using default syntax. From asciidoc.conf: 1184 | ;; (?su)(?<!\w)[\\]?(?P<name>http|https|ftp|file|irc|mailto|callto|image|link|anchor|xref|indexterm):(?P<target>\S*?)\[(?P<attrlist>.*?)\] 1185 | ;; 1186 | ;; asciidoc.conf itself says: Default (catchall) inline macro is not 1187 | ;; implemented. It _would_ be 1188 | ;; (?su)[\\]?(?P<name>\w(\w|-)*?):(?P<target>\S*?)\[(?P<passtext>.*?)(?<!\\)\]= 1189 | (defun adoc-re-inline-macro (&optional cmd-name target unconstrained attribute-list-constraints) 1190 | "Returns regex matching an inline macro. 1191 | 1192 | Id CMD-NAME is nil, any command is matched. It maybe a regexp 1193 | itself in order to match multiple commands. If TARGET is nil, any 1194 | target is matched. When UNCONSTRAINED is nil, the returned regexp 1195 | begins with '\<', i.e. it will _not_ match when CMD-NAME is part 1196 | of a previous word. When ATTRIBUTE-LIST-CONSTRAINTS is 'empty, 1197 | only an empty attribut list is matched, if it's 1198 | 'single-attribute, only an attribute list with exactly one 1199 | attribute is matched. 1200 | 1201 | Subgroups of returned regexp: 1202 | 1 cmd name 1203 | 2 : 1204 | 3 target 1205 | 4 [ 1206 | 5 attribute list, exclusive brackets [], also when 1207 | attribute-list-constraints is non-nil 1208 | 6 ]" 1209 | ;; !!! \< is not exactly what AsciiDoc does, see regex above 1210 | (concat 1211 | (unless unconstrained "\\<") 1212 | "\\(" (if cmd-name (concat "\\(?:" cmd-name "\\)") "\\w+") "\\)" 1213 | "\\(:\\)" 1214 | "\\(" (if target (regexp-quote target) "[^ \t\n]*?") "\\)" 1215 | "\\(\\[\\)\\(" 1216 | (cond 1217 | ((eq attribute-list-constraints 'empty) "") 1218 | ((eq attribute-list-constraints 'single-attribute) "[^\n,]*?\\(?:\n[^\n,]*?\\)??") 1219 | (t (adoc-re-content))) 1220 | "\\)\\(\\]\\)" )) 1221 | 1222 | ;; todo: use same regexps as for font lock 1223 | (defun adoc-re-paragraph-separate () 1224 | (concat 1225 | 1226 | ;; empty line 1227 | "[ \t]*$" 1228 | 1229 | ;; delimited blocks / two line titles 1230 | "\\|" 1231 | "\\(" 1232 | "^+" "\\|" 1233 | "\\++" "\\|" 1234 | "/+" "\\|" 1235 | "-+" "\\|" 1236 | "\\.+" "\\|" 1237 | "\\*+" "\\|" 1238 | "_*+" "\\|" 1239 | "=*+" "\\|" 1240 | "~*+" "\\|" 1241 | "^*+" "\\|" 1242 | "--" 1243 | "\\)" 1244 | "[ \t]*$" 1245 | )) 1246 | 1247 | ;; todo: use same regexps as for font lock 1248 | (defun adoc-re-paragraph-start () 1249 | (concat 1250 | paragraph-separate 1251 | 1252 | ;; list items 1253 | "\\|" 1254 | "[ \t]*" 1255 | "\\(" 1256 | "-" "\\|" 1257 | "\\*\\{1,5\\}" "\\|" 1258 | "\\.\\{1,5\\}" "\\|" 1259 | "[0-9]\\{,3\\}\\." "\\|" 1260 | "[a-z]\\{,3\\}\\." "\\|" 1261 | "[A-Z]\\{,3\\}\\." "\\|" 1262 | "[ivxmcIVXMC]+)" "\\|" 1263 | ".*?:\\{2,4\\}" 1264 | "\\)" 1265 | "\\( \\|$\\)" 1266 | 1267 | ;; table rows 1268 | "\\|" 1269 | "|" 1270 | 1271 | ;; one line titles 1272 | "\\|" 1273 | "[=.].*$" 1274 | 1275 | )) 1276 | 1277 | (defun adoc-re-aor(e1 e2) 1278 | "all or: Returns a regex matching \(e1\|e2\|e1e2\)? " 1279 | (concat "\\(?:" e1 "\\)?\\(?:" e2 "\\)?")) 1280 | 1281 | (defun adoc-re-ror(e1 e2) 1282 | "real or: Returns a regex matching \(e1\|e2\|e1e2\)" 1283 | (concat "\\(?:\\(?:" e1 "\\)\\|\\(?:" e2 "\\)\\|\\(?:" e1 "\\)\\(?:" e2 "\\)\\)")) 1284 | 1285 | ;; ((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<style>[a-z])?)?\|' 1286 | (defun adoc-re-cell-specifier () 1287 | (let* ((fullspan (concat (adoc-re-ror "[0-9]+" "\\.[0-9]+") "[*+]")) 1288 | (align (adoc-re-ror "[<^>]" "\\.[<^>]")) 1289 | (style "[demshalv]")) 1290 | (concat "\\(?:" fullspan "\\)?\\(?:" align "\\)?\\(?:" style "\\)?"))) 1291 | 1292 | ;; bug: if qualifier is "+", and the thing to match starts at the end of a 1293 | ;; line (i.e. the first char is newline), then wrongly this regexp does 1294 | ;; never match. 1295 | ;; Note: asciidoc uses Python's \s to determine blank lines, while _not_ 1296 | ;; setting either the LOCALE or UNICODE flag, see 1297 | ;; Reader.skip_blank_lines. Python uses [ \t\n\r\f\v] for it's \s . So 1298 | ;; the horizontal spaces are [ \t]. 1299 | (defun adoc-re-content (&optional qualifier) 1300 | "Matches content, possibly spawning multiple non-blank lines" 1301 | (concat 1302 | "\\(?:" 1303 | ;; content on initial line 1304 | "." (or qualifier "*") "?" 1305 | ;; if content spawns multiple lines 1306 | "\\(?:\n" 1307 | ;; complete non blank lines 1308 | "\\(?:[ \t]*\\S-.*\n\\)*?" 1309 | ;; leading content on last line 1310 | ".*?" 1311 | "\\)??" 1312 | "\\)")) 1313 | 1314 | 1315 | ;;;; font lock keywords 1316 | (defun adoc-kwf-std (end regexp &optional must-free-groups no-block-del-groups) 1317 | "Standart function for keywords 1318 | 1319 | Intendent to be called from font lock keyword functions. END is 1320 | the limit of the search. REXEXP the regexp to be searched. 1321 | MUST-FREE-GROUPS a list of regexp group numbers which may not 1322 | match text that has an adoc-reserved text-property with a non-nil 1323 | value. Likewise, groups in NO-BLOCK-DEL-GROUPS may not contain 1324 | text having adoc-reserved set to 'block-del." 1325 | (let ((found t) (prevented t) saved-point) 1326 | (while (and found prevented (<= (point) end) (not (eobp))) 1327 | (setq saved-point (point)) 1328 | (setq found (re-search-forward regexp end t)) 1329 | (setq prevented 1330 | (and found 1331 | (or 1332 | (some (lambda(x) 1333 | (and (match-beginning x) 1334 | (text-property-not-all (match-beginning x) 1335 | (match-end x) 1336 | 'adoc-reserved nil))) 1337 | must-free-groups) 1338 | (some (lambda(x) 1339 | (and (match-beginning x)) 1340 | (text-property-any (match-beginning x) 1341 | (match-end x) 1342 | 'adoc-reserved 'block-del)) 1343 | no-block-del-groups)))) 1344 | (when (and found prevented (<= (point) end)) 1345 | (goto-char (1+ saved-point)))) 1346 | (and found (not prevented)))) 1347 | 1348 | (defun adoc-kwf-attribute-list (end) 1349 | ;; for each attribute list before END 1350 | (while (< (point) end) 1351 | (goto-char (or (text-property-not-all (point) end 'adoc-attribute-list nil) 1352 | end)) 1353 | (when (< (point) end) 1354 | (let* ((attribute-list-end 1355 | (or (text-property-any (point) end 'adoc-attribute-list nil) 1356 | end)) 1357 | (prop-of-attribute-list 1358 | (get-text-property (point) 'adoc-attribute-list)) 1359 | ;; position (number) or name (string) of current 1360 | ;; attribute. Attribute list start with positional attributes, as 1361 | ;; opposed to named attributes, thus init with 0. 1362 | (pos-or-name-of-attribute 0)) 1363 | 1364 | (if (facep prop-of-attribute-list) 1365 | ;; The attribute list is not really an attribute list. As a whole 1366 | ;; it counts as text. 1367 | (put-text-property 1368 | (point) attribute-list-end 1369 | 'face prop-of-attribute-list) 1370 | 1371 | ;; for each attribute in current attribute list 1372 | (while (re-search-forward (adoc-re-attribute-list-elt) attribute-list-end t) 1373 | (when (match-beginning 1); i.e. when it'a named attribute 1374 | ;; get attribute's name 1375 | (setq pos-or-name-of-attribute 1376 | (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 1377 | ;; fontify the attribute's name with markup-attribute-face 1378 | (put-text-property 1379 | (match-beginning 1) (match-end 1) 'face markup-attribute-face)) 1380 | 1381 | ;; fontify the attribute's value 1382 | (let ((match-group-of-attribute-value (if (match-beginning 2) 2 3)) 1383 | (attribute-value-face 1384 | (adoc-face-for-attribute pos-or-name-of-attribute prop-of-attribute-list))) 1385 | (put-text-property 1386 | (match-beginning match-group-of-attribute-value) 1387 | (match-end match-group-of-attribute-value) 1388 | 'face attribute-value-face)) 1389 | 1390 | (when (numberp pos-or-name-of-attribute) 1391 | (setq pos-or-name-of-attribute (1+ pos-or-name-of-attribute))))) 1392 | 1393 | (goto-char attribute-list-end)))) 1394 | nil) 1395 | 1396 | (defun adoc-facespec-subscript () 1397 | (list 'quote 1398 | (append '(face markup-subscript-face) 1399 | (when (not (= 0 (car adoc-script-raise))) 1400 | `(display (raise ,(car adoc-script-raise))))))) 1401 | 1402 | (defun adoc-facespec-superscript () 1403 | (list 'quote 1404 | (append '(face markup-superscript-face) 1405 | (when (not (= 0 (car adoc-script-raise))) 1406 | `(display (raise ,(cadr adoc-script-raise))))))) 1407 | 1408 | ;; todo: use & learn some more macro magic so adoc-kw-unconstrained-quote and 1409 | ;; adoc-kw-constrained-quote are less redundant and have common parts in one 1410 | ;; macro. E.g. at least such 'lists' 1411 | ;; (not (text-property-not-all (match-beginning 1) (match-end 1) 'adoc-reserved nil)) 1412 | ;; (not (text-property-not-all (match-beginning 3) (match-end 3) 'adoc-reserved nil)) 1413 | ;; ... 1414 | ;; could surely be replaced by a single (adoc-not-reserved-bla-bla 1 3) 1415 | 1416 | ;; BUG: Remember that if a matcher function returns nil, font-lock does not 1417 | ;; further call it and abandons that keyword. Thus in adoc-mode in general, 1418 | ;; there should be a loop around (and (re-search-forward ...) (not 1419 | ;; (text-property-not-all...)) ...). Currently if say a constrained quote cant 1420 | ;; match because of adoc-reserved, following quotes of the same type which 1421 | ;; should be highlighed are not, because font-lock abandons that keyword. 1422 | 1423 | (defun adoc-kw-one-line-title (level text-face) 1424 | "Creates a keyword for font-lock which highlights one line titles" 1425 | (list 1426 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-one-line-title level) '(0))) 1427 | '(1 '(face markup-meta-hide-face adoc-reserved block-del) t) 1428 | `(2 ,text-face t) 1429 | '(3 '(face nil adoc-reserved block-del) t) 1430 | '(4 '(face markup-meta-hide-face) t t))) 1431 | 1432 | ;; todo: highlight bogous 'two line titles' with warning face 1433 | ;; todo: completly remove keyword when adoc-enable-two-line-title is nil 1434 | (defun adoc-kw-two-line-title (del text-face) 1435 | "Creates a keyword for font-lock which highlights two line titles" 1436 | (list 1437 | ;; matcher function 1438 | `(lambda (end) 1439 | (and adoc-enable-two-line-title 1440 | (re-search-forward ,(adoc-re-two-line-title del) end t) 1441 | (< (abs (- (length (match-string 2)) (length (match-string 3)))) 3) 1442 | (or (not (numberp adoc-enable-two-line-title)) 1443 | (not (equal adoc-enable-two-line-title (length (match-string 2))))) 1444 | (not (text-property-not-all (match-beginning 0) (match-end 0) 'adoc-reserved nil)))) 1445 | ;; highlighers 1446 | `(2 ,text-face t) 1447 | `(3 '(face markup-meta-hide-face adoc-reserved block-del) t))) 1448 | 1449 | ;; (defun adoc-?????-attributes (endpos enddelchar) 1450 | ;; (list 1451 | ;; (concat 1452 | ;; ",?[ \t\n]*" 1453 | ;; "\\(?:\\([a-zA-Z_]+\\)[ \t\n]*=[ \t\n]*\\)?" ; attribute name 1454 | ;; "\\([^" enddelchar ",]*\\|" (adoc-re-string) "\\)")) ; attribute value 1455 | ;; '(1 markup-attribute-face t) 1456 | ;; '(2 markup-value-face t))) 1457 | 1458 | (defun adoc-kw-oulisti (type &optional level sub-type) 1459 | "Creates a keyword for font-lock which highlights both (un)ordered list item. 1460 | Concerning TYPE, LEVEL and SUB-TYPE see `adoc-re-oulisti'" 1461 | (list 1462 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-oulisti type level sub-type) '(0))) 1463 | '(0 '(face nil adoc-reserved block-del) t) 1464 | '(2 markup-list-face t) 1465 | '(3 adoc-align t))) 1466 | 1467 | (defun adoc-kw-llisti (sub-type &optional level) 1468 | "Creates a keyword for font-lock which highlights labeled list item. 1469 | Concerning TYPE, LEVEL and SUB-TYPE see `adoc-re-llisti'." 1470 | (list 1471 | `(lambda (end) 1472 | (when (adoc-kwf-std end ,(adoc-re-llisti sub-type level) '(0)) 1473 | (let ((pos (match-beginning 0))) 1474 | (when (> pos (point-min)) 1475 | (put-text-property (1- pos) pos 'adoc-reserved 'block-del))) 1476 | t)) 1477 | '(1 '(face nil adoc-reserved block-del) t) 1478 | '(2 markup-gen-face t) 1479 | '(3 '(face adoc-align adoc-reserved block-del) t) 1480 | '(4 markup-list-face t))) 1481 | 1482 | (defun adoc-kw-list-continuation () 1483 | (list 1484 | ;; see also regexp of forced line break, which is similar. it is not directly 1485 | ;; obvious from asciidoc sourcecode what the exact rules are. 1486 | '(lambda (end) (adoc-kwf-std end "^\\(\\+\\)[ \t]*$" '(1))) 1487 | '(1 '(face markup-meta-face adoc-reserved block-del) t))) 1488 | 1489 | (defun adoc-kw-delimited-block (del &optional text-face inhibit-text-reserved) 1490 | "Creates a keyword for font-lock which highlights a delimited block." 1491 | (list 1492 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-delimited-block del) '(1 3))) 1493 | '(0 '(face nil font-lock-multiline t) t) 1494 | '(1 '(face markup-meta-hide-face adoc-reserved block-del) t) 1495 | (if (not inhibit-text-reserved) 1496 | `(2 '(face ,text-face face markup-verbatim-face adoc-reserved t) t t) 1497 | `(2 ,text-face t t)) 1498 | '(3 '(face markup-meta-hide-face adoc-reserved block-del) t))) 1499 | 1500 | ;; if adoc-kw-delimited-block, adoc-kw-two-line-title don't find the whole 1501 | ;; delimited block / two line title, at least 'use up' the delimiter line so it 1502 | ;; is later not misinterpreted as a funny serries of unconstrained quotes 1503 | (defun adoc-kw-delimiter-line-fallback () 1504 | (list 1505 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-delimited-block-line) '(0))) 1506 | '(0 '(face markup-meta-face adoc-reserved block-del) t))) 1507 | 1508 | ;; admonition paragraph. Note that there is also the style with the leading attribute list. 1509 | ;; (?s)^\s*(?P<style>NOTE|TIP|IMPORTANT|WARNING|CAUTION):\s+(?P<text>.+) 1510 | (defmacro adoc-kw-admonition-paragraph () 1511 | "Creates a keyword which highlights admonition paragraphs" 1512 | `(list 1513 | ;; matcher function 1514 | (lambda (end) 1515 | (and (re-search-forward "^[ \t]*\\(\\(?:CAUTION\\|WARNING\\|IMPORTANT\\|TIP\\|NOTE\\):\\)\\([ \t]+\\)" end t) 1516 | (not (text-property-not-all (match-beginning 0) (match-end 0) 'adoc-reserved nil)))) 1517 | ;; highlighers 1518 | '(1 '(face adoc-complex-replacement adoc-reserved t)) 1519 | '(2 '(face adoc-align adoc-reserved t)))) 1520 | 1521 | (defun adoc-kw-verbatim-paragraph-sequence () 1522 | "Creates a keyword which highlights a sequence of verbatim paragraphs." 1523 | (list 1524 | ;; matcher function 1525 | `(lambda (end) 1526 | (and (re-search-forward ,(adoc-re-verbatim-paragraph-sequence) end t) 1527 | (not (text-property-not-all (match-beginning 0) (match-end 0) adoc-reserved nil)))) 1528 | ;; highlighers 1529 | '(1 '(face adoc-monospace adoc-reserved t font-lock-multiline t)))) 1530 | 1531 | (defun adoc-kw-block-title () 1532 | (list 1533 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-block-title) '(1))) 1534 | '(1 '(face markup-meta-face adoc-reserved block-del)) 1535 | '(2 markup-gen-face) 1536 | '(3 '(face nil adoc-reserved block-del)))) 1537 | 1538 | (defun adoc-kw-quote (type ldel text-face-spec &optional del-face rdel literal-p) 1539 | "Return a keyword which highlights (un)constrained quotes. 1540 | When LITERAL-P is non-nil, the contained text is literal text." 1541 | (list 1542 | ;; matcher function 1543 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-quote type ldel rdel) '(1 2 4) '(3))) 1544 | ;; highlighers 1545 | '(1 '(face markup-meta-face adoc-reserved t) t t) ; attribute list 1546 | `(2 '(face ,(or del-face markup-meta-hide-face) adoc-reserved t) t) ; open del 1547 | `(3 ,text-face-spec append) ; text 1548 | (if literal-p 1549 | `(3 '(face ,markup-verbatim-face adoc-reserved t) append) 1550 | '(3 nil)) ; grumbl, I dont know how to get rid of it 1551 | `(4 '(face ,(or del-face markup-meta-hide-face) adoc-reserved t) t))); close del 1552 | 1553 | (defun adoc-kw-inline-macro (&optional cmd-name unconstrained attribute-list-constraints cmd-face target-faces target-meta-p attribute-list) 1554 | "Returns a kewyword which highlights an inline macro. 1555 | 1556 | For CMD-NAME and UNCONSTRAINED see 1557 | `adoc-re-inline-macro'. CMD-FACE determines face for the command 1558 | text. If nil, `markup-command-face' is used. TARGET-FACES 1559 | determines face for the target text. If nil `markup-meta-face' is 1560 | used. If a list, the first is used if the attribute list is the 1561 | empty string, the second is used if its not the empty string. If 1562 | TARGET-META-P is non-nil, the target text is considered to be 1563 | meta characters." 1564 | (list 1565 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-inline-macro cmd-name nil unconstrained attribute-list-constraints) '(1 2 4 5) '(0))) 1566 | `(1 '(face ,(or cmd-face markup-command-face) adoc-reserved t) t) ; cmd-name 1567 | '(2 '(face markup-meta-face adoc-reserved t) t) ; : 1568 | `(3 ,(cond ((not target-faces) markup-meta-face) ; target 1569 | ((listp target-faces) `(if (string= (match-string 5) "") ; 5=attribute-list 1570 | ,(car target-faces) 1571 | ,(cadr target-faces))) 1572 | (t target-faces)) 1573 | ,(if target-meta-p t 'append)) 1574 | '(4 '(face markup-meta-face adoc-reserved t) t) ; [ 1575 | `(5 '(face markup-meta-face adoc-attribute-list ,(or attribute-list t)) t) 1576 | '(6 '(face markup-meta-face adoc-reserved t) t))) ; ] 1577 | 1578 | ;; largely copied from adoc-kw-inline-macro 1579 | ;; todo: output text should be affected by quotes & co, e.g. bold, emph, ... 1580 | (defun adoc-kw-inline-macro-urls-attribute-list () 1581 | (let ((cmd-name (regexp-opt '("http" "https" "ftp" "file" "irc" "mailto" "callto" "link")))) 1582 | (list 1583 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-inline-macro cmd-name) '(0) '(0))) 1584 | `(1 '(face markup-internal-reference-face adoc-reserved t) t) ; cmd-name 1585 | `(2 '(face markup-internal-reference-face adoc-reserved t) t) ; : 1586 | `(3 '(face markup-internal-reference-face adoc-reserved t) t) ; target 1587 | '(4 '(face markup-meta-face adoc-reserved t) t) ; [ 1588 | `(5 '(face markup-reference-face adoc-attribute-list markup-reference-face) append) 1589 | '(6 '(face markup-meta-face adoc-reserved t) t)))) ; ] 1590 | 1591 | (defun adoc-kw-inline-macro-urls-no-attribute-list () 1592 | (let ((cmd-name (regexp-opt '("http" "https" "ftp" "file" "irc" "mailto" "callto" "link")))) 1593 | (list 1594 | `(lambda (end) (adoc-kwf-std end ,(adoc-re-inline-macro cmd-name nil nil 'empty) '(0) '(0))) 1595 | '(1 '(face markup-reference-face adoc-reserved t) append) ; cmd-name 1596 | '(2 '(face markup-reference-face adoc-reserved t) append) ; : 1597 | '(3 '(face markup-reference-face adoc-reserved t) append) ; target 1598 | '(4 '(face markup-meta-face adoc-reserved t) t) ; [ 1599 | ; 5 = attriblist is empty 1600 | '(6 '(face markup-meta-face adoc-reserved t) t)))) ; ] 1601 | 1602 | ;; standalone url 1603 | ;; From asciidoc.conf: 1604 | ;; # These URL types don't require any special attribute list formatting. 1605 | ;; (?su)(?<!\S)[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])= 1606 | ;; # Allow a leading parenthesis and square bracket. 1607 | ;; (?su)(?<\=[([])[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])= 1608 | ;; # Allow <> brackets. 1609 | ;; (?su)[\\]?<(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])>= 1610 | ;; 1611 | ;; asciidoc.conf bug? why is it so restrictive for urls without attribute 1612 | ;; list, that version can only have a limited set of characters before. Why 1613 | ;; not just have the rule that it must start with \b. 1614 | ;; 1615 | ;; standalone email 1616 | ;; From asciidoc.conf: 1617 | ;; (?su)(?<![">:\w._/-])[\\]?(?P<target>\w[\w._-]*@[\w._-]*\w)(?!["<\w_-])=mailto 1618 | ;; 1619 | ;; todo: properly handle leading backslash escapes 1620 | ;; 1621 | ;; non-bugs: __flo@gmail.com__ is also in AsciiDoc *not* an emphasised email, it's 1622 | ;; just an emphasised text. Thats because the quote transforms happen before 1623 | ;; the url transform, thus the middle stage is something like 1624 | ;; ...>flo@gmail.com<... According to asciidoc.conf regexps a leading > or a 1625 | ;; trailing < are not allowed. In adoc-mode, the fontification is as in 1626 | ;; AsciiDoc, but that's coincidence. The reason in adoc-mode is that the 1627 | ;; regexps quantifier are greedy instead lazy, thus the trailing __ behind the 1628 | ;; email are taken part as the email adress, and then adoc-kwf-std cant match 1629 | ;; because part of the match (the __) contains text properties with 1630 | ;; adoc-reserved non-nil, also because quote highlighting already happened. 1631 | (defun adoc-kw-standalone-urls () 1632 | (let* ((url "\\b\\(?:https?\\|ftp\\|file\\|irc\\)://[^ \t\n<>]*[a-zA-Z0-9_/]") 1633 | (url<> (concat "<\\(?:" url "\\)>")) 1634 | (email "[a-zA-Z0-9_][-a-zA-Z0-9_._]*@[-a-zA-Z0-9_._]*[a-zA-Z0-9_]") 1635 | (both (concat "\\(?:" url "\\)\\|\\(?:" url<> "\\)\\|\\(?:" email "\\)"))) 1636 | (list 1637 | `(lambda (end) (adoc-kwf-std end ,both '(0) '(0))) 1638 | '(0 '(face markup-reference-face adoc-reserved t) append t)))) 1639 | 1640 | ;; bug: escapes are not handled yet 1641 | ;; todo: give the inserted character a specific face. But I fear that is not 1642 | ;; possible. The string inserted with the ovlerlay property after-string gets 1643 | ;; the face of the text 'around' it, which is in this case the text following 1644 | ;; the replacement. 1645 | (defmacro adoc-kw-replacement (regexp &optional replacement) 1646 | "Creates a keyword for font-lock which highlights replacements." 1647 | `(list 1648 | ;; matcher function 1649 | (lambda (end) 1650 | (let ((found t) (prevented t) saved-point) 1651 | (while (and found prevented) 1652 | (setq saved-point (point)) 1653 | (setq found 1654 | (re-search-forward ,regexp end t)) 1655 | (setq prevented ; prevented is only meaningfull wenn found is non-nil 1656 | (or 1657 | (not found) ; the following is only needed when found 1658 | (text-property-not-all (match-beginning 1) (match-end 1) 'adoc-reserved nil))) 1659 | (when (and found prevented) 1660 | (goto-char (+ saved-point 1)))) 1661 | (when (and found (not prevented) adoc-insert-replacement ,replacement) 1662 | (let* ((s (cond 1663 | ((stringp ,replacement) 1664 | ,replacement) 1665 | ((functionp ,replacement) 1666 | (funcall ,replacement (match-string-no-properties 1))) 1667 | (t (error "Invalid replacement type")))) 1668 | (o (when (stringp s) 1669 | (make-overlay (match-end 1) (match-end 1))))) 1670 | (setq adoc-replacement-failed (not o)) 1671 | (unless adoc-replacement-failed 1672 | (overlay-put o 'after-string s)))) 1673 | (and found (not prevented)))) 1674 | 1675 | ;; highlighers 1676 | ;; todo: replacement instead warining face if resolver is not given 1677 | (if (and adoc-insert-replacement ,replacement) 1678 | ;; '((1 (if adoc-replacement-failed adoc-warning adoc-hide-delimiter) t) 1679 | ;; (1 '(face nil adoc-reserved t) t)) 1680 | '(1 '(face adoc-hide-delimiter adoc-reserved t) t) 1681 | '(1 '(face adoc-replacement adoc-reserved t) t)))) 1682 | 1683 | ;; - To ensure that indented lines are nicely aligned. They only look aligned if 1684 | ;; the whites at line beginning have a fixed with font. 1685 | ;; - Some faces have properties which are also visbile on whites 1686 | ;; (underlines/backgroundcolor/...), for example links typically gave 1687 | ;; underlines. If now a link in an indented paragraph (e.g. because its a list 1688 | ;; item), spawns multiple lines, then without countermeasures the blanks at 1689 | ;; line beginning would also be underlined, which looks akward. 1690 | (defun adoc-flf-first-whites-fixed-width(end) 1691 | ;; it makes no sense to do something with a blank line, so require at least one non blank char. 1692 | (and (re-search-forward "\\(^[ \t]+\\)[^ \t\n]" end t) 1693 | ;; dont replace a face with with adoc-align which already is a fixed with 1694 | ;; font (most probably), because then it also won't look aligned 1695 | (text-property-not-all (match-beginning 1) (match-end 1) 'face 'markup-typewriter-face) 1696 | (text-property-not-all (match-beginning 1) (match-end 1) 'face 'markup-code-face) 1697 | (text-property-not-all (match-beginning 1) (match-end 1) 'face 'markup-passthrough-face) 1698 | (text-property-not-all (match-beginning 1) (match-end 1) 'face 'markup-comment-face))) 1699 | 1700 | ;; See adoc-flf-first-whites-fixed-width 1701 | (defun adoc-kw-first-whites-fixed-width () 1702 | (list 1703 | 'adoc-flf-first-whites-fixed-width 1704 | '(1 adoc-align t))) 1705 | 1706 | ;; ensures that faces from the markup-text group don't overwrite faces from the 1707 | ;; markup-meta group 1708 | (defun adoc-flf-meta-face-cleanup (end) 1709 | (while (< (point) end) 1710 | (let* ((next-pos (next-single-property-change (point) 'face nil end)) 1711 | (faces-raw (get-text-property (point) 'face)) 1712 | (faces (if (listp faces-raw) faces-raw (list faces-raw))) 1713 | newfaces 1714 | meta-p) 1715 | (while faces 1716 | (if (member (car faces) '(markup-meta-hide-face markup-command-face markup-attribute-face markup-value-face markup-complex-replacement-face markup-list-face markup-table-face markup-table-row-face markup-table-cell-face markup-anchor-face markup-internal-reference-face markup-comment-face markup-preprocessor-face)) 1717 | (progn 1718 | (setq meta-p t) 1719 | (setq newfaces (cons (car faces) newfaces))) 1720 | (if (not (string-match "markup-" (symbol-name (car faces)))) 1721 | (setq newfaces (cons (car faces) newfaces)))) 1722 | (setq faces (cdr faces))) 1723 | (if meta-p 1724 | (put-text-property (point) next-pos 'face 1725 | (if (= 1 (length newfaces)) (car newfaces) newfaces))) 1726 | (goto-char next-pos))) 1727 | nil) 1728 | 1729 | 1730 | ;;;; font lock 1731 | (defun adoc-unfontify-region-function (beg end) 1732 | ;; 1733 | (font-lock-default-unfontify-region beg end) 1734 | 1735 | ;; remove overlays. Currently only used by AsciiDoc replacements 1736 | ;; todo: this is an extremly brute force solution and interacts very badly 1737 | ;; with many (minor) modes using overlays such as flyspell or ediff 1738 | (when adoc-insert-replacement 1739 | (remove-overlays beg end)) 1740 | 1741 | ;; text properties. Currently only display raise used for sub/superscripts. 1742 | ;; code snipped copied from tex-mode 1743 | (when (not (and (= 0 (car adoc-script-raise)) (= 0 (cadr adoc-script-raise)))) 1744 | (while (< beg end) 1745 | (let ((next (next-single-property-change beg 'display nil end)) 1746 | (prop (get-text-property beg 'display))) 1747 | (if (and (eq (car-safe prop) 'raise) 1748 | (member (car-safe (cdr prop)) adoc-script-raise) 1749 | (null (cddr prop))) 1750 | (put-text-property beg next 'display nil)) 1751 | (setq beg next))))) 1752 | 1753 | (defun adoc-font-lock-mark-block-function () 1754 | (mark-paragraph 2) 1755 | (forward-paragraph -1)) 1756 | 1757 | (defun adoc-get-font-lock-keywords () 1758 | (list 1759 | 1760 | ;; Asciidoc BUG: Lex.next has a different order than the following extract 1761 | ;; from the documentation states. 1762 | 1763 | ;; When a block element is encountered asciidoc(1) determines the type of 1764 | ;; block by checking in the following order (first to last): (section) 1765 | ;; Titles, BlockMacros, Lists, DelimitedBlocks, Tables, AttributeEntrys, 1766 | ;; AttributeLists, BlockTitles, Paragraphs. 1767 | 1768 | ;; sections / document structure 1769 | ;; ------------------------------ 1770 | (adoc-kw-one-line-title 0 markup-title-0-face) 1771 | (adoc-kw-one-line-title 1 markup-title-1-face) 1772 | (adoc-kw-one-line-title 2 markup-title-2-face) 1773 | (adoc-kw-one-line-title 3 markup-title-3-face) 1774 | (adoc-kw-one-line-title 4 markup-title-4-face) 1775 | (adoc-kw-two-line-title (nth 0 adoc-two-line-title-del) markup-title-0-face) 1776 | (adoc-kw-two-line-title (nth 1 adoc-two-line-title-del) markup-title-1-face) 1777 | (adoc-kw-two-line-title (nth 2 adoc-two-line-title-del) markup-title-2-face) 1778 | (adoc-kw-two-line-title (nth 3 adoc-two-line-title-del) markup-title-3-face) 1779 | (adoc-kw-two-line-title (nth 4 adoc-two-line-title-del) markup-title-4-face) 1780 | 1781 | 1782 | ;; block macros 1783 | ;; ------------------------------ 1784 | ;; todo: respect asciidoc.conf order 1785 | 1786 | ;; -- system block macros 1787 | ;; # Default system macro syntax. 1788 | ;; SYS_RE = r'(?u)^(?P<name>[\\]?\w(\w|-)*?)::(?P<target>\S*?)' + \ 1789 | ;; r'(\[(?P<attrlist>.*?)\])$' 1790 | ;; conditional inclusion 1791 | (list "^\\(\\(?:ifn?def\\|endif\\)::\\)\\([^ \t\n]*?\\)\\(\\[\\).+?\\(\\]\\)[ \t]*$" 1792 | '(1 '(face adoc-preprocessor adoc-reserved block-del)) ; macro name 1793 | '(2 '(face adoc-delimiter adoc-reserved block-del)) ; condition 1794 | '(3 '(face adoc-hide-delimiter adoc-reserved block-del)) ; [ 1795 | ; ... attribute list content = the conditionaly included text 1796 | '(4 '(face adoc-hide-delimiter adoc-reserved block-del))) ; ] 1797 | ;; include 1798 | (list "^\\(\\(include1?::\\)\\([^ \t\n]*?\\)\\(\\[\\)\\(.*?\\)\\(\\]\\)\\)[ \t]*$" 1799 | '(1 '(face nil adoc-reserved block-del)) ; the whole match 1800 | '(2 adoc-preprocessor) ; macro name 1801 | '(3 adoc-delimiter) ; file name 1802 | '(4 adoc-hide-delimiter) ; [ 1803 | '(5 adoc-delimiter) ; attribute list content 1804 | '(6 adoc-hide-delimiter)) ; ] 1805 | 1806 | 1807 | ;; -- special block macros 1808 | ;; ruler line. 1809 | ;; Is a block marcro in asciidoc.conf, altough manual has it in the "text formatting" section 1810 | ;; ^'{3,}$=#ruler 1811 | (list "^\\('\\{3,\\}+\\)[ \t]*$" 1812 | '(1 '(face adoc-complex-replacement adoc-reserved block-del))) 1813 | ;; forced pagebreak 1814 | ;; Is a block marcro in asciidoc.conf, altough manual has it in the "text formatting" section 1815 | ;; ^<{3,}$=#pagebreak 1816 | (list "^\\(<\\{3,\\}+\\)[ \t]*$" 1817 | '(1 '(face adoc-delimiter adoc-reserved block-del))) 1818 | ;; comment 1819 | ;; (?mu)^[\\]?//(?P<passtext>[^/].*|)$ 1820 | ;; I don't know what the [\\]? should mean 1821 | (list "^\\(//\\(?:[^/].*\\|\\)\\(?:\n\\|\\'\\)\\)" 1822 | '(1 '(face markup-comment-face adoc-reserved block-del))) 1823 | ;; image. The first positional attribute is per definition 'alt', see 1824 | ;; asciidoc manual, sub chapter 'Image macro attributes'. 1825 | (list `(lambda (end) (adoc-kwf-std end ,(adoc-re-block-macro "image") '(0))) 1826 | '(0 '(face markup-meta-face adoc-reserved block-del) t) ; whole match 1827 | '(1 markup-complex-replacement-face t) ; 'image' 1828 | '(2 markup-internal-reference-face t) ; file name 1829 | '(3 '(face markup-meta-face adoc-reserved nil adoc-attribute-list ("alt")) t)) ; attribute list 1830 | 1831 | ;; passthrough: (?u)^(?P<name>pass)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$ 1832 | ;; todo 1833 | 1834 | ;; -- general block macro 1835 | (list `(lambda (end) (adoc-kwf-std end ,(adoc-re-block-macro) '(0))) 1836 | '(0 '(face markup-meta-face adoc-reserved block-del)) ; whole match 1837 | '(1 markup-command-face t) ; command name 1838 | '(3 '(face markup-meta-face adoc-reserved nil adoc-attribute-list t) t)) ; attribute list 1839 | 1840 | ;; lists 1841 | ;; ------------------------------ 1842 | ;; todo: respect and insert adoc-reserved 1843 | ;; 1844 | ;; bug: for items begining with a label (i.e. user text): if might be that 1845 | ;; the label contains a bogous end delimiter such that you get a 1846 | ;; highlighting that starts in the line before the label item and ends 1847 | ;; within the label. Example: 1848 | ;; 1849 | ;; bla bli 2 ** 8 is 256 quote starts at this ** 1850 | ;; that is **important**:: bla bla ends at the first ** 1851 | ;; 1852 | ;; similary: 1853 | ;; 1854 | ;; bla 2 ** 3:: bla bla 2 ** 3 gives results in an untwanted unconstrained quote 1855 | ;; 1856 | ;; - dsfadsf sdf ** asfdfsad 1857 | ;; - asfdds fsda ** fsfas 1858 | ;; 1859 | ;; maybe the solution is invent a new value for adoc-reserved, or a new 1860 | ;; property alltogether. That would also be used for the trailing \n in other 1861 | ;; block elements. Text is not allowed to contain them. All font lock 1862 | ;; keywords standing for asciidoc inline substituions would have to be 1863 | ;; adapted. 1864 | ;; 1865 | ;; 1866 | ;; bug: the text of labelleled items gets inline macros such as anchor not 1867 | ;; highlighted. See for example [[X80]] in asciidoc manual source. 1868 | (adoc-kw-oulisti 'adoc-unordered 'adoc-all-levels) 1869 | (adoc-kw-oulisti 'adoc-unordered nil 'adoc-bibliography) 1870 | (adoc-kw-oulisti 'adoc-explicitly-numbered ) 1871 | (adoc-kw-oulisti 'adoc-implicitly-numbered 'adoc-all-levels) 1872 | (adoc-kw-oulisti 'adoc-callout) 1873 | (adoc-kw-llisti 'adoc-labeled-normal 0) 1874 | (adoc-kw-llisti 'adoc-labeled-normal 1) 1875 | (adoc-kw-llisti 'adoc-labeled-normal 2) 1876 | (adoc-kw-llisti 'adoc-labeled-normal 3) 1877 | (adoc-kw-llisti 'adoc-labeled-qanda) 1878 | (adoc-kw-llisti 'adoc-labeled-glossary) 1879 | (adoc-kw-list-continuation) 1880 | 1881 | ;; Delimited blocks 1882 | ;; ------------------------------ 1883 | (adoc-kw-delimited-block 0 markup-comment-face) ; comment 1884 | (adoc-kw-delimited-block 1 markup-passthrough-face) ; passthrough 1885 | (adoc-kw-delimited-block 2 markup-code-face) ; listing 1886 | (adoc-kw-delimited-block 3 markup-verbatim-face) ; literal 1887 | (adoc-kw-delimited-block 4 nil t) ; quote 1888 | (adoc-kw-delimited-block 5 nil t) ; example 1889 | (adoc-kw-delimited-block 6 adoc-secondary-text t) ; sidebar 1890 | (adoc-kw-delimited-block 7 nil t) ; open block 1891 | (adoc-kw-delimiter-line-fallback) 1892 | 1893 | 1894 | ;; tables 1895 | ;; ------------------------------ 1896 | ;; must come BEFORE block title, else rows starting like .2+| ... | ... are taken as 1897 | (cons "^|=\\{3,\\}[ \t]*$" 'adoc-table-del ) ; ^\|={3,}$ 1898 | (list (concat "^" "\\(" (adoc-re-cell-specifier) "\\)" "\\(|\\)" 1899 | "\\(?:[^|\n]*?[ \t]" "\\(" (adoc-re-cell-specifier) "\\)" "\\(|\\)" 1900 | "\\(?:[^|\n]*?[ \t]" "\\(" (adoc-re-cell-specifier) "\\)" "\\(|\\)" 1901 | "\\(?:[^|\n]*?[ \t]" "\\(" (adoc-re-cell-specifier) "\\)" "\\(|\\)" "\\)?\\)?\\)?") 1902 | '(1 '(face adoc-delimiter adoc-reserved block-del) nil t) '(2 '(face adoc-table-del adoc-reserved block-del) nil t) 1903 | '(3 '(face adoc-delimiter adoc-reserved block-del) nil t) '(4 '(face adoc-table-del adoc-reserved block-del) nil t) 1904 | '(5 '(face adoc-delimiter adoc-reserved block-del) nil t) '(6 '(face adoc-table-del adoc-reserved block-del) nil t) 1905 | '(7 '(face adoc-delimiter adoc-reserved block-del) nil t) '(8 '(face adoc-table-del adoc-reserved block-del) nil t)) 1906 | 1907 | 1908 | ;; attribute entry 1909 | ;; ------------------------------ 1910 | (list (adoc-re-attribute-entry) '(1 adoc-delimiter) '(2 adoc-secondary-text nil t)) 1911 | 1912 | 1913 | ;; attribute list 1914 | ;; ---------------------------------- 1915 | 1916 | ;; --- special attribute lists 1917 | ;; quote/verse 1918 | (list (concat 1919 | "^\\(" 1920 | "\\(\\[\\)" 1921 | "\\(quote\\|verse\\)" 1922 | "\\(?:\\(,\\)\\(.*?\\)\\(?:\\(,\\)\\(.*?\\)\\)?\\)?" 1923 | "\\(\\]\\)" 1924 | "\\)[ \t]*$") 1925 | '(1 '(face nil adoc-reserved block-del)) ; whole match 1926 | '(2 adoc-hide-delimiter) ; [ 1927 | '(3 adoc-delimiter) ; quote|verse 1928 | '(4 adoc-hide-delimiter nil t) ; , 1929 | '(5 adoc-secondary-text nil t) ; attribution(author) 1930 | '(6 adoc-delimiter nil t) ; , 1931 | '(7 adoc-secondary-text nil t) ; cite title 1932 | '(8 adoc-hide-delimiter)) ; ] 1933 | ;; admonition block 1934 | (list "^\\(\\[\\(?:CAUTION\\|WARNING\\|IMPORTANT\\|TIP\\|NOTE\\)\\]\\)[ \t]*$" 1935 | '(1 '(face adoc-complex-replacement adoc-reserved block-del))) 1936 | ;; block id 1937 | (list `(lambda (end) (adoc-kwf-std end ,(adoc-re-anchor 'block-id) '(0))) 1938 | '(0 '(face markup-meta-face adoc-reserved block-del)) 1939 | '(1 markup-anchor-face t) 1940 | '(2 markup-secondary-text-face t t)) 1941 | 1942 | ;; --- general attribute list block element 1943 | ;; ^\[(?P<attrlist>.*)\]$ 1944 | (list '(lambda (end) (adoc-kwf-std end "^\\(\\[\\(.*\\)\\]\\)[ \t]*$" '(0))) 1945 | '(1 '(face markup-meta-face adoc-reserved block-del)) 1946 | '(2 '(face markup-meta-face adoc-attribute-list t))) 1947 | 1948 | 1949 | ;; block title 1950 | ;; ----------------------------------- 1951 | (adoc-kw-block-title) 1952 | 1953 | 1954 | ;; paragraphs 1955 | ;; -------------------------- 1956 | (adoc-kw-verbatim-paragraph-sequence) 1957 | (adoc-kw-admonition-paragraph) 1958 | (list "^[ \t]+$" '(0 '(face nil adoc-reserved block-del) t)) 1959 | 1960 | ;; Inline substitutions 1961 | ;; ========================================== 1962 | ;; Inline substitutions within block elements are performed in the 1963 | ;; following default order: 1964 | ;; -. Passtrough stuff removal (seen in asciidoc source) 1965 | ;; 1. Special characters 1966 | ;; 2. Quotes 1967 | ;; 3. Special words 1968 | ;; 4. Replacements 1969 | ;; 5. Attributes 1970 | ;; 6. Inline Macros 1971 | ;; 7. Replacements2 1972 | 1973 | 1974 | ;; (passthrough stuff removal) 1975 | ;; ------------------------ 1976 | ;; todo. look in asciidoc source how exactly asciidoc does it 1977 | ;; 1) BUG: actually only ifdef::no-inline-literal[] 1978 | ;; 2) TODO: in asciidod.conf (but not yet here) also in inline macro section 1979 | 1980 | ;; AsciiDoc Manual: constitutes an inline literal passthrough. The enclosed 1981 | ;; text is rendered in a monospaced font and is only subject to special 1982 | ;; character substitution. 1983 | (adoc-kw-quote 'adoc-constrained "`" markup-typewriter-face nil nil t) ;1) 1984 | ;; AsciiDoc Manual: The triple-plus passthrough is functionally identical to 1985 | ;; the pass macro but you don’t have to escape ] characters and you can 1986 | ;; prefix with quoted attributes in the inline version 1987 | (adoc-kw-quote 'adoc-unconstrained "+++" markup-typewriter-face nil nil t) ;2) 1988 | ;;The double-dollar passthrough is functionally identical to the triple-plus 1989 | ;;passthrough with one exception: special characters are escaped. 1990 | (adoc-kw-quote 'adoc-unconstrained "$$" markup-typewriter-face nil nil t) ;2) 1991 | ;; todo: add pass:[...], latexmath:[...], asciimath[...] 1992 | 1993 | ;; special characters 1994 | ;; ------------------ 1995 | ;; no highlighting for them, since they are a property of the backend markup, 1996 | ;; not of AsciiDoc syntax 1997 | 1998 | 1999 | ;; quotes: unconstrained and constrained 2000 | ;; order given by asciidoc.conf 2001 | ;; ------------------------------ 2002 | (adoc-kw-quote 'adoc-unconstrained "**" markup-strong-face) 2003 | (adoc-kw-quote 'adoc-constrained "*" markup-strong-face) 2004 | (adoc-kw-quote 'adoc-constrained "``" nil adoc-replacement "''") ; double quoted text 2005 | (adoc-kw-quote 'adoc-constrained "'" markup-emphasis-face) ; single quoted text 2006 | (adoc-kw-quote 'adoc-constrained "`" nil adoc-replacement "'") 2007 | ;; `...` , +++...+++, $$...$$ are within passthrough stuff above 2008 | (adoc-kw-quote 'adoc-unconstrained "++" markup-typewriter-face) ; AsciiDoc manual: really onl '..are rendered in a monospaced font.' 2009 | (adoc-kw-quote 'adoc-constrained "+" markup-typewriter-face) 2010 | (adoc-kw-quote 'adoc-unconstrained "__" markup-emphasis-face) 2011 | (adoc-kw-quote 'adoc-constrained "_" markup-emphasis-face) 2012 | (adoc-kw-quote 'adoc-unconstrained "##" markup-gen-face) ; unquoted text 2013 | (adoc-kw-quote 'adoc-constrained "#" markup-gen-face) ; unquoted text 2014 | (adoc-kw-quote 'adoc-unconstrained "~" (adoc-facespec-subscript)) ; subscript 2015 | (adoc-kw-quote 'adoc-unconstrained "^" (adoc-facespec-superscript)) ; superscript 2016 | 2017 | 2018 | ;; special words 2019 | ;; -------------------- 2020 | ;; there are no default special words to highlight 2021 | 2022 | 2023 | ;; replacements 2024 | ;; -------------------------------- 2025 | ;; Asciidoc.conf surounds em dash with thin spaces. I think that does not 2026 | ;; make sense here, all that spaces you would see in the buffer would at best 2027 | ;; be confusing. 2028 | (adoc-kw-replacement "\\((C)\\)" "\u00A9") 2029 | (adoc-kw-replacement "\\((R)\\)" "\u00AE") 2030 | (adoc-kw-replacement "\\((TM)\\)" "\u2122") 2031 | ;; (^-- )=—  2032 | ;; (\n-- )|( -- )|( --\n)= —  2033 | ;; (\w)--(\w)=\1—\2 2034 | (adoc-kw-replacement "^\\(--\\)[ \t]" "\u2014") ; em dash. See also above 2035 | (adoc-kw-replacement "[ \t]\\(--\\)\\(?:[ \t]\\|$\\)" "\u2014") ; dito 2036 | (adoc-kw-replacement "[a-zA-Z0-9_]\\(--\\)[a-zA-Z0-9_]" "\u2014") ; dito 2037 | (adoc-kw-replacement "[a-zA-Z0-9_]\\('\\)[a-zA-Z0-9_]" "\u2019") ; punctuation apostrophe 2038 | (adoc-kw-replacement "\\(\\.\\.\\.\\)" "\u2026") ; ellipsis 2039 | (adoc-kw-replacement "\\(->\\)" "\u2192") 2040 | (adoc-kw-replacement "\\(=>\\)" "\u21D2") 2041 | (adoc-kw-replacement "\\(<-\\)" "\u2190") 2042 | (adoc-kw-replacement "\\(<=\\)" "\u21D0") 2043 | ;; general character entity reference 2044 | ;; (?<!\\)&([:_#a-zA-Z][:_.\-\w]*?;)=&\1 2045 | (adoc-kw-replacement "\\(&[:_#a-zA-Z]\\(?:[-:_.]\\|[a-zA-Z0-9_]\\)*?;\\)" 'adoc-entity-to-string) 2046 | 2047 | ;; attributes 2048 | ;; --------------------------------- 2049 | ;; attribute refrence 2050 | (cons "{\\(\\w+\\(?:\\w*\\|-\\)*\\)\\([=?!#%@$][^}\n]*\\)?}" 'adoc-replacement) 2051 | 2052 | 2053 | ;; inline macros (that includes anchors, links, footnotes,....) 2054 | ;; ------------------------------ 2055 | ;; todo: make adoc-kw-... macros to have less redundancy 2056 | ;; Note: Some regexp/kewyords are within the macro section 2057 | ;; TODO: 2058 | ;; - allow multiline 2059 | ;; - currently escpapes are not looked at 2060 | ;; - adapt to the adoc-reserved scheme 2061 | ;; - same order as in asciidoc.conf (is that in 'reverse'? cause 'default syntax' comes first) 2062 | 2063 | ;; Macros using default syntax, but having special highlighting in adoc-mode 2064 | (adoc-kw-inline-macro-urls-no-attribute-list) 2065 | (adoc-kw-inline-macro-urls-attribute-list) 2066 | (adoc-kw-inline-macro "anchor" nil nil nil markup-anchor-face t '("xreflabel")) 2067 | (adoc-kw-inline-macro "image" nil nil markup-complex-replacement-face markup-internal-reference-face t 2068 | '("alt")) 2069 | (adoc-kw-inline-macro "xref" nil nil nil '(markup-reference-face markup-internal-reference-face) t 2070 | '(("caption") (("caption" . markup-reference-face)))) 2071 | (adoc-kw-inline-macro "footnote" t nil nil nil nil markup-secondary-text-face) 2072 | (adoc-kw-inline-macro "footnoteref" t 'single-attribute nil nil nil 2073 | '(("id") (("id" . markup-internal-reference-face)))) 2074 | (adoc-kw-inline-macro "footnoteref" t nil nil nil nil '("id" "text")) 2075 | (adoc-kw-standalone-urls) 2076 | 2077 | ;; Macros using default syntax and having default highlighting in adoc-mod 2078 | (adoc-kw-inline-macro) 2079 | 2080 | ;; bibliographic anchor ala [[[id]]] 2081 | ;; actually in AsciiDoc the part between the innermost brackets is an 2082 | ;; attribute list, for simplicity adoc-mode doesn't really treat it as such. 2083 | ;; The attrib list can only contain one element anyway. 2084 | (list `(lambda (end) (adoc-kwf-std end ,(adoc-re-anchor 'biblio) '(1 3) '(0))) 2085 | '(1 '(face markup-meta-face adoc-reserved t) t) ; [[ 2086 | '(2 markup-gen-face) ; [id] 2087 | '(3 '(face markup-meta-face adoc-reserved t) t)) ; ]] 2088 | ;; anchor ala [[id]] or [[id,xreflabel]] 2089 | (list `(lambda (end) (adoc-kwf-std end ,(adoc-re-anchor 'inline-special) '(1 3) '(0))) 2090 | '(1 '(face markup-meta-face adoc-reserved t) t) 2091 | '(2 '(face markup-meta-face adoc-attribute-list ("id" "xreflabel")) t) 2092 | '(3 '(face markup-meta-face adoc-reserved t) t)) 2093 | 2094 | ;; see also xref: within inline macros 2095 | ;; reference with own/explicit caption 2096 | (list (adoc-re-xref 'inline-special-with-caption t) 2097 | '(1 adoc-hide-delimiter) ; << 2098 | '(2 adoc-delimiter) ; anchor-id 2099 | '(3 adoc-hide-delimiter) ; , 2100 | '(4 adoc-reference) ; link text 2101 | '(5 adoc-hide-delimiter)) ; >> 2102 | ;; reference without caption 2103 | (list (adoc-re-xref 'inline-special-no-caption t) 2104 | '(1 adoc-hide-delimiter) ; << 2105 | '(2 adoc-reference) ; link text = anchor id 2106 | '(3 adoc-hide-delimiter)) ; >> 2107 | 2108 | ;; index terms 2109 | ;; todo: 2110 | ;; - copy asciidocs regexps below 2111 | ;; - add the indexterm2?:...[...] syntax 2112 | ;; ifdef::asciidoc7compatible[] 2113 | ;; (?su)(?<!\S)[\\]?\+\+(?P<attrlist>[^+].*?)\+\+(?!\+)=indexterm 2114 | ;; (?<!\S)[\\]?\+(?P<attrlist>[^\s\+][^+].*?)\+(?!\+)=indexterm2 2115 | ;; ifndef::asciidoc7compatible[] 2116 | ;; (?su)(?<!\()[\\]?\(\(\((?P<attrlist>[^(].*?)\)\)\)(?!\))=indexterm 2117 | ;; (?<!\()[\\]?\(\((?P<attrlist>[^\s\(][^(].*?)\)\)(?!\))=indexterm2 2118 | ;; 2119 | (cons "(((?\\([^\\\n]\\|\\\\.\\)*?)))?" 'adoc-delimiter) 2120 | 2121 | ;; passthrough. Note that quote section has some of them also 2122 | ;; todo: passthrough stuff 2123 | ;; (?su)[\\]?(?P<name>pass):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[] 2124 | ;; (?su)[\\]?\+\+\+(?P<passtext>.*?)\+\+\+=pass[] 2125 | ;; (?su)[\\]?\$\$(?P<passtext>.*?)\$\$=pass[specialcharacters] 2126 | ;; # Inline literal (within ifndef::no-inline-literal[]) 2127 | ;; (?su)(?<!\w)([\\]?`(?P<passtext>\S|\S.*?\S)`)(?!\w)=literal[specialcharacters] 2128 | 2129 | 2130 | 2131 | ;; -- forced linebreak 2132 | ;; manual: A plus character preceded by at least one space character at the 2133 | ;; end of a non-blank line forces a line break. 2134 | ;; Asciidoc bug: If has that affect also on a non blank line. 2135 | ;; todo: what kind of element is that? Really text formatting? Its not in asciidoc.conf 2136 | (list "^.*[^ \t\n].*[ \t]\\(\\+\\)[ \t]*$" '(1 adoc-delimiter)) ; bug: only if not adoc-reserved 2137 | 2138 | ;; -- callout anchors (references are within list) 2139 | ;; commented out because they are only witin (literal?) blocks 2140 | ;; asciidoc.conf: [\\]?<(?P<index>\d+)>=callout 2141 | ;; (list "^\\(<\\)\\([0-9+]\\)\\(>\\)" '(1 adoc-delimiter) '(3 adoc-delimiter)) 2142 | 2143 | 2144 | ;; Replacements2 2145 | ;; ----------------------------- 2146 | ;; there default replacements2 section is empty 2147 | 2148 | 2149 | ;; misc 2150 | ;; ------------------------------ 2151 | 2152 | ;; -- misc 2153 | (adoc-kw-first-whites-fixed-width) 2154 | 2155 | ;; -- warnings 2156 | ;; todo: add tooltip explaining what is the warning all about 2157 | ;; bogous 'list continuation' 2158 | (list "^\\([ \t]+\\+[ \t]*\\)$" '(1 adoc-warning t)) 2159 | ;; list continuation witch appends a literal paragraph. The user probably 2160 | ;; wanted to add a normal paragraph. List paragraphs are appended 2161 | ;; implicitely. 2162 | (list "^\\(\\+[ \t]*\\)\n\\([ \t]+\\)[^ \t\n]" '(1 adoc-warning t) '(2 adoc-warning t)) 2163 | 2164 | ;; content of attribute lists 2165 | (list 'adoc-kwf-attribute-list) 2166 | 2167 | ;; cleanup 2168 | (list 'adoc-flf-meta-face-cleanup) 2169 | )) 2170 | 2171 | 2172 | ;;;; interactively-callable commands 2173 | (defun adoc-show-version () 2174 | "Show the version number in the minibuffer." 2175 | (interactive) 2176 | (message "adoc-mode, version %s" adoc-mode-version)) 2177 | 2178 | (defun adoc-goto-ref-label (id) 2179 | "Goto the anchor defining the id ID." 2180 | ;; KLUDGE: Getting the default, i.e. trying to parse the xref 'at' point, is 2181 | ;; not done nicely. backward-char 5 because the longest 'starting' of an xref 2182 | ;; construct is 'xref:' (others are '<<'). All this fails if point is within 2183 | ;; the id, opposed to the start the id. Or if the xref spawns over the current 2184 | ;; line. 2185 | (interactive (let* ((default (adoc-xref-id-at-point)) 2186 | (default-str (if default (concat "(default " default ")") ""))) 2187 | (list 2188 | (read-string 2189 | (concat "Goto anchor of reference/label " default-str ": ") 2190 | nil nil default)))) 2191 | (let ((pos (save-excursion 2192 | (goto-char 0) 2193 | (re-search-forward (adoc-re-anchor nil id) nil t)))) 2194 | (if (null pos) (error (concat "Can't find an anchor defining '" id "'"))) 2195 | (push-mark) 2196 | (goto-char pos))) 2197 | 2198 | (defun adoc-promote (&optional arg) 2199 | "Promotes the structure at point ARG levels. 2200 | 2201 | When ARG is nil (i.e. when no prefix arg is given), it defaults 2202 | to 1. When ARG is negative, level is demoted that many levels. 2203 | 2204 | The intention is that the structure can be a title or a list 2205 | element or anything else which has a 'level'. However currently 2206 | it works only for titles." 2207 | (interactive "p") 2208 | (adoc-promote-title arg)) 2209 | 2210 | (defun adoc-demote (&optional arg) 2211 | "Demotes the structure at point ARG levels. 2212 | 2213 | Analogous to `adoc-promote', see there." 2214 | (interactive "p") 2215 | (adoc-demote-title arg)) 2216 | 2217 | (defun adoc-promote-title (&optional arg) 2218 | "Promotes the title at point ARG levels. 2219 | 2220 | When ARG is nil (i.e. when no prefix arg is given), it defaults 2221 | to 1. When ARG is negative, level is demoted that many levels. If 2222 | ARG is 0, see `adoc-adjust-title-del'." 2223 | (interactive "p") 2224 | (adoc-modify-title arg)) 2225 | 2226 | (defun adoc-demote-title (&optional arg) 2227 | "Completely analgous to `adoc-promote-title'." 2228 | (interactive "p") 2229 | (adoc-promote-title (- arg))) 2230 | 2231 | ;; todo: 2232 | ;; - adjust while user is typing title 2233 | ;; - tempo template which uses alreay typed text to insert a 'new' title 2234 | ;; - auto convert one line title to two line title. is easy&fast to type, but 2235 | ;; gives two line titles for those liking them 2236 | (defun adoc-adjust-title-del () 2237 | "Adjusts underline length to match the length of the title's text. 2238 | 2239 | E.g. after editing a two line title, call `adoc-adjust-title-del' so 2240 | the underline has the correct length." 2241 | (interactive) 2242 | (adoc-modify-title)) 2243 | 2244 | (defun adoc-toggle-title-type (&optional type-type) 2245 | "Toggles title's type. 2246 | 2247 | If TYPE-TYPE is nil, title's type is toggled. If TYPE-TYPE is 2248 | non-nil, the sub type is toggled." 2249 | (interactive "P") 2250 | (when type-type 2251 | (setq type-type t)) 2252 | (adoc-modify-title nil nil (not type-type) type-type)) 2253 | 2254 | (defun adoc-calc () 2255 | "(Re-)calculates variables used in adoc-mode. 2256 | Needs to be called after changes to certain (customization) 2257 | variables. Mostly in order font lock highlighting works as the 2258 | new customization demands." 2259 | (interactive) 2260 | 2261 | (when (and (null adoc-insert-replacement) 2262 | adoc-unichar-name-resolver) 2263 | (message "Warning: adoc-unichar-name-resolver is non-nil, but is adoc-insert-replacement is nil")) 2264 | (when (and (eq adoc-unichar-name-resolver 'adoc-unichar-by-name) 2265 | (null adoc-unichar-alist)) 2266 | (adoc-make-unichar-alist)) 2267 | 2268 | (setq adoc-font-lock-keywords (adoc-get-font-lock-keywords)) 2269 | (when (and font-lock-mode (eq major-mode 'adoc-mode)) 2270 | (font-lock-fontify-buffer)) 2271 | 2272 | (adoc-easy-menu-define)) 2273 | 2274 | (defun adoc-easy-menu-define() 2275 | (easy-menu-define 2276 | adoc-mode-menu adoc-mode-map "Menu for adoc mode" 2277 | `("AsciiDoc" 2278 | ["Promote" adoc-promote] 2279 | ["Demote" adoc-demote] 2280 | ["Toggle title type" adoc-toggle-title-type] 2281 | ["Adjust title underline" adoc-adjust-title-del] 2282 | ["Goto anchor" adoc-goto-ref-label] 2283 | "---" 2284 | ;; names|wording / rough order/ help texts are from asciidoc manual 2285 | ("Templates / cheat sheet" 2286 | ("Text formatting - constrained quotes" 2287 | :help ,adoc-help-constrained-quotes 2288 | ["_Emphasis_" tempo-template-adoc-emphasis 2289 | :help ,adoc-help-emphasis ] 2290 | ["*Strong*" tempo-template-adoc-strong 2291 | :help ,adoc-help-strong ] 2292 | ["+Monospaced+" tempo-template-adoc-monospace 2293 | :help ,adoc-help-monospace] 2294 | ["`Monospaced literal`" tempo-template-adoc-monospace-literal ; redundant to the one in the passthrough section 2295 | :help ,adoc-help-monospace-literal] 2296 | ["`Single quote'" tempo-template-adoc-single-quote 2297 | :help ,adoc-help-single-quote] 2298 | ["``Double quote''" tempo-template-adoc-double-quote 2299 | :help ,adoc-help-double-quote] 2300 | ;; todo: insert underline, overline, strikethrough, big, small 2301 | ["[attributes]##text##" tempo-template-adoc-attributed 2302 | :help ,adoc-help-attributed]) 2303 | ("Text formatting - unconstrained quotes" 2304 | :help ,adoc-help-unconstrained-quotes 2305 | ["^Superscript^" tempo-template-adoc-superscript] 2306 | ["~Subscript~" tempo-template-adoc-subscript] 2307 | ["__Emphasis__" tempo-template-adoc-emphasis-uc 2308 | :help ,adoc-help-emphasis ] 2309 | ["**Strong**" tempo-template-adoc-strong-uc 2310 | :help ,adoc-help-strong ] 2311 | ["++Monospaced++" tempo-template-adoc-monospace-uc 2312 | :help ,adoc-help-monospace] 2313 | ["[attributes]##text##" tempo-template-adoc-attributed-uc 2314 | :help ,adoc-help-attributed]) 2315 | ("Text formatting - misc" 2316 | ["Line break: <SPC>+<NEWLINE>" tempo-template-adoc-line-break 2317 | :help ,adoc-help-line-break] 2318 | ["Page break: <<<" tempo-template-adoc-page-break 2319 | :help ,adoc-help-page-break] 2320 | ["Ruler line: ---" tempo-template-adoc-ruler-line 2321 | :help ,adoc-help-ruler-line]) 2322 | ("Text formatting - replacements" 2323 | ["Copyright: (C) \u2192 \u00A9" tempo-template-adoc-copyright] 2324 | ["Trademark: (TM) \u2192 \u2122" tempo-template-adoc-trademark] 2325 | ["Registered trademark: (R) \u2192 \u00AE" tempo-template-adoc-registered-trademark] 2326 | ["Dash: -- \u2192 \u2014" tempo-template-adoc-dash] 2327 | ["Ellipsis: ... \u2192 \u2026" tempo-template-adoc-ellipsis] 2328 | ["Right arrow: -> \u2192 \u2192" tempo-template-adoc-right-arrow] 2329 | ["Left arrow: <- \u2192 \u2190" tempo-template-adoc-left-arrow] 2330 | ["Right double arrow: => \u2192 \u21D2" tempo-template-adoc-right-double-arrow] 2331 | ["Left double arrow: <= \u2192 \u21D0" tempo-template-adoc-left-double-arrow] 2332 | "---" 2333 | ["Character entity reference: &...;" tempo-template-adoc-entity-reference 2334 | :help ,adoc-help-entity-reference]) 2335 | ("Titles" 2336 | [,(concat "Document title (level 0): " (adoc-template-str-title 0)) 2337 | tempo-template-adoc-title-1] 2338 | [,(concat "Section title (level 1): " (adoc-template-str-title 1)) 2339 | tempo-template-adoc-title-2] 2340 | [,(concat "Section title (level 2): " (adoc-template-str-title 2)) 2341 | tempo-template-adoc-title-3] 2342 | [,(concat "Section title (level 3): " (adoc-template-str-title 3)) 2343 | tempo-template-adoc-title-4] 2344 | [,(concat "Section title (level 4): " (adoc-template-str-title 4)) 2345 | tempo-template-adoc-title-5] 2346 | ["Block title: .foo" tempo-template-adoc-block-title] 2347 | ["BlockId: [[id]]" tempo-template-adoc-anchor]) ; redundant to anchor below 2348 | ("Paragraphs" 2349 | ["Literal paragraph" tempo-template-adoc-literal-paragraph 2350 | :help ,adoc-help-literal-paragraph] 2351 | "---" 2352 | ["TIP: " tempo-template-adoc-paragraph-tip] 2353 | ["NOTE: " tempo-template-adoc-paragraph-note] 2354 | ["IMPORTANT: " tempo-template-adoc-paragraph-important] 2355 | ["WARNING: " tempo-template-adoc-paragraph-warning] 2356 | ["CAUTION: " tempo-template-adoc-paragraph-caution]) 2357 | ("Delimited blocks" 2358 | :help ,adoc-help-delimited-block 2359 | ;; BUG: example does not reflect the content of adoc-delimited-block-del 2360 | ["Comment: ////" tempo-template-adoc-delimited-block-comment 2361 | :help ,adoc-help-delimited-block-comment] 2362 | ["Passthrough: ++++" tempo-template-adoc-delimited-block-passthrough 2363 | :help ,adoc-help-delimited-block-passthrouh] 2364 | ["Listing: ----" tempo-template-adoc-delimited-block-listing 2365 | :help ,adoc-help-delimited-block-listing] 2366 | ["Literal: ...." tempo-template-adoc-delimited-block-literal 2367 | :help ,adoc-help-delimited-block-literal] 2368 | ["Quote: ____" tempo-template-adoc-delimited-block-quote 2369 | :help ,adoc-help-delimited-block-quote] 2370 | ["Example: ====" tempo-template-adoc-delimited-block-example 2371 | :help ,adoc-help-delimited-block-example] 2372 | ["Sidebar: ****" tempo-template-adoc-delimited-block-sidebar 2373 | :help ,adoc-help-delimited-block-sidebar] 2374 | ["Open: --" tempo-template-adoc-delimited-block-open-block 2375 | :help ,adoc-help-delimited-block-open-block]) 2376 | ("Lists" 2377 | :help ,adoc-help-list 2378 | ("Bulleted" 2379 | :help ,adoc-help-bulleted-list 2380 | ["Item: -" tempo-template-adoc-bulleted-list-item-1] 2381 | ["Item: **" tempo-template-adoc-bulleted-list-item-2] 2382 | ["Item: ***" tempo-template-adoc-bulleted-list-item-3] 2383 | ["Item: ****" tempo-template-adoc-bulleted-list-item-4] 2384 | ["Item: *****" tempo-template-adoc-bulleted-list-item-5]) 2385 | ("Numbered - explicit" 2386 | ["Arabic (decimal) numbered item: 1." tempo-template-adoc-numbered-list-item] 2387 | ["Lower case alpha (letter) numbered item: a." tempo-template-adoc-numbered-list-item] 2388 | ["Upper case alpha (letter) numbered item: A." tempo-template-adoc-numbered-list-item] 2389 | ["Lower case roman numbered list item: i)" tempo-template-adoc-numbered-list-item-roman] 2390 | ["Upper case roman numbered list item: I)" tempo-template-adoc-numbered-list-item-roman]) 2391 | ("Numbered - implicit" 2392 | ["Arabic (decimal) numbered item: ." tempo-template-adoc-implicit-numbered-list-item-1] 2393 | ["Lower case alpha (letter) numbered item: .." tempo-template-adoc-implicit-numbered-list-item-2] 2394 | ["Upper case alpha (letter)numbered item: ..." tempo-template-adoc-implicit-numbered-list-item-3] 2395 | ["Lower case roman numbered list item: ...." tempo-template-adoc-implicit-numbered-list-item-4] 2396 | ["Upper case roman numbered list item: ....." tempo-template-adoc-implicit-numbered-list-item-5]) 2397 | ["Labeled item: label:: text" tempo-template-adoc-labeled-list-item] 2398 | ["List item continuation: <NEWLINE>+<NEWLINE>" tempo-template-adoc-list-item-continuation 2399 | :help ,adoc-help-list-item-continuation]) 2400 | ("Tables" 2401 | :help ,adoc-help-table 2402 | ["Example table" tempo-template-adoc-example-table]) 2403 | ("Macros (inline & block)" 2404 | :help ,adoc-help-macros 2405 | ["URL: http://foo.com" tempo-template-adoc-url 2406 | :help ,adoc-help-url] 2407 | ["URL with caption: http://foo.com[caption]" tempo-template-adoc-url-caption 2408 | :help ,adoc-help-url] 2409 | ["EMail: bob@foo.com" tempo-template-adoc-email 2410 | :help ,adoc-help-url] 2411 | ["EMail with caption: mailto:address[caption]" tempo-template-adoc-email-caption 2412 | :help ,adoc-help-url] 2413 | ["Anchor aka BlockId (syntax 1): [[id,xreflabel]]" tempo-template-adoc-anchor 2414 | :help ,adoc-help-anchor] 2415 | ["Anchor (syntax 2): anchor:id[xreflabel]" tempo-template-adoc-anchor-default-syntax 2416 | :help ,adoc-help-anchor] 2417 | ["Xref (syntax 1): <<id,caption>>" adoc-xref 2418 | :help ,adoc-help-xref] 2419 | ["Xref (syntax 2): xref:id[caption]" adoc-xref-default-syntax 2420 | :help ,adoc-help-xref] 2421 | ["Image: image:target-path[caption]" adoc-image] 2422 | ["Comment: //" tempo-template-adoc-comment 2423 | :help ,adoc-help-comment] 2424 | ("Passthrough macros" 2425 | :help adoc-help-passthrough-macros 2426 | ["pass:[text]" tempo-template-adoc-pass 2427 | :help ,adoc-help-pass] 2428 | ["ASCIIMath: asciimath:[text]" tempo-template-adoc-asciimath 2429 | :help ,adoc-help-asciimath] 2430 | ["LaTeX math: latexmath[text]" tempo-template-adoc-latexmath 2431 | :help ,adoc-help-latexmath] 2432 | ["+++text+++" tempo-template-adoc-pass-+++ 2433 | :help ,adoc-help-pass-+++] 2434 | ["$$text$$" tempo-template-pass-$$ 2435 | :help ,adoc-help-pass-$$] 2436 | ["`text`" tempo-template-monospace-literal ; redundant to the one in the quotes section 2437 | :help ,adoc-help-monospace-literal])))))) 2438 | 2439 | 2440 | ;;;; tempos 2441 | ;; todo: tell user to make use of tempo-interactive 2442 | ;; todo: tell user to how to use tempo-snippets?? that there are clear methods 2443 | ;; todo: tell user to how to use tempo-snippets?? suggested customizations working best with adoc 2444 | ;; todo: after changing adoc-tempo-frwk, all adoc-tempo-define need to be 2445 | ;; evaluated again. This doesn't feel right 2446 | ;; todo: titles,block titles,blockid,... should start on a new line 2447 | ;; PROBLEM: snippets dont allow empty 'field', e.g. empty caption 2448 | ;; Workaround: mark whole 'edit-field' and delete it 2449 | (if (eq adoc-tempo-frwk 'tempo-snippets) 2450 | (require 'tempo-snippets) 2451 | (require 'tempo)) 2452 | 2453 | (defun adoc-tempo-define (&rest args) 2454 | (if (eq adoc-tempo-frwk 'tempo-snippets) 2455 | (apply 'tempo-define-snippet args) 2456 | (apply 'tempo-define-template args))) 2457 | 2458 | (defun adoc-template-str-title (&optional level title-text) 2459 | "Returns the string tempo-template-adoc-title-x would insert" 2460 | (with-temp-buffer 2461 | (insert (or title-text "foo")) 2462 | (set-mark (point-min)) 2463 | (funcall (intern-soft (concat "tempo-template-adoc-title-" (number-to-string (1+ (or level 0)))))) 2464 | (replace-regexp-in-string "\n" "\\\\n" 2465 | (buffer-substring-no-properties (point-min) (point-max))))) 2466 | 2467 | ;; Text formatting - constrained quotes 2468 | (adoc-tempo-define "adoc-emphasis" '("_" (r "text" text) "_") nil adoc-help-emphasis) 2469 | (adoc-tempo-define "adoc-strong" '("*" (r "text" text) "*") nil adoc-help-strong) 2470 | (adoc-tempo-define "adoc-monospace" '("+" (r "text" text) "+") nil adoc-help-monospace) 2471 | (adoc-tempo-define "adoc-monospace-literal" '("`" (r "text" text) "`")) 2472 | (adoc-tempo-define "adoc-single-quote" '("`" (r "text" text) "'") nil adoc-help-single-quote) 2473 | (adoc-tempo-define "adoc-double-quote" '("``" (r "text" text) "''") nil adoc-help-double-quote) 2474 | (adoc-tempo-define "adoc-attributed" '("[" p "]#" (r "text" text) "#") nil adoc-help-double-quote) 2475 | 2476 | ;; Text formatting - unconstrained quotes 2477 | (adoc-tempo-define "adoc-emphasis-uc" '("__" (r "text" text) "__") nil adoc-help-emphasis) 2478 | (adoc-tempo-define "adoc-strong-uc" '("**" (r "text" text) "**") nil adoc-help-strong) 2479 | (adoc-tempo-define "adoc-monospace-uc" '("++" (r "text" text) "++") nil adoc-help-monospace) 2480 | (adoc-tempo-define "adoc-attributed-uc" '("[" p "]##" (r "text" text) "##") nil adoc-help-attributed) 2481 | (adoc-tempo-define "adoc-superscript" '("^" (r "text" text) "^")) 2482 | (adoc-tempo-define "adoc-subscript" '("~" (r "text" text) "~")) 2483 | 2484 | ;; Text formatting - misc 2485 | (adoc-tempo-define "adoc-line-break" '((if (looking-back " ") "" " ") "+" %) nil adoc-help-line-break) 2486 | (adoc-tempo-define "adoc-page-break" '(bol "<<<" %) nil adoc-help-page-break) 2487 | (adoc-tempo-define "adoc-ruler-line" '(bol "---" %) nil adoc-help-ruler-line) 2488 | 2489 | ;; Text formatting - replacements 2490 | (adoc-tempo-define "adoc-copyright" '("(C)")) 2491 | (adoc-tempo-define "adoc-trademark" '("(T)")) 2492 | (adoc-tempo-define "adoc-registered-trademark" '("(R)")) 2493 | (adoc-tempo-define "adoc-dash" '("---")) 2494 | (adoc-tempo-define "adoc-ellipsis" '("...")) 2495 | (adoc-tempo-define "adoc-right-arrow" '("->")) 2496 | (adoc-tempo-define "adoc-left-arrow" '("<-")) 2497 | (adoc-tempo-define "adoc-right-double-arrow" '("=>")) 2498 | (adoc-tempo-define "adoc-left-double-arrow" '("<=")) 2499 | (adoc-tempo-define "adoc-entity-reference" '("&" r ";") nil adoc-help-entity-reference) 2500 | 2501 | ;; Titles 2502 | ;; todo 2503 | ;; - merge with adoc-make-title 2504 | ;; - dwim: 2505 | ;; - when point is on a text line, convert that line to a title 2506 | ;; - when it is already a title .... correct underlines? 2507 | ;; - ensure n blank lines before and m blank lines after title, or unchanged if n/m nil 2508 | (dotimes (level 5) ; level starting at 0 2509 | (let ((one-line-del (make-string (1+ level) ?\=))) 2510 | 2511 | (adoc-tempo-define 2512 | (concat "adoc-title-" (number-to-string (1+ level))) 2513 | ;; see adoc-tempo-handler for what the (tr ...) does. 2514 | (list 2515 | `(cond 2516 | ((eq adoc-title-style 'adoc-title-style-one-line) 2517 | '(tr bol ,one-line-del " " (r "text" text))) 2518 | ((eq adoc-title-style 'adoc-title-style-one-line-enclosed) 2519 | '(tr bol ,one-line-del " " (r "text" text) " " ,one-line-del)) 2520 | ;; BUG in tempo: when first thing is a tempo element which introduces a marker, that 2521 | ;; marker is skipped 2522 | ((eq adoc-title-style 'adoc-title-style-two-line) 2523 | '(tr bol (r "text" text) "\n" 2524 | (adoc-make-two-line-title-underline ,level (if on-region (- tempo-region-stop tempo-region-start))))) 2525 | (t 2526 | (error "Unknown title style")))) 2527 | nil 2528 | (concat 2529 | "Inserts a level " (number-to-string (1+ level)) " (starting at 1) title. 2530 | Is influenced by customization variables such as `adoc-title-style'.")))) 2531 | 2532 | (adoc-tempo-define "adoc-block-title" '(bol "." (r "text" text) %)) 2533 | 2534 | ;; Paragraphs 2535 | (adoc-tempo-define "adoc-literal-paragraph" '(bol " " (r "text" text) %) nil adoc-help-literal-paragraph) 2536 | (adoc-tempo-define "adoc-paragraph-tip" '(bol "TIP: " (r "text" text) %)) 2537 | (adoc-tempo-define "adoc-paragraph-note" '(bol "NOTE: " (r "text" text) %)) 2538 | (adoc-tempo-define "adoc-paragraph-important" '(bol "IMPORTANT: " (r "text" text) %)) 2539 | (adoc-tempo-define "adoc-paragraph-warning" '(bol "WARNING: " (r "text" text) %)) 2540 | (adoc-tempo-define "adoc-paragraph-caution" '(bol "CAUTION: " (r "text" text) %)) 2541 | 2542 | ;; delimited blocks 2543 | (adoc-tempo-define "adoc-delimited-block-comment" 2544 | '(bol (make-string 50 ?/) n (r-or-n "text" text) bol (make-string 50 ?/) %) 2545 | nil adoc-help-delimited-block-comment) 2546 | (adoc-tempo-define "adoc-delimited-block-passthrough" 2547 | '(bol (make-string 50 ?+) n (r-or-n "text" text) bol (make-string 50 ?+) %) 2548 | nil adoc-help-delimited-block-passthrouh) 2549 | (adoc-tempo-define "adoc-delimited-block-listing" 2550 | '(bol (make-string 50 ?-) n (r-or-n "text" text) bol (make-string 50 ?-) %) 2551 | nil adoc-help-delimited-block-listing) 2552 | (adoc-tempo-define "adoc-delimited-block-literal" 2553 | '(bol (make-string 50 ?.) n (r-or-n "text" text) bol (make-string 50 ?.) %) 2554 | nil adoc-help-delimited-block-literal) 2555 | (adoc-tempo-define "adoc-delimited-block-quote" 2556 | '(bol (make-string 50 ?_) n (r-or-n "text" text) bol (make-string 50 ?_) %) 2557 | nil adoc-help-delimited-block-quote) 2558 | (adoc-tempo-define "adoc-delimited-block-example" 2559 | '(bol (make-string 50 ?=) n (r-or-n "text" text) bol (make-string 50 ?=) %) 2560 | nil adoc-help-delimited-block-example) 2561 | (adoc-tempo-define "adoc-delimited-block-sidebar" 2562 | '(bol (make-string 50 ?*) n (r-or-n "text" text) bol (make-string 50 ?*) %) 2563 | nil adoc-help-delimited-block-sidebar) 2564 | (adoc-tempo-define "adoc-delimited-block-open-block" 2565 | '(bol "--" n (r-or-n "text" text) bol "--" %) 2566 | nil adoc-help-delimited-block-open-block) 2567 | 2568 | ;; Lists 2569 | ;; todo: customize indentation 2570 | (adoc-tempo-define "adoc-bulleted-list-item-1" '(bol (adoc-insert-indented "- " 1) (r "text" text))) 2571 | (adoc-tempo-define "adoc-bulleted-list-item-2" '(bol (adoc-insert-indented "** " 2) (r "text" text))) 2572 | (adoc-tempo-define "adoc-bulleted-list-item-3" '(bol (adoc-insert-indented "*** " 3) (r "text" text))) 2573 | (adoc-tempo-define "adoc-bulleted-list-item-4" '(bol (adoc-insert-indented "**** " 4) (r "text" text))) 2574 | (adoc-tempo-define "adoc-bulleted-list-item-5" '(bol (adoc-insert-indented "***** " 5) (r "text" text))) 2575 | (adoc-tempo-define "adoc-numbered-list-item" '(bol (p "number" number) ". " (r "text" text))) 2576 | (adoc-tempo-define "adoc-numbered-list-item-roman" '(bol (p "number" number) ") " (r "text" text))) 2577 | (adoc-tempo-define "adoc-implicit-numbered-list-item-1" '(bol (adoc-insert-indented ". " 1) (r "text" text))) 2578 | (adoc-tempo-define "adoc-implicit-numbered-list-item-2" '(bol (adoc-insert-indented ".. " 2) (r "text" text))) 2579 | (adoc-tempo-define "adoc-implicit-numbered-list-item-3" '(bol (adoc-insert-indented "... " 3) (r "text" text))) 2580 | (adoc-tempo-define "adoc-implicit-numbered-list-item-4" '(bol (adoc-insert-indented ".... " 4) (r "text" text))) 2581 | (adoc-tempo-define "adoc-implicit-numbered-list-item-5" '(bol (adoc-insert-indented "..... " 5) (r "text" text))) 2582 | (adoc-tempo-define "adoc-labeled-list-item" '(bol (p "label" label) ":: " (r "text" text))) 2583 | (adoc-tempo-define "adoc-list-item-continuation" '(bol "+" %) nil adoc-help-list-item-continuation) 2584 | 2585 | ;; tables 2586 | (adoc-tempo-define "adoc-example-table" 2587 | '(bol "|====================\n" 2588 | "| cell 11 | cell 12\n" 2589 | "| cell 21 | cell 22\n" 2590 | "|====================\n" % )) 2591 | 2592 | ;; Macros (inline & block) 2593 | (adoc-tempo-define "adoc-url" '("http://foo.com") nil adoc-help-url) 2594 | (adoc-tempo-define "adoc-url-caption" '("http://foo.com[" (r "caption" caption) "]") nil adoc-help-url) 2595 | (adoc-tempo-define "adoc-email" '("bob@foo.com") nil adoc-help-url) 2596 | (adoc-tempo-define "adoc-email-caption" '("mailto:" (p "address" address) "[" (r "caption" caption) "]") nil adoc-help-url) 2597 | (adoc-tempo-define "adoc-anchor" '("[[" (r "id" id) "]]") nil adoc-help-anchor) 2598 | (adoc-tempo-define "adoc-anchor-default-syntax" '("anchor:" (r "id" id) "[" (p "xreflabel" xreflabel) "]") nil adoc-help-anchor) 2599 | (adoc-tempo-define "adoc-xref" '("<<" (p "id" id) "," (r "caption" caption) ">>") nil adoc-help-xref) 2600 | (adoc-tempo-define "adoc-xref-default-syntax" '("xref:" (p "id" id) "[" (r "caption" caption) "]") nil adoc-help-xref) 2601 | (adoc-tempo-define "adoc-image" '("image:" (r "target-path" target-path) "[" (p "caption" caption) "]")) 2602 | 2603 | ;; Passthrough 2604 | (adoc-tempo-define "adoc-pass" '("pass:[" (r "text" text) "]") nil adoc-help-pass) 2605 | (adoc-tempo-define "adoc-asciimath" '("asciimath:[" (r "text" text) "]") nil adoc-help-asciimath) 2606 | (adoc-tempo-define "adoc-latexmath" '("latexmath:[" (r "text" text) "]") nil adoc-help-latexmath) 2607 | (adoc-tempo-define "adoc-pass-+++" '("+++" (r "text" text) "+++") nil adoc-help-pass-+++) 2608 | (adoc-tempo-define "adoc-pass-$$" '("$$" (r "text" text) "$$") nil adoc-help-pass-$$) 2609 | ; backticks handled in tempo-template-adoc-monospace-literal 2610 | 2611 | 2612 | ;;;; misc 2613 | (defun adoc-insert-indented (str indent-level) 2614 | "Indents and inserts STR such that point is at INDENT-LEVEL." 2615 | (indent-to (- (* tab-width indent-level) (length str))) 2616 | (insert str)) 2617 | 2618 | (defun adoc-repeat-string (str n) 2619 | "Returns str n times concatenated" 2620 | (let ((retval "")) 2621 | (dotimes (i n) 2622 | (setq retval (concat retval str))) 2623 | retval)) 2624 | 2625 | (defun adoc-tempo-handler (element) 2626 | "Tempo user element handler, see `tempo-user-elements'." 2627 | (let ((on-region (adoc-tempo-on-region))) 2628 | (cond 2629 | 2630 | ;; tr / tempo-recurse : tempo-insert the remaining args of the list 2631 | ((and (listp element) 2632 | (memq (car element) '(tr tempo-recurse))) 2633 | (mapc (lambda (elt) (tempo-insert elt on-region)) (cdr element)) 2634 | "") 2635 | 2636 | ;; bol: ensure point is at the beginning of a line by inserting a newline if needed 2637 | ((eq element 'bol) 2638 | (if (bolp) "" "\n")) 2639 | 2640 | ;; r-or-n 2641 | ((eq element 'r-or-n) 2642 | (if on-region 'r '(tr p n))) 2643 | ;; (r-or-n ...) 2644 | ((and (consp element) 2645 | (eq (car element) 'r-or-n)) 2646 | (if on-region (cons 'r (cdr element)) '(tr p n)))))) 2647 | 2648 | (add-to-list 'tempo-user-elements 'adoc-tempo-handler) 2649 | 2650 | (defun adoc-tempo-on-region () 2651 | "Guesses the on-region argument `tempo-insert' is given. 2652 | 2653 | Is a workaround the problem that tempo's user handlers don't get 2654 | passed the on-region argument." 2655 | (let* ( 2656 | ;; try to determine the arg with which the tempo-template-xxx was 2657 | ;; called that eventually brought us here. If we came here not by an 2658 | ;; interactive call to tempo-template-xxx we can't have a clue - assume 2659 | ;; nil. 2660 | (arg (if (string-match "^tempo-template-" (symbol-name this-command)) 2661 | current-prefix-arg 2662 | nil)) 2663 | ;; copy from tempo-define-template 2664 | (on-region (if tempo-insert-region 2665 | (not arg) 2666 | arg))) 2667 | ;; copy from tempo-insert-template 2668 | (if (or (and (boundp 'transient-mark-mode) ; For Emacs 2669 | transient-mark-mode 2670 | mark-active) 2671 | (if (featurep 'xemacs) 2672 | (and zmacs-regions (mark)))) 2673 | (setq on-region t)) 2674 | on-region)) 2675 | 2676 | (defun adoc-forward-xref (&optional bound) 2677 | "Move forward to next xref and return it's id. 2678 | 2679 | Match data is the one of the found xref. Returns nil if there was 2680 | no xref found." 2681 | (cond 2682 | ((or (re-search-forward (adoc-re-xref 'inline-special-with-caption) bound t) 2683 | (re-search-forward (adoc-re-xref 'inline-special-no-caption) bound t)) 2684 | (match-string-no-properties 2)) 2685 | ((re-search-forward (adoc-re-xref 'inline-general-macro) bound t) 2686 | (match-string-no-properties 3)) 2687 | (t nil))) 2688 | 2689 | (defun adoc-xref-id-at-point () 2690 | "Returns id referenced by the xref point is at. 2691 | 2692 | Returns nil if there was no xref found." 2693 | (save-excursion 2694 | ;; search the xref within +-1 one line. I.e. if the xref spawns more than 2695 | ;; two lines, it wouldn't be found. 2696 | (let ((id) 2697 | (saved-point (point)) 2698 | (end (save-excursion (forward-line 1) (line-end-position)))) 2699 | (forward-line -1) 2700 | (while (and (setq id (adoc-forward-xref end)) 2701 | (or (< saved-point (match-beginning 0)) 2702 | (> saved-point (match-end 0))))) 2703 | id))) 2704 | 2705 | (defun adoc-title-descriptor() 2706 | "Returns title descriptor of title point is in. 2707 | 2708 | Title descriptor looks like this: (TYPE SUB-TYPE LEVEL TEXT START END) 2709 | 2710 | 0 TYPE: 1 fore one line title, 2 for two line title. 2711 | 2712 | 1 SUB-TYPE: Only applicable for one line title: 1 for only 2713 | starting delimiter ('== my title'), 2 for both starting and 2714 | trailing delimiter ('== my title =='). 2715 | 2716 | 2 LEVEL: Level of title. A value between 0 and 2717 | `adoc-title-max-level' inclusive. 2718 | 2719 | 3 TEXT: Title's text 2720 | 2721 | 4 START / 5 END: Start/End pos of match" 2722 | (save-excursion 2723 | (let ((level 0) 2724 | found 2725 | type sub-type text) 2726 | (beginning-of-line) 2727 | (while (and (not found) (<= level adoc-title-max-level)) 2728 | (cond 2729 | ((looking-at (adoc-re-one-line-title level)) 2730 | (setq type 1) 2731 | (setq text (match-string 2)) 2732 | (setq sub-type (if (< 0 (length (match-string 3))) 2 1)) 2733 | (setq found t)) 2734 | ;; WARNING: if you decide to replace adoc-re-two-line-title with a 2735 | ;; method ensuring the correct length of the underline, be aware that 2736 | ;; due to adoc-adjust-title-del we sometimes want to find a title which has 2737 | ;; the wrong underline length. 2738 | ((or (looking-at (adoc-re-two-line-title (nth level adoc-two-line-title-del))) 2739 | (save-excursion 2740 | (forward-line -1) 2741 | (beginning-of-line) 2742 | (looking-at (adoc-re-two-line-title (nth level adoc-two-line-title-del))))) 2743 | (setq type 2) 2744 | (setq text (match-string 2)) 2745 | (setq found t)) 2746 | (t 2747 | (setq level (+ level 1))))) 2748 | (when found 2749 | (list type sub-type level text (match-beginning 0) (match-end 0)))))) 2750 | 2751 | (defun adoc-make-title (descriptor) 2752 | (let ((type (nth 0 descriptor)) 2753 | (sub-type (nth 1 descriptor)) 2754 | (level (nth 2 descriptor)) 2755 | (text (nth 3 descriptor))) 2756 | (if (eq type 1) 2757 | (adoc-make-one-line-title sub-type level text) 2758 | (adoc-make-two-line-title level text)))) 2759 | 2760 | (defun adoc-modify-title (&optional new-level-rel new-level-abs new-type new-sub-type create) 2761 | "Modify properties of title point is on. 2762 | 2763 | NEW-LEVEL-REL defines the new title level relative to the current 2764 | one. Negative values are allowed. 0 or nil means don't change. 2765 | NEW-LEVEL-ABS defines the new level absolutely. When both 2766 | NEW-LEVEL-REL and NEW-LEVEL-ABS are non-nil, NEW-LEVEL-REL takes 2767 | precedence. When both are nil, level is not affected. 2768 | 2769 | When ARG is nil, it defaults to 1. When ARG is negative, level is 2770 | demoted that many levels. If ARG is 0, see `adoc-adjust-title-del'. 2771 | 2772 | When NEW-TYPE is nil, the title type is unaffected. If NEW-TYPE 2773 | is t, the type is toggled. If it's 1 or 2, the new type is one 2774 | line title or two line title respectively. 2775 | 2776 | NEW-SUB-TYPE is analogous to NEW-TYPE. However when the actual 2777 | title has no sub type, only the absolute values of NEW-SUB-TYPE 2778 | apply, otherise the new sub type becomes 2779 | `adoc-default-title-sub-type'. 2780 | 2781 | If CREATE is nil, an error is signaled if point is not on a 2782 | title. If CREATE is non-nil a new title is created if point is 2783 | currently not on a title. 2784 | 2785 | BUG: In one line title case: number of spaces between delimiters 2786 | and title's text are not preserved, afterwards its always one space." 2787 | (let ((descriptor (adoc-title-descriptor))) 2788 | (if (or create (not descriptor)) 2789 | (error "Point is not on a title")) 2790 | 2791 | ;; todo: set descriptor to default 2792 | ;; (if (not descriptor) 2793 | ;; (setq descriptor (list 1 1 2 ?? adoc-default-title-type adoc-default-title-sub-type))) 2794 | (let* ((type (nth 0 descriptor)) 2795 | (new-type-val (cond 2796 | ((eq new-type 1) 2) 2797 | ((eq new-type 2) 1) 2798 | ((not (or (eq type 1) (eq type 2))) 2799 | (error "Invalid title type")) 2800 | ((eq new-type nil) type) 2801 | ((eq new-type t) (if (eq type 1) 2 1)) 2802 | (t (error "NEW-TYPE has invalid value")))) 2803 | (sub-type (nth 1 descriptor)) 2804 | (new-sub-type-val (cond 2805 | ((eq new-sub-type 1) 2) 2806 | ((eq new-sub-type 2) 1) 2807 | ((null sub-type) adoc-default-title-sub-type) ; there wasn't a sub-type before 2808 | ((not (or (eq sub-type 1) (eq sub-type 2))) 2809 | (error "Invalid title sub-type")) 2810 | ((eq new-sub-type nil) sub-type) 2811 | ((eq new-sub-type t) (if (eq sub-type 1) 2 1)) 2812 | (t (error "NEW-SUB-TYPE has invalid value")))) 2813 | (level (nth 2 descriptor)) 2814 | (new-level (cond 2815 | ((or (null new-level-rel) (eq new-level-rel 0)) 2816 | level) 2817 | ((not (null new-level-rel)) 2818 | (let ((x (% (+ level arg) (+ adoc-title-max-level 1)))) 2819 | (if (< x 0) 2820 | (+ x adoc-title-max-level 1) 2821 | x))) 2822 | ((not (null new-level-abs)) 2823 | new-level-abs) 2824 | (t 2825 | level))) 2826 | (start (nth 4 descriptor)) 2827 | (end (nth 5 descriptor)) 2828 | (saved-col (current-column))) 2829 | 2830 | ;; set new title descriptor 2831 | (setcar (nthcdr 0 descriptor) new-type-val) 2832 | (setcar (nthcdr 1 descriptor) new-sub-type-val) 2833 | (setcar (nthcdr 2 descriptor) new-level) 2834 | 2835 | ;; replace old title by new 2836 | (let ((end-char (char-before end))) 2837 | (beginning-of-line) 2838 | (when (and (eq type 2) (looking-at (adoc-re-two-line-title-undlerline))) 2839 | (forward-line -1) 2840 | (beginning-of-line)) 2841 | (delete-region start end) 2842 | (insert (adoc-make-title descriptor)) 2843 | (when (equal end-char ?\n) 2844 | (insert "\n") 2845 | (forward-line -1))) 2846 | 2847 | ;; reposition point 2848 | (when (and (eq new-type-val 2) (eq type 1)) 2849 | (forward-line -1)) 2850 | (move-to-column saved-col)))) 2851 | 2852 | (defun adoc-make-unichar-alist() 2853 | "Creates `adoc-unichar-alist' from `unicode-character-list'" 2854 | (unless (boundp 'unicode-character-list) 2855 | (load-library "unichars.el")) 2856 | (let ((i unicode-character-list)) 2857 | (setq adoc-unichar-alist nil) 2858 | (while i 2859 | (let ((name (nth 2 (car i))) 2860 | (codepoint (nth 0 (car i)))) 2861 | (when name 2862 | (push (cons name codepoint) adoc-unichar-alist)) 2863 | (setq i (cdr i)))))) 2864 | 2865 | (defun adoc-unichar-by-name (name) 2866 | "Returns unicode codepoint of char with the given NAME" 2867 | (cdr (assoc name adoc-unichar-alist))) 2868 | 2869 | (defun adoc-entity-to-string (entity) 2870 | "Returns a string containing the character referenced by ENTITY. 2871 | 2872 | ENTITY is a string containing a character entity reference like 2873 | e.g. '&' or '&'. nil is returned if its an invalid 2874 | entity, or when customizations prevent `adoc-entity-to-string' from 2875 | knowing it. E.g. when `adoc-unichar-name-resolver' is nil." 2876 | (save-match-data 2877 | (let (ch) 2878 | (setq ch 2879 | (cond 2880 | ;; hex 2881 | ((string-match "&#x\\([0-9a-fA-F]+?\\);" entity) 2882 | (string-to-number (match-string 1 entity) 16)) 2883 | ;; dec 2884 | ((string-match "&#\\([0-9]+?\\);" entity) 2885 | (string-to-number (match-string 1 entity))) 2886 | ;; name 2887 | ((and adoc-unichar-name-resolver 2888 | (string-match "&\\(.+?\\);" entity)) 2889 | (funcall adoc-unichar-name-resolver 2890 | (match-string 1 entity))))) 2891 | (when (characterp ch) (make-string 1 ch))))) 2892 | 2893 | (defun adoc-face-for-attribute (pos-or-name &optional attribute-list-prop-val) 2894 | "Returns the face to be used for the given attribute. 2895 | 2896 | The face to be used is looked up in `adoc-attribute-face-alist', 2897 | unless that alist is overwritten by the content of 2898 | ATTRIBUTE-LIST-PROP-VAL. 2899 | 2900 | POS-OR-NAME identifies the attribute for which the face is 2901 | returned. When POS-OR-NAME satifies numberp, it is the number of 2902 | the positional attribute, where as the first positinal attribute 2903 | has position 0. Otherwise POS-OR-NAME is the name of the named 2904 | attribute. 2905 | 2906 | The value of ATTRIBUTE-LIST-PROP-VAL is one of the following: 2907 | - nil 2908 | - FACE 2909 | - POS-TO-NAME 2910 | - (POS-TO-NAME LOCAL-ATTRIBUTE-FACE-ALIST) 2911 | 2912 | POS-TO-NAME is a list of strings mapping positions to attribute 2913 | names. E.g. (\"foo\" \"bar\") means that the first positional 2914 | attribute corresponds to the named attribute foo, and the 2nd 2915 | positional attribute corresponds to the named attribute bar. 2916 | 2917 | FACE is something that satisfies facep; in that case the whole 2918 | attribute list is fontified with that face. However that case is 2919 | handled outside this function. 2920 | 2921 | An attribute name is first looked up in 2922 | LOCAL-ATTRIBUTE-FACE-ALIST before it is looked up in 2923 | `adoc-attribute-face-alist'." 2924 | (let* ((has-pos-to-name (listp attribute-list-prop-val)) 2925 | (has-local-alist (and has-pos-to-name (listp (car-safe attribute-list-prop-val)))) 2926 | (pos-to-name (cond ((not has-pos-to-name) nil) 2927 | (has-local-alist (car attribute-list-prop-val)) 2928 | (t attribute-list-prop-val))) 2929 | (local-attribute-face-alist (when has-local-alist (cadr attribute-list-prop-val))) 2930 | (name (cond ((stringp pos-or-name) pos-or-name) 2931 | ((numberp pos-or-name) (nth pos-or-name pos-to-name))))) 2932 | (or (when name (or (cdr (assoc name local-attribute-face-alist)) 2933 | (cdr (assoc name adoc-attribute-face-alist)))) 2934 | markup-value-face))) 2935 | 2936 | (defun adoc-imenu-create-index () 2937 | (let* ((index-alist) 2938 | (re-all-titles-core 2939 | (mapconcat 2940 | (lambda (level) 2941 | (concat 2942 | (adoc-re-one-line-title level) 2943 | "\\|" 2944 | (adoc-re-two-line-title (nth level adoc-two-line-title-del)))) 2945 | '(0 1 2 3 4) 2946 | "\\)\\|\\(?:")) 2947 | (re-all-titles 2948 | (concat "\\(?:" re-all-titles-core "\\)"))) 2949 | (save-restriction 2950 | (widen) 2951 | (goto-char 0) 2952 | (while (re-search-forward re-all-titles nil t) 2953 | (backward-char) ; skip backwards the trailing \n of a title 2954 | (let* ((descriptor (adoc-title-descriptor)) 2955 | (title-text (nth 3 descriptor)) 2956 | (title-pos (nth 4 descriptor))) 2957 | (setq 2958 | index-alist 2959 | (cons (cons title-text title-pos) index-alist))))) 2960 | (nreverse index-alist))) 2961 | 2962 | 2963 | ;;;###autoload 2964 | (define-derived-mode adoc-mode text-mode "adoc" 2965 | "Major mode for editing AsciiDoc text files. 2966 | Turning on Adoc mode runs the normal hook `adoc-mode-hook'." 2967 | 2968 | ;; syntax table 2969 | ;; todo: do it as other modes do it, eg rst-mode? 2970 | (modify-syntax-entry ?$ ".") 2971 | (modify-syntax-entry ?% ".") 2972 | (modify-syntax-entry ?& ".") 2973 | (modify-syntax-entry ?' ".") 2974 | (modify-syntax-entry ?` ".") 2975 | (modify-syntax-entry ?\" ".") 2976 | (modify-syntax-entry ?* ".") 2977 | (modify-syntax-entry ?+ ".") 2978 | (modify-syntax-entry ?. ".") 2979 | (modify-syntax-entry ?/ ".") 2980 | (modify-syntax-entry ?< ".") 2981 | (modify-syntax-entry ?= ".") 2982 | (modify-syntax-entry ?> ".") 2983 | (modify-syntax-entry ?\\ ".") 2984 | (modify-syntax-entry ?| ".") 2985 | (modify-syntax-entry ?_ ".") 2986 | 2987 | ;; comments 2988 | (set (make-local-variable 'comment-column) 0) 2989 | (set (make-local-variable 'comment-start) "// ") 2990 | (set (make-local-variable 'comment-end) "") 2991 | (set (make-local-variable 'comment-start-skip) "^//[ \t]*") 2992 | (set (make-local-variable 'comment-end-skip) "[ \t]*\\(?:\n\\|\\'\\)") 2993 | 2994 | ;; paragraphs 2995 | (set (make-local-variable 'paragraph-separate) (adoc-re-paragraph-separate)) 2996 | (set (make-local-variable 'paragraph-start) (adoc-re-paragraph-start)) 2997 | (set (make-local-variable 'paragraph-ignore-fill-prefix) t) 2998 | 2999 | ;; font lock 3000 | (set (make-local-variable 'font-lock-defaults) 3001 | '(adoc-font-lock-keywords 3002 | nil nil nil nil 3003 | (font-lock-multiline . t) 3004 | (font-lock-mark-block-function . adoc-font-lock-mark-block-function))) 3005 | (make-local-variable 'font-lock-extra-managed-props) 3006 | (setq font-lock-extra-managed-props '(adoc-reserved adoc-attribute-list)) 3007 | (make-local-variable 'font-lock-unfontify-region-function) 3008 | (setq font-lock-unfontify-region-function 'adoc-unfontify-region-function) 3009 | 3010 | ;; outline mode 3011 | ;; BUG: if there are many spaces\tabs after =, level becomes wrong 3012 | ;; Ideas make it work for two line titles: Investigate into 3013 | ;; outline-heading-end-regexp. It seams like outline-regexp could also contain 3014 | ;; newlines. 3015 | (set (make-local-variable 'outline-regexp) "=\\{1,5\\}[ \t]+[^ \t\n]") 3016 | 3017 | ;; misc 3018 | (set (make-local-variable 'page-delimiter) "^<<<+$") 3019 | (set (make-local-variable 'require-final-newline) t) 3020 | (set (make-local-variable 'parse-sexp-lookup-properties) t) 3021 | 3022 | ;; it's the user's decision whether he wants to set imenu-sort-function to 3023 | ;; nil, or even something else. See also similar comment in sgml-mode. 3024 | (set (make-local-variable 'imenu-create-index-function) 3025 | 'adoc-imenu-create-index) 3026 | 3027 | ;; compilation 3028 | (when (boundp 'compilation-error-regexp-alist-alist) 3029 | (add-to-list 'compilation-error-regexp-alist-alist 3030 | '(asciidoc 3031 | "^asciidoc: +\\(?:ERROR\\|\\(WARNING\\|DEPRECATED\\)\\): +\\([^:\n]*\\): line +\\([0-9]+\\)" 3032 | 2 3 nil (1 . nil)))) 3033 | (when (boundp 'compilation-error-regexp-alist) 3034 | (make-local-variable 'compilation-error-regexp-alist) 3035 | (add-to-list 'compilation-error-regexp-alist 'asciidoc)) 3036 | 3037 | (easy-menu-add adoc-mode-menu)) 3038 | 3039 | 3040 | ;;;; non-definitions evaluated during load 3041 | (adoc-calc) 3042 | 3043 | 3044 | (provide 'adoc-mode) 3045 | 3046 | ;;; adoc-mode.el ends here 3047 | 3048 | --------------------------------------------------------------------------------