/usr/share/emacs/site-lisp/emacs-goodies-el/markdown-mode.el is in emacs-goodies-el 35.2ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 | ;;; markdown-mode.el --- Emacs Major mode for Markdown-formatted text files
;; Copyright (C) 2007-2011 Jason R. Blevins <jrblevin@sdf.org>
;; Copyright (C) 2007, 2009 Edward O'Connor <ted@oconnor.cx>
;; Copyright (C) 2007 Conal Elliott <conal@conal.net>
;; Copyright (C) 2008 Greg Bognar <greg_bognar@hms.harvard.edu>
;; Copyright (C) 2008 Dmitry Dzhus <mail@sphinx.net.ru>
;; Copyright (C) 2008 Bryan Kyle <bryan.kyle@gmail.com>
;; Copyright (C) 2008 Ben Voui <intrigeri@boum.org>
;; Copyright (C) 2009 Ankit Solanki <ankit.solanki@gmail.com>
;; Copyright (C) 2009 Hilko Bengen <bengen@debian.org>
;; Copyright (C) 2009 Peter Williams <pezra@barelyenough.org>
;; Copyright (C) 2010 George Ogata <george.ogata@gmail.com>
;; Copyright (C) 2011 Eric Merritt <ericbmerritt@gmail.com>
;; Copyright (C) 2011 Philippe Ivaldi <pivaldi@sfr.fr>
;; Copyright (C) 2011 Jeremiah Dodds <jeremiah.dodds@gmail.com>
;; Author: Jason R. Blevins <jrblevin@sdf.org>
;; Maintainer: Jason R. Blevins <jrblevin@sdf.org>
;; Created: May 24, 2007
;; Version: 1.8.1
;; Keywords: Markdown, GitHub Flavored Markdown, itex
;; URL: http://jblevins.org/projects/markdown-mode/
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; markdown-mode is a major mode for editing [Markdown][]-formatted
;; text files in GNU Emacs. markdown-mode is free software, licensed
;; under the GNU GPL.
;;
;; [Markdown]: http://daringfireball.net/projects/markdown/
;;
;; The latest stable version is markdown-mode 1.8.1, released on August 15, 2011:
;;
;; * [markdown-mode.el][]
;; * [Screenshot][]
;; * [Release notes][]
;;
;; [markdown-mode.el]: http://jblevins.org/projects/markdown-mode/markdown-mode.el
;; [screenshot]: http://jblevins.org/projects/markdown-mode/screenshots/20110812-001.png
;; [release notes]: http://jblevins.org/projects/markdown-mode/rev-1-8-1
;;
;; markdown-mode is also available in several package managers, including:
;;
;; * Debian and Ubuntu Linux: [emacs-goodies-el][]
;; * RedHat and Fedora Linux: [emacs-goodies][]
;; * OpenBSD: [textproc/markdown-mode][]
;; * Arch Linux (AUR): [emacs-markdown-mode-git][]
;;
;; [emacs-goodies-el]: http://packages.debian.org/emacs-goodies-el
;; [emacs-goodies]: https://admin.fedoraproject.org/pkgdb/acls/name/emacs-goodies
;; [textproc/markdown-mode]: http://pkgsrc.se/textproc/markdown-mode
;; [emacs-markdown-mode-git]: http://aur.archlinux.org/packages.php?ID=30389
;;
;; The latest development version can be downloaded directly
;; ([markdown-mode.el][devel.el]) or it can be obtained from the
;; (browsable and clonable) Git repository at
;; <http://jblevins.org/git/markdown-mode.git>. The entire repository,
;; including the full project history, can be cloned via the Git protocol
;; by running
;;
;; git clone git://jblevins.org/git/markdown-mode.git
;;
;; [devel.el]: http://jblevins.org/git/markdown-mode.git/plain/markdown-mode.el
;;; Dependencies:
;; markdown-mode requires easymenu, a standard package since GNU Emacs
;; 19 and XEmacs 19, which provides a uniform interface for creating
;; menus in GNU Emacs and XEmacs.
;;; Installation:
;; Make sure to place `markdown-mode.el` somewhere in the load-path and add
;; the following lines to your `.emacs` file to associate markdown-mode
;; with `.text` files:
;;
;; (autoload 'markdown-mode "markdown-mode.el"
;; "Major mode for editing Markdown files" t)
;; (setq auto-mode-alist
;; (cons '("\\.text" . markdown-mode) auto-mode-alist))
;;
;; There is no consensus on an official file extension so change `.text` to
;; `.mdwn`, `.md`, `.mdt`, or whatever you call your markdown files.
;;; Customization:
;; Although no configuration is *necessary* there are a few things
;; that can be customized. The `M-x customize-mode` command
;; provides an interface to all of the possible customizations:
;;
;; * `markdown-command' - the command used to run Markdown (default:
;; `markdown'). This variable may be customized to pass
;; command-line options to your Markdown processor of choice, but
;; this command must accept input from `stdin`. If it does not, a
;; simple wrapper script can be used to write `stdin` to a file
;; and then pass that file to your Markdown interpreter. Ideally,
;; this command will produce an XHTML fragment around which
;; markdown-mode will wrap a header and footer (which can be
;; further customized). However, it attempts to detect whether
;; the command produces standalone XHTML output (via
;; `markdown-xhtml-standalone-regexp'), in which case no header
;; and footer content will be added.
;;
;; * `markdown-command-needs-filename' - set to non-nil if
;; `markdown-command' does not accept input from stdin (default: nil).
;; Instead, it will be passed a filename as the final command-line
;; option. As a result, you will only be able to run Markdown
;; from buffers which are visiting a file.
;;
;; * `markdown-hr-string' - string to use when inserting horizontal
;; rules (default: `* * * * *').
;;
;; * `markdown-bold-underscore' - set to a non-nil value to use two
;; underscores for bold instead of two asterisks (default: `nil').
;;
;; * `markdown-italic-underscore' - set to a non-nil value to use
;; underscores for italic instead of asterisks (default: `nil').
;;
;; * `markdown-indent-function' - the function to use for automatic
;; indentation (default: `markdown-indent-line').
;;
;; * `markdown-indent-on-enter' - set to a non-nil value to
;; automatically indent new lines when the enter key is pressed
;; (default: `t')
;;
;; * `markdown-follow-wiki-link-on-enter' - set to a non-nil value
;; to automatically open a linked document in a new buffer if the
;; cursor is an wiki link
;; (default: `t')
;;
;; * `markdown-uri-types' - a list of protocols for URIs that
;; `markdown-mode' should highlight.
;;
;; * `markdown-enable-math' - syntax highlighting for
;; LaTeX fragments (default: `nil').
;;
;; * `markdown-css-path' - CSS file to link to in XHTML output.
;;
;; * `markdown-xhtml-header-content' - additional content to include
;; in the XHTML `<head>` block.
;;
;; * `markdown-xhtml-standalone-regexp' - a regular expression which
;; indicates whether the output of `markdown-command' is standalone
;; XHTML (default: `^\\(\<\?xml\\|\<!DOCTYPE\\|\<html\\)`). If
;; this is not matched, we assume this output is a fragment and add
;; our own header and footer.
;;
;; Additionally, the faces used for syntax highlighting can be modified to
;; your liking by issuing `M-x customize-group RET markdown-faces`
;; or by using the "Markdown Faces" link at the bottom of the mode
;; customization screen.
;;; Usage:
;; Keybindings are grouped by prefixes based on their function. For
;; example, commands dealing with headers begin with `C-c C-t`. The
;; primary commands in each group will are described below. You can
;; obtain a list of all keybindings by pressing `C-c C-h`.
;;
;; * Anchors: `C-c C-a`
;;
;; `C-c C-a l` inserts inline links of the form `[text](url)`. If
;; there is an active region, text in the region is used for the
;; link text. `C-c C-a w` acts similarly for wiki links of the
;; form `[[WikiLink]]`.
;;
;; * Commands: `C-c C-c`
;;
;; `C-c C-c m` will run Markdown on the current buffer and preview
;; the output in another buffer while `C-c C-c p` runs Markdown on
;; the current buffer and previews the output in a browser.
;; `C-c C-c e` will run Markdown on the current buffer and save
;; the result in the file `basename.html`, where `basename` is the
;; name of the Markdown file with the extension removed. **This
;; file will be overwritten without notice.** Press `C-c C-c v`
;; to view the exported file in a browser.
;;
;; `C-c C-c c` will check for undefined references. If there are
;; any, a small buffer will open with a list of undefined
;; references and the line numbers on which they appear. In Emacs
;; 22 and greater, selecting a reference from this list and
;; pressing `RET` will insert an empty reference definition at the
;; end of the buffer. Similarly, selecting the line number will
;; jump to the corresponding line.
;;
;; * Images: `C-c C-i`
;;
;; `C-c C-i i` inserts an image, using the active region (if any)
;; as the alt text.
;;
;; * Physical styles: `C-c C-p`
;;
;; These commands all act on text in the active region, if any,
;; and insert empty markup fragments otherwise. `C-c C-p b` makes
;; the selected text bold, `C-c C-p f` formats the region as
;; fixed-width text, and `C-c C-p i` is used for italic text.
;;
;; * Logical styles: `C-c C-s`
;;
;; These commands all act on text in the active region, if any,
;; and insert empty markup fragments otherwise. Logical styles
;; include blockquote (`C-c C-s b`), preformatted (`C-c C-s p`),
;; code (`C-c C-s c`), emphasis (`C-c C-s e`), and strong
;; (`C-c C-s s`).
;;
;; * Headers: `C-c C-t`
;;
;; All header commands use text in the active region, if any, as
;; the header text. To insert an atx or hash style level-n
;; header, press `C-c C-t n` where n is between 1 and 6. For a
;; top-level setext or underline style header press `C-c C-t t`
;; (mnemonic: title) and for a second-level underline-style header
;; press `C-c C-t s` (mnemonic: section).
;;
;; * Other elements:
;;
;; `C-c -` inserts a horizontal rule.
;;
;; * Wiki-Link Navigation:
;;
;; Use `M-p` and `M-n` to quickly jump to the previous and next
;; wiki links, respectively.
;;
;; * Outline Navigation:
;;
;; Navigation between headings is possible using `outline-mode'.
;; Use `C-M-n` and `C-M-p` to move between the next and previous
;; visible headings. Similarly, `C-M-f` and `C-M-b` move to the
;; next and previous visible headings at the same level as the one
;; at the point. Finally, `C-M-u` will move up to a lower-level
;; (more inclusive) visible heading.
;;
;; Many of the commands described above behave differently depending on
;; whether Transient Mark mode is enabled or not. When it makes sense,
;; if Transient Mark mode is on and a region is active, the command
;; applies to the text in the region (e.g., `C-c C-p b` makes the region
;; bold). For users who prefer to work outside of Transient Mark mode,
;; in Emacs 22 it can be enabled temporarily by pressing `C-SPC C-SPC`.
;;
;; When applicable, commands that specifically act on the region even
;; outside of Transient Mark mode have the same keybinding as the with
;; the exception of an additional `C-` prefix. For example,
;; `markdown-insert-blockquote' is bound to `C-c C-s b` and only acts on
;; the region in Transient Mark mode while `markdown-blockquote-region'
;; is bound to `C-c C-s C-b` and always applies to the region (when
;; nonempty).
;;
;; markdown-mode attempts to be flexible in how it handles
;; indentation. When you press `TAB` repeatedly, the point will cycle
;; through several possible indentation levels corresponding to things
;; you might have in mind when you press `RET` at the end of a line or
;; `TAB`. For example, you may want to start a new list item,
;; continue a list item with hanging indentation, indent for a nested
;; pre block, and so on.
;;
;; markdown-mode supports outline-minor-mode as well as org-mode-style
;; visibility cycling for atx- or hash-style headers. There are two
;; types of visibility cycling: Pressing `S-TAB` cycles globally between
;; the table of contents view (headers only), outline view (top-level
;; headers only), and the full document view. Pressing `TAB` while the
;; point is at a header will cycle through levels of visibility for the
;; subtree: completely folded, visible children, and fully visible.
;; Note that mixing hash and underline style headers will give undesired
;; results.
;;; Extensions:
;; Besides supporting the basic Markdown syntax, markdown-mode also
;; includes syntax highlighting for `[[Wiki Links]]` by default. Wiki
;; links may be followed automatically by hitting the enter key when
;; your curser is on a wiki link or by hitting `C-c C-f`. The
;; autofollowing on enter key may be controlled with the
;; `markdown-follow-wiki-link-on-enter' customization. Use `M-p` and
;; `M-n` to quickly jump to the previous and next wiki links,
;; respectively. Aliased or piped wiki links of the form
;; `[[PageName|link text]]` are also supported.
;;
;; [SmartyPants][] support is possible by customizing `markdown-command'.
;; If you install `SmartyPants.pl` at, say, `/usr/local/bin/smartypants`,
;; then you can set `markdown-command' to `"markdown | smartypants"`.
;; You can do this either by using `M-x customize-group markdown`
;; or by placing the following in your `.emacs` file:
;;
;; (defun markdown-custom ()
;; "markdown-mode-hook"
;; (setq markdown-command "markdown | smartypants"))
;; (add-hook 'markdown-mode-hook '(lambda() (markdown-custom)))
;;
;; [SmartyPants]: http://daringfireball.net/projects/smartypants/
;;
;; Experimental syntax highlighting for mathematical expressions written
;; in LaTeX (only expressions denoted by `$..$`, `$$..$$`, or `\[..\]`)
;; can be enabled by setting `markdown-enable-math' to a non-nil value,
;; either via customize or by placing `(setq markdown-enable-itex t)`
;; in `.emacs`, and restarting Emacs.
;;
;; A [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/)
;; mode, `gfm-mode', is also available. The GitHub implementation of
;; differs slightly from standard Markdown. Most importantly, newlines are
;; significant and trigger hard line breaks. As such, `gfm-mode' turns off
;; `auto-fill-mode' and turns on `longlines-mode'.
;;; Acknowledgments:
;; markdown-mode has benefited greatly from the efforts of the
;; following people:
;;
;; * Cyril Brulebois <cyril.brulebois@enst-bretagne.fr> for Debian packaging.
;; * Conal Elliott <conal@conal.net> for a font-lock regexp patch.
;; * Edward O'Connor <hober0@gmail.com> for a font-lock regexp fix and
;; GitHub Flavored Markdown mode (`gfm-mode').
;; * Greg Bognar <greg_bognar@hms.harvard.edu> for menus and running
;; `markdown' with an active region.
;; * Daniel Burrows <dburrows@debian.org> for filing Debian bug #456592.
;; * Peter S. Galbraith <psg@debian.org> for maintaining emacs-goodies-el.
;; * Dmitry Dzhus <mail@sphinx.net.ru> for reference checking functions.
;; * Bryan Kyle <bryan.kyle@gmail.com> for indentation code.
;; * Ben Voui <intrigeri@boum.org> for font-lock face customizations.
;; * Ankit Solanki <ankit.solanki@gmail.com> for longlines.el
;; compatibility and custom CSS.
;; * Hilko Bengen <bengen@debian.org> for proper XHTML output.
;; * Jose A. Ortega Ruiz <jao@gnu.org> for Emacs 23 fixes.
;; * Alec Resnick <alec@sproutward.org> for bug reports.
;; * Joost Kremers <j.kremers@em.uni-frankfurt.de> for bug reports
;; regarding indentation.
;; * Peter Williams <pezra@barelyenough.org> for fill-paragraph
;; enhancements.
;; * George Ogata <george.ogata@gmail.com> for fixing several
;; byte-compilation warnings.
;; * Eric Merritt <ericbmerritt@gmail.com> for wiki link features.
;; * Philippe Ivaldi <pivaldi@sfr.fr> for XHTML preview
;; customizations and XHTML export.
;; * Jeremiah Dodds <jeremiah.dodds@gmail.com> for supporting
;; Markdown processors which do not accept input from stdin.
;; * Werner Dittmann <werner.dittmann@t-online.de> for bug reports
;; regarding the cl dependency and auto-fill-mode and indentation.
;;; Bugs:
;; Although markdown-mode is developed and tested primarily using
;; GNU Emacs 24, compatibility with earlier Emacsen is also a
;; priority.
;;
;; If you find any bugs in markdown-mode, please construct a test case
;; or a patch and email me at <jrblevin@sdf.org>.
;;; History:
;; markdown-mode was written and is maintained by Jason Blevins. The
;; first version was released on May 24, 2007.
;;
;; * 2007-05-24: Version 1.1
;; * 2007-05-25: Version 1.2
;; * 2007-06-05: [Version 1.3][]
;; * 2007-06-29: Version 1.4
;; * 2008-05-24: [Version 1.5][]
;; * 2008-06-04: [Version 1.6][]
;; * 2009-10-01: [Version 1.7][]
;; * 2011-08-12: [Version 1.8][]
;; * 2011-08-15: [Version 1.8.1][]
;;
;; [Version 1.3]: http://jblevins.org/projects/markdown-mode/rev-1-3
;; [Version 1.5]: http://jblevins.org/projects/markdown-mode/rev-1-5
;; [Version 1.6]: http://jblevins.org/projects/markdown-mode/rev-1-6
;; [Version 1.7]: http://jblevins.org/projects/markdown-mode/rev-1-7
;; [Version 1.8]: http://jblevins.org/projects/markdown-mode/rev-1-8
;; [Version 1.8.1]: http://jblevins.org/projects/markdown-mode/rev-1-8-1
;;; Code:
(require 'easymenu)
(require 'outline)
(require 'cl)
;;; Constants =================================================================
(defconst markdown-mode-version "1.8.1"
"Markdown mode version number.")
(defconst markdown-output-buffer-name "*markdown-output*"
"Name of temporary buffer for markdown command output.")
;;; Customizable variables ====================================================
(defvar markdown-mode-hook nil
"Hook runs when Markdown mode is loaded.")
(defgroup markdown nil
"Major mode for editing text files in Markdown format."
:prefix "markdown-"
:group 'wp
:link '(url-link "http://jblevins.org/projects/markdown-mode/"))
(defcustom markdown-command "markdown"
"Command to run markdown."
:group 'markdown
:type 'string)
(defcustom markdown-command-needs-filename nil
"Set to non-nil if `markdown-command' does not accept input from stdin.
Instead, it will be passed a filename as the final command-line
option. As a result, you will only be able to run Markdown from
buffers which are visiting a file."
:group 'markdown
:type 'boolean)
(defcustom markdown-hr-string "* * * * *"
"String to use for horizonal rules."
:group 'markdown
:type 'string)
(defcustom markdown-bold-underscore nil
"Use two underscores for bold instead of two asterisks."
:group 'markdown
:type 'boolean)
(defcustom markdown-italic-underscore nil
"Use underscores for italic instead of asterisks."
:group 'markdown
:type 'boolean)
(defcustom markdown-indent-function 'markdown-indent-line
"Function to use to indent."
:group 'markdown
:type 'function)
(defcustom markdown-indent-on-enter t
"Automatically indent new lines when enter key is pressed."
:group 'markdown
:type 'boolean)
(defcustom markdown-follow-wiki-link-on-enter t
"Follow wiki link at point (if any) when the enter key is pressed."
:group 'markdown
:type 'boolean)
(defcustom markdown-uri-types
'("acap" "cid" "data" "dav" "fax" "file" "ftp" "gopher" "http" "https"
"imap" "ldap" "mailto" "mid" "modem" "news" "nfs" "nntp" "pop" "prospero"
"rtsp" "service" "sip" "tel" "telnet" "tip" "urn" "vemmi" "wais")
"Link types for syntax highlighting of URIs."
:group 'markdown
:type 'list)
(defcustom markdown-enable-math nil
"Syntax highlighting for inline LaTeX expressions.
This will not take effect until Emacs is restarted."
:group 'markdown
:type 'boolean)
(defcustom markdown-css-path ""
"URL of CSS file to link to in the output XHTML."
:group 'markdown
:type 'string)
(defcustom markdown-xhtml-header-content ""
"Additional content to include in the XHTML <head> block."
:group 'markdown
:type 'string)
(defcustom markdown-xhtml-standalone-regexp
"^\\(\<\?xml\\|\<!DOCTYPE\\|\<html\\)"
"Regexp indicating whether `markdown-command' output is standalone XHTML."
:group 'markdown
:type 'regexp)
;;; Font lock =================================================================
(require 'font-lock)
(defvar markdown-italic-face 'markdown-italic-face
"Face name to use for italic text.")
(defvar markdown-bold-face 'markdown-bold-face
"Face name to use for bold text.")
(defvar markdown-header-face 'markdown-header-face
"Face name to use as a base for headers.")
(defvar markdown-header-face-1 'markdown-header-face-1
"Face name to use for level-1 headers.")
(defvar markdown-header-face-2 'markdown-header-face-2
"Face name to use for level-2 headers.")
(defvar markdown-header-face-3 'markdown-header-face-3
"Face name to use for level-3 headers.")
(defvar markdown-header-face-4 'markdown-header-face-4
"Face name to use for level-4 headers.")
(defvar markdown-header-face-5 'markdown-header-face-5
"Face name to use for level-5 headers.")
(defvar markdown-header-face-6 'markdown-header-face-6
"Face name to use for level-6 headers.")
(defvar markdown-inline-code-face 'markdown-inline-code-face
"Face name to use for inline code.")
(defvar markdown-list-face 'markdown-list-face
"Face name to use for list markers.")
(defvar markdown-blockquote-face 'markdown-blockquote-face
"Face name to use for blockquote.")
(defvar markdown-pre-face 'markdown-pre-face
"Face name to use for preformatted text.")
(defvar markdown-link-face 'markdown-link-face
"Face name to use for links.")
(defvar markdown-missing-link-face 'markdown-missing-link-face
"Face name to use for links where the linked file does not exist.")
(defvar markdown-reference-face 'markdown-reference-face
"Face name to use for reference.")
(defvar markdown-url-face 'markdown-url-face
"Face name to use for URLs.")
(defvar markdown-link-title-face 'markdown-link-title-face
"Face name to use for reference link titles.")
(defvar markdown-comment-face 'markdown-comment-face
"Face name to use for HTML comments.")
(defvar markdown-math-face 'markdown-math-face
"Face name to use for LaTeX expressions.")
(defgroup markdown-faces nil
"Faces used in Markdown Mode"
:group 'markdown
:group 'faces)
(defface markdown-italic-face
'((t :inherit font-lock-variable-name-face :italic t))
"Face for italic text."
:group 'markdown-faces)
(defface markdown-bold-face
'((t :inherit font-lock-variable-name-face :bold t))
"Face for bold text."
:group 'markdown-faces)
(defface markdown-header-face
'((t :inherit font-lock-function-name-face :weight bold))
"Base face for headers."
:group 'markdown-faces)
(defface markdown-header-face-1
'((t :inherit markdown-header-face))
"Face for level-1 headers."
:group 'markdown-faces)
(defface markdown-header-face-2
'((t :inherit markdown-header-face))
"Face for level-2 headers."
:group 'markdown-faces)
(defface markdown-header-face-3
'((t :inherit markdown-header-face))
"Face for level-3 headers."
:group 'markdown-faces)
(defface markdown-header-face-4
'((t :inherit markdown-header-face))
"Face for level-4 headers."
:group 'markdown-faces)
(defface markdown-header-face-5
'((t :inherit markdown-header-face))
"Face for level-5 headers."
:group 'markdown-faces)
(defface markdown-header-face-6
'((t :inherit markdown-header-face))
"Face for level-6 headers."
:group 'markdown-faces)
(defface markdown-inline-code-face
'((t :inherit font-lock-constant-face))
"Face for inline code."
:group 'markdown-faces)
(defface markdown-list-face
'((t :inherit font-lock-builtin-face))
"Face for list item markers."
:group 'markdown-faces)
(defface markdown-blockquote-face
'((t :inherit font-lock-doc-face))
"Face for blockquote sections."
:group 'markdown-faces)
(defface markdown-pre-face
'((t :inherit font-lock-constant-face))
"Face for preformatted text."
:group 'markdown-faces)
(defface markdown-link-face
'((t :inherit font-lock-keyword-face))
"Face for links."
:group 'markdown-faces)
(defface markdown-missing-link-face
'((t :inherit font-lock-warning-face))
"Face for missing links."
:group 'markdown-faces)
(defface markdown-reference-face
'((t :inherit font-lock-type-face))
"Face for link references."
:group 'markdown-faces)
(defface markdown-url-face
'((t :inherit font-lock-string-face))
"Face for URLs."
:group 'markdown-faces)
(defface markdown-link-title-face
'((t :inherit font-lock-comment-face))
"Face for reference link titles."
:group 'markdown-faces)
(defface markdown-comment-face
'((t :inherit font-lock-comment-face))
"Face for HTML comments."
:group 'markdown-faces)
(defface markdown-math-face
'((t :inherit font-lock-string-face))
"Face for LaTeX expressions."
:group 'markdown-faces)
(defconst markdown-regex-link-inline
"\\(!?\\[[^]]*?\\]\\)\\(([^\\)]*)\\)"
"Regular expression for a [text](file) or an image link ![text](file).")
(defconst markdown-regex-link-reference
"\\(!?\\[[^]]+?\\]\\)[ ]?\\(\\[[^]]*?\\]\\)"
"Regular expression for a reference link [text][id].")
(defconst markdown-regex-reference-definition
"^ \\{0,3\\}\\(\\[.+?\\]\\):\\s *\\(.*?\\)\\s *\\( \"[^\"]*\"$\\|$\\)"
"Regular expression for a link definition [id]: ...")
(defconst markdown-regex-header-1-atx
"^\\(# \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 1 atx-style (hash mark) headers.")
(defconst markdown-regex-header-2-atx
"^\\(## \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 2 atx-style (hash mark) headers.")
(defconst markdown-regex-header-3-atx
"^\\(### \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 3 atx-style (hash mark) headers.")
(defconst markdown-regex-header-4-atx
"^\\(#### \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 4 atx-style (hash mark) headers.")
(defconst markdown-regex-header-5-atx
"^\\(##### \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 5 atx-style (hash mark) headers.")
(defconst markdown-regex-header-6-atx
"^\\(###### \\)\\(.*?\\)\\($\\| #+$\\)"
"Regular expression for level 6 atx-style (hash mark) headers.")
(defconst markdown-regex-header-1-setext
"^\\(.*\\)\n\\(===+\\)$"
"Regular expression for level 1 setext-style (underline) headers.")
(defconst markdown-regex-header-2-setext
"^\\(.*\\)\n\\(---+\\)$"
"Regular expression for level 2 setext-style (underline) headers.")
(defconst markdown-regex-hr
"^\\(\\*[ ]?\\*[ ]?\\*[ ]?[\\* ]*\\|-[ ]?-[ ]?-[--- ]*\\)$"
"Regular expression for matching Markdown horizontal rules.")
(defconst markdown-regex-code
"\\(^\\|[^\\]\\)\\(\\(`\\{1,2\\}\\)\\([^ \\]\\|[^ ]\\(.\\|\n[^\n]\\)*?[^ \\]\\)\\3\\)"
"Regular expression for matching inline code fragments.")
(defconst markdown-regex-pre
"^\\( \\|\t\\).*$"
"Regular expression for matching preformatted text sections.")
(defconst markdown-regex-list
"^[ \t]*\\([0-9]+\\.\\|[\\*\\+-]\\) "
"Regular expression for matching list markers.")
(defconst markdown-regex-bold
"\\(^\\|[^\\]\\)\\(\\([*_]\\{2\\}\\)\\(.\\|\n[^\n]\\)*?[^\\ ]\\3\\)"
"Regular expression for matching bold text.")
(defconst markdown-regex-italic
"\\(^\\|[^\\]\\)\\(\\([*_]\\)\\([^ \\]\\3\\|[^ ]\\(.\\|\n[^\n]\\)*?[^\\ ]\\3\\)\\)"
"Regular expression for matching italic text.")
(defconst markdown-regex-blockquote
"^>.*$"
"Regular expression for matching blockquote lines.")
(defconst markdown-regex-line-break
" $"
"Regular expression for matching line breaks.")
(defconst markdown-regex-wiki-link
"\\[\\[\\([^]|]+\\)\\(|\\([^]]+\\)\\)?\\]\\]"
"Regular expression for matching wiki links.
This matches typical bracketed [[WikiLinks]] as well as 'aliased'
wiki links of the form [[PageName|link text]]. In this regular
expression, #1 matches the page name and #3 matches the link
text.")
(defconst markdown-regex-uri
(concat
"\\(" (mapconcat 'identity markdown-uri-types "\\|")
"\\):[^]\t\n\r<>,;() ]+")
"Regular expression for matching inline URIs.")
(defconst markdown-regex-angle-uri
(concat
"\\(<\\)\\("
(mapconcat 'identity markdown-uri-types "\\|")
"\\):[^]\t\n\r<>,;()]+\\(>\\)")
"Regular expression for matching inline URIs in angle brackets.")
(defconst markdown-regex-email
"<\\(\\sw\\|\\s_\\|\\s.\\)+@\\(\\sw\\|\\s_\\|\\s.\\)+>"
"Regular expression for matching inline email addresses.")
(defconst markdown-regex-latex-expression
"\\(^\\|[^\\]\\)\\(\\$\\($\\([^\\$]\\|\\\\.\\)*\\$\\|\\([^\\$]\\|\\\\.\\)*\\)\\$\\)"
"Regular expression for itex $..$ or $$..$$ math mode expressions.")
(defconst markdown-regex-latex-display
"^\\\\\\[\\(.\\|\n\\)*?\\\\\\]$"
"Regular expression for itex \[..\] display mode expressions.")
(defconst markdown-regex-list-indent
"^\\(\\s *\\)\\([0-9]+\\.\\|[\\*\\+-]\\)\\(\\s +\\)"
"Regular expression for matching indentation of list items.")
(defvar markdown-mode-font-lock-keywords-basic
(list
'(markdown-match-pre-blocks 0 markdown-pre-face t t)
(cons markdown-regex-blockquote 'markdown-blockquote-face)
(cons markdown-regex-header-1-setext 'markdown-header-face-1)
(cons markdown-regex-header-2-setext 'markdown-header-face-2)
(cons markdown-regex-header-1-atx 'markdown-header-face-1)
(cons markdown-regex-header-2-atx 'markdown-header-face-2)
(cons markdown-regex-header-3-atx 'markdown-header-face-3)
(cons markdown-regex-header-4-atx 'markdown-header-face-4)
(cons markdown-regex-header-5-atx 'markdown-header-face-5)
(cons markdown-regex-header-6-atx 'markdown-header-face-6)
(cons markdown-regex-hr 'markdown-header-face)
'(markdown-match-comments 0 markdown-comment-face t t)
(cons markdown-regex-code '(2 markdown-inline-code-face))
(cons markdown-regex-list 'markdown-list-face)
(cons markdown-regex-link-inline
'((1 markdown-link-face t)
(2 markdown-url-face t)))
(cons markdown-regex-link-reference
'((1 markdown-link-face t)
(2 markdown-reference-face t)))
(cons markdown-regex-reference-definition
'((1 markdown-reference-face t)
(2 markdown-url-face t)
(3 markdown-link-title-face t)))
(cons markdown-regex-bold '(2 markdown-bold-face))
(cons markdown-regex-italic '(2 markdown-italic-face))
(cons markdown-regex-angle-uri 'markdown-link-face)
(cons markdown-regex-uri 'markdown-link-face)
(cons markdown-regex-email 'markdown-link-face)
)
"Syntax highlighting for Markdown files.")
(defconst markdown-mode-font-lock-keywords-latex
(list
;; Math mode $..$ or $$..$$
(cons markdown-regex-latex-expression '(2 markdown-math-face))
;; Display mode equations with brackets: \[ \]
(cons markdown-regex-latex-display 'markdown-math-face)
;; Equation reference (eq:foo)
(cons "(eq:\\w+)" 'markdown-reference-face)
;; Equation reference \eqref{foo}
(cons "\\\\eqref{\\w+}" 'markdown-reference-face))
"Syntax highlighting for LaTeX fragments.")
(defvar markdown-mode-font-lock-keywords
(append
(if markdown-enable-math
markdown-mode-font-lock-keywords-latex)
markdown-mode-font-lock-keywords-basic)
"Default highlighting expressions for Markdown mode.")
;;; Markdown parsing functions ================================================
(defun markdown-cur-line-blank-p ()
"Return t if the current line is blank and nil otherwise."
(save-excursion
(beginning-of-line)
(re-search-forward "^\\s *$" (point-at-eol) t)))
(defun markdown-prev-line-blank-p ()
"Return t if the previous line is blank and nil otherwise.
If we are at the first line, then consider the previous line to be blank."
(save-excursion
(if (= (point-at-bol) (point-min))
t
(forward-line -1)
(markdown-cur-line-blank-p))))
(defun markdown-prev-line-indent-p ()
"Return t if the previous line is indented and nil otherwise."
(save-excursion
(forward-line -1)
(goto-char (point-at-bol))
(if (re-search-forward "^\\s " (point-at-eol) t) t)))
(defun markdown-cur-line-indent ()
"Return the number of leading whitespace characters in the current line."
(save-excursion
(goto-char (point-at-bol))
(re-search-forward "^\\s +" (point-at-eol) t)
(current-column)))
(defun markdown-prev-line-indent ()
"Return the number of leading whitespace characters in the previous line."
(save-excursion
(forward-line -1)
(markdown-cur-line-indent)))
(defun markdown-cur-non-list-indent ()
"Return the number of leading whitespace characters in the current line."
(save-excursion
(beginning-of-line)
(when (re-search-forward markdown-regex-list-indent (point-at-eol) t)
(current-column))))
(defun markdown-prev-non-list-indent ()
"Return position of the first non-list-marker on the previous line."
(save-excursion
(forward-line -1)
(markdown-cur-non-list-indent)))
(defun markdown--next-block ()
"Move the point to the start of the next text block."
(forward-line)
(while (and (or (not (markdown-prev-line-blank-p))
(markdown-cur-line-blank-p))
(not (eobp)))
(forward-line)))
(defun markdown--end-of-level (level)
"Move the point to the end of region with indentation at least LEVEL."
(let (indent)
(while (and (not (< (setq indent (markdown-cur-line-indent)) level))
(not (>= indent (+ level 4)))
(not (eobp)))
(markdown--next-block))
(unless (eobp)
;; Move back before any trailing blank lines
(while (and (markdown-prev-line-blank-p)
(not (bobp)))
(forward-line -1))
(forward-line -1)
(end-of-line))))
; From html-helper-mode
(defun markdown-match-comments (last)
"Match HTML comments from the point to LAST."
(cond ((search-forward "<!--" last t)
(backward-char 4)
(let ((beg (point)))
(cond ((search-forward-regexp "--[ \t]*>" last t)
(set-match-data (list beg (point)))
t)
(t nil))))
(t nil)))
(defun markdown-match-pre-blocks (last)
"Match Markdown pre blocks from point to LAST.
A region matches as if it is indented at least four spaces
relative to the nearest previous block of lesser non-list-marker
indentation."
(let (cur-begin cur-end cur-indent prev-indent prev-list stop match found)
;; Don't start in the middle of a block
(unless (and (bolp)
(markdown-prev-line-blank-p)
(not (markdown-cur-line-blank-p)))
(markdown--next-block))
;; Move to the first full block in the region with indent 4 or more
(while (and (not (>= (setq cur-indent (markdown-cur-line-indent)) 4))
(not (>= (point) last)))
(markdown--next-block))
(setq cur-begin (point))
(markdown--end-of-level cur-indent)
(setq cur-end (point))
(setq match nil)
(setq stop (> cur-begin cur-end))
(while (and (<= cur-end last) (not stop) (not match))
;; Move to the nearest preceding block of lesser (non-marker) indentation
(setq prev-indent (+ cur-indent 1))
(goto-char cur-begin)
(setq found nil)
(while (and (>= prev-indent cur-indent)
(not (and prev-list
(eq prev-indent cur-indent)))
(not (bobp)))
;; Move point to the last line of the previous block.
(forward-line -1)
(while (and (markdown-cur-line-blank-p)
(not (bobp)))
(forward-line -1))
;; Update the indentation level using either the
;; non-list-marker indentation, if the previous line is the
;; start of a list, or the actual indentation.
(setq prev-list (markdown-cur-non-list-indent))
(setq prev-indent (or prev-list
(markdown-cur-line-indent)))
(setq found t))
;; If the loop didn't execute
(unless found
(setq prev-indent 0))
;; Compare with prev-indent minus its remainder mod 4
(setq prev-indent (- prev-indent (mod prev-indent 4)))
;; Set match data and return t if we have a match
(if (>= cur-indent (+ prev-indent 4))
;; Match
(progn
(setq match t)
(set-match-data (list cur-begin cur-end))
;; Leave point at end of block
(goto-char cur-end)
(forward-line))
;; Move to the next block (if possible)
(goto-char cur-end)
(markdown--next-block)
(setq cur-begin (point))
(setq cur-indent (markdown-cur-line-indent))
(markdown--end-of-level cur-indent)
(setq cur-end (point))
(setq stop (equal cur-begin cur-end))))
match))
(defun markdown-font-lock-extend-region ()
"Extend the search region to include an entire block of text.
This helps improve font locking for block constructs such as pre blocks."
;; Avoid compiler warnings about these global variables from font-lock.el.
;; See the documentation for variable `font-lock-extend-region-functions'.
(eval-when-compile (defvar font-lock-beg) (defvar font-lock-end))
(save-excursion
(goto-char font-lock-beg)
(let ((found (re-search-backward "\n\n" nil t)))
(when found
(goto-char font-lock-end)
(when (re-search-forward "\n\n" nil t)
(beginning-of-line)
(setq font-lock-end (point)))
(setq font-lock-beg found)))))
;;; Syntax Table ==============================================================
(defvar markdown-mode-syntax-table
(let ((markdown-mode-syntax-table (make-syntax-table)))
(modify-syntax-entry ?\" "w" markdown-mode-syntax-table)
markdown-mode-syntax-table)
"Syntax table for `markdown-mode'.")
;;; Element Insertion =========================================================
(defun markdown-wrap-or-insert (s1 s2)
"Insert the strings S1 and S2.
If Transient Mark mode is on and a region is active, wrap the strings S1
and S2 around the region."
(if (and transient-mark-mode mark-active)
(let ((a (region-beginning)) (b (region-end)))
(goto-char a)
(insert s1)
(goto-char (+ b (length s1)))
(insert s2))
(insert s1 s2)))
(defun markdown-insert-hr ()
"Insert a horizonal rule using `markdown-hr-string'."
(interactive)
;; Leading blank line
(when (and (>= (point) (+ (point-min) 2))
(not (looking-back "\n\n" 2)))
(insert "\n"))
;; Insert custom HR string
(insert (concat markdown-hr-string "\n"))
;; Following blank line
(backward-char)
(unless (looking-at "\n\n")
(insert "\n")))
(defun markdown-insert-bold ()
"Insert markup for a bold word or phrase.
If Transient Mark mode is on and a region is active, it is made bold."
(interactive)
(if markdown-bold-underscore
(markdown-wrap-or-insert "__" "__")
(markdown-wrap-or-insert "**" "**"))
(backward-char 2))
(defun markdown-insert-italic ()
"Insert markup for an italic word or phrase.
If Transient Mark mode is on and a region is active, it is made italic."
(interactive)
(if markdown-italic-underscore
(markdown-wrap-or-insert "_" "_")
(markdown-wrap-or-insert "*" "*"))
(backward-char 1))
(defun markdown-insert-code ()
"Insert markup for an inline code fragment.
If Transient Mark mode is on and a region is active, it is marked
as inline code."
(interactive)
(markdown-wrap-or-insert "`" "`")
(backward-char 1))
(defun markdown-insert-link ()
"Insert an inline link of the form []().
If Transient Mark mode is on and a region is active, it is used
as the link text."
(interactive)
(markdown-wrap-or-insert "[" "]")
(insert "()")
(backward-char 1))
(defun markdown-insert-wiki-link ()
"Insert a wiki link of the form [[WikiLink]].
If Transient Mark mode is on and a region is active, it is used
as the link text."
(interactive)
(markdown-wrap-or-insert "[[" "]]")
(backward-char 2))
(defun markdown-insert-image ()
"Insert an inline image tag of the form ![]().
If Transient Mark mode is on and a region is active, it is used
as the alt text of the image."
(interactive)
(markdown-wrap-or-insert "![" "]")
(insert "()")
(backward-char 1))
(defun markdown-insert-header-1 ()
"Insert a first level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 1))
(defun markdown-insert-header-2 ()
"Insert a second level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 2))
(defun markdown-insert-header-3 ()
"Insert a third level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 3))
(defun markdown-insert-header-4 ()
"Insert a fourth level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 4))
(defun markdown-insert-header-5 ()
"Insert a fifth level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 5))
(defun markdown-insert-header-6 ()
"Insert a sixth level atx-style (hash mark) header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(markdown-insert-header 6))
(defun markdown-insert-header (n)
"Insert an atx-style (hash mark) header.
With no prefix argument, insert a level-1 header. With prefix N,
insert a level-N header. If Transient Mark mode is on and the
region is active, it is used as the header text."
(interactive "p")
(unless n ; Test to see if n is defined
(setq n 1)) ; Default to level 1 header
(let (hdr hdrl hdrr)
(dotimes (count n hdr)
(setq hdr (concat "#" hdr))) ; Build a hash mark header string
(setq hdrl (concat hdr " "))
(setq hdrr (concat " " hdr))
(markdown-wrap-or-insert hdrl hdrr))
(backward-char (+ 1 n)))
(defun markdown-insert-title ()
"Insert a setext-style (underline) first level header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(if (and transient-mark-mode mark-active)
(let ((a (region-beginning))
(b (region-end))
(len 0)
(hdr))
(setq len (- b a))
(dotimes (count len hdr)
(setq hdr (concat "=" hdr))) ; Build a === title underline
(end-of-line)
(insert "\n" hdr "\n"))
(insert "\n==========\n")
(backward-char 12)))
(defun markdown-insert-section ()
"Insert a setext-style (underline) second level header.
If Transient Mark mode is on and a region is active, it is used
as the header text."
(interactive)
(if (and transient-mark-mode mark-active)
(let ((a (region-beginning))
(b (region-end))
(len 0)
(hdr))
(setq len (- b a))
(dotimes (count len hdr)
(setq hdr (concat "-" hdr))) ; Build a --- section underline
(end-of-line)
(insert "\n" hdr "\n"))
(insert "\n----------\n")
(backward-char 12)))
(defun markdown-insert-blockquote ()
"Start a blockquote section (or blockquote the region).
If Transient Mark mode is on and a region is active, it is used as
the blockquote text."
(interactive)
(if (and (boundp 'transient-mark-mode) transient-mark-mode mark-active)
(markdown-blockquote-region (region-beginning) (region-end))
(insert "> ")))
(defun markdown-block-region (beg end prefix)
"Format the region using a block prefix.
Arguments BEG and END specify the beginning and end of the
region. The characters PREFIX will appear at the beginning
of each line."
(if mark-active
(save-excursion
;; Ensure that there is a leading blank line
(goto-char beg)
(when (and (>= (point) (+ (point-min) 2))
(not (looking-back "\n\n" 2)))
(insert "\n")
(setq beg (1+ beg))
(setq end (1+ end)))
;; Move back before any blank lines at the end
(goto-char end)
(while (and (looking-back "\n" 1)
(not (equal (point) (point-min))))
(backward-char)
(setq end (1- end)))
;; Ensure that there is a trailing blank line
(goto-char end)
(if (not (or (looking-at "\n\n")
(and (equal (1+ end) (point-max)) (looking-at "\n"))))
(insert "\n"))
;; Insert PREFIX
(goto-char beg)
(beginning-of-line)
(while (< (point-at-bol) end)
(insert prefix)
(setq end (+ (length prefix) end))
(forward-line)))))
(defun markdown-blockquote-region (beg end)
"Blockquote the region.
Arguments BEG and END specify the beginning and end of the region."
(interactive "*r")
(markdown-block-region beg end "> "))
(defun markdown-insert-pre ()
"Start a preformatted section (or apply to the region).
If Transient Mark mode is on and a region is active, it is marked
as preformatted text."
(interactive)
(if (and (boundp 'transient-mark-mode) transient-mark-mode mark-active)
(markdown-pre-region (region-beginning) (region-end))
(insert " ")))
(defun markdown-pre-region (beg end)
"Format the region as preformatted text.
Arguments BEG and END specify the beginning and end of the region."
(interactive "*r")
(markdown-block-region beg end " "))
;;; Indentation ====================================================================
(defun markdown-indent-find-next-position (cur-pos positions)
"Return the position after the index of CUR-POS in POSITIONS."
(while (and positions
(not (equal cur-pos (car positions))))
(setq positions (cdr positions)))
(or (cadr positions) 0))
(defun markdown-indent-line ()
"Indent the current line using some heuristics.
If the _previous_ command was either `markdown-enter-key' or
`markdown-cycle', then we should cycle to the next
reasonable indentation position. Otherwise, we could have been
called directly by `markdown-enter-key', by an initial call of
`markdown-cycle', or indirectly by `auto-fill-mode'. In
these cases, indent to the default position."
(interactive)
(let ((positions (markdown-calc-indents))
(cur-pos (current-column)))
(if (not (equal this-command 'markdown-cycle))
(indent-line-to (car positions))
(setq positions (sort (delete-dups positions) '<))
(indent-line-to
(markdown-indent-find-next-position cur-pos positions)))))
(defun markdown-calc-indents ()
"Return a list of indentation columns to cycle through.
The first element in the returned list should be considered the
default indentation level."
(let (pos prev-line-pos positions)
;; Previous line indent
(setq prev-line-pos (markdown-prev-line-indent))
(setq positions (cons prev-line-pos positions))
;; Previous non-list-marker indent
(setq pos (markdown-prev-non-list-indent))
(if pos
(setq positions (cons pos positions)))
;; Indentation of the previous line + tab-width
(cond
(prev-line-pos
(setq positions (cons (+ prev-line-pos tab-width) positions)))
(t
(setq positions (cons tab-width positions))))
;; Indentation of the previous line - tab-width
(if (and prev-line-pos
(> prev-line-pos tab-width))
(setq positions (cons (- prev-line-pos tab-width) positions)))
;; Indentation of preceeding list item
(setq pos
(save-excursion
(forward-line -1)
(catch 'break
(while (not (equal (point) (point-min)))
(forward-line -1)
(goto-char (point-at-bol))
(when (re-search-forward markdown-regex-list-indent (point-at-eol) t)
(throw 'break (length (match-string 1)))))
nil)))
(if (and pos (not (eq pos prev-line-pos)))
(setq positions (cons pos positions)))
;; First column
(setq positions (cons 0 positions))
(reverse positions)))
(defun markdown-do-normal-return ()
"Insert a newline and optionally indent the next line."
(newline)
(if markdown-indent-on-enter
(funcall indent-line-function)))
(defun markdown-enter-key ()
"Handle RET according to context.
If there is a wiki link at the point, follow it unless
`markdown-follow-wiki-link-on-enter' is nil. Otherwise, process
it in the usual way."
(interactive)
(if (and markdown-follow-wiki-link-on-enter (markdown-wiki-link-p))
(markdown-follow-wiki-link-at-point)
(markdown-do-normal-return)))
;;; Keymap ====================================================================
(defvar markdown-mode-map
(let ((map (make-keymap)))
;; Element insertion
(define-key map "\C-c\C-al" 'markdown-insert-link)
(define-key map "\C-c\C-aw" 'markdown-insert-wiki-link)
(define-key map "\C-c\C-ii" 'markdown-insert-image)
(define-key map "\C-c\C-t1" 'markdown-insert-header-1)
(define-key map "\C-c\C-t2" 'markdown-insert-header-2)
(define-key map "\C-c\C-t3" 'markdown-insert-header-3)
(define-key map "\C-c\C-t4" 'markdown-insert-header-4)
(define-key map "\C-c\C-t5" 'markdown-insert-header-5)
(define-key map "\C-c\C-t6" 'markdown-insert-header-6)
(define-key map "\C-c\C-pb" 'markdown-insert-bold)
(define-key map "\C-c\C-ss" 'markdown-insert-bold)
(define-key map "\C-c\C-pi" 'markdown-insert-italic)
(define-key map "\C-c\C-se" 'markdown-insert-italic)
(define-key map "\C-c\C-pf" 'markdown-insert-code)
(define-key map "\C-c\C-sc" 'markdown-insert-code)
(define-key map "\C-c\C-sb" 'markdown-insert-blockquote)
(define-key map "\C-c\C-s\C-b" 'markdown-blockquote-region)
(define-key map "\C-c\C-sp" 'markdown-insert-pre)
(define-key map "\C-c\C-s\C-p" 'markdown-pre-region)
(define-key map "\C-c-" 'markdown-insert-hr)
(define-key map "\C-c\C-tt" 'markdown-insert-title)
(define-key map "\C-c\C-ts" 'markdown-insert-section)
;; WikiLink Following
(define-key map "\C-c\C-f" 'markdown-follow-wiki-link-at-point)
(define-key map "\M-n" 'markdown-next-wiki-link)
(define-key map "\M-p" 'markdown-previous-wiki-link)
;; Indentation
(define-key map "\C-m" 'markdown-enter-key)
;; Visibility cycling
(define-key map (kbd "<tab>") 'markdown-cycle)
(define-key map (kbd "<S-iso-lefttab>") 'markdown-shifttab)
;; Header navigation
(define-key map (kbd "C-M-n") 'outline-next-visible-heading)
(define-key map (kbd "C-M-p") 'outline-previous-visible-heading)
(define-key map (kbd "C-M-f") 'outline-forward-same-level)
(define-key map (kbd "C-M-b") 'outline-backward-same-level)
(define-key map (kbd "C-M-u") 'outline-up-heading)
;; Markdown functions
(define-key map "\C-c\C-cm" 'markdown)
(define-key map "\C-c\C-cp" 'markdown-preview)
(define-key map "\C-c\C-ce" 'markdown-export)
(define-key map "\C-c\C-cv" 'markdown-export-and-view)
;; References
(define-key map "\C-c\C-cc" 'markdown-check-refs)
map)
"Keymap for Markdown major mode.")
;;; Menu ==================================================================
(easy-menu-define markdown-mode-menu markdown-mode-map
"Menu for Markdown mode"
'("Markdown"
("Show/Hide"
["Cycle visibility" markdown-cycle (outline-on-heading-p)]
["Cycle global visibility" markdown-shifttab])
"---"
["Compile" markdown]
["Preview" markdown-preview]
["Export" markdown-export]
["Export & View" markdown-export-and-view]
"---"
("Headers (setext)"
["Insert Title" markdown-insert-title]
["Insert Section" markdown-insert-section])
("Headers (atx)"
["First level" markdown-insert-header-1]
["Second level" markdown-insert-header-2]
["Third level" markdown-insert-header-3]
["Fourth level" markdown-insert-header-4]
["Fifth level" markdown-insert-header-5]
["Sixth level" markdown-insert-header-6])
"---"
["Bold" markdown-insert-bold]
["Italic" markdown-insert-italic]
["Blockquote" markdown-insert-blockquote]
["Preformatted" markdown-insert-pre]
["Code" markdown-insert-code]
"---"
["Insert inline link" markdown-insert-link]
["Insert image" markdown-insert-image]
["Insert horizontal rule" markdown-insert-hr]
"---"
["Check references" markdown-check-refs]
"---"
["Version" markdown-show-version]
))
;;; References ================================================================
;;; Undefined reference checking code by Dmitry Dzhus <mail@sphinx.net.ru>.
(defconst markdown-refcheck-buffer
"*Undefined references for %BUFFER%*"
"Pattern for name of buffer for listing undefined references.
The string %BUFFER% will be replaced by the corresponding
`markdown-mode' buffer name.")
(defun markdown-has-reference-definition (reference)
"Find out whether Markdown REFERENCE is defined.
REFERENCE should include the square brackets, like [this]."
(let ((reference (downcase reference)))
(save-excursion
(goto-char (point-min))
(catch 'found
(while (re-search-forward markdown-regex-reference-definition nil t)
(when (string= reference (downcase (match-string-no-properties 1)))
(throw 'found t)))))))
(defun markdown-get-undefined-refs ()
"Return a list of undefined Markdown references.
Result is an alist of pairs (reference . occurencies), where
occurencies is itself another alist of pairs (label .
line-number).
For example, an alist corresponding to [Nice editor][Emacs] at line 12,
\[GNU Emacs][Emacs] at line 45 and [manual][elisp] at line 127 is
\((\"[emacs]\" (\"[Nice editor]\" . 12) (\"[GNU Emacs]\" . 45)) (\"[elisp]\" (\"[manual]\" . 127)))."
(let ((missing))
(save-excursion
(goto-char (point-min))
(while
(re-search-forward markdown-regex-link-reference nil t)
(let* ((label (match-string-no-properties 1))
(reference (match-string-no-properties 2))
(target (downcase (if (string= reference "[]") label reference))))
(unless (markdown-has-reference-definition target)
(let ((entry (assoc target missing)))
(if (not entry)
(add-to-list 'missing (cons target
(list (cons label (markdown-line-number-at-pos)))) t)
(setcdr entry
(append (cdr entry) (list (cons label (markdown-line-number-at-pos))))))))))
missing)))
(defun markdown-add-missing-ref-definition (ref buffer &optional recheck)
"Add blank REF definition to the end of BUFFER.
REF is a Markdown reference in square brackets, like \"[lisp-history]\".
When RECHECK is non-nil, BUFFER gets rechecked for undefined
references so that REF disappears from the list of those links."
(with-current-buffer buffer
(when (not (eq major-mode 'markdown-mode))
(error "Not available in current mode"))
(goto-char (point-max))
(indent-new-comment-line)
(insert (concat ref ": ")))
(switch-to-buffer-other-window buffer)
(goto-char (point-max))
(when recheck
(markdown-check-refs t)))
;; Button which adds an empty Markdown reference definition to the end
;; of buffer specified as its 'target-buffer property. Reference name
;; is button's label
(when (>= emacs-major-version 22)
(define-button-type 'markdown-ref-button
'help-echo "Push to create an empty reference definition"
'face 'bold
'action (lambda (b)
(markdown-add-missing-ref-definition
(button-label b) (button-get b 'target-buffer) t))))
;; Button jumping to line in buffer specified as its 'target-buffer
;; property. Line number is button's 'line property.
(when (>= emacs-major-version 22)
(define-button-type 'goto-line-button
'help-echo "Push to go to this line"
'face 'italic
'action (lambda (b)
(message (button-get b 'buffer))
(switch-to-buffer-other-window (button-get b 'target-buffer))
;; use call-interactively to silence compiler
(call-interactively 'goto-line (button-get b 'target-line)))))
(defun markdown-check-refs (&optional silent)
"Show all undefined Markdown references in current `markdown-mode' buffer.
If SILENT is non-nil, do not message anything when no undefined
references found.
Links which have empty reference definitions are considered to be
defined."
(interactive "P")
(when (not (eq major-mode 'markdown-mode))
(error "Not available in current mode"))
(let ((oldbuf (current-buffer))
(refs (markdown-get-undefined-refs))
(refbuf (get-buffer-create (replace-regexp-in-string
"%BUFFER%" (buffer-name)
markdown-refcheck-buffer t))))
(if (null refs)
(progn
(when (not silent)
(message "No undefined references found"))
(kill-buffer refbuf))
(with-current-buffer refbuf
(when view-mode
(View-exit-and-edit))
(erase-buffer)
(insert "Following references lack definitions:")
(newline 2)
(dolist (ref refs)
(let ((button-label (format "%s" (car ref))))
(if (>= emacs-major-version 22)
;; Create a reference button in Emacs 22
(insert-text-button button-label
:type 'markdown-ref-button
'target-buffer oldbuf)
;; Insert reference as text in Emacs < 22
(insert button-label)))
(insert " (")
(dolist (occurency (cdr ref))
(let ((line (cdr occurency)))
(if (>= emacs-major-version 22)
;; Create a line number button in Emacs 22
(insert-button (number-to-string line)
:type 'goto-line-button
'target-buffer oldbuf
'target-line line)
;; Insert line number as text in Emacs < 22
(insert (number-to-string line)))
(insert " "))) (delete-char -1)
(insert ")")
(newline))
(view-buffer-other-window refbuf)
(goto-char (point-min))
(forward-line 2)))))
;;; Outline ===================================================================
;; The following visibility cycling code was taken from org-mode
;; by Carsten Dominik and adapted for markdown-mode.
(defvar markdown-cycle-global-status 1)
(defvar markdown-cycle-subtree-status nil)
;; Based on org-end-of-subtree from org.el
(defun markdown-end-of-subtree (&optional invisible-OK)
"Move to the end of the current subtree.
Only visible heading lines are considered, unless INVISIBLE-OK is
non-nil."
(outline-back-to-heading invisible-OK)
(let ((first t)
(level (funcall outline-level)))
(while (and (not (eobp))
(or first (> (funcall outline-level) level)))
(setq first nil)
(outline-next-heading))
(if (memq (preceding-char) '(?\n ?\^M))
(progn
;; Go to end of line before heading
(forward-char -1)
(if (memq (preceding-char) '(?\n ?\^M))
;; leave blank line before heading
(forward-char -1)))))
(point))
;; Based on org-cycle from org.el.
(defun markdown-cycle (&optional arg)
"Visibility cycling for Markdown mode.
If ARG is t, perform global visibility cycling. If the point is
at an atx-style header, cycle visibility of the corresponding
subtree. Otherwise, insert a tab using `indent-relative'."
(interactive "P")
(cond
((eq arg t) ;; Global cycling
(cond
((and (eq last-command this-command)
(eq markdown-cycle-global-status 2))
;; Move from overview to contents
(hide-sublevels 1)
(message "CONTENTS")
(setq markdown-cycle-global-status 3))
((and (eq last-command this-command)
(eq markdown-cycle-global-status 3))
;; Move from contents to all
(show-all)
(message "SHOW ALL")
(setq markdown-cycle-global-status 1))
(t
;; Defaults to overview
(hide-body)
(message "OVERVIEW")
(setq markdown-cycle-global-status 2))))
((save-excursion (beginning-of-line 1) (looking-at outline-regexp))
;; At a heading: rotate between three different views
(outline-back-to-heading)
(let ((goal-column 0) eoh eol eos)
;; Determine boundaries
(save-excursion
(outline-back-to-heading)
(save-excursion
(beginning-of-line 2)
(while (and (not (eobp)) ;; this is like `next-line'
(get-char-property (1- (point)) 'invisible))
(beginning-of-line 2)) (setq eol (point)))
(outline-end-of-heading) (setq eoh (point))
(markdown-end-of-subtree t)
(skip-chars-forward " \t\n")
(beginning-of-line 1) ; in case this is an item
(setq eos (1- (point))))
;; Find out what to do next and set `this-command'
(cond
((= eos eoh)
;; Nothing is hidden behind this heading
(message "EMPTY ENTRY")
(setq markdown-cycle-subtree-status nil))
((>= eol eos)
;; Entire subtree is hidden in one line: open it
(show-entry)
(show-children)
(message "CHILDREN")
(setq markdown-cycle-subtree-status 'children))
((and (eq last-command this-command)
(eq markdown-cycle-subtree-status 'children))
;; We just showed the children, now show everything.
(show-subtree)
(message "SUBTREE")
(setq markdown-cycle-subtree-status 'subtree))
(t
;; Default action: hide the subtree.
(hide-subtree)
(message "FOLDED")
(setq markdown-cycle-subtree-status 'folded)))))
(t
(indent-for-tab-command))))
;; Based on org-shifttab from org.el.
(defun markdown-shifttab ()
"Global visibility cycling.
Calls `markdown-cycle' with argument t."
(interactive)
(markdown-cycle t))
;;; Commands ==================================================================
(defun markdown (&optional output-buffer-name)
"Run `markdown' on current buffer and insert output in buffer BUFFER-OUTPUT."
(interactive)
(let ((title (buffer-name))
(begin-region)
(end-region))
(if (and (boundp 'transient-mark-mode) transient-mark-mode mark-active)
(setq begin-region (region-beginning)
end-region (region-end))
(setq begin-region (point-min)
end-region (point-max)))
(unless output-buffer-name
(setq output-buffer-name markdown-output-buffer-name))
(if markdown-command-needs-filename
;; Handle case when `markdown-command' does not read from stdin
(if (not buffer-file-name)
(error "Must be visiting a file")
(shell-command (concat markdown-command " " buffer-file-name)
output-buffer-name))
;; Pass region to `markdown-command' via stdin
(shell-command-on-region begin-region end-region markdown-command
output-buffer-name))
;; Add header and footer and switch to html-mode.
(save-current-buffer
(set-buffer output-buffer-name)
(goto-char (point-min))
(unless (markdown-output-standalone-p)
(markdown-add-xhtml-header-and-footer title))
(html-mode))
;; Ensure buffer gets raised, even with short command output
(switch-to-buffer-other-window output-buffer-name)))
(defun markdown-output-standalone-p ()
"Determine whether `markdown-command' output is standalone XHTML.
Standalone XHTML output is identified by an occurrence of
`markdown-xhtml-standalone-regexp' in the first five lines of output."
(re-search-forward
markdown-xhtml-standalone-regexp
(save-excursion (goto-char (point-min)) (forward-line 4) (point))
t))
(defun markdown-add-xhtml-header-and-footer (title)
"Wrap XHTML header and footer with given TITLE around current buffer."
(insert "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
"\t\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\n"
"<head>\n<title>")
(insert title)
(insert "</title>\n")
(if (> (length markdown-css-path) 0)
(insert "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\""
markdown-css-path
"\" />\n"))
(when (> (length markdown-xhtml-header-content) 0)
(insert markdown-xhtml-header-content))
(insert "\n</head>\n\n"
"<body>\n\n")
(goto-char (point-max))
(insert "\n"
"</body>\n"
"</html>\n"))
(defun markdown-preview ()
"Run `markdown' on the current buffer and preview the output in a browser."
(interactive)
(markdown markdown-output-buffer-name)
(browse-url-of-buffer markdown-output-buffer-name))
(defun markdown-export-file-name ()
"Attempt to generate a filename for Markdown output.
If the current buffer is visiting a file, we construct a new
output filename based on that filename. Otherwise, return nil."
(when (buffer-file-name)
(concat (file-name-sans-extension (buffer-file-name)) ".html")))
(defun markdown-export ()
"Run Markdown on the current buffer, save to a file, and return the filename.
The resulting filename will be constructed using the current filename, but
with the extension removed and replaced with .html."
(interactive)
(let ((output-file (markdown-export-file-name))
(output-buffer-name))
(when output-file
(setq output-buffer-name (buffer-name (find-file-noselect output-file)))
(markdown output-buffer-name)
(with-current-buffer output-buffer-name
(save-buffer)
(kill-buffer-and-window))
output-file)))
(defun markdown-export-and-view ()
"Export to XHTML using `markdown-export' and browse the resulting file."
(interactive)
(browse-url (markdown-export)))
;;; WikiLink Following/Markup =================================================
(require 'thingatpt)
(defun markdown-wiki-link-p ()
"Return non-nil when `point' is at a true wiki link.
A true wiki link name matches `markdown-regex-wiki-link' but does not
match the current file name after conversion. This modifies the data
returned by `match-data'. Note that the potential wiki link name must
be available via `match-string'."
(let ((case-fold-search nil))
(and (thing-at-point-looking-at markdown-regex-wiki-link)
(or (not buffer-file-name)
(not (string-equal (buffer-file-name)
(markdown-convert-wiki-link-to-filename
(match-string 1)))))
(not (save-match-data
(save-excursion))))))
(defun markdown-convert-wiki-link-to-filename (name)
"Generate a filename from the wiki link NAME.
Spaces are converted to underscores, following the convention
used by the Python Markdown WikiLinks extension."
(let ((new-ext (file-name-extension (buffer-file-name)))
(new-basename (replace-regexp-in-string "[[:space:]\n]" "_" name)))
(concat new-basename "." new-ext)))
(defun markdown-follow-wiki-link (name)
"Follow the wiki link NAME.
Convert the name to a file name and call `find-file'. Ensure that
the new buffer remains in `markdown-mode'."
(let ((filename (markdown-convert-wiki-link-to-filename name)))
(find-file filename))
(markdown-mode))
(defun markdown-follow-wiki-link-at-point ()
"Find Wiki Link at point.
See `markdown-wiki-link-p' and `markdown-follow-wiki-link'."
(interactive)
(if (markdown-wiki-link-p)
(markdown-follow-wiki-link (match-string 1))
(error "Point is not at a Wiki Link")))
(defun markdown-next-wiki-link ()
"Jump to next wiki link.
See `markdown-wiki-link-p'."
(interactive)
(if (markdown-wiki-link-p)
; At a wiki link already, move past it.
(goto-char (+ 1 (match-end 0))))
(save-match-data
; Search for the next wiki link and move to the beginning.
(re-search-forward markdown-regex-wiki-link nil t)
(goto-char (match-beginning 0))))
(defun markdown-previous-wiki-link ()
"Jump to previous wiki link.
See `markdown-wiki-link-p'."
(interactive)
(re-search-backward markdown-regex-wiki-link nil t))
(defun markdown-highlight-wiki-link (from to face)
"Highlight the wiki link in the region between FROM and TO using FACE."
(let ((ov (make-overlay from to)))
(overlay-put ov 'face face)))
(defun markdown-unfontify-region-wiki-links (from to)
"Remove wiki link faces from the region specified by FROM and TO."
(interactive "nfrom: \nnto: ")
(remove-overlays from to 'face markdown-link-face)
(remove-overlays from to 'face markdown-missing-link-face))
(defun markdown-fontify-region-wiki-links (from to)
"Search region given by FROM and TO for wiki links and fontify them.
If a wiki link is found check to see if the backing file exists
and highlight accordingly."
(goto-char from)
(while (re-search-forward markdown-regex-wiki-link to t)
(let ((highlight-beginning (match-beginning 0))
(highlight-end (match-end 0))
(file-name
(markdown-convert-wiki-link-to-filename (match-string 1))))
(if (file-exists-p file-name)
(markdown-highlight-wiki-link
highlight-beginning highlight-end markdown-link-face)
(markdown-highlight-wiki-link
highlight-beginning highlight-end markdown-missing-link-face)))))
(defun markdown-extend-changed-region (from to)
"Extend region given by FROM and TO so that we can fontify all links.
The region is extended to the first newline before and the first
newline after."
;; start looking for the first new line before 'from
(goto-char from)
(re-search-backward "\n" nil t)
(let ((new-from (point-min))
(new-to (point-max)))
(if (not (= (point) from))
(setq new-from (point)))
;; do the same thing for the first new line after 'to
(goto-char to)
(re-search-forward "\n" nil t)
(if (not (= (point) to))
(setq new-to (point)))
(list new-from new-to)))
(defun markdown-check-change-for-wiki-link (from to change)
"Check region between FROM and TO for wiki links and re-fontfy as needed.
Designed to be used with the `after-change-functions' hook.
CHANGE is the number of bytes of pre-change text replaced by the
given range."
(interactive "nfrom: \nnto: \nnchange: ")
(let* ((inhibit-point-motion-hooks t)
(inhibit-quit t)
(modified (buffer-modified-p))
(buffer-undo-list t)
(inhibit-read-only t)
(inhibit-point-motion-hooks t)
(inhibit-modification-hooks t)
(current-point (point))
deactivate-mark)
(unwind-protect
(save-restriction
;; Extend the region to fontify so that it starts
;; and ends at safe places.
(multiple-value-bind (new-from new-to)
(markdown-extend-changed-region from to)
;; Unfontify existing fontification (start from scratch)
(markdown-unfontify-region-wiki-links new-from new-to)
;; Now do the fontification.
(markdown-fontify-region-wiki-links new-from new-to)))
(unless modified
(restore-buffer-modified-p nil)))
(goto-char current-point)))
(defun markdown-fontify-buffer-wiki-links ()
"Refontify all wiki links in the buffer."
(interactive)
(markdown-check-change-for-wiki-link (point-min) (point-max) 0))
;;; Miscellaneous =============================================================
(defun markdown-line-number-at-pos (&optional pos)
"Return (narrowed) buffer line number at position POS.
If POS is nil, use current buffer location.
This is an exact copy of `line-number-at-pos' for use in emacs21."
(let ((opoint (or pos (point))) start)
(save-excursion
(goto-char (point-min))
(setq start (point))
(goto-char opoint)
(forward-line 0)
(1+ (count-lines start (point))))))
(defun markdown-nobreak-p ()
"Return nil if it is acceptable to break the current line at the point."
;; inside in square brackets (e.g., link anchor text)
(looking-back "\\[[^]]*"))
;;; Mode definition ==========================================================
(defun markdown-show-version ()
"Show the version number in the minibuffer."
(interactive)
(message "markdown-mode, version %s" markdown-mode-version))
;;;###autoload
(define-derived-mode markdown-mode text-mode "Markdown"
"Major mode for editing Markdown files."
;; Natural Markdown tab width
(setq tab-width 4)
;; Comments
(make-local-variable 'comment-start)
(setq comment-start "<!-- ")
(make-local-variable 'comment-end)
(setq comment-end " -->")
(make-local-variable 'comment-start-skip)
(setq comment-start-skip "<!--[ \t]*")
(make-local-variable 'comment-column)
(setq comment-column 0)
;; Font lock.
(set (make-local-variable 'font-lock-defaults)
'(markdown-mode-font-lock-keywords))
(set (make-local-variable 'font-lock-multiline) t)
;; For menu support in XEmacs
(easy-menu-add markdown-mode-menu markdown-mode-map)
;; Make filling work with lists (unordered, ordered, and definition)
(set (make-local-variable 'paragraph-start)
"\f\\|[ \t]*$\\|^[ \t]*[*+-] \\|^[ \t*][0-9]+\\.\\|^[ \t]*: ")
;; Outline mode
(make-local-variable 'outline-regexp)
(setq outline-regexp "#+")
;; Cause use of ellipses for invisible text.
(add-to-invisibility-spec '(outline . t))
;; Indentation and filling
(make-local-variable 'fill-nobreak-predicate)
(add-hook 'fill-nobreak-predicate 'markdown-nobreak-p)
(setq indent-line-function markdown-indent-function)
;; Prepare hooks for XEmacs compatibility
(when (featurep 'xemacs)
(make-local-hook 'after-change-functions)
(make-local-hook 'font-lock-extend-region-functions)
(make-local-hook 'window-configuration-change-hook))
;; Multiline font lock
(add-hook 'font-lock-extend-region-functions
'markdown-font-lock-extend-region)
;; Anytime text changes make sure it gets fontified correctly
(add-hook 'after-change-functions 'markdown-check-change-for-wiki-link t t)
;; If we left the buffer there is a really good chance we were
;; creating one of the wiki link documents. Make sure we get
;; refontified when we come back.
(add-hook 'window-configuration-change-hook
'markdown-fontify-buffer-wiki-links t t)
;; do the initial link fontification
(markdown-fontify-buffer-wiki-links))
;(add-to-list 'auto-mode-alist '("\\.text$" . markdown-mode))
;;; GitHub Flavored Markdown Mode ============================================
(define-derived-mode gfm-mode markdown-mode "GFM"
"Major mode for editing GitHub Flavored Markdown files."
(auto-fill-mode 0)
(longlines-mode 1))
(provide 'markdown-mode)
;;; markdown-mode.el ends here
|