Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rtl.nvim

Persian/Arabic text is stored and edited in logical order in your buffer (normal, editable, searchable — nothing about the file changes). The problem is purely display: terminals lay out codepoints left-to-right in fixed cells, so Persian letters show up disconnected and in the wrong order.

rtl.nvim draws a corrected overlay on top of Persian text using Neovim extmarks:

  • Shaping — picks the contextually correct glyph form (isolated / initial / medial / final) for each letter, using the standard Arabic joining rules and the Unicode Presentation Forms blocks.
  • Reordering — reverses word/letter order within each detected Persian clause so it reads right-to-left, while embedded numbers keep their own left-to-right digit order and any English text is left untouched. Half-space (ZWNJ / U+200C) is handled transparently — letters on each side are shaped independently.

Your buffer content is never modified — only what's drawn on screen.

Requirements

  • Neovim ≥ 0.9 (uses virt_text_pos = "overlay" extmarks)
  • A terminal font that includes glyphs for the Arabic Presentation Forms-A/B Unicode blocks (U+FB50–FDFF, U+FE70–FEFF) — e.g. Vazirmatn, Noto Naskh Arabic, Amiri. If your font is Latin-only, the overlay will show blank boxes/tofu even though the logic is correct — check this first.

Install

{
  "mavomen/rtl.nvim",
  config = function()
    require("rtl").setup({
      auto_filetypes = { "markdown", "text" }, -- optional auto-enable
    })
  end,
}

Usage

:RtlEnable     " turn on overlay for current buffer
:RtlDisable    " turn off
:RtlToggle     " toggle

Or from Lua: require("rtl").enable(), .disable(), .toggle(), .is_enabled().

:help rtl for full documentation.

Configuration

require("rtl").setup({
  auto_filetypes = { "markdown", "text" }, -- buffers to auto-enable on; {} = manual only
  highlight = { fg = "#89b4fa" },          -- default: { link = "String" }; any nvim_set_hl() opts
})

Known limitations (MVP)

  • Not a full UAX#9 bidi implementation. Reordering is done per detected "Persian clause" (a run of Persian letters/spaces/digits bounded by non-Arabic-script content). This handles the common case — a Persian sentence, possibly with embedded English words or numbers — correctly, but doesn't implement the full recursive embedding-level algorithm real bidi text needs for deeply nested mixed-direction text.
  • No ligatures (e.g. lam-alef لا isn't merged into a single ligature glyph) — kept intentionally 1-to-1 so overlay column alignment stays simple and exact.
  • No diacritics (tashkeel) handling yet — combining marks aren't currently treated as transparent for joining purposes.
  • Overlay alignment assumes your font renders presentation-form glyphs at the same cell width as the base letters (true for most Arabic-support monospace fonts, but worth a visual check).
  • Rendering is debounced at 40ms. Only the visible window is rendered for performance — scrolling or editing triggers a re-render of the newly visible lines.

Development

Tests cover the shaping, bidi, and joining modules:

lua tests/run.lua

No external dependencies — runs on plain Lua 5.x.

About

Persian & using nvim? c'mere, my man...

Resources

Stars

Watchers

Forks

Contributors

Languages