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}