├── .gitignore ├── Makefile ├── README.md ├── bin └── xmd.js ├── examples ├── simple.xmd ├── structure.xmd └── structure.xmd.json ├── grammar.md ├── package.json ├── src ├── Ast.ts ├── BlockParser.ts ├── BlockParserTest.ts ├── Line.ts ├── LineType.ts ├── Main.ts ├── Node.ts ├── Reader.ts ├── ReaderTest.ts ├── Tag.ts ├── TagInfo.ts ├── TagTest.ts ├── TextParser.ts ├── TextParserTest.ts ├── Xmd.ts ├── XmdTest.ts ├── index.d.ts ├── test.d.ts └── test.ts └── tsd.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | scratch 3 | typings -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | tsc=tsc --module commonjs --outDir lib src/*.ts 2 | mocha=mocha -r lib/test.js lib/*Test.js 3 | gendts=dts-generator --baseDir src/ --name xmd --main xmd/Xmd --out xmd.d.ts src/Xmd.ts 4 | 5 | .PHONY: js 6 | js: 7 | $(tsc) 8 | 9 | .PHONY: jsw 10 | jsw: 11 | $(tsc) -w 12 | 13 | .PHONY: test 14 | test: js 15 | $(mocha) 16 | 17 | .PHONY: testw 18 | testw: 19 | $(mocha) -w --reporter min 20 | 21 | .PHONY: wc 22 | wc: 23 | find src -name \*.ts | xargs wc 24 | 25 | .PHONY: release 26 | release: js xmd.d.ts 27 | echo run: npm publish 28 | 29 | .PHONY: xmd.d.ts 30 | xmd.d.ts: 31 | @echo Generate module definition file: xmd.d.ts 32 | $(gendts) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Markdown is great if you can find a dialect that suits your needs precisely. But if you need some feature that it doesn't support, you are tempted to enter into a state of sin. You might try to: 2 | 3 | 1. Embed XML in your Markdown. 4 | 2. Have a fancy pre/post-process chain. 5 | 3. Find another markdown dialect. 6 | 7 | Either of these makes me feel dirty. 8 | 9 | Nor do I want to add more ad-hoc, nilly-willy extensions to Markdown. 10 | 11 | One way to think about Markdown is that it is a dialect of XML that makes writing in it more pleasant. To create an extensible Markdown dialect, all you need to do is to make its syntax regular enough that it can express arbitrary XML in it. 12 | 13 | I want a Markdown dialect to have equivalent expressive power as XML. Something like a hybrid between HAML and Markdown. 14 | 15 | ## Example 16 | 17 | A simple document looks very similar to markdown. 18 | 19 | ``` 20 | # The Title 21 | 22 | The _first_ paragraph of text 23 | spans two lines. 24 | 25 | The *second* paragraph of text 26 | spans 27 | three lines. 28 | 29 | ## A `Subtitle` 30 | 31 | #aside 32 | Marginally interesting aside. 33 | 34 | Also, see [> http://example.com][external link] 35 | 36 | A code snippet: 37 | 38 | ```[javascript theme=dark] 39 | function() { 40 | console.log("hello world"); 41 | } 42 | ``` 43 | 44 | Consecutive lines are joined together to form a paragraph. 45 | 46 | # Install 47 | 48 | From NPM, 49 | 50 | ``` 51 | npm install xmd 52 | ``` 53 | 54 | ## Command line 55 | 56 | The command line tool can render an `.xmd` file to XML or JSON. 57 | 58 | ``` 59 | $ xmd --help 60 | Renders extensible markdown to xml (defualt) or json. 61 | 62 | --ast output parsed document in JSON 63 | -j, --json output JSON 64 | -p, --pretty pretty print the output 65 | -h, --help show help 66 | ``` 67 | 68 | By default it renders to XML: 69 | 70 | ``` 71 | $ xmd --pretty example.xmd 72 | 73 |

The Title

74 |

The first paragraph of text spans two lines.

75 |

The second paragraph of text spans three lines.

76 |

77 | A 78 | Subtitle

79 | 82 |

A code snippet:

83 |
function() {
 84 |   console.log("hello world");
 85 | }
86 | ``` 87 | 88 | Or you can choose to output to JSON, which you can then process with another tool: 89 | 90 | ``` 91 | $ xmd --json --pretty examples/simple.xmd 92 | { 93 | "name": "document", 94 | "children": [ 95 | { 96 | "name": "h1", 97 | "children": [ 98 | "The Title" 99 | ] 100 | }, 101 | // ... 102 | } 103 | ``` 104 | 105 | You can also print out the AST in JSON: 106 | 107 | ``` 108 | xmd --ast --pretty examples/simple.xmd 109 | { 110 | "name": "document", 111 | "children": [ 112 | { 113 | "name": "", 114 | "children": [ 115 | "The Title" 116 | ] 117 | }, 118 | // ... 119 | } 120 | ``` 121 | 122 | ## Use in code 123 | 124 | ```javascript 125 | > var xmd = require("xmd") 126 | 127 | // xml output 128 | > xmd.renderXML("# hello *world*") 129 | '

hello world

' 130 | 131 | // JSON output 132 | > xmd.renderJSON("# hello *world*") 133 | { 134 | "name": "document", 135 | "children": [ 136 | { 137 | "name": "h1", 138 | "children": [ 139 | "hello ", 140 | { 141 | "name": "b", 142 | "children": [ 143 | "world" 144 | ] 145 | } 146 | ] 147 | } 148 | ] 149 | } 150 | 151 | // AST 152 | > xmd.renderJSON("# hello *world*",{raw: true}) 153 | '{"name":"document","children":[{"name":"","children":["hello ",{"name":"*","children":["world"]}]}]}' 154 | ``` 155 | 156 | # Extensible Markdown 157 | 158 | The design criteria for this dialect are: 159 | 160 | + Minimal syntax that resembles markdown (not xml). 161 | + Well-defined escape and quoting mechanism. 162 | + Easy to attach metadata to content. 163 | + Pure syntax. Plug in your own semantics and renderer. 164 | 165 | ## Tag 166 | 167 | We want to be able to express arbitrary XML tags. The character `#` opens a tag. 168 | 169 | ``` 170 | #foo 171 | ``` 172 | 173 | is translated to: 174 | 175 | ``` 176 | 177 | ``` 178 | 179 | A single line of content can follow the tag opening, 180 | 181 | ``` 182 | #foo a line of content 183 | ``` 184 | 185 | is translated to: 186 | 187 | ``` 188 | a line of content 189 | ``` 190 | 191 | The tag name can be _almost_ anything. All of the followings are valid tags: 192 | 193 | ``` 194 | #+hoho+ 195 | #123blah123 196 | #<> 197 | # 198 | ``` 199 | 200 | In fact, the tag `#` that stands for `h1` is actually a tag whose name is empty! 201 | 202 | ``` 203 | # is h1 whose tag name is "" 204 | ## is h2 whose tag name is "#" 205 | ### is h3 whose tag name is "##" 206 | #### is h4 whose tag name is "###" 207 | ##### is h5 whose tag name is "####" 208 | ###### is h6 whose tag name is "#####" 209 | ``` 210 | 211 | ## Nesting 212 | 213 | We use indentation to structure the document. Content nested within a tag MUST be indented with 2 spaces. 214 | 215 | ``` 216 | #tag1 content of tag 1 217 | #tag2 content of tag 2 218 | #tag3 content of tag 3 219 | ``` 220 | 221 | is translated to: 222 | 223 | ``` 224 | 225 | content of tag 1 226 | 227 | content of tag 2 228 | content of tag 3 229 | ``` 230 | 231 | ## Paragraphs 232 | 233 | Lines of of text are joined together to form a paragraph. The content of a paragraph would be wrapped in a `p`. 234 | 235 | ``` 236 | The first text block 237 | has a *bolded* word. 238 | 239 | The second text block 240 | has an _italic_ word. 241 | ``` 242 | 243 | is translated to: 244 | 245 | ``` 246 |

The first text block has a bolded word.

247 |

The second text block has an italic word.

248 | ``` 249 | 250 | This works in the indented body of a tag. 251 | 252 | ``` 253 | #tag1 254 | first paragraph of tag 1 255 | 256 | #tag2 257 | content of tag 2 258 | 259 | second paragraph of tag 1 260 | ``` 261 | 262 | is translated to: 263 | 264 | ``` 265 | 266 |

first paragraph of tag 1

267 | 268 |

content of tag 2

269 |

second paragraph of tag 1

270 | ``` 271 | 272 | Note the difference between the following two tags: 273 | 274 | ``` 275 | #tag1 a line of content 276 | 277 | #tag2 278 | a paragraph of content 279 | ``` 280 | 281 | In this case, tag1's content isn't wrapped in a `p`, but tag2's content is: 282 | 283 | ``` 284 | a line of content 285 |

a paragraph of content

286 | ``` 287 | 288 | ## Tag Attributes 289 | 290 | It's possible to specify tag attributes: 291 | 292 | ``` 293 | #tag[a=1 b=2 c=3] 294 | content of tag 295 | ``` 296 | 297 | is translated to: 298 | 299 | ``` 300 | 301 | content of tag 302 | 303 | ``` 304 | 305 | Tags can also be given non-key-value arguments that are collected into an array of strings: 306 | 307 | ``` 308 | #tag[a b c] 309 | ``` 310 | 311 | Key-value pairs and arguments can appear in any order: 312 | 313 | ``` 314 | #tag[a k1=1 b k2=2 c k3=3] 315 | ``` 316 | 317 | This tag is represented by the JSON object: 318 | 319 | ``` 320 | {name: "tag", 321 | opts: { 322 | "k1": "1", 323 | "k2": "2", 324 | "k3": "3" 325 | }, 326 | args: ["a", "b", "c"] 327 | } 328 | ``` 329 | 330 | Arguments are stripped away when translated to XML. They are only useful when you want to transform the AST. The above tag outputs this XML: 331 | 332 | ``` 333 | 334 | ``` 335 | 336 | Note: That attributes MUST be on the same line. The following is illegal: 337 | 338 | ``` 339 | #tag[ 340 | a=1 341 | b=2 342 | c=3] 343 | bad tag. arguments must be on the same line. 344 | ``` 345 | 346 | # heredoc 347 | 348 | We can use a tag argument to emulate how Github Flavored Markdown quotes a snippet of code. 349 | 350 | ``` 351 | #```[javascript] 352 | function foo() { 353 | console.log("foo"); 354 | } 355 | ``` 356 | 357 | is translated to: 358 | 359 | ``` 360 |
function foo() {
361 |   console.log("foo");
362 | }
363 | ``` 364 | 365 | xmd has builtin syntax for this purpose: 366 | 367 | ```[javascript] 368 | function foo() { 369 | console.log("foo"); 370 | } 371 | ``` 372 | 373 | Aside from the language, you might also want to specify the theme: 374 | 375 | ```[javascript theme=dark] 376 | function foo() { 377 | console.log("foo"); 378 | } 379 | ``` 380 | 381 | We can omit the tag arguments if we want to: 382 | 383 | ``` 384 | function foo() { 385 | console.log("foo"); 386 | } 387 | ``` 388 | 389 | We can use a heredoc to indicate the end of the snippet: 390 | 391 | ```HERE 392 | function foo() { 393 | console.log("foo"); 394 | } 395 | ```HERE 396 | 397 | Finally, to combine heredoc and tag argument: 398 | 399 | ```[javascript theme=dark]HERE 400 | function foo() { 401 | console.log("foo"); 402 | } 403 | ```HERE 404 | 405 | ## Text Formatting 406 | 407 | Text formatting looks as you'd expect: 408 | 409 | ``` 410 | A piece of *bolded text*. 411 | Followed by some _italic text_. 412 | Then it ends with a bit of `code`! 413 | ``` 414 | 415 | ## Inline Tag 416 | 417 | Aside from "*", "_" and "`", markdown has special syntax for links and images. Some dialects support footnotes. We want to be able to express all of these in a generic way. 418 | 419 | While the `#tag` syntax is good to express structure, it is not so nice for inline text. It'd be tedious to write something like: 420 | 421 | ``` 422 | #a[href="http://google.com"] 423 | #b The 424 | #i Google 425 | ``` 426 | 427 | Borrowing markdown's link syntax, we can write an inline tag like this: 428 | 429 | ``` 430 | [tag a b c k1=1 k2=2 k3=3][*content* of tag] 431 | ``` 432 | 433 | which gets translated to: 434 | 435 | ``` 436 | content of tag 437 | ``` 438 | 439 | We can use the same inline tag syntax to express bold, italic, and code: 440 | 441 | ``` 442 | [*][bolded text] 443 | [_][italic text] 444 | [`][pieceOfCode] 445 | ``` 446 | 447 | We can nest inline tags: 448 | 449 | ``` 450 | A piece of [`][*bolded*[_][AndItalicCode]] 451 | ``` 452 | 453 | Which translates to: 454 | 455 | ``` 456 | A piece of boldedAndItalicCode 457 | ``` 458 | 459 | ### Link 460 | 461 | We use the inline-tag syntax for links. 462 | 463 | ``` 464 | [> http://google.com][*The* Google] 465 | ``` 466 | 467 | Or omitting the content: 468 | 469 | ``` 470 | [> http://google.com] 471 | ``` 472 | 473 | ### No Nesting for * _ ` 474 | 475 | It is not allowed to nesting the followings: 476 | 477 | + `_` 478 | + ` 479 | + `*` 480 | 481 | The rule for interpreting these is simple. The parser reads everything until it finds the matching special character. 482 | 483 | ``` 484 | *_bold_* 485 | ``` 486 | 487 | get translated to: 488 | 489 | ``` 490 | _bold_ 491 | ``` 492 | 493 | If you really want italic bold, use inline-tag syntax: 494 | 495 | ``` 496 | [*][_italic bold_] 497 | ``` 498 | 499 | `*` and `_` can be terribly confusing when they are nested. 500 | 501 | ## Escape 502 | 503 | The only special characters are: 504 | 505 | + `#` 506 | + `*` 507 | + `_` 508 | + ` 509 | + `[` and `]` 510 | + `\` 511 | 512 | The backslash is used for escaping. A character that follows `\` is interpreted as itself: 513 | 514 | ``` 515 | \# => # 516 | \* => * 517 | \\ => \ 518 | \a => a 519 | \n => n 520 | etc. 521 | ``` 522 | 523 | ## Quotation 524 | 525 | It should be easy to copy and paste an arbitrary chunk of text into the document without having to massage it into proper syntax. 526 | 527 | Suppose we want to embed a LaTeX equation into the document; 528 | 529 | ``` 530 | E &= \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}} 531 | ``` 532 | 533 | It'd be terribly tedious (and error prone) to escape it manually with backslashes: 534 | 535 | ``` 536 | #latex 537 | E &= \\frac{mc^2}{\\sqrt{1-\\frac{v^2}{c^2}}} 538 | ``` 539 | 540 | For this purpose, we use heredoc but with 4 backticks: 541 | 542 | #latex 543 | ```` 544 | E &= \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}} 545 | ```` 546 | It gets translated to: 547 | 548 | ``` 549 | E &= \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}} 550 | ``` 551 | 552 | The inline quote uses 2 backticks. The same LaTeX equation written as an inline-tag; 553 | 554 | ``` 555 | [latex][``E &= \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}}``] 556 | ``` 557 | 558 | # List 559 | 560 | (Provisional syntax. Could change in the future.) 561 | 562 | An unordered list looks like its markdown counterpart. 563 | 564 | ``` 565 | + a 566 | + b 567 | + c 568 | ``` 569 | 570 | Nesting works. 571 | 572 | ``` 573 | + a 574 | + a1 575 | + a2 576 | + b 577 | + b1 578 | + b2 579 | ``` 580 | 581 | is translated to: 582 | 583 | ``` 584 |