1
0
Fork 0
dot/.config/vis/lexers/markdown.lua

129 lines
4.1 KiB
Lua
Raw Normal View History

2024-07-31 12:05:39 +02:00
-- Adapted from Markdown LPeg lexer by Mitchell.
-- See <https://git.sr.ht/~martanne/vis>.
local lexer = lexer
local P, S, B = lpeg.P, lpeg.S, lpeg.B
local lex = lexer.new(..., {no_user_word_lists = true})
local md = '.markdown'
local ws = lex:get_rule('whitespace')
local nl = P('\n')
local bl = nl * nl
local nws = lexer.any - lexer.space
-- Heading.
lex:add_rule('heading_delimiter',
lex:tag('delimiter' .. md,
lexer.starts_line(P('#')^-6 * P(' '))))
lex:add_rule('heading',
lex:tag(lexer.HEADING .. md,
(
B(bl * P('# ')) +
B(bl * P('## ')) +
B(bl * P('### ')) +
B(bl * P('#### ')) +
B(bl * P('##### ')) +
B(bl * P('###### '))
) *
lexer.to_eol(nws) *
#bl))
-- Blockquote.
lex:add_rule('blockquote_delimiter',
lex:tag('delimiter' .. md,
lexer.starts_line(P('> ')^1)))
-- Horizontal rule.
lex:add_rule('hr',
lex:tag('hr' .. md, B(bl) * S('*-_')^3 * #bl))
-- Native divs.
lex:add_rule("native_div_delimiter",
lex:tag('delimiter' .. md,
lexer.starts_line(lexer.to_eol(':::'))))
-- Code block.
local code_line = lexer.starts_line(
(B(' ') + B('\t')) * lexer.to_eol(), true)
local code_block = lexer.range(lexer.starts_line('```', true), lexer.starts_line(P('```'))) + lexer.range(lexer.starts_line('~~~', true), lexer.starts_line(P('~~~')))
local code_inline = lpeg.Cmt(
lpeg.C(P('`')^1), function(input, index, bt)
local _, e = input:find('[^`]' .. bt .. '%f[^`]', index)
return (e or #input) + 1
end)
lex:add_rule('block_code', lex:tag(lexer.CODE .. md, code_line + code_block))
-- Escape.
lex:add_rule('escape', lex:tag(lexer.DEFAULT, P('\\') * 1))
-- Bracket.
lex:add_rule('brackets',
lex:tag('delimiter' .. md, S('[]')))
-- Footnote.
lex:add_rule('footnote_key',
lex:tag(lexer.REFERENCE .. md,
(P('^') * #P('[')) +
(B('[') * P('^') * (lexer.any - lexer.space - S('^[]'))^1 * #P(']'))))
lex:add_rule('cite_key',
lex:tag(lexer.REFERENCE .. md,
B(lexer.space + P('[')) * P('-')^-1 * P('@') * (lexer.alnum + P('_')) * (lexer.alnum + S(':.#$%&-+?<>~/'))^0 +
B(lexer.space + P('[')) * P('-')^-1 * P('@') * lexer.range('{', '}')))
-- Link.
lex:add_rule('link_text',
lex:tag(lexer.LINK .. md,
B('[') * (lexer.any - P(']'))^1 * #P(P(']') * lexer.range('(', ')'))))
lex:add_rule('link_target',
lex:tag('delimiter' .. md, B(']') * lexer.range('(', ')')))
lex:add_rule('link_ref',
lex:tag('delimiter' .. md, B(']') * P(':') * #lexer.space))
-- Image
lex:add_rule('image_bang', lex:tag(lexer.REFERENCE .. md, P('!') * #P('[')))
-- Attribute.
lex:add_rule('attribute',
lex:tag('delimiter' .. md, B(S('])')) * lexer.range('{', '}')))
local punct_space = lexer.punct + lexer.space
-- Handles flanking delimiters as described in
-- https://github.github.com/gfm/#emphasis-and-strong-emphasis in the cases
-- where simple delimited ranges are not sufficient.
local function flanked_range(s, not_inword)
local fl_char = lexer.any - s - lexer.space
local left_fl = B(punct_space - s) * s * #fl_char + s * #(fl_char - lexer.punct)
local right_fl = B(lexer.punct) * s * #(punct_space - s) + B(fl_char) * s
return left_fl * (lexer.any - bl - (not_inword and s * #punct_space or s))^0 * right_fl
end
local asterisk_strong = flanked_range('**')
local underscore_strong = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('__', true) * #(punct_space + -1)
lex:add_rule('strong', lex:tag(lexer.BOLD, asterisk_strong + underscore_strong))
local asterisk_em = flanked_range('*')
local underscore_em = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('_', true) * #(punct_space + -1)
lex:add_rule('em', lex:tag(lexer.ITALIC, asterisk_em + underscore_em))
-- Embedded HTML.
local html = lexer.load('html')
local html_start_rule = lexer.starts_line(P(' ')^-3) *
#P('<') *
html:get_rule('tag') +
html:get_rule('comment')
local html_end_rule = #bl * ws
lex:embed(html, html_start_rule, html_end_rule)
-- Embedded YAML.
local yaml = lexer.load('yaml')
local yaml_start_rule = #lexer.starts_line(P('---'), false) * yaml:get_rule('doc_bounds')
local yaml_end_rule = #lexer.starts_line(P('...'), false) * yaml:get_rule('doc_bounds')
lex:embed(yaml, yaml_start_rule, yaml_end_rule)
return lex