1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![cfg(all(feature = "codemap", feature = "codemap-diagnostic"))]

use std::vec::Vec;

use codemap_diagnostic::SpanLabel;

use crate::CodemapFiles;
#[cfg(feature = "pulldown-cmark")]
use crate::{CMarkSpan, TextSource};

/// Codemap span labels temporary storage used to create diagnostic messages.
#[derive(Debug)]
pub struct CodemapSpans<'a> {
    codemap_files: &'a mut CodemapFiles,
    span_labels: Vec<SpanLabel>,
}

impl<'a> CodemapSpans<'a> {
    /// Creates a new codemap spans storage.
    pub fn new(codemap_files: &'a mut CodemapFiles) -> Self {
        CodemapSpans {
            codemap_files,
            span_labels: Vec::new(),
        }
    }

    /// Returns codemap files storage.
    pub fn codemap_files(&self) -> &CodemapFiles {
        &self.codemap_files
    }

    /// Returns a slice of span labels.
    pub fn span_labels(&self) -> &[SpanLabel] {
        &self.span_labels
    }

    /// Converts this codemap spans to span labels.
    pub fn into_span_labels(self) -> Vec<SpanLabel> {
        self.span_labels
    }

    #[cfg(feature = "pulldown-cmark")]
    /// Generate span labels from the given codemap files and CMark spans.
    pub fn span_labels_from<I>(codemap_files: &'a mut CodemapFiles, iter: I) -> Vec<SpanLabel>
    where
        I: IntoIterator<Item = CMarkSpan<'a>>,
    {
        let mut codemap_spans = Self::new(codemap_files);
        codemap_spans.extend(iter);
        codemap_spans.into_span_labels()
    }
}

trait FileSubSpan {
    fn subspan(&self, range: &core::ops::Range<usize>) -> codemap::Span;
}

impl FileSubSpan for codemap::File {
    fn subspan(&self, range: &core::ops::Range<usize>) -> codemap::Span {
        self.span.subspan(range.start as u64, range.end as u64)
    }
}

#[cfg(feature = "pulldown-cmark")]
impl<'a> Extend<CMarkSpan<'a>> for CodemapSpans<'_> {
    fn extend<T: IntoIterator<Item = CMarkSpan<'a>>>(&mut self, iter: T) {
        use codemap_diagnostic::SpanStyle;

        let iter = iter.into_iter();
        if let Some(upper) = iter.size_hint().1 {
            self.span_labels.reserve(upper);
        }
        for item in iter {
            match item.text_source {
                TextSource::File(file) => {
                    let span = self
                        .codemap_files
                        .get_or_insert_codemap_file(file)
                        .subspan(item.range);
                    self.span_labels.push(SpanLabel {
                        span,
                        style: SpanStyle::Primary,
                        label: Some(item.note),
                    });
                }
                TextSource::FileDocs(file_docs) => {
                    let span = self
                        .codemap_files
                        .get_or_insert_codemap_docs_file(file_docs)
                        .subspan(item.range);
                    self.span_labels.push(SpanLabel {
                        span,
                        style: SpanStyle::Primary,
                        label: Some(item.note.clone()),
                    });

                    let file = file_docs.file();
                    let file_range = file_docs.remap_to_file(item.range.clone());
                    if let Some(file_range) = file_range {
                        let span = self
                            .codemap_files
                            .get_or_insert_codemap_file(file)
                            .subspan(&file_range);
                        self.span_labels.push(SpanLabel {
                            span,
                            style: SpanStyle::Secondary,
                            label: Some(item.note),
                        });
                    }
                }
            }
        }
    }
}