miette/highlighters/mod.rs
1//! This module provides a trait for creating custom syntax highlighters that
2//! highlight [`Diagnostic`](crate::Diagnostic) source code with ANSI escape
3//! sequences when rendering with the [`GraphicalReportHighlighter`](crate::GraphicalReportHandler).
4//!
5//! It also provides built-in highlighter implementations that you can use out of the box.
6//! By default, there are no syntax highlighters exported by miette
7//! (except for the no-op [`BlankHighlighter`]).
8//! To enable support for specific highlighters, you should enable their associated feature flag.
9//!
10//! Currently supported syntax highlighters and their feature flags:
11//! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`]
12//!
13
14use std::{ops::Deref, sync::Arc};
15
16use crate::SpanContents;
17use owo_colors::Styled;
18
19#[cfg(feature = "syntect-highlighter")]
20pub use self::syntect::*;
21pub use blank::*;
22
23mod blank;
24#[cfg(feature = "syntect-highlighter")]
25mod syntect;
26
27/// A syntax highlighter for highlighting miette [`SourceCode`](crate::SourceCode) snippets.
28pub trait Highlighter {
29 /// Creates a new [`HighlighterState`] to begin parsing and highlighting
30 /// a [`SpanContents`].
31 ///
32 /// The [`GraphicalReportHandler`](crate::GraphicalReportHandler) will call
33 /// this method at the start of rendering a [`SpanContents`].
34 ///
35 /// The [`SpanContents`] is provided as input only so that the [`Highlighter`]
36 /// can detect language syntax and make other initialization decisions prior
37 /// to highlighting, but it is not intended that the Highlighter begin
38 /// highlighting at this point. The returned [`HighlighterState`] is
39 /// responsible for the actual rendering.
40 fn start_highlighter_state<'h>(
41 &'h self,
42 source: &dyn SpanContents<'_>,
43 ) -> Box<dyn HighlighterState + 'h>;
44}
45
46/// A stateful highlighter that incrementally highlights lines of a particular
47/// source code.
48///
49/// The [`GraphicalReportHandler`](crate::GraphicalReportHandler)
50/// will create a highlighter state by calling
51/// [`start_highlighter_state`](Highlighter::start_highlighter_state) at the
52/// start of rendering, then it will iteratively call
53/// [`highlight_line`](HighlighterState::highlight_line) to render individual
54/// highlighted lines. This allows [`Highlighter`] implementations to maintain
55/// mutable parsing and highlighting state.
56pub trait HighlighterState {
57 /// Highlight an individual line from the source code by returning a vector of [Styled]
58 /// regions.
59 fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>>;
60}
61
62/// Arcified trait object for Highlighter. Used internally by [`GraphicalReportHandler`]
63///
64/// Wrapping the trait object in this way allows us to implement `Debug` and `Clone`.
65#[derive(Clone)]
66#[repr(transparent)]
67pub(crate) struct MietteHighlighter(Arc<dyn Highlighter + Send + Sync>);
68
69impl MietteHighlighter {
70 pub(crate) fn nocolor() -> Self {
71 Self::from(BlankHighlighter)
72 }
73
74 #[cfg(feature = "syntect-highlighter")]
75 pub(crate) fn syntect_truecolor() -> Self {
76 Self::from(SyntectHighlighter::default())
77 }
78}
79
80impl Default for MietteHighlighter {
81 #[cfg(feature = "syntect-highlighter")]
82 fn default() -> Self {
83 use std::io::IsTerminal;
84 match std::env::var("NO_COLOR") {
85 _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
86 //TODO: should use ANSI styling instead of 24-bit truecolor here
87 Self(Arc::new(SyntectHighlighter::default()))
88 }
89 Ok(string) if string != "0" => MietteHighlighter::nocolor(),
90 _ => Self(Arc::new(SyntectHighlighter::default())),
91 }
92 }
93 #[cfg(not(feature = "syntect-highlighter"))]
94 fn default() -> Self {
95 MietteHighlighter::nocolor()
96 }
97}
98
99impl<T: Highlighter + Send + Sync + 'static> From<T> for MietteHighlighter {
100 fn from(value: T) -> Self {
101 Self(Arc::new(value))
102 }
103}
104
105impl std::fmt::Debug for MietteHighlighter {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "MietteHighlighter(...)")
108 }
109}
110
111impl Deref for MietteHighlighter {
112 type Target = dyn Highlighter + Send + Sync;
113 fn deref(&self) -> &Self::Target {
114 &*self.0
115 }
116}