const _uppercase_word = /[A-Z0-9.()][-A-Z0-9.()_]+/;
const _li_token = /[-•][ ]+/;
module.exports = grammar({
name: 'vimdoc',
conflicts: $ => [
[$._line_noli, $._column_heading],
[$._column_heading],
],
extras: () => [/[\t ]/],
rules: {
help_file: ($) =>
seq(
repeat($._blank), repeat($.block),
repeat($.modeline),
),
_atom: ($) => choice(
$.word,
$._atom_common,
),
word: ($) => choice(
token(prec(-1, /[^.,(\[\n\t ]+/)),
$._word_common,
),
_atom_noli: ($) => choice(
alias($.word_noli, $.word),
$._atom_common,
),
word_noli: ($) => choice(
token(prec(-1, /[^-•\n\t ][^(\[\n\t ]*/)),
token(prec(-1, /[-•][^\n\t ]+/)),
$._word_common,
),
_atom_common: ($) =>
choice(
$.tag,
$.url,
$.optionlink,
$.taglink,
$.codespan,
$.argument,
$.keycode,
$.note,
),
_word_common: () => choice(
'*',
"'",
seq("'", token.immediate(/[^'\n\t ]/)),
seq("'", token.immediate(/[a-z]*[^'a-z\n\t ][a-z]*/), optional(token.immediate("'"))),
seq("'", token.immediate(/[^'\n\t ]/), token.immediate("'")),
/\|\|+/,
'|', "'|'",
'{',
'{}',
/\{\{+[0-9]*/,
'(',
')',
'[',
']',
'~',
'>',
',',
'.',
),
note: () => choice(
'Note:', 'NOTE:', 'Notes:',
'Warning:', 'WARNING:',
'Deprecated', 'DEPRECATED:'
),
keycode: () => choice(
/<[-a-zA-Z0-9_]+>/,
/<[SCMAD]-.>/,
/CTRL-./,
/CTRL-SHIFT-./,
/CTRL-(Break|PageUp|PageDown|Insert|Del)/,
'CTRL-{char}',
/META-./,
/ALT-./,
),
uppercase_name: () => seq(
token.immediate(_uppercase_word), repeat(_uppercase_word),
),
_uppercase_words: ($) => prec.left(-1, seq(
alias(token.immediate(_uppercase_word), $.word),
alias(repeat(_uppercase_word), $.word),
)),
block: ($) => seq(
choice(
repeat1($.line),
repeat1($.line_li),
seq(repeat1($.line), repeat1($.line_li)),
),
choice(
token.immediate('<'), $._blank),
repeat($._blank),
),
codeblock: ($) => prec.right(seq(
'>',
choice(
alias(token.immediate(/[a-z0-9]+\n/), $.language),
token.immediate(/\n/)),
alias(repeat1(alias($.line_code, $.line)), $.code),
)),
_blank: () => field('blank', /\n/),
line: ($) => choice(
$.column_heading,
$.h1,
$.h2,
$.h3,
$.codeblock,
$._line_noli,
),
line_li: ($) => prec.right(1, seq(
optional(token.immediate('<')), _li_token,
choice(
alias(seq(repeat1($._atom), /\n/), $.line),
seq(alias(repeat1($._atom), $.line), $.codeblock),
),
repeat(alias($._line_noli, $.line)),
)),
line_code: () => choice(/\n/, /[\t ]+[^\n]+\n/),
_line_noli: ($) => seq(
choice($._atom_noli, $._uppercase_words),
repeat($._atom),
choice($.codeblock, /\n/)
),
modeline: ($) => token(prec(2, /vim:[^\n]+\n/)),
column_heading: ($) => seq(
alias($._column_heading, $.heading),
alias('~', $.delimiter),
token.immediate(/\n/),
),
_column_heading: $ => prec.dynamic(1, seq(
choice($._atom_noli, $._uppercase_words),
repeat($._atom)
)),
h1: ($) =>
prec(1, seq(
alias(token.immediate(/============+[\t ]*\n/), $.delimiter),
alias(repeat1($._atom), $.heading),
optional(seq($.tag, repeat($._atom))),
/\n/,
)),
h2: ($) =>
prec(1, seq(
alias(token.immediate(/------------+[\t ]*\n/), $.delimiter),
alias(repeat1($._atom), $.heading),
optional(seq($.tag, repeat($._atom))),
/\n/,
)),
h3: ($) =>
seq(
alias($.uppercase_name, $.heading),
optional(seq($.tag, repeat($._atom))),
/\n/,
),
tag: ($) => _word($,
prec(1, /[^*\n\t ]+/), '*', '*'),
url_word: () => /https?:\/\/[^\n\t\] ]*[^\n\t )\].,:]/,
url: ($) => choice(
field('text', alias($.url_word, $.word))
),
optionlink: ($) => _word($, /[a-z][a-z]+/, "'", "'"),
taglink: ($) => _word($, prec(1, /[^|\n\t ]+/), '|', '|'),
codespan: ($) => _word($, /[^``\n]+/, '`', '`'),
argument: ($) => seq(_word($, /[^}\n\t ]+/, '{', '}'), optional(token.immediate('?'))),
},
});
function _word($, rule, c1, c2, fname) {
fname = fname ?? 'text';
return seq(c1, field(fname, alias(token.immediate(rule), $.word)), token.immediate(c2));
}