1use crate::{
4 Error, HighlightConfig, HighlightResult, backend::Backend, highlight, language::Registry,
5 syntax::Syntax,
6};
7
8pub use crate::syntax::Edit;
9
10pub struct Document<B: Backend> {
17 backend: B,
18 registry: Registry<B>,
19 language: String,
20 source: String,
21 syntax: Option<Syntax<B>>,
22 cached: Option<(bool, HighlightResult)>,
23}
24
25impl<B: Backend> Document<B> {
26 pub(crate) fn new(
27 registry: Registry<B>,
28 mut backend: B,
29 name: &str,
30 source: &str,
31 ) -> Result<Self, Error> {
32 let language = registry.resolve(name)?.to_owned();
33 let syntax = Syntax::parse(&mut backend, ®istry, &language, source, None, None)?;
34 Ok(Self {
35 backend,
36 registry,
37 language,
38 source: source.to_owned(),
39 syntax: Some(syntax),
40 cached: None,
41 })
42 }
43
44 pub fn source(&self) -> &str {
46 &self.source
47 }
48
49 pub fn update(&mut self, source: &str) -> Result<(), Error> {
57 if self.source == source {
58 return Ok(());
59 }
60 let edit = Edit::between(&self.source, source);
61 let syntax = Syntax::parse(
62 &mut self.backend,
63 &self.registry,
64 &self.language,
65 source,
66 self.syntax.take(),
67 Some(&edit),
68 )?;
69 self.source.clear();
70 self.source.push_str(source);
71 self.syntax = Some(syntax);
72 self.cached = None;
73 Ok(())
74 }
75
76 pub fn highlight(&mut self, config: &HighlightConfig) -> Result<HighlightResult, Error> {
79 Ok(self.highlight_ref(config)?.clone())
80 }
81
82 pub fn highlight_ref(&mut self, config: &HighlightConfig) -> Result<&HighlightResult, Error> {
89 if self
90 .cached
91 .as_ref()
92 .is_none_or(|(rainbow, _)| *rainbow != config.rainbow)
93 {
94 if self.syntax.is_none() {
95 self.syntax = Some(Syntax::parse(
96 &mut self.backend,
97 &self.registry,
98 &self.language,
99 &self.source,
100 None,
101 None,
102 )?);
103 }
104 let highlighted = highlight::run(
105 &mut self.backend,
106 &self.registry,
107 &self.source,
108 self.syntax.as_ref().expect("parsed above"),
109 config,
110 )?;
111 self.cached = Some((config.rainbow, highlighted.to_owned()));
112 }
113 let result = &mut self.cached.as_mut().expect("highlighted above").1;
114 let mut selected = config.highlighted_lines.clone();
115 selected.sort_unstable();
116 let mut selected = selected.into_iter().peekable();
117
118 for line in &mut result.lines {
119 while selected.peek().is_some_and(|number| *number < line.number) {
120 selected.next();
121 }
122 line.highlighted = selected.peek() == Some(&line.number);
123 }
124 Ok(result)
125 }
126}