Skip to main content

iced_markdown_editor/
highlight.rs

1//! Code highlighting plugin interface.
2//!
3//! The editor does not highlight code itself. A highlighter is a plugin:
4//! any type implementing [`CodeHighlighter`]. With the `syntect` feature the
5//! crate provides a ready-made implementation,
6//! [`SyntectHighlighter`](crate::SyntectHighlighter).
7//!
8//! ```
9//! use iced_markdown_editor::{CodeHighlighter, HighlightRequest, HighlightSpan};
10//! use iced_markdown_editor::highlight::Color;
11//!
12//! /// Colours every line starting with `#` as a comment.
13//! struct Comments;
14//!
15//! impl CodeHighlighter for Comments {
16//!     fn highlight(&self, request: &HighlightRequest<'_>) -> Vec<HighlightSpan> {
17//!         let mut spans = Vec::new();
18//!         let mut offset = 0;
19//!         for line in request.code.split('\n') {
20//!             let len = line.chars().count();
21//!             if line.trim_start().starts_with('#') {
22//!                 spans.push(HighlightSpan {
23//!                     range: offset..offset + len,
24//!                     color: Color::from_rgb(0.4, 0.55, 0.4),
25//!                 });
26//!             }
27//!             offset += len + 1;
28//!         }
29//!         spans
30//!     }
31//! }
32//! ```
33
34use std::ops::Range;
35
36/// Colour type used by highlighters (re-export of `iced::Color`).
37pub use iced::Color;
38
39/// One code block to highlight.
40#[derive(Debug, Clone, Copy)]
41pub struct HighlightRequest<'a> {
42    /// The code block language: the first word of the fence info string.
43    pub language: &'a str,
44    /// The code. Lines are separated by `\n`.
45    pub code: &'a str,
46    /// Background colour of the code block.
47    pub background: Color,
48    /// `true` when the background is dark; pick a matching colour theme.
49    pub dark: bool,
50}
51
52/// A coloured range of the code.
53#[derive(Debug, Clone, PartialEq)]
54pub struct HighlightSpan {
55    /// **Character** offsets (not bytes) into [`HighlightRequest::code`].
56    pub range: Range<usize>,
57    pub color: Color,
58}
59
60/// A code highlighting plugin. Results are cached per code block content,
61/// language and background.
62pub trait CodeHighlighter: Send + Sync + 'static {
63    /// Colours for one code block. Return an empty list for unknown languages.
64    fn highlight(&self, request: &HighlightRequest<'_>) -> Vec<HighlightSpan>;
65
66    /// Language names offered in the toolbar's language list. When empty,
67    /// the editor offers [`COMMON_LANGUAGES`].
68    fn languages(&self) -> Vec<String> {
69        Vec::new()
70    }
71}
72
73/// Language list used when no highlighter is set or it lists none.
74pub const COMMON_LANGUAGES: &[&str] = &[
75    "bash",
76    "c",
77    "cpp",
78    "csharp",
79    "css",
80    "go",
81    "html",
82    "java",
83    "javascript",
84    "json",
85    "kotlin",
86    "markdown",
87    "python",
88    "ruby",
89    "rust",
90    "sql",
91    "swift",
92    "toml",
93    "typescript",
94    "xml",
95    "yaml",
96];
97
98/// Relative luminance used to decide whether a background is dark.
99pub(crate) fn is_dark(color: Color) -> bool {
100    0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b < 0.5
101}