Class: Rouge::Lexers::Carve
- Inherits:
-
RegexLexer
- Object
- RegexLexer
- Rouge::Lexers::Carve
- Defined in:
- lib/rouge/lexers/carve.rb
Overview
Carve is a post-Markdown lightweight markup language. Its inline delimiters deliberately differ from Markdown's, which is why lexing a Carve document as Markdown produces actively WRONG output rather than merely plain text:
Carve means Markdown would read it as
--------- ------------- -------------------------
*bold* strong emphasis
/italic/ emphasis literal slashes
_under_ underline emphasis
~strike~ strikethrough subscript / literal
{=mark=} highlight literal braces
{^sup^} superscript literal braces
WHY THE BLOCK OPENERS ARE NOT ANCHORED AT COLUMN 0. Carve opens a block at
column 0, or at an enclosing container's content column - nowhere in
between. So # H at document level is a paragraph, while the same
opener at a list item's content column is a real heading. Telling those
apart needs a container model that tracks the item's content column. A
regex lexer's state stack could carry one, but the sibling grammars
(Prism, highlight.js, Pygments) do not, and this lexer keeps their
trade-off on purpose so they agree: block openers match at any indent and
knowingly over-colour the rare indented-at-document-level case, rather
than under-colouring the common valid shape of an indented construct
inside a list item.
WHY AN ATTRIBUTE BLOCK IS ONE TOKEN. {#id .cls key="v" :lang} is emitted
whole as Name::Attribute rather than split into id, class, key, value and
language parts. Splitting reads better in isolation, but an attribute
block can carry a brace inside a quoted value, a language tag, and a bare
key that is not an attribute at all, and the sibling grammars treat the
block as one unit; a consumer asking "is this text inside an attribute
block" must get the same answer here as it does there.
WHERE ROUGE CANNOT SAY WHAT CARVE MEANS. Carve's _x_ is UNDERLINE, not
emphasis, and Rouge has no underline token - the vocabulary stops at
Generic::Emph, Generic::Strong and Generic::EmphStrong. The content takes
Generic::Emph so it is at least marked up, and the delimiters stay
Punctuation so a consumer can still tell underline from italic by the
delimiter it carries. Pygments, which has Generic.Underline, does make
the distinction.
Constant Summary collapse
- MARGIN =
A leading margin. A byte order mark at the start of a document is not content, so a block opener behind one is still a block opener.
/[ \t]*/.freeze
- ATTRS =
One attribute block, brace to brace. Quoted values may contain a brace and an escaped quote, so the value alternatives come before the bare-character one; a single nested brace pair is allowed for a braced span written inside.
%r/ \{(?=[.:}'"]|\#[\w-]+[\s}]|[A-Za-z][\w-]*(?:[=\s}]|$))(?: "(?:[^"\\\n]|\\.)*" |'(?:[^'\\\n]|\\.)*' |\{[^{}\n]*\} |[^{}\n] )*\} /x.freeze
- ATTRS_LINE =
The same block as a STANDALONE ATTRIBUTE LINE, which may span lines. The inline form deliberately cannot, because an unclosed inline
{would otherwise swallow the rest of the document. %r/ \{(?=[.:}'"]|\#[\w-]+[\s}]|[A-Za-z][\w-]*(?:[=\s}]|$))(?: "(?:[^"\\]|\\.)*" |'(?:[^'\\]|\\.)*' |\{[^{}]*\} |[^{}] )*\} /x.freeze
- LABEL =
A bracketed label that may itself contain three levels of brackets. A regex cannot match arbitrarily balanced brackets, and a link label in practice nests a level or two (
[t[z]](/u)). Bounding the nesting keeps the common shapes matching instead of stopping at the first inner bracket, which is what a naive [[^]]*] does. begin inner = '[^\[\]\\\\\n]|\\\\.' pattern = "(?:#{inner})*" 3.times { pattern = "(?:#{inner}|\\[#{pattern}\\])*" } Regexp.new("\\[#{pattern}\\]").freeze end
- INLINE_STARTERS =
Characters that can begin an inline construct. A run of anything else is ordinary content and is emitted as ONE token - without this every content character becomes its own token, which is both noisy and unusable to a consumer asking whether a phrase carries a scope.
'\\\\%!`${\\[\\^<:@\\#*\\/_~=.\\-'- PLAIN =
plain_run.freeze
- PLAIN_NO_PIPE =
plain_run('|').freeze
- PLAIN_NO_BRACKET =
plain_run('\\]').freeze
Class Method Summary collapse
-
.plain_run(extra = '') ⇒ Object
A run of content characters, excluding
extraas well.
Class Method Details
.plain_run(extra = '') ⇒ Object
A run of content characters, excluding extra as well.
106 107 108 |
# File 'lib/rouge/lexers/carve.rb', line 106 def self.plain_run(extra = '') Regexp.new("[^\n#{INLINE_STARTERS}#{extra}]+") end |