Skip to content

Commit

Permalink
refactor: Bubble in to service
Browse files Browse the repository at this point in the history
  • Loading branch information
Dustin Blackman committed Nov 23, 2023
1 parent 50e573b commit 145abb6
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 109 deletions.
2 changes: 0 additions & 2 deletions src/domain/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod action;
mod backend;
mod bubble;
mod editor;
mod event;
mod loading;
Expand All @@ -11,7 +10,6 @@ mod textarea;

pub use action::*;
pub use backend::*;
pub use bubble::*;
pub use editor::*;
pub use event::*;
pub use loading::*;
Expand Down
177 changes: 83 additions & 94 deletions src/domain/models/bubble.rs → src/domain/services/bubble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,18 @@
#[path = "bubble_test.rs"]
mod tests;

use once_cell::sync::Lazy;
use ratatui::style::Color;
use ratatui::style::Style;
use ratatui::text::Line;
use ratatui::text::Span;
use syntect::easy::HighlightLines;
use syntect::highlighting::Theme;
use syntect::parsing::SyntaxReference;
use syntect::parsing::SyntaxSet;

use super::Author;
use super::Message;
use super::MessageType;
use crate::domain::services::Syntaxes;

// TODO include this in refactors, it shouldn't be here.
static SYNTAX: Lazy<SyntaxSet> = Lazy::new(Syntaxes::load);
use super::Syntaxes;
use super::SYNTAX_SET;
use crate::domain::models::Author;
use crate::domain::models::Message;
use crate::domain::models::MessageType;

#[derive(PartialEq, Eq)]
pub enum BubbleAlignment {
Expand All @@ -27,62 +22,41 @@ pub enum BubbleAlignment {
}

pub struct Bubble {
alignment: BubbleAlignment,
message: Message,
window_max_width: u16,
codeblock_counter: usize,
}

// TODO this entire model has gotten out of hand. Refactor.
impl<'a> Bubble {
pub fn new(message: Message) -> Bubble {
return Bubble { message };
}

pub fn as_lines(
&self,
pub fn new(
message: Message,
alignment: BubbleAlignment,
theme: &Theme,
window_max_width: u16,
total_codeblock_counter: usize,
) -> Vec<Line<'a>> {
// Lazy defaults
let syntax = get_syntax("text");
let mut highlight = HighlightLines::new(syntax, theme);

// Add a minimum 4% of padding on the side.
let min_bubble_padding_length = ((window_max_width as f32 * 0.04).ceil()) as usize;

// left border + left padding + (text, not counted) + right padding + right
// border + scrollbar. And then minimum bubble padding.
let line_border_width = 5 + min_bubble_padding_length;

let message_lines = self
.message
.as_string_lines(window_max_width - line_border_width as u16);

let username = &self.message.author_formatted;
let mut max_line_length = message_lines
.iter()
.map(|line| {
return line.len();
})
.max()
.unwrap();
if max_line_length < username.len() {
max_line_length = username.len();
}
codeblock_counter: usize,
) -> Bubble {
return Bubble {
alignment,
message,
window_max_width,
codeblock_counter,
};
}

pub fn as_lines(&mut self, theme: &Theme) -> Vec<Line<'a>> {
// Lazy default
let mut highlight = HighlightLines::new(Syntaxes::get("text"), theme);
let mut in_codeblock = false;
let mut codeblock_count = 0;
let mut lines: Vec<Line> = vec![];

let (message_lines, max_line_length) = self.get_message_lines();

for line in message_lines {
let (formatted_line, mut spans) = self.format_line(line.to_string(), max_line_length);
let bubble_padding = [" "]
.repeat(window_max_width as usize - formatted_line.len())
.join("");
let (mut spans, line_length) = self.format_line(line.to_string(), max_line_length);

if in_codeblock {
let highlighted_spans: Vec<Span> = highlight
.highlight_line(&line, &SYNTAX)
.highlight_line(&line, &SYNTAX_SET)
.unwrap()
.iter()
.map(|segment| {
Expand All @@ -91,7 +65,7 @@ impl<'a> Bubble {
return Span::styled(
content.to_string(),
Style {
fg: translate_colour(style.foreground),
fg: Syntaxes::translate_colour(style.foreground),
..Style::default()
},
);
Expand All @@ -100,59 +74,97 @@ impl<'a> Bubble {

spans = self
.format_spans(line.to_string(), max_line_length, highlighted_spans)
.1;
.0;
}

if line.trim().starts_with("```") {
let lang = line.trim().replace("```", "");
let syntax = get_syntax(&lang);
let syntax = Syntaxes::get(&lang);
if syntax.name.to_lowercase() != "plain text" {
highlight = HighlightLines::new(syntax, theme);
in_codeblock = true;

codeblock_count += 1;
self.codeblock_counter += 1;
spans = self
.format_spans(
format!("{line} ({})", total_codeblock_counter + codeblock_count),
format!("{line} ({})", self.codeblock_counter),
max_line_length,
vec![
Span::from(line),
Span::styled(
format!(" ({})", total_codeblock_counter + codeblock_count),
format!(" ({})", self.codeblock_counter),
Style {
fg: Some(Color::White),
..Style::default()
},
),
],
)
.1;
.0;
} else {
in_codeblock = false;
}
}

if alignment == BubbleAlignment::Left {
let bubble_padding = [" "]
.repeat(self.window_max_width as usize - line_length)
.join("");

if self.alignment == BubbleAlignment::Left {
spans.push(Span::from(bubble_padding));
lines.push(Line::from(spans));
} else {
let mut res = vec![Span::from(bubble_padding)];
res.extend(spans);
lines.push(Line::from(res));
let mut line_spans = vec![Span::from(bubble_padding)];
line_spans.extend(spans);
lines.push(Line::from(line_spans));
}
}

return self.wrap_lines_in_buddle(lines, max_line_length);
}

fn get_message_lines(&self) -> (Vec<String>, usize) {
// Add a minimum 4% of padding on the side.
let min_bubble_padding_length = ((self.window_max_width as f32 * 0.04).ceil()) as usize;

// left border + left padding + (text, not counted) + right padding + right
// border + scrollbar. And then minimum bubble padding.
let line_border_width = 5 + min_bubble_padding_length;

let message_lines = self
.message
.as_string_lines(self.window_max_width - line_border_width as u16);

let mut max_line_length = message_lines
.iter()
.map(|line| {
return line.len();
})
.max()
.unwrap();

let username = &self.message.author_formatted;
if max_line_length < username.len() {
max_line_length = username.len();
}

return (message_lines, max_line_length);
}

fn wrap_lines_in_buddle(&self, lines: Vec<Line<'a>>, max_line_length: usize) -> Vec<Line<'a>> {
// Add 2 for the vertical bars.
let inner_bar = ["─"].repeat(max_line_length + 2).join("");
let top_left_border = "╭";
let mut top_bar = format!("{top_left_border}{inner_bar}╮");
let bottom_bar = format!("╰{inner_bar}╯");
let bar_bubble_padding = [" "]
// TODO WTF is 8?
.repeat(window_max_width as usize - max_line_length - 8)
.repeat(self.window_max_width as usize - max_line_length - 8)
.join("");

if alignment == BubbleAlignment::Left {
let username = &self.message.author_formatted;

if self.alignment == BubbleAlignment::Left {
let top_replace = ["─"].repeat(username.len()).join("");
top_bar = top_bar.replace(
format!("{top_left_border}{top_replace}").as_str(),
Expand All @@ -179,20 +191,20 @@ impl<'a> Bubble {

fn format_spans(
&self,
line: String,
line_str: String,
max_line_length: usize,
mut spans: Vec<Span<'a>>,
) -> (String, Vec<Span<'a>>) {
let fill = [" "].repeat(max_line_length - line.len()).join("");
let formatted_line = format!("│ {line}{fill} │");
) -> (Vec<Span<'a>>, usize) {
let fill = [" "].repeat(max_line_length - line_str.len()).join("");
let line_length = format!("│ {line_str}{fill} │").len();

let mut spans_res = vec![self.highlight_span("│ ".to_string())];
spans_res.append(&mut spans);
spans_res.push(self.highlight_span(format!("{fill} │").to_string()));
return (formatted_line, spans_res);
return (spans_res, line_length);
}

fn format_line(&self, line: String, max_line_length: usize) -> (String, Vec<Span<'a>>) {
fn format_line(&self, line: String, max_line_length: usize) -> (Vec<Span<'a>>, usize) {
return self.format_spans(
line.to_string(),
max_line_length,
Expand Down Expand Up @@ -226,26 +238,3 @@ impl<'a> Bubble {
return Line::from(self.highlight_span(text));
}
}

fn translate_colour(syntect_color: syntect::highlighting::Color) -> Option<Color> {
match syntect_color {
syntect::highlighting::Color { r, g, b, a } if a > 0 => return Some(Color::Rgb(r, g, b)),
_ => return None,
}
}

fn get_syntax(name: &str) -> &SyntaxReference {
if let Some(syntax) = SYNTAX.find_syntax_by_extension(name) {
return syntax;
}

if let Some(syntax) = SYNTAX.find_syntax_by_name(name) {
return syntax;
}

if let Some(syntax) = SYNTAX.find_syntax_by_token(name) {
return syntax;
}

return SYNTAX.find_syntax_plain_text();
}
13 changes: 5 additions & 8 deletions src/domain/services/bubble_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use ratatui::widgets::Paragraph;
use ratatui::Frame;
use syntect::highlighting::Theme;

use super::Bubble;
use super::BubbleAlignment;
use crate::domain::models::Author;
use crate::domain::models::Bubble;
use crate::domain::models::BubbleAlignment;
use crate::domain::models::Message;

#[cfg(test)]
Expand Down Expand Up @@ -67,12 +67,9 @@ impl<'a> BubbleList<'a> {
align = BubbleAlignment::Right;
}

let bubble_lines = Bubble::new(message.clone()).as_lines(
align,
&self.theme,
line_width,
total_codeblock_counter,
);
let bubble_lines =
Bubble::new(message.clone(), align, line_width, total_codeblock_counter)
.as_lines(&self.theme);

let codeblocks_count = message.codeblocks().len();
total_codeblock_counter += codeblocks_count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ fn create_lines(
Config::set(ConfigKey::Model, "codellama:latest");

let message = Message::new(author, text);
let bubble = Bubble::new(message);
let theme = Themes::load("base16-seti", "")?;
let lines = bubble.as_lines(alignment, &theme, 50, codeblock_count);
let lines = Bubble::new(message, alignment, 50, codeblock_count).as_lines(&theme);
let lines_str = lines
.iter()
.map(|line| {
Expand Down
2 changes: 2 additions & 0 deletions src/domain/services/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod actions;
mod app_state;
mod bubble;
mod bubble_list;
pub mod clipboard;
mod code_blocks;
Expand All @@ -10,6 +11,7 @@ mod syntaxes;
mod themes;

pub use app_state::*;
pub use bubble::*;
pub use bubble_list::*;
pub use code_blocks::*;
pub use scroll::*;
Expand Down
32 changes: 31 additions & 1 deletion src/domain/services/syntaxes.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
use once_cell::sync::Lazy;
use ratatui::style::Color;
use syntect::parsing::SyntaxReference;
use syntect::parsing::SyntaxSet;

pub static SYNTAX_SET: Lazy<SyntaxSet> = Lazy::new(Syntaxes::load);

pub struct Syntaxes {}

impl Syntaxes {
pub fn load() -> SyntaxSet {
fn load() -> SyntaxSet {
let payload = include_bytes!("../../../.cache/syntaxes/syntaxes.bin");
let syntax_set: SyntaxSet = bincode::deserialize_from(&payload[..]).unwrap();
return syntax_set;
}

pub fn get(name: &str) -> &SyntaxReference {
if let Some(syntax) = SYNTAX_SET.find_syntax_by_extension(name) {
return syntax;
}

if let Some(syntax) = SYNTAX_SET.find_syntax_by_name(name) {
return syntax;
}

if let Some(syntax) = SYNTAX_SET.find_syntax_by_token(name) {
return syntax;
}

return SYNTAX_SET.find_syntax_plain_text();
}

pub fn translate_colour(syntect_color: syntect::highlighting::Color) -> Option<Color> {
match syntect_color {
syntect::highlighting::Color { r, g, b, a } if a > 0 => {
return Some(Color::Rgb(r, g, b))
}
_ => return None,
}
}
}
Loading

0 comments on commit 145abb6

Please sign in to comment.