11bool isSpace(
char c) {
return c ==
' ' || c ==
'\t'; }
15size_t nextLine(
const char* src,
size_t n,
size_t pos, Span& out) {
18 while (i < n && src[i] !=
'\n') ++i;
20 if (end > start && src[end - 1] ==
'\r') --end;
22 out.len =
static_cast<uint16_t
>(end - start);
23 return (i < n) ? i + 1 : n;
27Span trimmed(Span raw, uint16_t& leading) {
29 while (a < raw.len && isSpace(raw.s[a])) ++a;
31 while (b > a && isSpace(raw.s[b - 1])) --b;
33 return Span{raw.s + a,
static_cast<uint16_t
>(b - a)};
38uint8_t headingLevel(Span t) {
40 while (h < t.len && t.s[h] ==
'#') ++h;
41 if (h >= 1 && h <= 6 && (h == t.len || t.s[h] ==
' '))
return h;
47 if (t.len < 3)
return false;
50 for (uint16_t i = 0; i < t.len; ++i) {
52 if (c ==
' ')
continue;
53 if (c !=
'-' && c !=
'*' && c !=
'_')
return false;
54 if (marker == 0) marker = c;
55 else if (c != marker)
return false;
61bool isUnorderedMarker(Span t) {
62 return t.len >= 2 && (t.s[0] ==
'-' || t.s[0] ==
'*' || t.s[0] ==
'+') && t.s[1] ==
' ';
66uint16_t orderedMarkerLen(Span t) {
68 while (i < t.len && t.s[i] >=
'0' && t.s[i] <=
'9') ++i;
69 if (i == 0 || i + 1 >= t.len)
return 0;
70 if ((t.s[i] ==
'.' || t.s[i] ==
')') && t.s[i + 1] ==
' ')
return static_cast<uint16_t
>(i + 2);
74bool isFenceDelim(Span t) {
76 ((t.s[0] ==
'`' && t.s[1] ==
'`' && t.s[2] ==
'`') ||
77 (t.s[0] ==
'~' && t.s[1] ==
'~' && t.s[2] ==
'~'));
81Span headingText(Span t, uint8_t level) {
83 if (a < t.len && t.s[a] ==
' ') ++a;
85 while (b > a && (t.s[b - 1] ==
'#' || t.s[b - 1] ==
' ')) --b;
86 return Span{t.s + a,
static_cast<uint16_t
>(b - a)};
98 if (!src)
return result;
102 bool present[7] = {
false,
false,
false,
false,
false,
false,
false};
108 pos = nextLine(src, n, pos, raw);
110 Span t = trimmed(raw, lead);
111 if (isFenceDelim(t)) { fence = !fence;
continue; }
113 uint8_t lvl = headingLevel(t);
114 if (lvl) present[lvl] =
true;
123 for (
int lvl = 6; lvl >= 1; --lvl) {
124 if (!present[lvl])
continue;
125 levelFont[lvl] = fonts[idx < 3 ? idx : 2];
135 pos = nextLine(src, n, pos, raw);
137 Span t = trimmed(raw, lead);
141 if (isFenceDelim(t)) {
158 }
else if (uint8_t lvl = headingLevel(t)) {
159 Span h = headingText(t, lvl);
161 line.
font = levelFont[lvl];
164 }
else if (isRule(t)) {
168 }
else if (t.s[0] ==
'>') {
170 if (a < t.len && t.s[a] ==
' ') ++a;
173 line.
len =
static_cast<uint16_t
>(t.len - a);
174 }
else if (uint16_t om = orderedMarkerLen(t)) {
176 line.
indent =
static_cast<uint8_t
>(lead / 2);
177 line.
text = t.s + om;
178 line.
len =
static_cast<uint16_t
>(t.len - om);
179 }
else if (isUnorderedMarker(t)) {
181 line.
indent =
static_cast<uint8_t
>(lead / 2);
183 line.
len =
static_cast<uint16_t
>(t.len - 2);
184 }
else if (lead >= 4) {
187 line.
text = raw.s + 4;
188 line.
len =
static_cast<uint16_t
>(raw.len - 4);
Centralized key-code constants for cdc_views.
MarkdownParseResult parseMarkdown(const char *src, size_t len, StyledLineSink &sink, size_t maxBytes)
Parses Markdown source into styled logical lines via a sink.
bool truncated
True when the source exceeded maxBytes.
uint16_t lineCount
Number of styled lines emitted.
Sink the parser emits styled lines to (decouples model from storage).
virtual void emit(const StyledLine &line)=0
One rendered logical line produced by the Markdown parser.
uint16_t len
Span length in bytes.
uint8_t font
One of mdfont.
uint8_t indent
Nesting depth (view multiplies by columns).
const char * text
Span into source (not null-terminated).
bool inverted
Inverse background (code).