https://github.com/dnouri/md-ts-mode
A Emacs major mode for Markdown, powered by tree-sitter, works on Emacs 29+
https://github.com/dnouri/md-ts-mode
Last synced: 2 months ago
JSON representation
A Emacs major mode for Markdown, powered by tree-sitter, works on Emacs 29+
- Host: GitHub
- URL: https://github.com/dnouri/md-ts-mode
- Owner: dnouri
- License: gpl-3.0
- Created: 2026-03-01T01:50:54.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-02T23:05:59.000Z (3 months ago)
- Last Synced: 2026-03-03T00:35:50.581Z (3 months ago)
- Language: Emacs Lisp
- Size: 36.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
README
#+title: md-ts-mode
#+author: Daniel Nouri
~md-ts-mode~ is a tree-sitter-based major mode for editing Markdown.
It is derived from ~markdown-ts-mode~ in GNU Emacs 31 and works on
Emacs 29, 30, and 31.
** Features
- Syntax highlighting for headings (atx and setext), emphasis, strong
emphasis, strikethrough (=~~deleted~~=), code spans, links (inline,
full reference, collapsed reference, shortcut), images, block
quotes, list markers, and indented code blocks.
- Task list markers (=[ ]=, =[x]=) rendered as checkbox glyphs (☐, ☑).
- Thematic breaks (=---=, =***=, =___=) rendered as horizontal rules.
- Pipe tables with bold headers, subdued delimiter rows, and inline
markup (emphasis, code, links) fontified inside cells.
- HTML blocks highlighted with ~font-lock-doc-face~.
- Fenced code blocks fontified by the corresponding language grammar
when available.
- YAML and TOML front matter fontified when their grammars are
available.
- Hide-markup mode: ~md-ts-toggle-hide-markup~ hides delimiters
(=#=, =*=, =~=, brackets, URLs) so only content is visible.
- Imenu and ~outline-minor-mode~ integration via headings.
- Custom faces: ~md-ts-heading-1~ through ~md-ts-heading-6~,
~md-ts-setext-heading~, ~md-ts-delimiter~, ~md-ts-list-marker~,
~md-ts-block-quote~, ~md-ts-strikethrough~,
~md-ts-language-keyword~, and ~md-ts-task-list-marker~.
** Requirements
- GNU Emacs 29.1 or later, built with tree-sitter support.
- Tree-sitter grammars: ~markdown~ and ~markdown-inline~ from
[[https://github.com/tree-sitter-grammars/tree-sitter-markdown][tree-sitter-markdown]] (version 0.4.1 or compatible).
** Installation
Install from [[https://melpa.org/#/md-ts-mode][MELPA]]:
#+begin_src
M-x package-install RET md-ts-mode RET
#+end_src
Or with =use-package=:
#+begin_src emacs-lisp
(use-package md-ts-mode
:ensure t)
#+end_src
If you do not already have MELPA configured, add this to your init
file:
#+begin_src emacs-lisp
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
#+end_src
Installing or loading ~md-ts-mode~ does not change how Markdown files
are chosen globally. If you want ~md-ts-mode~ to handle Markdown
explicitly, add:
#+begin_src emacs-lisp
(md-ts-mode-enable-global)
#+end_src
The helper is autoloaded and idempotent. On Emacs 31 it also prefers
~md-ts-mode~ over the built-in ~markdown-ts-mode~.
*** Install tree-sitter grammars
After loading ~md-ts-mode~ once, install the Markdown grammars:
#+begin_example
M-x treesit-install-language-grammar RET markdown RET
M-x treesit-install-language-grammar RET markdown-inline RET
#+end_example
If only part of a file is highlighted, one of the grammars is probably
missing.
** Embedded code blocks
Fenced code blocks with a language tag (e.g., =```python=) are
parsed and fontified by the corresponding language grammar.
Install the grammar for any language you want highlighted:
#+begin_example
M-x treesit-install-language-grammar RET python RET
#+end_example
The variable ~md-ts-code-block-source-mode-map~ maps language
symbols to their tree-sitter major modes. The mode must be
installed and its grammar available for highlighting to work.
** Compatibility
The compatibility shims in ~md-ts-mode.el~ backport Emacs 31
tree-sitter range infrastructure to Emacs 29 and 30. This
includes replacements for ~treesit-range-fn-exclude-children~,
~treesit-query-range~ (with ~RANGE-FN~ argument),
~treesit-range-rules~ (with ~:range-fn~ keyword and function-form
~:embed~), ~treesit-update-ranges~, and
~treesit--update-ranges-local~.
On Emacs 29, additional shims provide ~treesit-node-children~,
~derived-mode-add-parents~, and wrappers for C functions whose
signatures changed in Emacs 30 (~treesit-parser-create~,
~treesit-parser-list~).
On Emacs 31 all guards are false and no shims are defined.
Tested on Emacs 29.4, 30.1, and development snapshot (31).
** Acknowledgments
Based on ~markdown-ts-mode~ from GNU Emacs 31, originally written
by Rahul Martim Juliato.