1use std::ops::Range;
2
3use crate::parser::utils::yaml_regions::hashpipe_language_and_prefix;
4use crate::syntax::{AstNode, PanacheLanguage, SyntaxKind, SyntaxNode};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct YamlFrontmatterRegion {
8 pub id: String,
9 pub host_range: Range<usize>,
10 pub content_range: Range<usize>,
11 pub content: String,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum YamlRegionKind {
16 Frontmatter,
17 Hashpipe,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct YamlRegion {
22 pub id: String,
23 pub kind: YamlRegionKind,
24 pub host_range: Range<usize>,
25 pub region_range: Range<usize>,
26 pub content_range: Range<usize>,
27 pub content: String,
28 pub yaml_to_host_offsets: Vec<usize>,
29}
30
31#[derive(Debug, Clone)]
32pub struct ParsedYamlRegion {
33 region: YamlRegion,
34 parse_result: Result<yaml_parser::SyntaxNode, yaml_parser::SyntaxError>,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ParsedYamlRegionSnapshot {
39 region: YamlRegion,
40 parse_ok: bool,
41 error: Option<YamlParseError>,
42 document_shape_summary: Option<String>,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum YamlEmbeddingHostKind {
47 FrontmatterMetadata,
48 HashpipePreamble,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub struct YamlMetadata(SyntaxNode);
53
54impl AstNode for YamlMetadata {
55 type Language = PanacheLanguage;
56
57 fn can_cast(kind: SyntaxKind) -> bool {
58 kind == SyntaxKind::YAML_METADATA
59 }
60
61 fn cast(node: SyntaxNode) -> Option<Self> {
62 Self::can_cast(node.kind()).then(|| Self(node))
63 }
64
65 fn syntax(&self) -> &SyntaxNode {
66 &self.0
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub struct HashpipeYamlPreamble(SyntaxNode);
72
73impl AstNode for HashpipeYamlPreamble {
74 type Language = PanacheLanguage;
75
76 fn can_cast(kind: SyntaxKind) -> bool {
77 kind == SyntaxKind::HASHPIPE_YAML_PREAMBLE
78 }
79
80 fn cast(node: SyntaxNode) -> Option<Self> {
81 Self::can_cast(node.kind()).then(|| Self(node))
82 }
83
84 fn syntax(&self) -> &SyntaxNode {
85 &self.0
86 }
87}
88
89#[derive(Debug, Clone)]
90pub enum YamlEmbeddingHost {
91 FrontmatterMetadata(YamlMetadata),
92 HashpipePreamble(HashpipeYamlPreamble),
93}
94
95#[derive(Debug, Clone)]
96pub struct YamlEmbeddedCst {
97 host: YamlEmbeddingHost,
98 parsed: ParsedYamlRegion,
99}
100
101impl YamlEmbeddedCst {
102 pub fn host_kind(&self) -> YamlEmbeddingHostKind {
103 match self.host {
104 YamlEmbeddingHost::FrontmatterMetadata(_) => YamlEmbeddingHostKind::FrontmatterMetadata,
105 YamlEmbeddingHost::HashpipePreamble(_) => YamlEmbeddingHostKind::HashpipePreamble,
106 }
107 }
108
109 pub fn host_node(&self) -> &SyntaxNode {
110 match &self.host {
111 YamlEmbeddingHost::FrontmatterMetadata(host) => host.syntax(),
112 YamlEmbeddingHost::HashpipePreamble(host) => host.syntax(),
113 }
114 }
115
116 pub fn frontmatter_host(&self) -> Option<&YamlMetadata> {
117 match &self.host {
118 YamlEmbeddingHost::FrontmatterMetadata(host) => Some(host),
119 _ => None,
120 }
121 }
122
123 pub fn hashpipe_host(&self) -> Option<&HashpipeYamlPreamble> {
124 match &self.host {
125 YamlEmbeddingHost::HashpipePreamble(host) => Some(host),
126 _ => None,
127 }
128 }
129
130 pub fn parsed(&self) -> &ParsedYamlRegion {
131 &self.parsed
132 }
133
134 pub fn yaml_content(&self) -> &str {
135 self.parsed.content()
136 }
137
138 pub fn host_offset_for_yaml_offset(&self, yaml_offset: usize) -> Option<usize> {
139 self.parsed.host_offset_for_yaml_offset(yaml_offset)
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum YamlAstRootKind {
145 Root,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum YamlDocumentKind {
150 BlockMap,
151 BlockSeq,
152 BlockScalar,
153 Flow,
154 Empty,
155}
156
157#[derive(Debug, Clone, Copy)]
158pub struct YamlAstRoot<'a> {
159 node: &'a yaml_parser::SyntaxNode,
160}
161
162impl<'a> YamlAstRoot<'a> {
163 pub fn kind(&self) -> YamlAstRootKind {
164 debug_assert_eq!(self.node.kind(), yaml_parser::SyntaxKind::ROOT);
165 YamlAstRootKind::Root
166 }
167
168 pub fn document_count(&self) -> usize {
169 yaml_parser::ast::Root::cast(self.node.clone())
170 .map(|root| root.documents().count())
171 .unwrap_or(0)
172 }
173
174 pub fn first_document_kind(&self) -> Option<YamlDocumentKind> {
175 let root = yaml_parser::ast::Root::cast(self.node.clone())?;
176 let doc = root.documents().next()?;
177 if let Some(block) = doc.block() {
178 if block.block_map().is_some() {
179 return Some(YamlDocumentKind::BlockMap);
180 }
181 if block.block_seq().is_some() {
182 return Some(YamlDocumentKind::BlockSeq);
183 }
184 if block.block_scalar().is_some() {
185 return Some(YamlDocumentKind::BlockScalar);
186 }
187 return Some(YamlDocumentKind::Empty);
188 }
189 if doc.flow().is_some() {
190 return Some(YamlDocumentKind::Flow);
191 }
192 Some(YamlDocumentKind::Empty)
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct YamlParseError {
198 offset: usize,
199 message: String,
200}
201
202impl YamlParseError {
203 pub fn offset(&self) -> usize {
204 self.offset
205 }
206
207 pub fn message(&self) -> &str {
208 &self.message
209 }
210
211 fn from_parser(err: &yaml_parser::SyntaxError) -> Self {
212 Self {
213 offset: err.offset(),
214 message: err.message().to_string(),
215 }
216 }
217}
218
219impl ParsedYamlRegion {
220 pub fn id(&self) -> &str {
221 &self.region.id
222 }
223
224 pub fn kind(&self) -> &YamlRegionKind {
225 &self.region.kind
226 }
227
228 pub fn is_frontmatter(&self) -> bool {
229 matches!(self.region.kind, YamlRegionKind::Frontmatter)
230 }
231
232 pub fn is_hashpipe(&self) -> bool {
233 matches!(self.region.kind, YamlRegionKind::Hashpipe)
234 }
235
236 pub fn root(&self) -> Option<YamlAstRoot<'_>> {
237 self.parse_result
238 .as_ref()
239 .ok()
240 .map(|node| YamlAstRoot { node })
241 }
242
243 pub fn error(&self) -> Option<YamlParseError> {
244 self.parse_result.as_ref().err().map(|err| YamlParseError {
245 offset: err.offset(),
246 message: err.message().to_string(),
247 })
248 }
249
250 pub fn root_kind(&self) -> Option<YamlAstRootKind> {
251 self.root().map(|root| root.kind())
252 }
253
254 pub fn is_valid(&self) -> bool {
255 self.parse_result.is_ok()
256 }
257
258 pub fn host_range(&self) -> Range<usize> {
259 self.region.host_range.clone()
260 }
261
262 pub fn content_range(&self) -> Range<usize> {
263 self.region.content_range.clone()
264 }
265
266 pub fn region_range(&self) -> Range<usize> {
267 self.region.region_range.clone()
268 }
269
270 pub fn to_region(&self) -> YamlRegion {
271 self.region.clone()
272 }
273
274 pub fn content(&self) -> &str {
275 &self.region.content
276 }
277
278 pub fn host_offset_for_yaml_offset(&self, yaml_offset: usize) -> Option<usize> {
279 self.region.yaml_to_host_offsets.get(yaml_offset).copied()
280 }
281
282 pub fn parse_error_host_offset(&self) -> Option<usize> {
283 self.error()
284 .and_then(|err| self.host_offset_for_yaml_offset(err.offset()))
285 }
286
287 pub fn document_shape_summary(&self) -> Option<String> {
288 let root = self.root()?;
289 let doc_count = root.document_count();
290 let first_kind = root.first_document_kind();
291 Some(match first_kind {
292 Some(kind) => format!("{:?} docs={} first={:?}", root.kind(), doc_count, kind),
293 None => format!("{:?} docs={}", root.kind(), doc_count),
294 })
295 }
296
297 pub fn to_snapshot(&self) -> ParsedYamlRegionSnapshot {
298 ParsedYamlRegionSnapshot {
299 region: self.region.clone(),
300 parse_ok: self.is_valid(),
301 error: self.error(),
302 document_shape_summary: self.document_shape_summary(),
303 }
304 }
305}
306
307impl ParsedYamlRegionSnapshot {
308 pub fn id(&self) -> &str {
309 &self.region.id
310 }
311
312 pub fn is_frontmatter(&self) -> bool {
313 matches!(self.region.kind, YamlRegionKind::Frontmatter)
314 }
315
316 pub fn is_hashpipe(&self) -> bool {
317 matches!(self.region.kind, YamlRegionKind::Hashpipe)
318 }
319
320 pub fn is_valid(&self) -> bool {
321 self.parse_ok
322 }
323
324 pub fn error(&self) -> Option<&YamlParseError> {
325 self.error.as_ref()
326 }
327
328 pub fn host_range(&self) -> Range<usize> {
329 self.region.host_range.clone()
330 }
331
332 pub fn parse_error_host_offset(&self) -> Option<usize> {
333 let err = self.error()?;
334 self.region.yaml_to_host_offsets.get(err.offset()).copied()
335 }
336
337 pub fn document_shape_summary(&self) -> Option<&str> {
338 self.document_shape_summary.as_deref()
339 }
340
341 pub fn to_region(&self) -> YamlRegion {
342 self.region.clone()
343 }
344}
345
346pub fn collect_frontmatter_region(tree: &SyntaxNode) -> Option<YamlFrontmatterRegion> {
347 let metadata = tree
348 .descendants()
349 .find(|node| node.kind() == SyntaxKind::YAML_METADATA)?;
350 let content_node = metadata
351 .children()
352 .find(|child| child.kind() == SyntaxKind::YAML_METADATA_CONTENT)?;
353
354 let host_start: usize = metadata.text_range().start().into();
355 let host_end: usize = metadata.text_range().end().into();
356 let content_start: usize = content_node.text_range().start().into();
357 let content_end: usize = content_node.text_range().end().into();
358
359 Some(YamlFrontmatterRegion {
360 id: format!("frontmatter:{}:{}", content_start, content_end),
361 host_range: host_start..host_end,
362 content_range: content_start..content_end,
363 content: content_node.text().to_string(),
364 })
365}
366
367pub fn collect_frontmatter_yaml_region(tree: &SyntaxNode) -> Option<YamlRegion> {
368 let frontmatter = collect_frontmatter_region(tree)?;
369 let content_range = frontmatter.content_range.clone();
370 Some(YamlRegion {
371 id: frontmatter.id,
372 kind: YamlRegionKind::Frontmatter,
373 host_range: frontmatter.host_range.clone(),
374 region_range: frontmatter.host_range,
375 content_range: content_range.clone(),
376 yaml_to_host_offsets: (0..=frontmatter.content.len())
377 .map(|offset| content_range.start + offset)
378 .collect(),
379 content: frontmatter.content,
380 })
381}
382
383pub fn collect_hashpipe_regions(tree: &SyntaxNode) -> Vec<YamlRegion> {
384 let mut regions = Vec::new();
385 for node in tree
386 .descendants()
387 .filter(|n| n.kind() == SyntaxKind::CODE_BLOCK)
388 {
389 let mut info_text: Option<String> = None;
390 let mut content_node: Option<SyntaxNode> = None;
391 for child in node.children() {
392 match child.kind() {
393 SyntaxKind::CODE_FENCE_OPEN => {
394 for nested in child.children() {
395 if nested.kind() == SyntaxKind::CODE_INFO {
396 info_text = Some(nested.text().to_string());
397 }
398 }
399 }
400 SyntaxKind::CODE_CONTENT => content_node = Some(child),
401 _ => {}
402 }
403 }
404 let (Some(info_text), Some(content_node)) = (info_text, content_node) else {
405 continue;
406 };
407 let Some((language, prefix)) = hashpipe_language_and_prefix(&info_text) else {
408 continue;
409 };
410
411 let host_start: usize = node.text_range().start().into();
412 let host_end: usize = node.text_range().end().into();
413 let Some(preamble) = content_node
414 .children()
415 .find(|n| n.kind() == SyntaxKind::HASHPIPE_YAML_PREAMBLE)
416 else {
417 continue;
418 };
419 let Some(preamble_content) = preamble
420 .children()
421 .find(|n| n.kind() == SyntaxKind::HASHPIPE_YAML_CONTENT)
422 else {
423 continue;
424 };
425 let preamble_text = preamble_content.text().to_string();
426 let preamble_start: usize = preamble_content.text_range().start().into();
427 if let Some(region) = extract_hashpipe_region(
428 &preamble_text,
429 host_start,
430 host_end,
431 preamble_start,
432 prefix,
433 language.as_str(),
434 ) {
435 regions.push(region);
436 }
437 }
438 regions
439}
440
441pub fn collect_yaml_regions(tree: &SyntaxNode) -> Vec<YamlRegion> {
442 let mut regions = Vec::new();
443 if let Some(frontmatter) = collect_frontmatter_yaml_region(tree) {
444 regions.push(frontmatter);
445 }
446 regions.extend(collect_hashpipe_regions(tree));
447 regions
448}
449
450pub fn collect_parsed_yaml_regions(tree: &SyntaxNode) -> Vec<ParsedYamlRegion> {
451 collect_yaml_regions(tree)
452 .into_iter()
453 .map(|region| ParsedYamlRegion {
454 parse_result: yaml_parser::parse(®ion.content),
455 region,
456 })
457 .collect()
458}
459
460pub fn collect_parsed_frontmatter_region(tree: &SyntaxNode) -> Option<ParsedYamlRegion> {
461 collect_parsed_yaml_regions(tree)
462 .into_iter()
463 .find(|region| region.is_frontmatter())
464}
465
466pub fn collect_parsed_yaml_region_snapshots(tree: &SyntaxNode) -> Vec<ParsedYamlRegionSnapshot> {
467 collect_parsed_yaml_regions(tree)
468 .iter()
469 .map(ParsedYamlRegion::to_snapshot)
470 .collect()
471}
472
473pub fn validate_yaml_text(input: &str) -> Result<(), YamlParseError> {
474 yaml_parser::parse(input)
475 .map(|_| ())
476 .map_err(|err| YamlParseError::from_parser(&err))
477}
478
479pub fn collect_embedded_yaml_cst(tree: &SyntaxNode) -> Vec<YamlEmbeddedCst> {
480 let parsed_regions = collect_parsed_yaml_regions(tree);
481 let frontmatter_node = tree.descendants().find_map(YamlMetadata::cast);
482 let hashpipe_preambles: Vec<HashpipeYamlPreamble> = tree
483 .descendants()
484 .filter_map(HashpipeYamlPreamble::cast)
485 .collect();
486
487 let mut embedded = Vec::new();
488 for parsed in parsed_regions {
489 match parsed.kind() {
490 YamlRegionKind::Frontmatter => {
491 if let Some(node) = frontmatter_node.clone() {
492 embedded.push(YamlEmbeddedCst {
493 host: YamlEmbeddingHost::FrontmatterMetadata(node),
494 parsed,
495 });
496 }
497 }
498 YamlRegionKind::Hashpipe => {
499 if let Some(node) = hashpipe_preambles.iter().find(|node| {
500 let range: Range<usize> = node.syntax().text_range().start().into()
501 ..node.syntax().text_range().end().into();
502 range == parsed.region_range()
503 }) {
504 embedded.push(YamlEmbeddedCst {
505 host: YamlEmbeddingHost::HashpipePreamble(node.clone()),
506 parsed,
507 });
508 }
509 }
510 }
511 }
512 embedded
513}
514
515pub fn collect_embedded_frontmatter_yaml_cst(tree: &SyntaxNode) -> Option<YamlEmbeddedCst> {
516 collect_embedded_yaml_cst(tree)
517 .into_iter()
518 .find(|embedded| embedded.frontmatter_host().is_some())
519}
520
521fn extract_hashpipe_region(
522 content: &str,
523 host_start: usize,
524 host_end: usize,
525 content_start: usize,
526 prefix: &str,
527 language: &str,
528) -> Option<YamlRegion> {
529 let lines: Vec<&str> = content.split_inclusive('\n').collect();
530 if lines.is_empty() {
531 return None;
532 }
533 let mut collected = String::new();
534 let mut yaml_to_host_offsets = Vec::new();
535 let mut offset = 0usize;
536 for line in &lines {
537 let line = *line;
538 let line_core = line.strip_suffix('\n').unwrap_or(line);
539 let line_core = line_core.strip_suffix('\r').unwrap_or(line_core);
540 let eol = &line[line_core.len()..];
541 let indent_len = line_core
542 .chars()
543 .take_while(|ch| *ch == ' ' || *ch == '\t')
544 .map(char::len_utf8)
545 .sum::<usize>();
546 let trimmed = &line_core[indent_len..];
547 let after_prefix = trimmed.strip_prefix(prefix)?;
548 let payload = after_prefix
549 .strip_prefix(' ')
550 .or_else(|| after_prefix.strip_prefix('\t'))
551 .unwrap_or(after_prefix);
552 let after_prefix_start = indent_len + (trimmed.len() - after_prefix.len());
553 let payload_start = after_prefix_start + (after_prefix.len() - payload.len());
554 let line_host_start = content_start + offset;
555 yaml_to_host_offsets
556 .extend((0..payload.len()).map(|i| line_host_start + payload_start + i));
557 yaml_to_host_offsets.extend((0..eol.len()).map(|i| line_host_start + line_core.len() + i));
558 collected.push_str(payload);
559 collected.push_str(eol);
560 offset += line.len();
561 }
562 let start = content_start;
563 let region_end = content_start + offset;
564 yaml_to_host_offsets.push(region_end);
565 let id = format!("hashpipe:{}:{}:{}", language, host_start, start);
566 Some(YamlRegion {
567 id,
568 kind: YamlRegionKind::Hashpipe,
569 host_range: host_start..host_end,
570 region_range: start..region_end,
571 content_range: start..region_end,
572 content: collected,
573 yaml_to_host_offsets,
574 })
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580
581 #[test]
582 fn parsed_yaml_regions_include_frontmatter_and_hashpipe_cst_roots() {
583 let input = "---\ntitle: Test\n---\n\n```{r}\n#| echo: false\n1 + 1\n```\n";
584 let config = crate::config::Config {
585 flavor: crate::config::Flavor::Quarto,
586 extensions: crate::config::Extensions::for_flavor(crate::config::Flavor::Quarto),
587 ..Default::default()
588 };
589 let tree = crate::parser::parse(input, Some(config));
590 let regions = collect_parsed_yaml_regions(&tree);
591 assert_eq!(regions.len(), 2);
592 assert!(regions.iter().any(|parsed| {
593 parsed.is_frontmatter() && parsed.root_kind() == Some(YamlAstRootKind::Root)
594 }));
595 assert!(regions.iter().any(|parsed| {
596 parsed.is_hashpipe() && parsed.root_kind() == Some(YamlAstRootKind::Root)
597 }));
598 }
599
600 #[test]
601 fn parsed_yaml_region_maps_parse_error_to_host_offset() {
602 let input = "```{r}\n#| echo: [\n1 + 1\n```\n";
603 let config = crate::config::Config {
604 flavor: crate::config::Flavor::Quarto,
605 extensions: crate::config::Extensions::for_flavor(crate::config::Flavor::Quarto),
606 ..Default::default()
607 };
608 let tree = crate::parser::parse(input, Some(config));
609 let parsed = collect_parsed_yaml_regions(&tree);
610 let hashpipe = parsed
611 .iter()
612 .find(|region| region.is_hashpipe())
613 .expect("hashpipe region");
614 let host_offset = hashpipe
615 .parse_error_host_offset()
616 .expect("expected parse error host offset");
617 let expected = input.find('[').expect("expected '[' in input");
618 assert_eq!(host_offset, expected);
619 }
620
621 #[test]
622 fn yaml_ast_root_reports_document_shape() {
623 let input = "---\ntitle: Test\n---\n";
624 let tree = crate::parser::parse(input, None);
625 let parsed = collect_parsed_frontmatter_region(&tree).expect("frontmatter");
626 let root = parsed.root().expect("yaml root");
627 assert_eq!(root.document_count(), 1);
628 assert_eq!(root.first_document_kind(), Some(YamlDocumentKind::BlockMap));
629 }
630
631 #[test]
632 fn embedded_yaml_cst_attaches_to_frontmatter_and_hashpipe_hosts() {
633 let input = "---\ntitle: Test\n---\n\n```{r}\n#| echo: false\nx <- 1\n```\n";
634 let config = crate::config::Config {
635 flavor: crate::config::Flavor::Quarto,
636 extensions: crate::config::Extensions::for_flavor(crate::config::Flavor::Quarto),
637 ..Default::default()
638 };
639 let tree = crate::parser::parse(input, Some(config));
640 let embedded = collect_embedded_yaml_cst(&tree);
641 assert_eq!(embedded.len(), 2);
642 assert!(
643 embedded
644 .iter()
645 .any(|item| item.frontmatter_host().is_some())
646 );
647 assert!(embedded.iter().any(|item| item.hashpipe_host().is_some()));
648 }
649
650 #[test]
651 fn embedded_yaml_cst_exposes_frontmatter_and_hashpipe_payloads() {
652 let input = "---\ntitle: Test\n---\n\n```{r}\n#| fig-cap: |\n#| A caption\nx <- 1\n```\n";
653 let config = crate::config::Config {
654 flavor: crate::config::Flavor::Quarto,
655 extensions: crate::config::Extensions::for_flavor(crate::config::Flavor::Quarto),
656 ..Default::default()
657 };
658 let tree = crate::parser::parse(input, Some(config));
659 let embedded = collect_embedded_yaml_cst(&tree);
660 assert_eq!(embedded.len(), 2);
661
662 let frontmatter = embedded
663 .iter()
664 .find(|item| item.frontmatter_host().is_some())
665 .expect("frontmatter embedding");
666 assert!(frontmatter.parsed().is_valid());
667 assert_eq!(
668 frontmatter.parsed().document_shape_summary().as_deref(),
669 Some("Root docs=1 first=BlockMap")
670 );
671
672 let hashpipe = embedded
673 .iter()
674 .find(|item| item.hashpipe_host().is_some())
675 .expect("hashpipe embedding");
676 assert!(hashpipe.parsed().is_valid());
677 assert!(hashpipe.parsed().to_region().content.contains("fig-cap: |"));
678 }
679
680 #[test]
681 fn embedded_frontmatter_query_returns_typed_host_wrapper() {
682 let input = "---\ntitle: Test\n---\n\nBody\n";
683 let tree = crate::parser::parse(input, None);
684 let embedded = collect_embedded_frontmatter_yaml_cst(&tree).expect("frontmatter embedding");
685 let _host = embedded.frontmatter_host().expect("frontmatter host");
686 assert!(embedded.hashpipe_host().is_none());
687 }
688
689 #[test]
690 fn yaml_offset_map_includes_eof_position() {
691 let input = "---\ntitle: Test\n---\n";
692 let tree = crate::parser::parse(input, None);
693 let parsed = collect_parsed_frontmatter_region(&tree).expect("frontmatter");
694 let eof_yaml_offset = parsed.content().len();
695 assert_eq!(
696 parsed.host_offset_for_yaml_offset(eof_yaml_offset),
697 Some(parsed.content_range().end)
698 );
699 }
700}