readme_sync/
codemap_spans.rs

1use std::vec::Vec;
2
3use codemap_diagnostic::SpanLabel;
4
5use crate::{CMarkSpan, CodemapFiles, TextSource};
6
7/// Codemap span labels temporary storage used to create diagnostic messages.
8#[derive(Debug)]
9pub struct CodemapSpans<'a> {
10    codemap_files: &'a mut CodemapFiles,
11    span_labels: Vec<SpanLabel>,
12}
13
14impl<'a> CodemapSpans<'a> {
15    /// Creates a new codemap spans storage.
16    pub fn new(codemap_files: &'a mut CodemapFiles) -> Self {
17        CodemapSpans {
18            codemap_files,
19            span_labels: Vec::new(),
20        }
21    }
22
23    /// Returns codemap files storage.
24    pub fn codemap_files(&self) -> &CodemapFiles {
25        self.codemap_files
26    }
27
28    /// Returns a slice of span labels.
29    pub fn span_labels(&self) -> &[SpanLabel] {
30        &self.span_labels
31    }
32
33    /// Converts this codemap spans to span labels.
34    pub fn into_span_labels(self) -> Vec<SpanLabel> {
35        self.span_labels
36    }
37
38    /// Generate span labels from the given codemap files and CMark spans.
39    pub fn span_labels_from<I>(codemap_files: &'a mut CodemapFiles, iter: I) -> Vec<SpanLabel>
40    where
41        I: IntoIterator<Item = CMarkSpan<'a>>,
42    {
43        let mut codemap_spans = Self::new(codemap_files);
44        codemap_spans.extend(iter);
45        codemap_spans.into_span_labels()
46    }
47}
48
49trait FileSubSpan {
50    fn subspan(&self, range: &core::ops::Range<usize>) -> codemap::Span;
51}
52
53impl FileSubSpan for codemap::File {
54    fn subspan(&self, range: &core::ops::Range<usize>) -> codemap::Span {
55        self.span.subspan(range.start as u64, range.end as u64)
56    }
57}
58
59impl<'a> Extend<CMarkSpan<'a>> for CodemapSpans<'_> {
60    fn extend<T: IntoIterator<Item = CMarkSpan<'a>>>(&mut self, iter: T) {
61        use codemap_diagnostic::SpanStyle;
62
63        let iter = iter.into_iter();
64        if let Some(upper) = iter.size_hint().1 {
65            self.span_labels.reserve(upper);
66        }
67        for item in iter {
68            match item.text_source {
69                TextSource::File(file) => {
70                    let span = self
71                        .codemap_files
72                        .get_or_insert_codemap_file(file)
73                        .subspan(item.range);
74                    self.span_labels.push(SpanLabel {
75                        span,
76                        style: SpanStyle::Primary,
77                        label: None,
78                    });
79                }
80                TextSource::FileDocs(file_docs) => {
81                    let span = self
82                        .codemap_files
83                        .get_or_insert_codemap_docs_file(file_docs)
84                        .subspan(item.range);
85                    self.span_labels.push(SpanLabel {
86                        span,
87                        style: SpanStyle::Primary,
88                        label: None,
89                    });
90
91                    let file = file_docs.file();
92                    let file_range = file_docs.remap_to_file(item.range.clone());
93                    if let Some(file_range) = file_range {
94                        let span = self
95                            .codemap_files
96                            .get_or_insert_codemap_file(file)
97                            .subspan(&file_range);
98                        self.span_labels.push(SpanLabel {
99                            span,
100                            style: SpanStyle::Secondary,
101                            label: None,
102                        });
103                    }
104                }
105            }
106        }
107    }
108}