From cc8667004b2aee4247956e9ee6f4efe4b88769bc Mon Sep 17 00:00:00 2001 From: Felix Van der Jeugt Date: Fri, 4 Dec 2020 14:52:07 +0100 Subject: [PATCH] add a markdown lexer to vis --- config/vis/lexers/markdown.lua | 110 +++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 config/vis/lexers/markdown.lua diff --git a/config/vis/lexers/markdown.lua b/config/vis/lexers/markdown.lua new file mode 100644 index 0000000..50405fe --- /dev/null +++ b/config/vis/lexers/markdown.lua @@ -0,0 +1,110 @@ +-- Copyright 2006-2017 Mitchell mitchell.att.foicica.com. See LICENSE. +-- Markdown LPeg lexer. + +local l = require('lexer') +local token, word_match = l.token, l.word_match +local P, R, S = lpeg.P, lpeg.R, lpeg.S + +local M = {_NAME = 'markdown'} + +-- Whitespace. +local ws = token(l.WHITESPACE, l.space^1) + +-- Block elements. +local header = token('h6', l.starts_line('######') * l.nonnewline^0) + + token('h5', l.starts_line('#####') * l.nonnewline^0) + + token('h4', l.starts_line('####') * l.nonnewline^0) + + token('h3', l.starts_line('###') * l.nonnewline^0) + + token('h2', l.starts_line('##') * l.nonnewline^0) + + token('h1', l.starts_line('#') * l.nonnewline^0) + +local blockquote = token(l.STRING, + lpeg.Cmt(l.starts_line(S(' \t')^0 * '>'), + function(input, index) + local _, e = input:find('\n[ \t]*\r?\n', + index) + return (e or #input) + 1 + end)) + +local blockcode = token('code', l.starts_line(P(' ')^4 + P('\t')) * -P('<') * + l.nonnewline^0) + +local hr = token('hr', lpeg.Cmt(l.starts_line(S(' \t')^0 * lpeg.C(S('*-_'))), + function(input, index, c) + local line = input:match('[^\n]*', index) + line = line:gsub('[ \t]', '') + if line:find('[^'..c..']') or #line < 2 then + return nil + end + return (input:find('\n', index) or #input) + 1 + end)) + +-- Span elements. +local dq_str = token(l.STRING, l.delimited_range('"', false, true)) +local sq_str = token(l.STRING, l.delimited_range("'", false, true)) +local paren_str = token(l.STRING, l.delimited_range('()')) +local link = token('link', P('!')^-1 * l.delimited_range('[]') * + (P('(') * (l.any - S(') \t'))^0 * + (S(' \t')^1 * + l.delimited_range('"', false, true))^-1 * ')' + + S(' \t')^0 * l.delimited_range('[]')) + + P('http://') * (l.any - l.space)^1) +local link_label = token('link_label', l.delimited_range('[]') * ':') * ws * + token('link_url', (l.any - l.space)^1) * + (ws * (dq_str + sq_str + paren_str))^-1 + +local strong = token('strong', (P('**') * (l.any - '**')^0 * P('**')^-1) + + (P('__') * (l.any - '__')^0 * P('__')^-1)) +local em = token('em', + l.delimited_range('*', true) + l.delimited_range('_', true)) +local code = token('code', (P('``') * (l.any - '``')^0 * P('``')^-1) + + l.delimited_range('`', true, true)) + +local escape = token(l.DEFAULT, P('\\') * 1) + +local list = token('list', + l.starts_line(S(' \t')^0 * (S('*+-') + R('09')^1 * '.')) * + S(' \t')) + +M._rules = { + {'header', header}, + {'blockquote', blockquote}, + {'blockcode', blockcode}, + {'hr', hr}, + {'list', list}, + {'whitespace', ws}, + {'link_label', link_label}, + {'escape', escape}, + {'link', link}, + {'strong', strong}, + {'em', em}, + {'code', code}, +} + +local font_size = 10 +local hstyle = 'fore:red' +M._tokenstyles = { + h6 = hstyle, + h5 = hstyle..',size:'..(font_size + 1), + h4 = hstyle..',size:'..(font_size + 2), + h3 = hstyle..',size:'..(font_size + 3), + h2 = hstyle..',size:'..(font_size + 4), + h1 = hstyle..',size:'..(font_size + 5), + -- code = l.STYLE_EMBEDDED..',eolfilled', + code = 'fore:blue'..',eolfilled', + hr = l.STYLE_DEFAULT..',bold', + link = 'underlined', + link_url = 'underlined', + link_label = l.STYLE_LABEL, + strong = 'bold', + em = 'italics', + list = l.STYLE_CONSTANT, +} + +-- Embedded HTML. +local html = l.load('html') +local start_rule = token('tag', l.starts_line(S(' \t')^0 * '<')) +local end_rule = token(l.DEFAULT, P('\n')) -- TODO: l.WHITESPACE causes errors +l.embed_lexer(M, html, start_rule, end_rule) + +return M