Skip to content

gbudiman/mdx-tex

Repository files navigation

mdx-tex

Converts between Markdown (MDX) and Textile, for those of you unfortunate enough to have to maintain a legacy system that relies on Textile.

Pure Ruby, no runtime dependencies.

Installation

Add to your Gemfile:

gem 'mdx-tex'
bundle install

Or install directly:

gem install mdx-tex

Usage

Markdown -> Textile

require 'mdx_tex'

MdxTex.to_textile(markdown: '# Hello **world**')
# => "h3. Hello *world*"

# Override options per call
MdxTex.to_textile(markdown: '# Hello', header_level: 'h1')
# => "h1. Hello"

# Or set them globally
MdxTex.configure do |config|
  config.header_level = 'h2'
  config.list_depth = 1
end

# Per-call options take precedence over global config
MdxTex.to_textile(markdown: '- item', list_depth: 2)

Textile -> Markdown

MdxTex.to_markdown(textile: 'h3. Hello *world*')
# => "### Hello **world**"

Unlike to_textile, to_markdown takes no options. Heading level maps directly (h1 -> #, h2 -> ##, ..., h6 -> ######), the list base depth is auto-detected per document, and ordered list numbers are computed from per-depth counters that reset on blank or interrupting lines.

String Extension

Adds to_textile and to_markdown directly to String. Off by default.

Any Ruby app:

require 'mdx_tex/core_ext/string'

'# Hello **world**'.to_textile
# => "h3. Hello *world*"

'- item'.to_textile(list_depth: 1)
# => "* item"

'h3. Hello *world*'.to_markdown
# => "### Hello **world**"

Or load it through config:

MdxTex.configure do |config|
  config.enable_string_extension = true
end
MdxTex.load_string_extension!

Rails (config/initializers/mdx_tex.rb):

MdxTex.configure do |config|
  config.enable_string_extension = true
end

The Railtie loads this automatically after initialization.

Supported Syntaxes

Headers

Markdown -> Textile converts all headings (# through ######) to the same Textile tag. The number of # doesn't matter; header_level controls which tag to use (default: h3).

Markdown header_level Textile
# Title h3 (default) h3. Title
## Title h3 (default) h3. Title
###### Title h3 (default) h3. Title
# Title h1 h1. Title
### Title h1 h1. Title

Requires a space after #. #NoSpace won't convert. Heading hierarchy is not preserved (## and ### both produce the same tag) — we plan to fix this in a future version.

Textile -> Markdown maps the Textile tag directly to the matching number of #s.

Textile Markdown
h1. Title # Title
h3. Title ### Title
h6. Title ###### Title

Requires a space after hN.. h3.NoSpace won't convert.

Bold

Markdown -> Textile turns **text** and __text__ into *text*. Multiple spans and mixed delimiters work on the same line.

Markdown Textile
**hello** *hello*
__hello__ *hello*
**a** and **b** *a* and *b*
**a** and __b__ *a* and *b*

Textile -> Markdown turns *text* into **text**. Whitespace-padded asterisks (* text *) are not bold per Textile rules and are left unchanged.

Textile Markdown
*hello* **hello**
*a* and *b* **a** and **b**
* hello * * hello *

Unordered Lists

Markdown -> Textile converts - item to * item. Nesting uses 2-space indentation. list_depth controls the base asterisk count (default: 3).

Markdown list_depth Textile
- Item 1 * Item
- Item 3 *** Item
- Nested 3 **** Nested
- Deep 3 ***** Deep

Textile -> Markdown auto-detects the depth-1 base per document (the smallest leading-asterisk run anywhere in the input) and emits Markdown with 2-space indentation per nesting level. This makes the conversion robust to input authored at different base depths.

Textile (input) Markdown (output)
* item
** nested
- item
- nested
*** item
**** nested
- item
- nested
*** a
***** deep
- a
- deep

Ordered Lists

Markdown -> Textile converts 1. item to # item. The actual number is ignored. Nesting uses 2-space indentation.

Markdown Textile
1. First # First
99. Any number # Any number
1. Nested ## Nested
1. Deep ### Deep

Textile -> Markdown auto-detects the depth-1 base (smallest leading-# run) and emits incrementing numbers per depth. Counters reset on any non-ordered-list line (blank line, plain text, header, unordered item), so each separate list starts at 1. again.

Textile (input) Markdown (output)
# a
# b
# c
1. a
2. b
3. c
# a
## x
## y
# b
1. a
1. x
2. y
2. b
# a
# b
(blank)
# c
1. a
2. b
(blank)
1. c

Not Supported

The following syntax passes through as-is in both directions (no conversion):

  • Italic (*text* / _text_ in Markdown; _text_ in Textile)
  • Strikethrough (~~text~~ / -text-)
  • Links ([text](url) / "text":url)
  • Images (![alt](url) / !url!)
  • Inline code (`code`) and code blocks
  • Blockquotes (> / bq.)
  • Tables
  • Horizontal rules (---)
  • Task lists (- [ ], - [x])
  • Textile-specific syntax: attributes (h3{color:red}.), block modifiers other than hN., footnotes, acronyms

Configuration

Configuration applies to to_textile only. to_markdown auto-detects everything from the input and takes no options.

Option Type Valid Values Default Description
header_level String h1..h6 h3 Textile heading tag
list_depth Integer Positive integer 3 Base asterisk count for unordered lists

Bad values raise InvalidHeaderLevelError or InvalidListDepthError.

Full Example

Markdown input:

### **Title**

- item one
- **item two**
  - nested with **bold**

1. **first**
2. second

After MdxTex.to_textile (default config):

h3. *Title*

*** item one
*** *item two*
**** nested with *bold*

# *first*
# second

After feeding that back through MdxTex.to_markdown:

### **Title**

- item one
- **item two**
  - nested with **bold**

1. **first**
2. second

The round-trip is stable for canonical inputs: headers at the configured header_level, bold using ** (not __), unordered lists at depth 1 matching list_depth, and ordered list numbers already incrementing per depth.

Development

git clone https://github.com/gbudiman/mdx-tex.git
cd mdx-tex
bundle install

Git Hooks

Uses overcommit for pre-commit RuboCop. After cloning:

bundle exec overcommit --install
bundle exec overcommit --sign

Tests

bundle exec rspec

Lint

bundle exec rubocop

Releasing

bin/release patch   # 0.1.10 → 0.1.11
bin/release minor   # 0.1.10 → 0.2.0
bin/release major   # 0.1.10 → 1.0.0

Runs specs, bumps the version, commits, tags, and pushes. GitHub Actions handles the RubyGems publish.

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors