Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 1470 lines (1255 sloc) 52.2 KB
;;; markdown-mode.el --- Major mode to edit Markdown files in Emacs
;; Copyright (C) 2007, 2008, 2009 Jason Blevins
;; Version: 1.7
;; Keywords: Markdown major mode
;; Author: Jason Blevins <>
;; URL:
;; 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
;; 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., 675 Mass Ave, Cambridge, MA 02139, 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]:
;; The latest stable version is markdown-mode 1.7, released on October 1, 2009:
;; * [markdown-mode.el][]
;; * [Screenshot][]
;; * [Release notes][]
;; markdown-mode is also available in the Debian
;; [emacs-goodies-el](
;; package (beginning with revision 27.0-1) and the OpenBSD
;; [textproc/markdown-mode]( package.
;; [markdown-mode.el]:
;; [screenshot]:
;; [release notes]:
;; 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
;; <>. The entire repository,
;; including the full project history, can be cloned via the Git protocol
;; by running
;; git clone git://
;; [devel.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`).
;; * `markdown-hr-length` - the length of horizontal rules
;; (default: `5`).
;; * `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-uri-types` - a list of protocols for URIs that
;; `markdown-mode' should highlight.
;; * `markdown-enable-math` - syntax highlighting for
;; LaTeX fragments (default: `nil`).
;; 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 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 commands
;; `C-c -` inserts a horizontal rule.
;; 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 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.
;; [SmartyPants][] support is possible by customizing `markdown-command`.
;; If you install `` 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]:
;; 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.
;;; Acknowledgments:
;; markdown-mode has benefited greatly from the efforts of the
;; following people:
;; * Cyril Brulebois <> for Debian packaging.
;; * Conal Elliott <> for a font-lock regexp patch.
;; * Edward O'Connor <> for a font-lock regexp fix.
;; * Greg Bognar <> for menus and a patch.
;; * Daniel Burrows <> for filing Debian bug #456592.
;; * Peter S. Galbraith <> for maintaining emacs-goodies-el.
;; * Dmitry Dzhus <> for reference checking functions.
;; * Bryan Kyle <> for indentation code.
;; * intrigeri <> for face customizations.
;; * Ankit Solanki <> for longlines.el compatibility.
;; * Hilko Bengen <> for proper XHTML output.
;; * Jose A. Ortega Ruiz <> for Emacs 23 fixes.
;; * Alec Resnick <> for bug reports.
;; * Peter Williams <> for fill-paragraph enhancements.
;;; Bugs:
;; Although markdown-mode is developed and tested primarily using
;; GNU Emacs 23, compatibility with GNU Emacs 21 and 22 is also a
;; priority.
;; markdown-mode's syntax highlighting is accomplished using the
;; search-based fontification features of Emacs through a series of
;; regular expressions. Unfortunately, Emacs has trouble highlighting
;; multi-line constructs using regular expressions and this creates
;; several syntax-highlighting quirks such as mistaking indented
;; lists for preformatted text, etc. Making markdown-mode's syntax
;; highlighting more robust through the use of matching functions
;; or syntactic font lock is a high-priority item for future work.
;; If you find any bugs not mentioned here, please construct a test
;; case and/or a patch and email me at <>.
;;; 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][]
;; * 2008-10-01: [Version 1.7][]
;; [Version 1.3]:
;; [Version 1.5]:
;; [Version 1.6]:
;; [Version 1.7]:
;;; Code:
(require 'easymenu)
(require 'outline)
;;; Customizable variables ====================================================
;; Current revision
(defconst markdown-mode-version "1.7-dev")
;; A hook for users to run their own code when the mode is loaded.
(defvar markdown-mode-hook nil)
;;; Customizable variables ====================================================
(defgroup markdown nil
"Major mode for editing text files in Markdown format."
:prefix "markdown-"
:group 'wp
:link '(url-link ""))
(defcustom markdown-command "markdown"
"Command to run markdown."
:group 'markdown
:type 'string)
(defcustom markdown-hr-length 5
"Length of horizonal rules."
:group 'markdown
:type 'integer)
(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-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 nil
"CSS file to include in the output XHTML"
:group 'markdown
:type 'string)
;;; 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-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-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
"Regular expression for level 1 setext-style (underline) headers.")
(defconst markdown-regex-header-2-setext
"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\\}\\)\\([^ \\]\\|[^ ].*?[^ \\]\\)\\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\\)*?[^\\ ]\\3\\)"
"Regular expression for matching bold text.")
(defconst markdown-regex-italic
"\\(^\\|[^\\]\\)\\(\\([*_]\\)\\([^ \\]\\3\\|[^ ]\\(.\\|\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.")
(defconst markdown-regex-uri
"\\(" (mapconcat 'identity markdown-uri-types "\\|")
"\\):[^]\t\n\r<>,;() ]+")
"Regular expression for matching inline URIs.")
(defconst markdown-regex-angle-uri
(mapconcat 'identity markdown-uri-types "\\|")
"Regular expression for matching inline URIs in angle brackets.")
(defconst markdown-regex-email
"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
"Regular expression for itex \[..\] display mode expressions.")
(defconst markdown-regex-list-indent
"^\\(\\s *\\)\\([0-9]+\\.\\|[\\*\\+-]\\)\\(\\s +\\)"
"Regular expression for matching indentation of list items.")
; From html-helper-mode
(defun markdown-match-comments (last)
"Matches 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 nil))))
(t nil)))
(defvar markdown-mode-font-lock-keywords-basic
'(markdown-match-comments 0 markdown-comment-face t t)
(cons markdown-regex-code '(2 markdown-inline-code-face))
(cons markdown-regex-pre 'markdown-pre-face)
(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)
(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-wiki-link 'markdown-link-face)
(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
;; 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
(if markdown-enable-math
"Default highlighting expressions for Markdown mode.")
;;; Syntax Table ==============================================================
(defvar markdown-mode-syntax-table
(let ((markdown-mode-syntax-table (make-syntax-table)))
(modify-syntax-entry ?\" "w" 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."
(let (hr)
(dotimes (count (- markdown-hr-length 1) hr) ; Count to n - 1
(setq hr (concat "* " hr))) ; Build HR string
(setq hr (concat hr "*\n")) ; Add the n-th *
(insert hr)))
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(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."
(if (and transient-mark-mode mark-active)
(let ((a (region-beginning))
(b (region-end))
(len 0)
(setq len (- b a))
(dotimes (count len hdr)
(setq hdr (concat "=" hdr))) ; Build a === title underline
(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."
(if (and transient-mark-mode mark-active)
(let ((a (region-beginning))
(b (region-end))
(len 0)
(setq len (- b a))
(dotimes (count len hdr)
(setq hdr (concat "-" hdr))) ; Build a --- section underline
(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."
(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
(let ((endpos end))
; Ensure that there is a leading blank line
(goto-char beg)
(while (not (looking-back "\n\n" 2))
(insert "\n")
(setq endpos (+ 1 endpos)))
; Insert blockquote characters
(while (< (point-at-bol) endpos)
(insert prefix)
(setq endpos (+ (length prefix) endpos))
; Move back before any blank lines at the end
(goto-char endpos)
(while (looking-back "\n" 1)
; Ensure one blank line at the end
(while (not (looking-at "\n\n"))
(insert "\n")
(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."
(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 ====================================================================
;;; Indentation functions contributed by Bryan Kyle <>..
(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-prev-line-indent-p ()
"Return t if the previous line is indented."
(forward-line -1)
(goto-char (point-at-bol))
(if (re-search-forward "^\\s " (point-at-eol) t) t)))
(defun markdown-prev-line-indent ()
"Return the number of leading whitespace characters in the previous line."
(forward-line -1)
(goto-char (point-at-bol))
(when (re-search-forward "^\\s +" (point-at-eol) t)
(defun markdown-prev-list-indent ()
"Return position of the first non-list-marker on the previous line."
(forward-line -1)
(goto-char (point-at-bol))
(when (re-search-forward markdown-regex-list-indent (point-at-eol) t)
(defun markdown-indent-line ()
"Indent the current line using some heuristics."
(if (markdown-prev-line-indent-p)
;; If the current column is any of the positions, remove all
;; of the positions up-to and including the current column
(current-column) (markdown-calc-indents)))))
(defun markdown-calc-indents ()
"Return a list of indentation columns to cycle through."
(let (pos
;; Previous line indent
(setq prev-line-pos (markdown-prev-line-indent))
(setq positions (cons prev-line-pos positions))
;; Previous non-list-marker indent
(setq positions (cons (markdown-prev-list-indent) positions))
;; Indentation of the previous line + tab-width
(setq positions (cons (+ prev-line-pos tab-width) positions)))
(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
(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)))))
(if pos
(setq positions (cons pos positions)))
;; First column
(setq positions (cons 0 (reverse positions)))
(defun markdown-enter-key ()
"Insert a newline and optionally indent the next line."
(if markdown-indent-on-enter
(funcall indent-line-function)))
;;; Keymap ====================================================================
(defvar markdown-mode-map
(let ((markdown-mode-map (make-keymap)))
;; Element insertion
(define-key markdown-mode-map "\C-c\C-al" 'markdown-insert-link)
(define-key markdown-mode-map "\C-c\C-aw" 'markdown-insert-wiki-link)
(define-key markdown-mode-map "\C-c\C-ii" 'markdown-insert-image)
(define-key markdown-mode-map "\C-c\C-t1" 'markdown-insert-header-1)
(define-key markdown-mode-map "\C-c\C-t2" 'markdown-insert-header-2)
(define-key markdown-mode-map "\C-c\C-t3" 'markdown-insert-header-3)
(define-key markdown-mode-map "\C-c\C-t4" 'markdown-insert-header-4)
(define-key markdown-mode-map "\C-c\C-t5" 'markdown-insert-header-5)
(define-key markdown-mode-map "\C-c\C-t6" 'markdown-insert-header-6)
(define-key markdown-mode-map "\C-c\C-pb" 'markdown-insert-bold)
(define-key markdown-mode-map "\C-c\C-ss" 'markdown-insert-bold)
(define-key markdown-mode-map "\C-c\C-pi" 'markdown-insert-italic)
(define-key markdown-mode-map "\C-c\C-se" 'markdown-insert-italic)
(define-key markdown-mode-map "\C-c\C-pf" 'markdown-insert-code)
(define-key markdown-mode-map "\C-c\C-sc" 'markdown-insert-code)
(define-key markdown-mode-map "\C-c\C-sb" 'markdown-insert-blockquote)
(define-key markdown-mode-map "\C-c\C-s\C-b" 'markdown-blockquote-region)
(define-key markdown-mode-map "\C-c\C-sp" 'markdown-insert-pre)
(define-key markdown-mode-map "\C-c\C-s\C-p" 'markdown-pre-region)
(define-key markdown-mode-map "\C-c-" 'markdown-insert-hr)
(define-key markdown-mode-map "\C-c\C-tt" 'markdown-insert-title)
(define-key markdown-mode-map "\C-c\C-ts" 'markdown-insert-section)
;; Indentation
(define-key markdown-mode-map "\C-m" 'markdown-enter-key)
;; Visibility cycling
(define-key markdown-mode-map (kbd "<tab>") 'markdown-cycle)
(define-key markdown-mode-map (kbd "<S-iso-lefttab>") 'markdown-shifttab)
;; Markdown functions
(define-key markdown-mode-map "\C-c\C-cm" 'markdown)
(define-key markdown-mode-map "\C-c\C-cp" 'markdown-preview)
;; References
(define-key markdown-mode-map "\C-c\C-cc" 'markdown-check-refs)
"Keymap for Markdown major mode.")
;;; Menu ==================================================================
(easy-menu-define markdown-mode-menu markdown-mode-map
"Menu for Markdown mode"
["Cycle visibility" markdown-cycle (outline-on-heading-p)]
["Cycle global visibility" markdown-shifttab])
["Compile" markdown]
["Preview" markdown-preview]
("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 <>.
(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)))
(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 .
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))
(goto-char (point-min))
(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))))))))))
(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))
(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)
(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))
(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
(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)
(when (not silent)
(message "No undefined references found"))
(kill-buffer refbuf))
(with-current-buffer refbuf
(when view-mode
(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-backward-char 1)
(insert ")")
(view-buffer-other-window refbuf)
(goto-line 4)))))
;;; 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
(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)
(if (memq (preceding-char) '(?\n ?\^M))
;; Go to end of line before heading
(forward-char -1)
(if (memq (preceding-char) '(?\n ?\^M))
;; leave blank line before heading
(forward-char -1)))))
;; 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")
((eq arg t) ;; Global cycling
((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
(message "SHOW ALL")
(setq markdown-cycle-global-status 1))
;; Defaults to overview
(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
(let ((goal-column 0) eoh eol eos)
;; Determine boundaries
(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'
((= 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
(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.
(message "SUBTREE")
(setq markdown-cycle-subtree-status 'subtree))
;; Default action: hide the subtree.
(message "FOLDED")
(setq markdown-cycle-subtree-status 'folded)))))
(message "TAB")
(funcall indent-line-function))))
;; Based on org-shifttab from org.el.
(defun markdown-shifttab ()
"Global visibility cycling.
Calls `markdown-cycle' with argument t."
(markdown-cycle t))
;;; Commands ==================================================================
(defun markdown ()
"Run markdown on the current buffer and preview the output in another buffer."
(if (and (boundp 'transient-mark-mode) transient-mark-mode mark-active)
(shell-command-on-region (region-beginning) (region-end) markdown-command
"*markdown-output*" nil)
(shell-command-on-region (point-min) (point-max) markdown-command
"*markdown-output*" nil))
(let (title)
(setq title (buffer-name))
(set-buffer "*markdown-output*")
(goto-char (point-min))
(insert "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
"<html xmlns=\"\">\n\n"
(insert title)
(insert "</title>\n")
(if markdown-css-path
(insert "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\""
"\" />\n"))
(insert "</head>\n\n"
(goto-char (point-max))
(insert "\n"
(defun markdown-preview ()
"Run markdown on the current buffer and preview the output in a browser."
(browse-url-of-buffer "*markdown-output*"))
;;; 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)
(goto-char (point-min))
(setq start (point))
(goto-char opoint)
(forward-line 0)
(1+ (count-lines start (point))))))
(defun markdown-nobreak-p ()
"Returns nil if it is ok for fill-paragraph to insert a line
break at point"
;; are we inside in square brackets
(looking-back "\\[[^]]*"))
;;; Mode definition ==========================================================
(defun markdown-show-version ()
"Show the version number in the minibuffer."
(message "markdown-mode, version %s" markdown-mode-version))
(define-derived-mode markdown-mode text-mode "Markdown"
"Major mode for editing Markdown files."
;; 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)
(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))
;(add-to-list 'auto-mode-alist '("\\.text$" . markdown-mode))
(provide 'markdown-mode)
;;; markdown-mode.el ends here