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 115 116
//! This module provides a trait for creating custom syntax highlighters that
//! highlight [`Diagnostic`](crate::Diagnostic) source code with ANSI escape
//! sequences when rendering with the [`GraphicalReportHighlighter`](crate::GraphicalReportHandler).
//!
//! It also provides built-in highlighter implementations that you can use out of the box.
//! By default, there are no syntax highlighters exported by miette
//! (except for the no-op [`BlankHighlighter`]).
//! To enable support for specific highlighters, you should enable their associated feature flag.
//!
//! Currently supported syntax highlighters and their feature flags:
//! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`]
//!
use std::{ops::Deref, sync::Arc};
use crate::SpanContents;
use owo_colors::Styled;
#[cfg(feature = "syntect-highlighter")]
pub use self::syntect::*;
pub use blank::*;
mod blank;
#[cfg(feature = "syntect-highlighter")]
mod syntect;
/// A syntax highlighter for highlighting miette [`SourceCode`](crate::SourceCode) snippets.
pub trait Highlighter {
/// Creates a new [HighlighterState] to begin parsing and highlighting
/// a [SpanContents].
///
/// The [GraphicalReportHandler](crate::GraphicalReportHandler) will call
/// this method at the start of rendering a [SpanContents].
///
/// The [SpanContents] is provided as input only so that the [Highlighter]
/// can detect language syntax and make other initialization decisions prior
/// to highlighting, but it is not intended that the Highlighter begin
/// highlighting at this point. The returned [HighlighterState] is
/// responsible for the actual rendering.
fn start_highlighter_state<'h>(
&'h self,
source: &dyn SpanContents<'_>,
) -> Box<dyn HighlighterState + 'h>;
}
/// A stateful highlighter that incrementally highlights lines of a particular
/// source code.
///
/// The [GraphicalReportHandler](crate::GraphicalReportHandler)
/// will create a highlighter state by calling
/// [start_highlighter_state](Highlighter::start_highlighter_state) at the
/// start of rendering, then it will iteratively call
/// [highlight_line](HighlighterState::highlight_line) to render individual
/// highlighted lines. This allows [Highlighter] implementations to maintain
/// mutable parsing and highlighting state.
pub trait HighlighterState {
/// Highlight an individual line from the source code by returning a vector of [Styled]
/// regions.
fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>>;
}
/// Arcified trait object for Highlighter. Used internally by [GraphicalReportHandler]
///
/// Wrapping the trait object in this way allows us to implement Debug and Clone.
#[derive(Clone)]
#[repr(transparent)]
pub(crate) struct MietteHighlighter(Arc<dyn Highlighter + Send + Sync>);
impl MietteHighlighter {
pub(crate) fn nocolor() -> Self {
Self::from(BlankHighlighter)
}
#[cfg(feature = "syntect-highlighter")]
pub(crate) fn syntect_truecolor() -> Self {
Self::from(SyntectHighlighter::default())
}
}
impl Default for MietteHighlighter {
#[cfg(feature = "syntect-highlighter")]
fn default() -> Self {
use std::io::IsTerminal;
match std::env::var("NO_COLOR") {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
//TODO: should use ANSI styling instead of 24-bit truecolor here
Self(Arc::new(SyntectHighlighter::default()))
}
Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())),
}
}
#[cfg(not(feature = "syntect-highlighter"))]
fn default() -> Self {
return MietteHighlighter::nocolor();
}
}
impl<T: Highlighter + Send + Sync + 'static> From<T> for MietteHighlighter {
fn from(value: T) -> Self {
Self(Arc::new(value))
}
}
impl std::fmt::Debug for MietteHighlighter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MietteHighlighter(...)")
}
}
impl Deref for MietteHighlighter {
type Target = dyn Highlighter + Send + Sync;
fn deref(&self) -> &Self::Target {
&*self.0
}
}