diff options
author | 2023-09-19 20:48:50 +0200 | |
---|---|---|
committer | 2023-09-19 20:48:50 +0200 | |
commit | f806d001e6fb44b5a45029ca257261e6e0d4d4b2 (patch) | |
tree | 4a9292fde7f8db76915d3596d326eb71ff7b92a9 /examples/editor | |
parent | c0a141ab026f5686d6bd92c8807b174396cb9105 (diff) | |
download | iced-f806d001e6fb44b5a45029ca257261e6e0d4d4b2.tar.gz iced-f806d001e6fb44b5a45029ca257261e6e0d4d4b2.tar.bz2 iced-f806d001e6fb44b5a45029ca257261e6e0d4d4b2.zip |
Introduce new `iced_highlighter` subcrate
Diffstat (limited to 'examples/editor')
-rw-r--r-- | examples/editor/Cargo.toml | 2 | ||||
-rw-r--r-- | examples/editor/src/main.rs | 251 |
2 files changed, 15 insertions, 238 deletions
diff --git a/examples/editor/Cargo.toml b/examples/editor/Cargo.toml index eeb34aa1..a77b1e9f 100644 --- a/examples/editor/Cargo.toml +++ b/examples/editor/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["advanced", "tokio", "debug"] +iced.features = ["highlighter", "tokio", "debug"] tokio.workspace = true tokio.features = ["fs"] diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 36d4287c..d513090f 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -1,4 +1,5 @@ use iced::executor; +use iced::highlighter::{self, Highlighter}; use iced::keyboard; use iced::theme::{self, Theme}; use iced::widget::{ @@ -10,8 +11,6 @@ use iced::{ Subscription, }; -use highlighter::Highlighter; - use std::ffi; use std::io; use std::path::{Path, PathBuf}; @@ -210,16 +209,19 @@ impl Application for Editor { controls, text_editor(&self.content) .on_edit(Message::Edit) - .highlight::<Highlighter>(highlighter::Settings { - theme: self.theme, - extension: self - .file - .as_deref() - .and_then(Path::extension) - .and_then(ffi::OsStr::to_str) - .map(str::to_string) - .unwrap_or(String::from("rs")), - }), + .highlight::<Highlighter>( + highlighter::Settings { + theme: self.theme, + extension: self + .file + .as_deref() + .and_then(Path::extension) + .and_then(ffi::OsStr::to_str) + .map(str::to_string) + .unwrap_or(String::from("rs")), + }, + |highlight, _theme| highlight.to_format() + ), status, ] .spacing(10) @@ -321,228 +323,3 @@ fn icon<'a, Message>(codepoint: char) -> Element<'a, Message> { text(codepoint).font(ICON_FONT).into() } - -mod highlighter { - use iced::advanced::text::highlighter; - use iced::widget::text_editor; - use iced::{Color, Font}; - - use std::ops::Range; - use syntect::highlighting; - use syntect::parsing; - - #[derive(Debug, Clone, PartialEq)] - pub struct Settings { - pub theme: Theme, - pub extension: String, - } - - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum Theme { - SolarizedDark, - InspiredGitHub, - Base16Mocha, - } - - impl Theme { - pub const ALL: &[Self] = - &[Self::SolarizedDark, Self::InspiredGitHub, Self::Base16Mocha]; - - fn key(&self) -> &'static str { - match self { - Theme::InspiredGitHub => "InspiredGitHub", - Theme::Base16Mocha => "base16-mocha.dark", - Theme::SolarizedDark => "Solarized (dark)", - } - } - } - - impl std::fmt::Display for Theme { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Theme::InspiredGitHub => write!(f, "Inspired GitHub"), - Theme::Base16Mocha => write!(f, "Mocha"), - Theme::SolarizedDark => write!(f, "Solarized Dark"), - } - } - } - - pub struct Highlight(highlighting::StyleModifier); - - impl text_editor::Highlight for Highlight { - fn format(&self, _theme: &iced::Theme) -> highlighter::Format<Font> { - highlighter::Format { - color: self.0.foreground.map(|color| { - Color::from_rgba8( - color.r, - color.g, - color.b, - color.a as f32 / 255.0, - ) - }), - font: None, - } - } - } - - pub struct Highlighter { - syntaxes: parsing::SyntaxSet, - syntax: parsing::SyntaxReference, - theme: highlighting::Theme, - caches: Vec<(parsing::ParseState, parsing::ScopeStack)>, - current_line: usize, - } - - const LINES_PER_SNAPSHOT: usize = 50; - - impl highlighter::Highlighter for Highlighter { - type Settings = Settings; - type Highlight = Highlight; - - type Iterator<'a> = - Box<dyn Iterator<Item = (Range<usize>, Self::Highlight)> + 'a>; - - fn new(settings: &Self::Settings) -> Self { - let syntaxes = parsing::SyntaxSet::load_defaults_nonewlines(); - - let syntax = syntaxes - .find_syntax_by_token(&settings.extension) - .unwrap_or_else(|| syntaxes.find_syntax_plain_text()); - - let theme = highlighting::ThemeSet::load_defaults() - .themes - .remove(settings.theme.key()) - .unwrap(); - - let parser = parsing::ParseState::new(syntax); - let stack = parsing::ScopeStack::new(); - - Highlighter { - syntax: syntax.clone(), - syntaxes, - theme, - caches: vec![(parser, stack)], - current_line: 0, - } - } - - fn update(&mut self, new_settings: &Self::Settings) { - self.syntax = self - .syntaxes - .find_syntax_by_token(&new_settings.extension) - .unwrap_or_else(|| self.syntaxes.find_syntax_plain_text()) - .clone(); - - self.theme = highlighting::ThemeSet::load_defaults() - .themes - .remove(new_settings.theme.key()) - .unwrap(); - - // Restart the highlighter - self.change_line(0); - } - - fn change_line(&mut self, line: usize) { - let snapshot = line / LINES_PER_SNAPSHOT; - - if snapshot <= self.caches.len() { - self.caches.truncate(snapshot); - self.current_line = snapshot * LINES_PER_SNAPSHOT; - } else { - self.caches.truncate(1); - self.current_line = 0; - } - - let (parser, stack) = - self.caches.last().cloned().unwrap_or_else(|| { - ( - parsing::ParseState::new(&self.syntax), - parsing::ScopeStack::new(), - ) - }); - - self.caches.push((parser, stack)); - } - - fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_> { - if self.current_line / LINES_PER_SNAPSHOT >= self.caches.len() { - let (parser, stack) = - self.caches.last().expect("Caches must not be empty"); - - self.caches.push((parser.clone(), stack.clone())); - } - - self.current_line += 1; - - let (parser, stack) = - self.caches.last_mut().expect("Caches must not be empty"); - - let ops = - parser.parse_line(line, &self.syntaxes).unwrap_or_default(); - - let highlighter = highlighting::Highlighter::new(&self.theme); - - Box::new( - ScopeRangeIterator { - ops, - line_length: line.len(), - index: 0, - last_str_index: 0, - } - .filter_map(move |(range, scope)| { - let _ = stack.apply(&scope); - - if range.is_empty() { - None - } else { - Some(( - range, - Highlight( - highlighter.style_mod_for_stack(&stack.scopes), - ), - )) - } - }), - ) - } - - fn current_line(&self) -> usize { - self.current_line - } - } - - pub struct ScopeRangeIterator { - ops: Vec<(usize, parsing::ScopeStackOp)>, - line_length: usize, - index: usize, - last_str_index: usize, - } - - impl Iterator for ScopeRangeIterator { - type Item = (std::ops::Range<usize>, parsing::ScopeStackOp); - - fn next(&mut self) -> Option<Self::Item> { - if self.index > self.ops.len() { - return None; - } - - let next_str_i = if self.index == self.ops.len() { - self.line_length - } else { - self.ops[self.index].0 - }; - - let range = self.last_str_index..next_str_i; - self.last_str_index = next_str_i; - - let op = if self.index == 0 { - parsing::ScopeStackOp::Noop - } else { - self.ops[self.index - 1].1.clone() - }; - - self.index += 1; - Some((range, op)) - } - } -} |