Skip to content

Commit

Permalink
feat: 🚧 i have a vague idea of what i'm doing
Browse files Browse the repository at this point in the history
  • Loading branch information
melody-rs committed Dec 2, 2023
1 parent cc86398 commit 1b250ec
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 80 deletions.
126 changes: 49 additions & 77 deletions crates/components/src/syntax_highlighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@

#![allow(missing_docs)]

use egui::text::LayoutJob;
use egui::{
text::{LayoutJob, LayoutSection},
FontId, TextFormat,
};
use prism::Visit;

/// View some code with syntax highlighting and selection.
pub fn code_view_ui(ui: &mut egui::Ui, mut code: &str, theme: luminol_config::CodeTheme) {
let language = "rb";

let mut layouter = |ui: &egui::Ui, string: &str, _wrap_width: f32| {
let layout_job = highlight(ui.ctx(), theme, string, language);
let layout_job = highlight(ui.ctx(), theme, string);
// layout_job.wrap.max_width = wrap_width; // no wrapping
ui.fonts(|f| f.layout_job(layout_job))
};
Expand All @@ -48,49 +50,32 @@ pub fn code_view_ui(ui: &mut egui::Ui, mut code: &str, theme: luminol_config::Co

/// Memoized Code highlighting
#[must_use]
pub fn highlight(
ctx: &egui::Context,
theme: luminol_config::CodeTheme,
code: &str,
language: &str,
) -> LayoutJob {
impl egui::util::cache::ComputerMut<(luminol_config::CodeTheme, &str, &str), LayoutJob>
for Highlighter
{
fn compute(
&mut self,
(theme, code, lang): (luminol_config::CodeTheme, &str, &str),
) -> LayoutJob {
self.highlight(theme, code, lang)
pub fn highlight(ctx: &egui::Context, theme: luminol_config::CodeTheme, code: &str) -> LayoutJob {
impl egui::util::cache::ComputerMut<(luminol_config::CodeTheme, &str), LayoutJob> for Highlighter {
fn compute(&mut self, (theme, code): (luminol_config::CodeTheme, &str)) -> LayoutJob {
self.highlight(theme, code)
}
}

type HighlightCache = egui::util::cache::FrameCache<LayoutJob, Highlighter>;

ctx.memory_mut(|m| {
let highlight_cache = m.caches.cache::<HighlightCache>();
highlight_cache.get((theme, code, language))
highlight_cache.get((theme, code))
})
}

struct Highlighter {
ps: syntect::parsing::SyntaxSet,
ts: syntect::highlighting::ThemeSet,
}
#[derive(Default)]
struct Highlighter {}

impl Default for Highlighter {
fn default() -> Self {
Self {
ps: syntect::parsing::SyntaxSet::load_defaults_newlines(),
ts: syntect::highlighting::ThemeSet::load_defaults(),
}
}
struct Visitor<'job> {
job: &'job mut LayoutJob,
}

impl Highlighter {
#[allow(clippy::unused_self, clippy::unnecessary_wraps)]
fn highlight(&self, theme: luminol_config::CodeTheme, code: &str, lang: &str) -> LayoutJob {
self.highlight_impl(theme, code, lang).unwrap_or_else(|| {
fn highlight(&mut self, theme: luminol_config::CodeTheme, code: &str) -> LayoutJob {
self.highlight_impl(theme, code).unwrap_or_else(|| {
// Fallback:
LayoutJob::simple(
code.into(),
Expand All @@ -106,63 +91,50 @@ impl Highlighter {
}

fn highlight_impl(
&self,
&mut self,
theme: luminol_config::CodeTheme,
text: &str,
language: &str,
) -> Option<LayoutJob> {
use egui::text::{LayoutSection, TextFormat};
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]);

let mut job = LayoutJob {
text: text.into(),
..Default::default()
};

for line in LinesWithEndings::from(text) {
for (style, range) in h.highlight_line(line, &self.ps).ok()? {
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 {
egui::Stroke::NONE
};
job.sections.push(LayoutSection {
leading_space: 0.0,
byte_range: as_byte_range(text, range),
format: TextFormat {
font_id: egui::FontId::monospace(12.0),
color: text_color,
italics,
underline,
..Default::default()
},
});
}
let results = prism::parse(text.as_bytes());
for diagnostic in results.errors() {
eprintln!("{}", diagnostic.message());
}
println!("{:#?}", results.node());

let mut visitor = Visitor { job: &mut job };
visitor.visit(&results.node());

Some(job)
}
}

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())
impl<'jobs> Visitor<'jobs> {
fn keyword(&mut self, location: prism::Location<'_>) {
self.job.sections.push(LayoutSection {
leading_space: 0.0,
byte_range: location_range(location),
format: TextFormat {
font_id: FontId::monospace(12.0),
color: egui::Color32::from_rgb(204, 153, 204),
..Default::default()
},
});
}
}

impl<'jobs, 'node> prism::Visit<'node> for Visitor<'jobs> {
fn visit_class_node(&mut self, node: &prism::ClassNode<'node>) {
self.keyword(node.class_keyword_loc());
prism::visit_class_node(self, node);
self.keyword(node.end_keyword_loc());
}
}

fn location_range(location: prism::Location) -> std::ops::Range<usize> {
location.start_offset()..location.end_offset()
}
3 changes: 1 addition & 2 deletions crates/ui/src/windows/appearance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ impl luminol_core::Window for Window {
def bar(baz)
end
print 1, 2.0
puts [0x3, :4, '5']
puts [0x3, :abc, '5']
"#,
"rb",
));
});

Expand Down
1 change: 0 additions & 1 deletion crates/ui/src/windows/script_edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ impl luminol_core::Tab for ScriptTab {
ui.ctx(),
update_state.global_config.theme,
string,
"rb",
);
layout_job.wrap.max_width = wrap_width;
ui.fonts(|f| f.layout_job(layout_job))
Expand Down

0 comments on commit 1b250ec

Please sign in to comment.