logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

markdown.lua (4284B)


  1. -- Copyright 2006-2024 Mitchell. See LICENSE.
  2. -- Markdown LPeg lexer.
  3. local lexer = lexer
  4. local P, S, B = lpeg.P, lpeg.S, lpeg.B
  5. local lex = lexer.new(..., {no_user_word_lists = true})
  6. -- Distinguish between horizontal and vertical space so html start rule has a chance to match.
  7. lex:modify_rule('whitespace', lex:tag(lexer.WHITESPACE, S(' \t')^1 + S('\r\n')^1))
  8. -- Block elements.
  9. local function h(n)
  10. return lex:tag(string.format('%s.h%s', lexer.HEADING, n),
  11. lexer.to_eol(lexer.starts_line(string.rep('#', n))))
  12. end
  13. lex:add_rule('header', h(6) + h(5) + h(4) + h(3) + h(2) + h(1))
  14. lex:add_rule('hr',
  15. lex:tag('hr', lpeg.Cmt(lexer.starts_line(lpeg.C(S('*-_')), true), function(input, index, c)
  16. local line = input:match('[^\r\n]*', index):gsub('[ \t]', '')
  17. if line:find('[^' .. c .. ']') or #line < 2 then return nil end
  18. return (select(2, input:find('\r?\n', index)) or #input) + 1 -- include \n for eolfilled styles
  19. end)))
  20. lex:add_rule('list', lex:tag(lexer.LIST,
  21. lexer.starts_line(lexer.digit^1 * '.' + S('*+-'), true) * S(' \t')))
  22. local hspace = lexer.space - '\n'
  23. local blank_line = '\n' * hspace^0 * ('\n' + P(-1))
  24. local code_line = lexer.starts_line((B(' ') + B('\t')) * lexer.to_eol(), true)
  25. local code_block =
  26. lexer.range(lexer.starts_line('```', true), '\n```' * hspace^0 * ('\n' + P(-1))) +
  27. lexer.range(lexer.starts_line('~~~', true), '\n~~~' * hspace^0 * ('\n' + P(-1)))
  28. local code_inline = lpeg.Cmt(lpeg.C(P('`')^1), function(input, index, bt)
  29. -- `foo`, ``foo``, ``foo`bar``, `foo``bar` are all allowed.
  30. local _, e = input:find('[^`]' .. bt .. '%f[^`]', index)
  31. return (e or #input) + 1
  32. end)
  33. lex:add_rule('block_code', lex:tag(lexer.CODE, code_line + code_block + code_inline))
  34. lex:add_rule('blockquote',
  35. lex:tag(lexer.STRING, lpeg.Cmt(lexer.starts_line('>', true), function(input, index)
  36. local _, e = input:find('\n[ \t]*\r?\n', index) -- the next blank line (possibly with indentation)
  37. return (e or #input) + 1
  38. end)))
  39. -- Span elements.
  40. lex:add_rule('escape', lex:tag(lexer.DEFAULT, P('\\') * 1))
  41. local link_text = lexer.range('[', ']', true)
  42. local link_target =
  43. '(' * (lexer.any - S(') \t'))^0 * (S(' \t')^1 * lexer.range('"', false, false))^-1 * ')'
  44. local link_url = 'http' * P('s')^-1 * '://' * (lexer.any - lexer.space)^1 +
  45. ('<' * lexer.alpha^2 * ':' * (lexer.any - lexer.space - '>')^1 * '>')
  46. lex:add_rule('link', lex:tag(lexer.LINK, P('!')^-1 * link_text * link_target + link_url))
  47. local link_ref = lex:tag(lexer.REFERENCE, link_text * S(' \t')^0 * lexer.range('[', ']', true))
  48. local ref_link_label = lex:tag(lexer.REFERENCE, lexer.range('[', ']', true) * ':')
  49. local ws = lex:get_rule('whitespace')
  50. local ref_link_url = lex:tag(lexer.LINK, (lexer.any - lexer.space)^1)
  51. local ref_link_title = lex:tag(lexer.STRING, lexer.range('"', true, false) +
  52. lexer.range("'", true, false) + lexer.range('(', ')', true))
  53. lex:add_rule('link_ref', link_ref + ref_link_label * ws * ref_link_url * (ws * ref_link_title)^-1)
  54. local punct_space = lexer.punct + lexer.space
  55. -- Handles flanking delimiters as described in
  56. -- https://github.github.com/gfm/#emphasis-and-strong-emphasis in the cases where simple
  57. -- delimited ranges are not sufficient.
  58. local function flanked_range(s, not_inword)
  59. local fl_char = lexer.any - s - lexer.space
  60. local left_fl = B(punct_space - s) * s * #fl_char + s * #(fl_char - lexer.punct)
  61. local right_fl = B(lexer.punct) * s * #(punct_space - s) + B(fl_char) * s
  62. return left_fl * (lexer.any - blank_line - (not_inword and s * #punct_space or s))^0 * right_fl
  63. end
  64. local asterisk_strong = flanked_range('**')
  65. local underscore_strong = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('__', true) *
  66. #(punct_space + -1)
  67. lex:add_rule('strong', lex:tag(lexer.BOLD, asterisk_strong + underscore_strong))
  68. local asterisk_em = flanked_range('*')
  69. local underscore_em = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('_', true) *
  70. #(punct_space + -1)
  71. lex:add_rule('em', lex:tag(lexer.ITALIC, asterisk_em + underscore_em))
  72. -- Embedded HTML.
  73. local html = lexer.load('html')
  74. local start_rule = lexer.starts_line(P(' ')^-3) * #P('<') * html:get_rule('tag') -- P(' ')^4 starts code_line
  75. local end_rule = #blank_line * ws
  76. lex:embed(html, start_rule, end_rule)
  77. return lex