2021-10-07 19:31:58 +00:00
|
|
|
use egui::text::LayoutJob;
|
|
|
|
|
2022-03-10 07:14:06 +00:00
|
|
|
/// View some code with syntax highlighting and selection.
|
2021-10-07 19:31:58 +00:00
|
|
|
pub fn code_view_ui(ui: &mut egui::Ui, mut code: &str) {
|
|
|
|
let language = "rs";
|
|
|
|
let theme = CodeTheme::from_memory(ui.ctx());
|
|
|
|
|
|
|
|
let mut layouter = |ui: &egui::Ui, string: &str, _wrap_width: f32| {
|
|
|
|
let layout_job = highlight(ui.ctx(), &theme, string, language);
|
2022-04-03 18:28:47 +00:00
|
|
|
// layout_job.wrap.max_width = wrap_width; // no wrapping
|
2021-10-07 19:31:58 +00:00
|
|
|
ui.fonts().layout_job(layout_job)
|
|
|
|
};
|
|
|
|
|
|
|
|
ui.add(
|
|
|
|
egui::TextEdit::multiline(&mut code)
|
2022-01-24 13:32:36 +00:00
|
|
|
.font(egui::TextStyle::Monospace) // for cursor height
|
2021-10-07 19:31:58 +00:00
|
|
|
.code_editor()
|
|
|
|
.desired_rows(1)
|
|
|
|
.lock_focus(true)
|
|
|
|
.layouter(&mut layouter),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Memoized Code highlighting
|
|
|
|
pub fn highlight(ctx: &egui::Context, theme: &CodeTheme, code: &str, language: &str) -> LayoutJob {
|
2022-03-10 07:14:06 +00:00
|
|
|
impl egui::util::cache::ComputerMut<(&CodeTheme, &str, &str), LayoutJob> for Highlighter {
|
2021-10-07 19:31:58 +00:00
|
|
|
fn compute(&mut self, (theme, code, lang): (&CodeTheme, &str, &str)) -> LayoutJob {
|
|
|
|
self.highlight(theme, code, lang)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 09:18:21 +00:00
|
|
|
type HighlightCache = egui::util::cache::FrameCache<LayoutJob, Highlighter>;
|
2021-10-07 19:31:58 +00:00
|
|
|
|
|
|
|
let mut memory = ctx.memory();
|
2022-07-26 09:18:21 +00:00
|
|
|
let highlight_cache = memory.caches.cache::<HighlightCache>();
|
2021-10-07 19:31:58 +00:00
|
|
|
highlight_cache.get((theme, code, language))
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
|
|
#[derive(enum_map::Enum)]
|
|
|
|
enum TokenType {
|
|
|
|
Comment,
|
|
|
|
Keyword,
|
|
|
|
Literal,
|
|
|
|
StringLiteral,
|
|
|
|
Punctuation,
|
|
|
|
Whitespace,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
|
|
enum SyntectTheme {
|
|
|
|
Base16EightiesDark,
|
|
|
|
Base16MochaDark,
|
|
|
|
Base16OceanDark,
|
|
|
|
Base16OceanLight,
|
|
|
|
InspiredGitHub,
|
|
|
|
SolarizedDark,
|
|
|
|
SolarizedLight,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
|
|
|
impl SyntectTheme {
|
2022-01-15 12:59:52 +00:00
|
|
|
fn all() -> impl ExactSizeIterator<Item = Self> {
|
2021-10-07 19:31:58 +00:00
|
|
|
[
|
|
|
|
Self::Base16EightiesDark,
|
|
|
|
Self::Base16MochaDark,
|
|
|
|
Self::Base16OceanDark,
|
|
|
|
Self::Base16OceanLight,
|
|
|
|
Self::InspiredGitHub,
|
|
|
|
Self::SolarizedDark,
|
|
|
|
Self::SolarizedLight,
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Base16EightiesDark => "Base16 Eighties (dark)",
|
|
|
|
Self::Base16MochaDark => "Base16 Mocha (dark)",
|
|
|
|
Self::Base16OceanDark => "Base16 Ocean (dark)",
|
|
|
|
Self::Base16OceanLight => "Base16 Ocean (light)",
|
|
|
|
Self::InspiredGitHub => "InspiredGitHub (light)",
|
|
|
|
Self::SolarizedDark => "Solarized (dark)",
|
|
|
|
Self::SolarizedLight => "Solarized (light)",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn syntect_key_name(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Base16EightiesDark => "base16-eighties.dark",
|
|
|
|
Self::Base16MochaDark => "base16-mocha.dark",
|
|
|
|
Self::Base16OceanDark => "base16-ocean.dark",
|
|
|
|
Self::Base16OceanLight => "base16-ocean.light",
|
|
|
|
Self::InspiredGitHub => "InspiredGitHub",
|
|
|
|
Self::SolarizedDark => "Solarized (dark)",
|
|
|
|
Self::SolarizedLight => "Solarized (light)",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_dark(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Self::Base16EightiesDark
|
|
|
|
| Self::Base16MochaDark
|
|
|
|
| Self::Base16OceanDark
|
|
|
|
| Self::SolarizedDark => true,
|
|
|
|
|
|
|
|
Self::Base16OceanLight | Self::InspiredGitHub | Self::SolarizedLight => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-24 13:32:36 +00:00
|
|
|
#[derive(Clone, Hash, PartialEq)]
|
2021-10-07 19:31:58 +00:00
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
|
|
#[cfg_attr(feature = "serde", serde(default))]
|
|
|
|
pub struct CodeTheme {
|
|
|
|
dark_mode: bool,
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
|
|
|
syntect_theme: SyntectTheme,
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
formats: enum_map::EnumMap<TokenType, egui::TextFormat>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for CodeTheme {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::dark()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CodeTheme {
|
|
|
|
pub fn from_style(style: &egui::Style) -> Self {
|
|
|
|
if style.visuals.dark_mode {
|
|
|
|
Self::dark()
|
|
|
|
} else {
|
|
|
|
Self::light()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_memory(ctx: &egui::Context) -> Self {
|
|
|
|
if ctx.style().visuals.dark_mode {
|
2022-01-29 16:53:41 +00:00
|
|
|
ctx.data()
|
2021-10-27 06:51:34 +00:00
|
|
|
.get_persisted(egui::Id::new("dark"))
|
|
|
|
.unwrap_or_else(CodeTheme::dark)
|
2021-10-07 19:31:58 +00:00
|
|
|
} else {
|
2022-01-29 16:53:41 +00:00
|
|
|
ctx.data()
|
2021-10-27 06:51:34 +00:00
|
|
|
.get_persisted(egui::Id::new("light"))
|
|
|
|
.unwrap_or_else(CodeTheme::light)
|
2021-10-07 19:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-24 13:32:36 +00:00
|
|
|
pub fn store_in_memory(self, ctx: &egui::Context) {
|
2021-10-07 19:31:58 +00:00
|
|
|
if self.dark_mode {
|
2022-01-29 16:53:41 +00:00
|
|
|
ctx.data().insert_persisted(egui::Id::new("dark"), self);
|
2021-10-07 19:31:58 +00:00
|
|
|
} else {
|
2022-01-29 16:53:41 +00:00
|
|
|
ctx.data().insert_persisted(egui::Id::new("light"), self);
|
2021-10-07 19:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
|
|
|
impl CodeTheme {
|
|
|
|
pub fn dark() -> Self {
|
|
|
|
Self {
|
|
|
|
dark_mode: true,
|
|
|
|
syntect_theme: SyntectTheme::Base16MochaDark,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn light() -> Self {
|
|
|
|
Self {
|
|
|
|
dark_mode: false,
|
|
|
|
syntect_theme: SyntectTheme::SolarizedLight,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
|
|
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
|
|
|
|
|
|
|
for theme in SyntectTheme::all() {
|
|
|
|
if theme.is_dark() == self.dark_mode {
|
|
|
|
ui.radio_value(&mut self.syntect_theme, theme, theme.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
impl CodeTheme {
|
|
|
|
pub fn dark() -> Self {
|
Fix text sizes being too small (#2069)
Closes https://github.com/emilk/egui/issues/2068
Before this PR, the default font, Ubuntu-Light, was ~11% smaller
than it should have been, and the default monospace font, Hack,
was ~14% smaller. This means that setting the font size `12` in egui
would yield smaller text than using that font size in any other app.
Ooops!
The change is that this PR now takes into account the ttf properties
`units_per_em` and `height_unscaled`.
If your egui application has specified you own font sizes or text styles
you will see the text in your application grow
larger, unless you go in and compensate by dividing all font sizes by
~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace,
and with something else if you are using a custom font!
This effects any use of `FontId`, `RichText::size`, etc.
This PR changes the default `Style::text_styles` to compensate,
so the default egui style should look the same before and after this PR.
2022-09-21 19:31:08 +00:00
|
|
|
let font_id = egui::FontId::monospace(10.0);
|
2021-10-07 19:31:58 +00:00
|
|
|
use egui::{Color32, TextFormat};
|
|
|
|
Self {
|
|
|
|
dark_mode: true,
|
|
|
|
formats: enum_map::enum_map![
|
2022-01-24 13:32:36 +00:00
|
|
|
TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::from_gray(120)),
|
|
|
|
TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(255, 100, 100)),
|
|
|
|
TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(87, 165, 171)),
|
|
|
|
TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(109, 147, 226)),
|
|
|
|
TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::LIGHT_GRAY),
|
|
|
|
TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),
|
2021-10-07 19:31:58 +00:00
|
|
|
],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn light() -> Self {
|
Fix text sizes being too small (#2069)
Closes https://github.com/emilk/egui/issues/2068
Before this PR, the default font, Ubuntu-Light, was ~11% smaller
than it should have been, and the default monospace font, Hack,
was ~14% smaller. This means that setting the font size `12` in egui
would yield smaller text than using that font size in any other app.
Ooops!
The change is that this PR now takes into account the ttf properties
`units_per_em` and `height_unscaled`.
If your egui application has specified you own font sizes or text styles
you will see the text in your application grow
larger, unless you go in and compensate by dividing all font sizes by
~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace,
and with something else if you are using a custom font!
This effects any use of `FontId`, `RichText::size`, etc.
This PR changes the default `Style::text_styles` to compensate,
so the default egui style should look the same before and after this PR.
2022-09-21 19:31:08 +00:00
|
|
|
let font_id = egui::FontId::monospace(10.0);
|
2021-10-07 19:31:58 +00:00
|
|
|
use egui::{Color32, TextFormat};
|
|
|
|
Self {
|
|
|
|
dark_mode: false,
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
formats: enum_map::enum_map![
|
2022-01-24 13:32:36 +00:00
|
|
|
TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::GRAY),
|
|
|
|
TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(235, 0, 0)),
|
|
|
|
TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(153, 134, 255)),
|
|
|
|
TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(37, 203, 105)),
|
|
|
|
TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::DARK_GRAY),
|
|
|
|
TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),
|
2021-10-07 19:31:58 +00:00
|
|
|
],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
|
|
|
ui.horizontal_top(|ui| {
|
2021-10-27 06:51:34 +00:00
|
|
|
let selected_id = egui::Id::null();
|
|
|
|
let mut selected_tt: TokenType = *ui
|
2022-01-29 16:53:41 +00:00
|
|
|
.data()
|
2021-10-27 06:51:34 +00:00
|
|
|
.get_persisted_mut_or(selected_id, TokenType::Comment);
|
2021-10-07 19:31:58 +00:00
|
|
|
|
|
|
|
ui.vertical(|ui| {
|
|
|
|
ui.set_width(150.0);
|
|
|
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
|
|
|
|
|
|
|
ui.add_space(8.0);
|
|
|
|
ui.separator();
|
|
|
|
ui.add_space(8.0);
|
|
|
|
|
|
|
|
ui.scope(|ui| {
|
|
|
|
for (tt, tt_name) in [
|
|
|
|
(TokenType::Comment, "// comment"),
|
|
|
|
(TokenType::Keyword, "keyword"),
|
|
|
|
(TokenType::Literal, "literal"),
|
|
|
|
(TokenType::StringLiteral, "\"string literal\""),
|
|
|
|
(TokenType::Punctuation, "punctuation ;"),
|
|
|
|
// (TokenType::Whitespace, "whitespace"),
|
|
|
|
] {
|
|
|
|
let format = &mut self.formats[tt];
|
2022-01-24 13:32:36 +00:00
|
|
|
ui.style_mut().override_font_id = Some(format.font_id.clone());
|
2021-10-07 19:31:58 +00:00
|
|
|
ui.visuals_mut().override_text_color = Some(format.color);
|
|
|
|
ui.radio_value(&mut selected_tt, tt, tt_name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let reset_value = if self.dark_mode {
|
|
|
|
CodeTheme::dark()
|
|
|
|
} else {
|
|
|
|
CodeTheme::light()
|
|
|
|
};
|
|
|
|
|
|
|
|
if ui
|
2021-10-17 20:17:50 +00:00
|
|
|
.add_enabled(*self != reset_value, egui::Button::new("Reset theme"))
|
2021-10-07 19:31:58 +00:00
|
|
|
.clicked()
|
|
|
|
{
|
|
|
|
*self = reset_value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.add_space(16.0);
|
|
|
|
|
2022-01-29 16:53:41 +00:00
|
|
|
ui.data().insert_persisted(selected_id, selected_tt);
|
2021-10-07 19:31:58 +00:00
|
|
|
|
|
|
|
egui::Frame::group(ui.style())
|
2022-03-21 20:44:36 +00:00
|
|
|
.inner_margin(egui::Vec2::splat(2.0))
|
2021-10-07 19:31:58 +00:00
|
|
|
.show(ui, |ui| {
|
|
|
|
// ui.group(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
|
|
|
ui.spacing_mut().slider_width = 128.0; // Controls color picker size
|
|
|
|
egui::widgets::color_picker::color_picker_color32(
|
|
|
|
ui,
|
|
|
|
&mut self.formats[selected_tt].color,
|
|
|
|
egui::color_picker::Alpha::Opaque,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
2022-03-10 07:14:06 +00:00
|
|
|
struct Highlighter {
|
2021-10-07 19:31:58 +00:00
|
|
|
ps: syntect::parsing::SyntaxSet,
|
|
|
|
ts: syntect::highlighting::ThemeSet,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
2022-03-10 07:14:06 +00:00
|
|
|
impl Default for Highlighter {
|
2021-10-07 19:31:58 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
ps: syntect::parsing::SyntaxSet::load_defaults_newlines(),
|
|
|
|
ts: syntect::highlighting::ThemeSet::load_defaults(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
2022-03-10 07:14:06 +00:00
|
|
|
impl Highlighter {
|
2021-10-07 19:31:58 +00:00
|
|
|
#[allow(clippy::unused_self, clippy::unnecessary_wraps)]
|
|
|
|
fn highlight(&self, theme: &CodeTheme, code: &str, lang: &str) -> LayoutJob {
|
|
|
|
self.highlight_impl(theme, code, lang).unwrap_or_else(|| {
|
|
|
|
// Fallback:
|
|
|
|
LayoutJob::simple(
|
|
|
|
code.into(),
|
Fix text sizes being too small (#2069)
Closes https://github.com/emilk/egui/issues/2068
Before this PR, the default font, Ubuntu-Light, was ~11% smaller
than it should have been, and the default monospace font, Hack,
was ~14% smaller. This means that setting the font size `12` in egui
would yield smaller text than using that font size in any other app.
Ooops!
The change is that this PR now takes into account the ttf properties
`units_per_em` and `height_unscaled`.
If your egui application has specified you own font sizes or text styles
you will see the text in your application grow
larger, unless you go in and compensate by dividing all font sizes by
~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace,
and with something else if you are using a custom font!
This effects any use of `FontId`, `RichText::size`, etc.
This PR changes the default `Style::text_styles` to compensate,
so the default egui style should look the same before and after this PR.
2022-09-21 19:31:08 +00:00
|
|
|
egui::FontId::monospace(12.0),
|
2021-10-07 19:31:58 +00:00
|
|
|
if theme.dark_mode {
|
|
|
|
egui::Color32::LIGHT_GRAY
|
|
|
|
} else {
|
|
|
|
egui::Color32::DARK_GRAY
|
|
|
|
},
|
|
|
|
f32::INFINITY,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn highlight_impl(&self, theme: &CodeTheme, text: &str, language: &str) -> Option<LayoutJob> {
|
|
|
|
use syntect::easy::HighlightLines;
|
|
|
|
use syntect::highlighting::FontStyle;
|
|
|
|
use syntect::util::LinesWithEndings;
|
|
|
|
|
|
|
|
let syntax = self
|
|
|
|
.ps
|
|
|
|
.find_syntax_by_name(language)
|
|
|
|
.or_else(|| self.ps.find_syntax_by_extension(language))?;
|
|
|
|
|
|
|
|
let theme = theme.syntect_theme.syntect_key_name();
|
|
|
|
let mut h = HighlightLines::new(syntax, &self.ts.themes[theme]);
|
|
|
|
|
|
|
|
use egui::text::{LayoutSection, TextFormat};
|
|
|
|
|
|
|
|
let mut job = LayoutJob {
|
|
|
|
text: text.into(),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
for line in LinesWithEndings::from(text) {
|
2022-07-03 18:12:57 +00:00
|
|
|
for (style, range) in h.highlight_line(line, &self.ps).ok()? {
|
2021-10-07 19:31:58 +00:00
|
|
|
let fg = style.foreground;
|
|
|
|
let text_color = egui::Color32::from_rgb(fg.r, fg.g, fg.b);
|
|
|
|
let italics = style.font_style.contains(FontStyle::ITALIC);
|
|
|
|
let underline = style.font_style.contains(FontStyle::ITALIC);
|
|
|
|
let underline = if underline {
|
|
|
|
egui::Stroke::new(1.0, text_color)
|
|
|
|
} else {
|
2022-12-05 11:59:02 +00:00
|
|
|
egui::Stroke::NONE
|
2021-10-07 19:31:58 +00:00
|
|
|
};
|
|
|
|
job.sections.push(LayoutSection {
|
|
|
|
leading_space: 0.0,
|
|
|
|
byte_range: as_byte_range(text, range),
|
|
|
|
format: TextFormat {
|
Fix text sizes being too small (#2069)
Closes https://github.com/emilk/egui/issues/2068
Before this PR, the default font, Ubuntu-Light, was ~11% smaller
than it should have been, and the default monospace font, Hack,
was ~14% smaller. This means that setting the font size `12` in egui
would yield smaller text than using that font size in any other app.
Ooops!
The change is that this PR now takes into account the ttf properties
`units_per_em` and `height_unscaled`.
If your egui application has specified you own font sizes or text styles
you will see the text in your application grow
larger, unless you go in and compensate by dividing all font sizes by
~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace,
and with something else if you are using a custom font!
This effects any use of `FontId`, `RichText::size`, etc.
This PR changes the default `Style::text_styles` to compensate,
so the default egui style should look the same before and after this PR.
2022-09-21 19:31:08 +00:00
|
|
|
font_id: egui::FontId::monospace(12.0),
|
2021-10-07 19:31:58 +00:00
|
|
|
color: text_color,
|
|
|
|
italics,
|
|
|
|
underline,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(job)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "syntect")]
|
|
|
|
fn as_byte_range(whole: &str, range: &str) -> std::ops::Range<usize> {
|
|
|
|
let whole_start = whole.as_ptr() as usize;
|
|
|
|
let range_start = range.as_ptr() as usize;
|
|
|
|
assert!(whole_start <= range_start);
|
|
|
|
assert!(range_start + range.len() <= whole_start + whole.len());
|
|
|
|
let offset = range_start - whole_start;
|
|
|
|
offset..(offset + range.len())
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
#[derive(Default)]
|
2022-03-10 07:14:06 +00:00
|
|
|
struct Highlighter {}
|
2021-10-07 19:31:58 +00:00
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
2022-03-10 07:14:06 +00:00
|
|
|
impl Highlighter {
|
2021-10-07 19:31:58 +00:00
|
|
|
#[allow(clippy::unused_self, clippy::unnecessary_wraps)]
|
|
|
|
fn highlight(&self, theme: &CodeTheme, mut text: &str, _language: &str) -> LayoutJob {
|
|
|
|
// Extremely simple syntax highlighter for when we compile without syntect
|
|
|
|
|
|
|
|
let mut job = LayoutJob::default();
|
|
|
|
|
|
|
|
while !text.is_empty() {
|
|
|
|
if text.starts_with("//") {
|
2022-04-07 15:03:19 +00:00
|
|
|
let end = text.find('\n').unwrap_or(text.len());
|
2022-01-24 13:32:36 +00:00
|
|
|
job.append(&text[..end], 0.0, theme.formats[TokenType::Comment].clone());
|
2021-10-07 19:31:58 +00:00
|
|
|
text = &text[end..];
|
|
|
|
} else if text.starts_with('"') {
|
|
|
|
let end = text[1..]
|
|
|
|
.find('"')
|
|
|
|
.map(|i| i + 2)
|
|
|
|
.or_else(|| text.find('\n'))
|
2022-04-07 15:03:19 +00:00
|
|
|
.unwrap_or(text.len());
|
2022-01-24 13:32:36 +00:00
|
|
|
job.append(
|
|
|
|
&text[..end],
|
|
|
|
0.0,
|
|
|
|
theme.formats[TokenType::StringLiteral].clone(),
|
|
|
|
);
|
2021-10-07 19:31:58 +00:00
|
|
|
text = &text[end..];
|
|
|
|
} else if text.starts_with(|c: char| c.is_ascii_alphanumeric()) {
|
|
|
|
let end = text[1..]
|
|
|
|
.find(|c: char| !c.is_ascii_alphanumeric())
|
2021-10-20 14:33:59 +00:00
|
|
|
.map_or_else(|| text.len(), |i| i + 1);
|
2021-10-07 19:31:58 +00:00
|
|
|
let word = &text[..end];
|
|
|
|
let tt = if is_keyword(word) {
|
|
|
|
TokenType::Keyword
|
|
|
|
} else {
|
|
|
|
TokenType::Literal
|
|
|
|
};
|
2022-01-24 13:32:36 +00:00
|
|
|
job.append(word, 0.0, theme.formats[tt].clone());
|
2021-10-07 19:31:58 +00:00
|
|
|
text = &text[end..];
|
|
|
|
} else if text.starts_with(|c: char| c.is_ascii_whitespace()) {
|
|
|
|
let end = text[1..]
|
|
|
|
.find(|c: char| !c.is_ascii_whitespace())
|
2021-10-20 14:33:59 +00:00
|
|
|
.map_or_else(|| text.len(), |i| i + 1);
|
2022-01-24 13:32:36 +00:00
|
|
|
job.append(
|
|
|
|
&text[..end],
|
|
|
|
0.0,
|
|
|
|
theme.formats[TokenType::Whitespace].clone(),
|
|
|
|
);
|
2021-10-07 19:31:58 +00:00
|
|
|
text = &text[end..];
|
|
|
|
} else {
|
|
|
|
let mut it = text.char_indices();
|
|
|
|
it.next();
|
|
|
|
let end = it.next().map_or(text.len(), |(idx, _chr)| idx);
|
2022-01-24 13:32:36 +00:00
|
|
|
job.append(
|
|
|
|
&text[..end],
|
|
|
|
0.0,
|
|
|
|
theme.formats[TokenType::Punctuation].clone(),
|
|
|
|
);
|
2021-10-07 19:31:58 +00:00
|
|
|
text = &text[end..];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
job
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntect"))]
|
|
|
|
fn is_keyword(word: &str) -> bool {
|
|
|
|
matches!(
|
|
|
|
word,
|
|
|
|
"as" | "async"
|
|
|
|
| "await"
|
|
|
|
| "break"
|
|
|
|
| "const"
|
|
|
|
| "continue"
|
|
|
|
| "crate"
|
|
|
|
| "dyn"
|
|
|
|
| "else"
|
|
|
|
| "enum"
|
|
|
|
| "extern"
|
|
|
|
| "false"
|
|
|
|
| "fn"
|
|
|
|
| "for"
|
|
|
|
| "if"
|
|
|
|
| "impl"
|
|
|
|
| "in"
|
|
|
|
| "let"
|
|
|
|
| "loop"
|
|
|
|
| "match"
|
|
|
|
| "mod"
|
|
|
|
| "move"
|
|
|
|
| "mut"
|
|
|
|
| "pub"
|
|
|
|
| "ref"
|
|
|
|
| "return"
|
|
|
|
| "self"
|
|
|
|
| "Self"
|
|
|
|
| "static"
|
|
|
|
| "struct"
|
|
|
|
| "super"
|
|
|
|
| "trait"
|
|
|
|
| "true"
|
|
|
|
| "type"
|
|
|
|
| "unsafe"
|
|
|
|
| "use"
|
|
|
|
| "where"
|
|
|
|
| "while"
|
|
|
|
)
|
|
|
|
}
|