rouge-ddot 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +105 -0
- data/README.md +36 -16
- data/lib/rouge/lexers/ddot.rb +227 -102
- data/lib/rouge-ddot.rb +13 -0
- data/rouge-ddot.gemspec +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a99cbaae721a52151bcfff4eb8d656c88aade697eab78160a3841f8274c0041e
|
|
4
|
+
data.tar.gz: adda0083411ae5755d34944ca3a194947b95827f73af93b8c55094a7660df8cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 285d6debe396c45d56abd71d15998bb414c877d74d6d792a6d2cd3531957bd97187cfd2e3fcde1a84aab842a3cf94a15029a66d0b4f3cab17832b80b5a3d2f47
|
|
7
|
+
data.tar.gz: 422c3c2721efe2b5ecb062f23cd65e7bc518f3733f2aa2f1859f3f9ea3615680610caaf967440da55f18e8803f640294678909a54378f1b5f5642b0ba461e1ac
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
No lexer changes — token output is identical to 0.1.2.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`require 'rouge-ddot'` now works.** The gem shipped only
|
|
10
|
+
`lib/rouge/lexers/ddot.rb`, so requiring it by gem *name* raised
|
|
11
|
+
`LoadError: cannot load such file -- rouge-ddot`. Since Rouge does not
|
|
12
|
+
auto-discover third-party lexers, and `Bundler.require` (Jekyll's
|
|
13
|
+
`:jekyll_plugins` group, Rails initializers, …) requires gems by name,
|
|
14
|
+
hosts could not load the lexer the conventional way: adding it to a
|
|
15
|
+
`:jekyll_plugins` group *crashed the build*. Every host therefore had to
|
|
16
|
+
know the internal path and write `require 'rouge/lexers/ddot'` into a plugin
|
|
17
|
+
file of its own. A `lib/rouge-ddot.rb` entry point now requires the lexer,
|
|
18
|
+
so this is enough to highlight `[source,ddot.it]` blocks in Jekyll:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Gemfile
|
|
22
|
+
group :jekyll_plugins do
|
|
23
|
+
gem 'rouge-ddot', '~> 0.1.3'
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`require 'rouge/lexers/ddot'` keeps working unchanged.
|
|
28
|
+
|
|
29
|
+
## 0.1.2
|
|
30
|
+
|
|
31
|
+
Brings the lexer in line with the ddot.it Parse and Highlight specifications.
|
|
32
|
+
Conformance is asserted against all 33 cases of the cross-implementation
|
|
33
|
+
corpus (`../ddot.it/test-data/cases/`), with the TextMate grammar canonical.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **`FrozenError` on any block containing a non-triple line.** The file carried
|
|
38
|
+
`# frozen_string_literal: true`, but Rouge mutates the string handed to
|
|
39
|
+
`token`, so lexing a `!!block` body, an annotation line or an end marker
|
|
40
|
+
raised `can't modify frozen String` (Rouge 4.7). This crashed Asciidoctor
|
|
41
|
+
outright, making 0.1.1 unusable on real ddot.it documentation.
|
|
42
|
+
- **`Error` token on the blank line inside a `?end=` block.** The blank-line
|
|
43
|
+
rule matched zero-width, so Rouge could not advance and emitted `Error` for
|
|
44
|
+
the newline — an error-styled blank line in every marked block. The rule now
|
|
45
|
+
consumes the newline. Invisible to the corpus, whose harness drops
|
|
46
|
+
whitespace-only chunks; it showed only in rendered output.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **`!!off` / `!!on` recognised anywhere on a line**, not only alone on one, so
|
|
51
|
+
the commands work inside host-language comments (`<!-- ddot.it/off -->`,
|
|
52
|
+
`# !!off`, `// !!on`). Only the command itself is coloured; the line yields no
|
|
53
|
+
triple and the region starts on the next line. The command *name* is still
|
|
54
|
+
matched exactly, so `!!office` is not `!!off`.
|
|
55
|
+
- **Metadata continuation after a block** (Parse Spec *block-continuation*): a
|
|
56
|
+
line whose first token is `,,` continues the triple whose **object** a block
|
|
57
|
+
filled, and `;;` continues after a block **meta object**. Previously both read
|
|
58
|
+
as a fresh triple with the subject `,,` / `;;`, and the unmatched delimiter
|
|
59
|
+
produced an `Error` token.
|
|
60
|
+
- **Continuation lines that open a block** (`..knows.. !!block`), so one logical
|
|
61
|
+
line can carry a block subject *and* a block object.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **Canonical token names** (`test-data/tokens.md`), which changes the emitted
|
|
66
|
+
Rouge token types:
|
|
67
|
+
|
|
68
|
+
| ddot.it token | 0.1.1 | 0.1.2 |
|
|
69
|
+
|-----------------|----------------------|-----------------------------|
|
|
70
|
+
| `doubledot` | `Operator` → *operator* | `Operator` |
|
|
71
|
+
| `meta-doubledot`| `Comment::Hashbang` → *meta-operator* | `Comment::Hashbang` |
|
|
72
|
+
| `excluded` | `Comment::Single` → *disabled* | `Comment::Single` |
|
|
73
|
+
| `meta-separator`| — | `Punctuation` |
|
|
74
|
+
| `command-param` | — | `Name::Attribute` |
|
|
75
|
+
| `block-end` | — | `Name::Label` |
|
|
76
|
+
| `verbatim` | — | `Literal::String::Backtick` |
|
|
77
|
+
|
|
78
|
+
Themes that styled `operator`, `meta-operator` or `disabled` by *name* need
|
|
79
|
+
updating; those styling by Rouge token class are unaffected except for the
|
|
80
|
+
four newly emitted classes.
|
|
81
|
+
- **Lexical tokens follow the spec's counting rule.** `DT2`/`DT4`/`CM2`/`SC2`
|
|
82
|
+
are runs of *exactly* n, expressed with lookarounds, so `...` and `......`
|
|
83
|
+
are text rather than operators.
|
|
84
|
+
- **The line-shape gate.** A line is ddot.it only when it carries a complete
|
|
85
|
+
operator skeleton — two `DT2`, or one `DT4` / `.. ..`. Prose with a lone `..`,
|
|
86
|
+
an ellipsis, `Node.js` or `U.S.A.` stays plain.
|
|
87
|
+
- **Commands require the slash and a name**: bare `ddot.it` is a document
|
|
88
|
+
marker, not a command, and `?`/`#` introduce a command's query and fragment
|
|
89
|
+
instead of terminating it (`!!block?end=EOS` is one command).
|
|
90
|
+
- **`!!block` as a field value** in the Subject, Object and MetaObject
|
|
91
|
+
positions, blank-line and `?end=MARKER` terminated, with the body emitted as
|
|
92
|
+
`verbatim`.
|
|
93
|
+
- **`;;` separates inline metadata pairs only.** Inside a `,,` block a metadata
|
|
94
|
+
object runs to the end of its line, so `;;` there is ordinary text.
|
|
95
|
+
|
|
96
|
+
## 0.1.1
|
|
97
|
+
|
|
98
|
+
First release published via OIDC trusted publishing
|
|
99
|
+
(`Calpano/ddot.it-syntax-tools` → RubyGems). No functional
|
|
100
|
+
changes vs 0.1.0.
|
|
101
|
+
|
|
102
|
+
## 0.1.0
|
|
103
|
+
|
|
104
|
+
Initial release. Rouge lexer for ddot.it. See
|
|
105
|
+
[`design.adoc`](./design.adoc).
|
data/README.md
CHANGED
|
@@ -6,35 +6,53 @@ graph notation (https://ddot.it).
|
|
|
6
6
|
## Usage
|
|
7
7
|
|
|
8
8
|
```ruby
|
|
9
|
-
require 'rouge'
|
|
10
|
-
require 'rouge/lexers/ddot'
|
|
9
|
+
require 'rouge-ddot'
|
|
11
10
|
|
|
12
11
|
Rouge::Lexers::Ddot.new.lex("Project Eagle ..started in.. 2024") do |tok, val|
|
|
13
12
|
puts [tok.qualname, val].inspect
|
|
14
13
|
end
|
|
15
14
|
```
|
|
16
15
|
|
|
16
|
+
Rouge does not auto-discover third-party lexers, so the gem has to be required
|
|
17
|
+
before a `ddot.it` block can be highlighted. Requiring it by name is enough
|
|
18
|
+
(since 0.1.3); `require 'rouge/lexers/ddot'` also works.
|
|
19
|
+
|
|
20
|
+
In **Jekyll**, put it in the `:jekyll_plugins` group and Bundler requires it at
|
|
21
|
+
startup — no `_plugins/` file needed:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Gemfile
|
|
25
|
+
group :jekyll_plugins do
|
|
26
|
+
gem 'rouge-ddot', '~> 0.1.3'
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
17
30
|
In Asciidoctor with `:source-highlighter: rouge` set, `[source,ddot]` blocks
|
|
18
|
-
are dispatched here automatically — no extra wiring per document.
|
|
31
|
+
are dispatched here automatically — no extra wiring per document. The lexer
|
|
32
|
+
registers under the tag `ddot`, with the aliases `ddotit` and `ddot.it`.
|
|
19
33
|
|
|
20
34
|
## Token mapping
|
|
21
35
|
|
|
22
36
|
The lexer emits standard Rouge tokens; they map to the canonical ddot.it
|
|
23
37
|
token vocabulary in [`../../ddot.it/test-data/tokens.md`](../../ddot.it/test-data/tokens.md):
|
|
24
38
|
|
|
25
|
-
| Rouge | Canonical
|
|
26
|
-
|
|
27
|
-
| `Name::Class` | `subject`
|
|
28
|
-
| `Name::Function` | `relation`
|
|
29
|
-
| `Literal::String::Symbol` | `object`
|
|
30
|
-
| `Operator` | `
|
|
31
|
-
| `Keyword::Pseudo` | `command`
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `Comment::
|
|
35
|
-
| `
|
|
36
|
-
| `Comment::
|
|
37
|
-
| `Comment::
|
|
39
|
+
| Rouge | Canonical |
|
|
40
|
+
|-----------------------------|------------------|
|
|
41
|
+
| `Name::Class` | `subject` |
|
|
42
|
+
| `Name::Function` | `relation` |
|
|
43
|
+
| `Literal::String::Symbol` | `object` |
|
|
44
|
+
| `Operator` | `doubledot` |
|
|
45
|
+
| `Keyword::Pseudo` | `command` |
|
|
46
|
+
| `Name::Attribute` | `command-param` |
|
|
47
|
+
| `Name::Label` | `block-end` |
|
|
48
|
+
| `Comment::Preproc` | `meta-delim` |
|
|
49
|
+
| `Punctuation` | `meta-separator` |
|
|
50
|
+
| `Comment::Hashbang` | `meta-doubledot` |
|
|
51
|
+
| `Comment::Doc` | `meta-relation` |
|
|
52
|
+
| `Comment::Special` | `meta-object` |
|
|
53
|
+
| `Comment::Multiline` | `meta-text` |
|
|
54
|
+
| `Comment::Single` | `excluded` |
|
|
55
|
+
| `Literal::String::Backtick` | `verbatim` |
|
|
38
56
|
|
|
39
57
|
## Conformance
|
|
40
58
|
|
|
@@ -57,9 +75,11 @@ lockstep.
|
|
|
57
75
|
```
|
|
58
76
|
rouge/
|
|
59
77
|
├── lib/
|
|
78
|
+
│ ├── rouge-ddot.rb # entry point: require 'rouge-ddot'
|
|
60
79
|
│ └── rouge/
|
|
61
80
|
│ └── lexers/
|
|
62
81
|
│ └── ddot.rb # the lexer
|
|
82
|
+
├── CHANGELOG.md
|
|
63
83
|
├── README.md
|
|
64
84
|
└── rouge-ddot.gemspec
|
|
65
85
|
```
|
data/lib/rouge/lexers/ddot.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
#
|
|
3
1
|
# Rouge lexer for ddot.it (https://ddot.it). Emits tokens that — after the
|
|
4
2
|
# scope-name mapping in `tools/conformance-rouge.rb` — equal the canonical
|
|
5
3
|
# token streams in `../../ddot.it/test-data/cases/*/expected.tokens.json`.
|
|
@@ -8,18 +6,24 @@
|
|
|
8
6
|
# Name::Class → subject
|
|
9
7
|
# Name::Function → relation
|
|
10
8
|
# Literal::String::Symbol → object
|
|
11
|
-
# Operator →
|
|
9
|
+
# Operator → doubledot
|
|
12
10
|
# Keyword::Pseudo → command
|
|
11
|
+
# Name::Attribute → command-param
|
|
12
|
+
# Name::Label → block-end
|
|
13
13
|
# Comment::Preproc → meta-delim
|
|
14
|
-
#
|
|
14
|
+
# Punctuation → meta-separator
|
|
15
|
+
# Comment::Hashbang → meta-doubledot
|
|
15
16
|
# Comment::Doc → meta-relation
|
|
16
17
|
# Comment::Special → meta-object
|
|
17
18
|
# Comment::Multiline → meta-text
|
|
18
|
-
# Comment::Single →
|
|
19
|
+
# Comment::Single → excluded
|
|
20
|
+
# Literal::String::Backtick → verbatim
|
|
21
|
+
#
|
|
22
|
+
# NOT `# frozen_string_literal: true`: Rouge mutates the string handed to
|
|
23
|
+
# `token`, so a frozen literal raises FrozenError (observed with Rouge 4.7).
|
|
19
24
|
#
|
|
20
25
|
# State machine is flat by design: we use `goto` exclusively (no `push`), so
|
|
21
26
|
# the stack is always one deep and `\n` simply transitions back to `:root`.
|
|
22
|
-
# This keeps line-by-line dispatch clean — every line starts fresh.
|
|
23
27
|
#
|
|
24
28
|
# Rouge gotcha: `rule pat, Token do |m| ... end` IGNORES the Token arg when
|
|
25
29
|
# a block is given. To both emit a token AND transition, use a block that
|
|
@@ -37,34 +41,144 @@ module Rouge
|
|
|
37
41
|
filenames '*.ddot'
|
|
38
42
|
mimetypes 'text/x-ddot'
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
# --- lexical tokens (see the Parse Spec's "Token Sequences") ----------
|
|
45
|
+
# Each is a run of EXACTLY n, neither preceded nor followed by the same
|
|
46
|
+
# character — the lookarounds are what express that. `\.{2}` alone would
|
|
47
|
+
# happily match the first two dots of `...`.
|
|
48
|
+
DT2 = /(?<!\.)\.{2}(?!\.)/.freeze
|
|
49
|
+
DT4 = /(?<!\.)\.{4}(?!\.)/.freeze
|
|
50
|
+
QUAD = /(?<!\.)(?:\.{4}|\.{2}[ \t]+\.{2})(?!\.)/.freeze
|
|
51
|
+
CM2 = /(?<!,),,(?!,)/.freeze
|
|
52
|
+
SC2 = /(?<!;);;(?!;)/.freeze
|
|
53
|
+
|
|
54
|
+
OFF_RE = %r{(?:ddot\.it/off|!!off)}.freeze
|
|
55
|
+
ON_RE = %r{(?:ddot\.it/on|!!on)}.freeze
|
|
56
|
+
# Pins the command NAME: `!!office` must not read as `!!off`.
|
|
57
|
+
BOUND_SRC = /(?![^\s?#])/.freeze
|
|
58
|
+
# A command needs the slash and a non-empty name; bare `ddot.it` is not one.
|
|
59
|
+
CMD_RE = %r{(?:(?:https?://)?ddot\.it/|!!)[^\s?#]+(?:\?[^\s#]*)?(?:\#\S*)?}.freeze
|
|
60
|
+
BLOCK_RE = %r{(?:(?:https?://)?ddot\.it/|!!)block}.freeze
|
|
61
|
+
|
|
62
|
+
# The line-shape gate: a line is ddot.it only if it carries a complete
|
|
63
|
+
# operator skeleton — two DT2, or one DT4 / '.. ..'. A lone `..` in prose
|
|
64
|
+
# does not qualify, which is what keeps 23-not-a-triple entirely plain.
|
|
65
|
+
GATE = /(?=[^\n]*(?:#{DT4}|#{DT2}[^\n]*#{DT2}))/.freeze
|
|
66
|
+
|
|
67
|
+
# `!!block` filling a whole field: it must END its physical line.
|
|
68
|
+
BLOCK_OPENER = /#{BLOCK_RE}(?:(\?end=)(\S+))?[ \t]*(?=\n|\z)/.freeze
|
|
69
|
+
|
|
70
|
+
def reset!
|
|
71
|
+
super
|
|
72
|
+
@block_end = nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Emit a slot's text as `slot_token`, unless it is exactly a command —
|
|
76
|
+
# then it is a command (tokens.md: "a slot whose text is a command is
|
|
77
|
+
# emitted as command, not subject").
|
|
78
|
+
def slot(match, slot_token)
|
|
79
|
+
text = match[0]
|
|
80
|
+
token(text.match?(/\A#{CMD_RE}\z/) ? Keyword::Pseudo : slot_token, text)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A `!!block` opener was matched: emit its parts, remember any ?end=
|
|
84
|
+
# marker, and switch to verbatim.
|
|
85
|
+
def open_block(match)
|
|
86
|
+
raw = match[0]
|
|
87
|
+
lead = raw[/\A[ \t]*/]
|
|
88
|
+
body = raw[lead.length..].rstrip
|
|
89
|
+
tail = raw[(lead.length + body.length)..]
|
|
90
|
+
token Text, lead unless lead.empty?
|
|
91
|
+
if match[2]
|
|
92
|
+
token Keyword::Pseudo, body[0...body.index('?end=')]
|
|
93
|
+
token Name::Attribute, match[1]
|
|
94
|
+
token Name::Label, match[2]
|
|
95
|
+
@block_end = match[2]
|
|
96
|
+
else
|
|
97
|
+
token Keyword::Pseudo, body
|
|
98
|
+
@block_end = nil
|
|
99
|
+
end
|
|
100
|
+
token Text, tail unless tail.nil? || tail.empty?
|
|
101
|
+
goto :verbatim
|
|
102
|
+
end
|
|
43
103
|
|
|
44
104
|
# ─────────── line-dispatch root ───────────
|
|
45
105
|
state :root do
|
|
46
106
|
# Standalone ,, opens a free-form meta block.
|
|
47
|
-
rule(%r/^[ \t]
|
|
107
|
+
rule(%r/^[ \t]*#{CM2}[ \t]*(?=\n|\z)/) do |m|
|
|
48
108
|
token Comment::Preproc, m[0]
|
|
49
109
|
goto :meta_block
|
|
50
110
|
end
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
111
|
+
# Parse Spec "block-continuation": after a block that filled the OBJECT,
|
|
112
|
+
# a line whose first token is CM2 CONTINUES the logical line as metadata
|
|
113
|
+
# (and SC2 after a block meta object).
|
|
114
|
+
# Without this it lexes as a fresh triple whose subject is ",," (or, in
|
|
115
|
+
# this lexer, as an unmatched ",," followed by subject "since").
|
|
116
|
+
# Keyed on shape — a state machine over physical lines cannot see that a
|
|
117
|
+
# block object preceded: ",," then whitespace then "..".
|
|
118
|
+
rule(%r/^[ \t]*(#{CM2})(?=[ \t]+#{DT2})/) do |m|
|
|
119
|
+
token Comment::Preproc, m[1]
|
|
120
|
+
push :inline_meta
|
|
121
|
+
end
|
|
122
|
+
# ... and SC2 after a block META OBJECT (`;; ..until.. 2020`).
|
|
123
|
+
rule(%r/^[ \t]*(#{SC2})(?=[ \t]+#{DT2})/) do |m|
|
|
124
|
+
token Punctuation, m[1]
|
|
125
|
+
push :inline_meta
|
|
126
|
+
end
|
|
127
|
+
# Off-marker enters an excluded span.
|
|
128
|
+
# Recognised ANYWHERE on the line (host-language comments), but the whole
|
|
129
|
+
# line is consumed so only the command itself is coloured.
|
|
130
|
+
rule(%r/^([^\n]*?)(#{OFF_RE}#{BOUND_SRC})([^\n]*)(?=\n|\z)/) do |m|
|
|
131
|
+
token Text, m[1]
|
|
132
|
+
token Keyword::Pseudo, m[2]
|
|
133
|
+
token Text, m[3]
|
|
54
134
|
goto :disabled
|
|
55
135
|
end
|
|
136
|
+
# `!!block` alone on a line = a SUBJECT opener (block-as-field).
|
|
137
|
+
rule(%r/^[ \t]*#{BLOCK_OPENER}/) { |m| open_block(m) }
|
|
56
138
|
# Empty / whitespace-only line.
|
|
57
139
|
rule %r/^[ \t]*\n/, Text
|
|
58
|
-
#
|
|
59
|
-
rule(
|
|
60
|
-
|
|
61
|
-
|
|
140
|
+
# Gate: only lines with a full operator skeleton parse as triples.
|
|
141
|
+
rule(/^#{GATE}(?=[ \t]*(?:#{DT2}|#{QUAD}))/) { goto :after_subject }
|
|
142
|
+
rule(/^#{GATE}/) { goto :subject }
|
|
143
|
+
# Anything else is not a triple: consume the line, emit no token.
|
|
144
|
+
rule %r/[^\n]+/, Text
|
|
145
|
+
rule %r/\n/, Text
|
|
62
146
|
end
|
|
63
147
|
|
|
64
|
-
# ───────────
|
|
148
|
+
# ─────────── verbatim (a !!block body) ───────────
|
|
149
|
+
state :verbatim do
|
|
150
|
+
# A blank line ends the unmarked form; in a marked block it is content.
|
|
151
|
+
# The newline MUST be consumed here: a zero-width match leaves the
|
|
152
|
+
# stream where it was, Rouge cannot advance, and it emits Error for the
|
|
153
|
+
# newline instead — visible as an error-styled blank line inside every
|
|
154
|
+
# `?end=` block. (The conformance harness drops whitespace-only chunks,
|
|
155
|
+
# so the corpus cannot catch this; it only shows in rendered output.)
|
|
156
|
+
rule(%r/^[ \t]*\n/) do |m|
|
|
157
|
+
token Text, m[0]
|
|
158
|
+
goto :root if @block_end.nil?
|
|
159
|
+
end
|
|
160
|
+
rule(%r/^[ \t]*\z/) do |m|
|
|
161
|
+
token Text, m[0]
|
|
162
|
+
goto :root if @block_end.nil?
|
|
163
|
+
end
|
|
164
|
+
rule(%r/^[^\n]+/) do |m|
|
|
165
|
+
if @block_end && m[0].strip == @block_end
|
|
166
|
+
token Name::Label, m[0]
|
|
167
|
+
@block_end = nil
|
|
168
|
+
goto :root
|
|
169
|
+
else
|
|
170
|
+
token Literal::String::Backtick, m[0]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
rule %r/\n/, Text
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# ─────────── excluded span ───────────
|
|
65
177
|
state :disabled do
|
|
66
|
-
rule(%r/^[
|
|
67
|
-
token
|
|
178
|
+
rule(%r/^([^\n]*?)(#{ON_RE}#{BOUND_SRC})([^\n]*)(?=\n|\z)/) do |m|
|
|
179
|
+
token Text, m[1]
|
|
180
|
+
token Keyword::Pseudo, m[2]
|
|
181
|
+
token Text, m[3]
|
|
68
182
|
goto :root
|
|
69
183
|
end
|
|
70
184
|
rule %r/^[ \t]*\n/, Text
|
|
@@ -72,200 +186,211 @@ module Rouge
|
|
|
72
186
|
rule %r/\n/, Text
|
|
73
187
|
end
|
|
74
188
|
|
|
75
|
-
# ─────────── multi-line meta block (free-form) ───────────
|
|
189
|
+
# ─────────── multi-line meta block (free-form or typed) ───────────
|
|
76
190
|
state :meta_block do
|
|
77
|
-
rule(%r/^[ \t]
|
|
191
|
+
rule(%r/^[ \t]*#{CM2}[ \t]*(?=\n|\z)/) do |m|
|
|
78
192
|
token Comment::Preproc, m[0]
|
|
79
193
|
goto :root
|
|
80
194
|
end
|
|
81
195
|
rule %r/^[ \t]*\n/, Text
|
|
82
|
-
rule
|
|
196
|
+
rule(/^(?=[ \t]*(?:#{DT2}|#{QUAD}))/) { goto :typed_meta_op }
|
|
197
|
+
rule %r/[^\n]+/, Comment::Multiline # free-form meta text
|
|
83
198
|
rule %r/\n/, Text
|
|
84
199
|
end
|
|
85
200
|
|
|
86
201
|
# ─────────── triple line: subject phase ───────────
|
|
87
202
|
state :subject do
|
|
88
203
|
rule %r/[ \t]+/, Text
|
|
89
|
-
rule(
|
|
90
|
-
|
|
91
|
-
goto :after_subject
|
|
92
|
-
end
|
|
93
|
-
rule(/[^\s.,][^\n,]*?(?=[ \t]+\.{2,4}|[ \t]*,,|[ \t]*$|\n|\z)/) do |m|
|
|
94
|
-
token Name::Class, m[0]
|
|
204
|
+
rule(/[^\s.,][^\n,]*?(?=[ \t]*(?:#{DT2}|#{QUAD})|[ \t]*#{CM2}|[ \t]*$|\n|\z)/) do |m|
|
|
205
|
+
slot m, Name::Class
|
|
95
206
|
goto :after_subject
|
|
96
207
|
end
|
|
97
|
-
rule(%r/\n/) { token Text,
|
|
208
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
98
209
|
end
|
|
99
210
|
|
|
100
|
-
# ─────────── after subject (or continuation entry) ───────────
|
|
211
|
+
# ─────────── after subject (or continuation-line entry) ───────────
|
|
101
212
|
state :after_subject do
|
|
102
213
|
rule %r/[ \t]+/, Text
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
rule(%r/\.{2}[ \t]+\.{2}/) do |m|
|
|
106
|
-
token Operator, m[0]
|
|
107
|
-
goto :object
|
|
108
|
-
end
|
|
109
|
-
rule(%r/\.{4}/) do |m|
|
|
214
|
+
# QuadDot FIRST: '.. ..' is ONE operator, not two.
|
|
215
|
+
rule(%r/#{QUAD}/) do |m|
|
|
110
216
|
token Operator, m[0]
|
|
111
217
|
goto :object
|
|
112
218
|
end
|
|
113
|
-
rule(%r
|
|
219
|
+
rule(%r/#{DT2}/) do |m|
|
|
114
220
|
token Operator, m[0]
|
|
115
221
|
goto :relation
|
|
116
222
|
end
|
|
117
|
-
rule(%r
|
|
223
|
+
rule(%r/#{CM2}/) do |m|
|
|
118
224
|
token Comment::Preproc, m[0]
|
|
119
225
|
goto :inline_meta
|
|
120
226
|
end
|
|
121
|
-
rule(%r/\n/) { token Text,
|
|
227
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
122
228
|
end
|
|
123
229
|
|
|
124
230
|
# ─────────── relation phase ───────────
|
|
125
231
|
state :relation do
|
|
126
|
-
rule
|
|
127
|
-
|
|
232
|
+
rule %r/[ \t]+/, Text
|
|
233
|
+
rule(/[^\s.,][^\n.,]*?(?=[ \t]*#{DT2}|[ \t]*$|\n|\z)/) do |m|
|
|
234
|
+
slot m, Name::Function
|
|
128
235
|
goto :after_relation
|
|
129
236
|
end
|
|
130
|
-
rule
|
|
131
|
-
rule(%r/\n/) { token Text, "\n"; goto :root }
|
|
237
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
132
238
|
end
|
|
133
239
|
|
|
134
240
|
state :after_relation do
|
|
135
241
|
rule %r/[ \t]+/, Text
|
|
136
|
-
rule(%r
|
|
242
|
+
rule(%r/#{DT2}/) do |m|
|
|
137
243
|
token Operator, m[0]
|
|
138
244
|
goto :object
|
|
139
245
|
end
|
|
140
|
-
rule(%r
|
|
246
|
+
rule(%r/#{CM2}/) do |m|
|
|
141
247
|
token Comment::Preproc, m[0]
|
|
142
248
|
goto :inline_meta
|
|
143
249
|
end
|
|
144
|
-
rule(%r/\n/) { token Text,
|
|
250
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
145
251
|
end
|
|
146
252
|
|
|
147
253
|
# ─────────── object phase ───────────
|
|
254
|
+
# Object := TextExcept{NL,CM2} — it MAY contain `..` (20-object-dotdot),
|
|
255
|
+
# so it runs to end of line or to a `,,`; it does not stop at a DT2.
|
|
148
256
|
state :object do
|
|
149
|
-
rule
|
|
150
|
-
|
|
257
|
+
rule %r/[ \t]+/, Text
|
|
258
|
+
rule(%r/#{BLOCK_OPENER}/) { |m| open_block(m) }
|
|
259
|
+
rule(/[^\s,][^\n,]*?(?=[ \t]*#{CM2}|[ \t]*$|\n|\z)/) do |m|
|
|
260
|
+
slot m, Literal::String::Symbol
|
|
151
261
|
goto :after_object
|
|
152
262
|
end
|
|
153
|
-
rule
|
|
154
|
-
rule(%r/\n/) { token Text, "\n"; goto :root }
|
|
263
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
155
264
|
end
|
|
156
265
|
|
|
157
266
|
state :after_object do
|
|
158
267
|
rule %r/[ \t]+/, Text
|
|
159
|
-
# Trailing `,,` at end-of-line opens a
|
|
160
|
-
|
|
161
|
-
# until a closing standalone `,,`.
|
|
162
|
-
rule(%r/,,(?=[ \t]*(?:\n|\z))/) do |m|
|
|
268
|
+
# Trailing `,,` at end-of-line opens a multi-line meta block.
|
|
269
|
+
rule(%r/#{CM2}(?=[ \t]*(?:\n|\z))/) do |m|
|
|
163
270
|
token Comment::Preproc, m[0]
|
|
164
|
-
goto :
|
|
271
|
+
goto :meta_block_open
|
|
165
272
|
end
|
|
166
|
-
|
|
167
|
-
rule(%r/,,/) do |m|
|
|
273
|
+
rule(%r/#{CM2}/) do |m|
|
|
168
274
|
token Comment::Preproc, m[0]
|
|
169
275
|
goto :inline_meta
|
|
170
276
|
end
|
|
171
|
-
rule(%r/\n/) { token Text,
|
|
277
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
172
278
|
end
|
|
173
279
|
|
|
174
|
-
|
|
175
|
-
# :typed_meta_block for subsequent lines.
|
|
176
|
-
state :typed_meta_open do
|
|
280
|
+
state :meta_block_open do
|
|
177
281
|
rule %r/[ \t]+/, Text
|
|
178
|
-
rule(%r/\n/) { token Text,
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Typed meta block: each line is `..relation.. object`, closed by `,,`.
|
|
182
|
-
state :typed_meta_block do
|
|
183
|
-
rule(%r/^[ \t]*,,[ \t]*(?=\n|\z)/) do |m|
|
|
184
|
-
token Comment::Preproc, m[0]
|
|
185
|
-
goto :root
|
|
186
|
-
end
|
|
187
|
-
rule %r/^[ \t]*\n/, Text
|
|
188
|
-
rule(/^(?=[ \t]*\.{2,4})/) { goto :typed_meta_op1 }
|
|
189
|
-
rule %r/[^\n]+/, Comment::Multiline # free-form fallback
|
|
190
|
-
rule %r/\n/, Text
|
|
282
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
191
283
|
end
|
|
192
284
|
|
|
193
|
-
|
|
285
|
+
# ─────────── typed meta inside a ,, block ───────────
|
|
286
|
+
state :typed_meta_op do
|
|
194
287
|
rule %r/[ \t]+/, Text
|
|
195
|
-
rule(%r
|
|
288
|
+
rule(%r/#{QUAD}/) do |m| # implicit meta relation
|
|
289
|
+
token Comment::Hashbang, m[0]
|
|
290
|
+
goto :typed_meta_object
|
|
291
|
+
end
|
|
292
|
+
rule(%r/#{DT2}/) do |m|
|
|
196
293
|
token Comment::Hashbang, m[0]
|
|
197
294
|
goto :typed_meta_relation
|
|
198
295
|
end
|
|
199
|
-
rule(%r/\n/) { token Text,
|
|
296
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
200
297
|
end
|
|
201
298
|
|
|
202
299
|
state :typed_meta_relation do
|
|
203
|
-
rule
|
|
204
|
-
|
|
300
|
+
rule %r/[ \t]+/, Text
|
|
301
|
+
rule(/[^\s.,][^\n.,]*?(?=[ \t]*#{DT2}|[ \t]*$|\n|\z)/) do |m|
|
|
302
|
+
slot m, Comment::Doc
|
|
205
303
|
goto :typed_meta_after_relation
|
|
206
304
|
end
|
|
207
|
-
rule
|
|
208
|
-
rule(%r/\n/) { token Text, "\n"; goto :typed_meta_block }
|
|
305
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
209
306
|
end
|
|
210
307
|
|
|
211
308
|
state :typed_meta_after_relation do
|
|
212
309
|
rule %r/[ \t]+/, Text
|
|
213
|
-
rule(%r
|
|
310
|
+
rule(%r/#{DT2}/) do |m|
|
|
214
311
|
token Comment::Hashbang, m[0]
|
|
215
312
|
goto :typed_meta_object
|
|
216
313
|
end
|
|
217
|
-
rule(%r/\n/) { token Text,
|
|
314
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
218
315
|
end
|
|
219
316
|
|
|
317
|
+
# MetaObjectInBlock := TextExcept{NL} — `;;` is CONTENT here, not a
|
|
318
|
+
# separator (that is the inline form only). See 28-semicolon-in-meta-block.
|
|
220
319
|
state :typed_meta_object do
|
|
221
|
-
rule
|
|
222
|
-
|
|
320
|
+
rule %r/[ \t]+/, Text
|
|
321
|
+
rule(%r/#{BLOCK_OPENER}/) { |m| open_block(m) }
|
|
322
|
+
rule(/[^\s][^\n]*?(?=[ \t]*$|\n|\z)/) do |m|
|
|
323
|
+
slot m, Comment::Special
|
|
223
324
|
goto :typed_meta_after_object
|
|
224
325
|
end
|
|
225
|
-
rule
|
|
226
|
-
rule(%r/\n/) { token Text, "\n"; goto :typed_meta_block }
|
|
326
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
227
327
|
end
|
|
228
328
|
|
|
229
329
|
state :typed_meta_after_object do
|
|
230
330
|
rule %r/[ \t]+/, Text
|
|
231
|
-
rule(%r/\n/) { token Text,
|
|
331
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :meta_block }
|
|
232
332
|
end
|
|
233
333
|
|
|
234
334
|
# ─────────── inline metadata (after `,,` on a triple line) ───────────
|
|
235
335
|
state :inline_meta do
|
|
336
|
+
# A `..` / `....` right after the `,,` selects the triple form…
|
|
337
|
+
rule(/(?=[ \t]*(?:#{DT2}|#{QUAD}))/) { goto :inline_meta_op }
|
|
338
|
+
# …anything else is free-form meta text, trimmed of surrounding WS.
|
|
339
|
+
rule %r/[ \t]+/, Text
|
|
340
|
+
rule(/[^\s][^\n]*?(?=[ \t]*$|\n|\z)/) { |m| token Comment::Multiline, m[0] }
|
|
341
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
state :inline_meta_op do
|
|
236
345
|
rule %r/[ \t]+/, Text
|
|
237
|
-
rule(%r
|
|
346
|
+
rule(%r/#{QUAD}/) do |m| # implicit meta relation
|
|
347
|
+
token Comment::Hashbang, m[0]
|
|
348
|
+
goto :inline_meta_object
|
|
349
|
+
end
|
|
350
|
+
rule(%r/#{DT2}/) do |m|
|
|
238
351
|
token Comment::Hashbang, m[0]
|
|
239
352
|
goto :inline_meta_relation
|
|
240
353
|
end
|
|
241
|
-
rule(%r/\n/) { token Text,
|
|
354
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
242
355
|
end
|
|
243
356
|
|
|
244
357
|
state :inline_meta_relation do
|
|
245
|
-
rule
|
|
246
|
-
|
|
358
|
+
rule %r/[ \t]+/, Text
|
|
359
|
+
rule(/[^\s.,][^\n.,]*?(?=[ \t]*#{DT2}|[ \t]*$|\n|\z)/) do |m|
|
|
360
|
+
slot m, Comment::Doc
|
|
247
361
|
goto :inline_meta_after_relation
|
|
248
362
|
end
|
|
249
|
-
rule
|
|
250
|
-
rule(%r/\n/) { token Text, "\n"; goto :root }
|
|
363
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
251
364
|
end
|
|
252
365
|
|
|
253
366
|
state :inline_meta_after_relation do
|
|
254
367
|
rule %r/[ \t]+/, Text
|
|
255
|
-
rule(%r
|
|
368
|
+
rule(%r/#{DT2}/) do |m|
|
|
256
369
|
token Comment::Hashbang, m[0]
|
|
257
370
|
goto :inline_meta_object
|
|
258
371
|
end
|
|
259
|
-
rule(%r/\n/) { token Text,
|
|
372
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
260
373
|
end
|
|
261
374
|
|
|
375
|
+
# MetaObjectInline := TextExcept{NL,SC2} — stops at `;;`.
|
|
262
376
|
state :inline_meta_object do
|
|
263
|
-
rule
|
|
264
|
-
|
|
265
|
-
|
|
377
|
+
rule %r/[ \t]+/, Text
|
|
378
|
+
rule(%r/#{BLOCK_OPENER}/) { |m| open_block(m) }
|
|
379
|
+
rule(/[^\s;][^\n;]*?(?=[ \t]*#{SC2}|[ \t]*$|\n|\z)/) do |m|
|
|
380
|
+
slot m, Comment::Special
|
|
381
|
+
goto :inline_meta_next
|
|
266
382
|
end
|
|
383
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# `;;` divides several (relation, object) pairs about one triple.
|
|
387
|
+
state :inline_meta_next do
|
|
267
388
|
rule %r/[ \t]+/, Text
|
|
268
|
-
rule(%r
|
|
389
|
+
rule(%r/#{SC2}/) do |m|
|
|
390
|
+
token Punctuation, m[0]
|
|
391
|
+
goto :inline_meta_op
|
|
392
|
+
end
|
|
393
|
+
rule(%r/\n/) { |m| token Text, m[0]; goto :root }
|
|
269
394
|
end
|
|
270
395
|
end
|
|
271
396
|
end
|
data/lib/rouge-ddot.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Entry point for `require 'rouge-ddot'` — the path Bundler takes when the gem
|
|
2
|
+
# is listed in a Jekyll `:jekyll_plugins` group, or under any other
|
|
3
|
+
# `Bundler.require`, which requires a gem by its *name*.
|
|
4
|
+
#
|
|
5
|
+
# Rouge does not auto-discover third-party lexers: a lexer is registered only
|
|
6
|
+
# when its file is loaded. Up to 0.1.2 the gem shipped `lib/rouge/lexers/ddot.rb`
|
|
7
|
+
# alone, so `require 'rouge-ddot'` raised LoadError and every host had to know
|
|
8
|
+
# the internal path and write `require 'rouge/lexers/ddot'` into a plugin file
|
|
9
|
+
# of its own. This file makes the conventional require work.
|
|
10
|
+
#
|
|
11
|
+
# `require_relative` rather than `require` so loading does not depend on this
|
|
12
|
+
# gem's lib directory being on `$LOAD_PATH`. The lexer requires 'rouge' itself.
|
|
13
|
+
require_relative 'rouge/lexers/ddot'
|
data/rouge-ddot.gemspec
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'rouge-ddot'
|
|
5
|
-
s.version = '0.1.
|
|
5
|
+
s.version = '0.1.3'
|
|
6
6
|
s.summary = 'Rouge lexer for ddot.it'
|
|
7
7
|
s.description = 'Syntax highlighter for the ddot.it knowledge graph notation. ' \
|
|
8
8
|
'Plugs into Rouge so any tool that uses Rouge for source ' \
|
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
|
19
19
|
s.files = Dir[
|
|
20
20
|
'lib/**/*.rb',
|
|
21
21
|
'README.md',
|
|
22
|
+
'CHANGELOG.md',
|
|
22
23
|
'rouge-ddot.gemspec'
|
|
23
24
|
]
|
|
24
25
|
s.require_paths = ['lib']
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rouge-ddot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Calpano
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rouge
|
|
@@ -33,7 +33,9 @@ executables: []
|
|
|
33
33
|
extensions: []
|
|
34
34
|
extra_rdoc_files: []
|
|
35
35
|
files:
|
|
36
|
+
- CHANGELOG.md
|
|
36
37
|
- README.md
|
|
38
|
+
- lib/rouge-ddot.rb
|
|
37
39
|
- lib/rouge/lexers/ddot.rb
|
|
38
40
|
- rouge-ddot.gemspec
|
|
39
41
|
homepage: https://ddot.it
|