From 1e89b1720d0017cfd99dae2a4e424ba95db7281a Mon Sep 17 00:00:00 2001 From: Anthony Potappel Date: Mon, 5 Aug 2024 09:36:17 +0200 Subject: [PATCH] refactor text_buffer, text_display --- .../src/tui/events/handle_prompt_window.rs | 6 +- .../src/tui/events/handle_response_window.rs | 6 +- .../src/tui/events/text_window_event.rs | 6 +- .../apps/builtin/llm/prompt/src/tui/mod.rs | 2 +- .../llm/prompt/src/tui/window/cursor.rs | 46 +-- .../builtin/llm/prompt/src/tui/window/mod.rs | 14 +- .../llm/prompt/src/tui/window/text_display.rs | 173 ---------- .../prompt/src/tui/window/text_display/mod.rs | 307 ++++++++++++++++++ .../window/{ => text_display}/text_render.rs | 4 +- .../src/tui/window/text_document/mod.rs | 3 +- .../tui/window/text_document/read_document.rs | 2 +- .../text_document/read_write_document.rs | 2 +- .../{text_window.rs => text_window/mod.rs} | 222 +------------ .../window/{ => text_window}/text_buffer.rs | 231 +++---------- .../window/text_window/text_window_trait.rs | 209 ++++++++++++ .../prompt/src/tui/window/window_config.rs | 2 +- 16 files changed, 624 insertions(+), 611 deletions(-) delete mode 100644 lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display.rs create mode 100644 lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/mod.rs rename lumni/src/apps/builtin/llm/prompt/src/tui/window/{ => text_display}/text_render.rs (98%) rename lumni/src/apps/builtin/llm/prompt/src/tui/window/{text_window.rs => text_window/mod.rs} (63%) rename lumni/src/apps/builtin/llm/prompt/src/tui/window/{ => text_window}/text_buffer.rs (66%) create mode 100644 lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window/text_window_trait.rs diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_prompt_window.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_prompt_window.rs index 20a60591..50875d74 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_prompt_window.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_prompt_window.rs @@ -26,7 +26,7 @@ pub fn handle_prompt_window_event( return Ok(Some(app_ui.set_response_window())); } } - }, + } KeyCode::Tab => { if !in_editing_block(&mut app_ui.prompt) { return Ok(Some(app_ui.prompt.next_window_status())); @@ -99,7 +99,9 @@ pub fn handle_prompt_window_event( if let Some(prev) = key_track.previous_key_str() { if prev == " " { // change to insert mode if double space - return Ok(Some(app_ui.set_prompt_window(true))); + return Ok(Some( + app_ui.set_prompt_window(true), + )); } } } diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_response_window.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_response_window.rs index 63ec99e4..bf6dc9da 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_response_window.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/events/handle_response_window.rs @@ -21,7 +21,7 @@ pub fn handle_response_window_event( // jump from response window to prompt window return Ok(Some(app_ui.set_prompt_window(true))); } - }, + } KeyCode::Tab => { return Ok(Some(app_ui.set_prompt_window(false))); } @@ -60,7 +60,9 @@ pub fn handle_response_window_event( if let Some(prev) = key_track.previous_key_str() { if prev == " " { // change to insert mode if double space - return Ok(Some(app_ui.set_prompt_window(true))); + return Ok(Some( + app_ui.set_prompt_window(true), + )); } } } diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/events/text_window_event.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/events/text_window_event.rs index d2707675..5261cc3f 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/events/text_window_event.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/events/text_window_event.rs @@ -141,13 +141,11 @@ where window.move_cursor(MoveCursor::EndOfFile); } 'j' => { - let lines_to_move = - key_track.take_numeric_input().unwrap_or(1); + let lines_to_move = key_track.take_numeric_input().unwrap_or(1); window.move_cursor(MoveCursor::Down(lines_to_move)); } 'k' => { - let lines_to_move = - key_track.take_numeric_input().unwrap_or(1); + let lines_to_move = key_track.take_numeric_input().unwrap_or(1); window.move_cursor(MoveCursor::Up(lines_to_move)); } 'v' => { diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs index 71ea4bdf..2e29bd5a 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs @@ -29,4 +29,4 @@ pub use super::chat::{ pub use super::server::{ ModelServer, ServerManager, ServerTrait, SUPPORTED_MODEL_ENDPOINTS, }; -pub use crate::external as lumni; +use crate::external as lumni; diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/cursor.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/cursor.rs index 24c202d6..d0f14c3d 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/cursor.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/cursor.rs @@ -1,4 +1,5 @@ -use super::{text_document::TextLine, TextDocumentTrait}; +use super::text_document::TextLine; +use super::TextDocumentTrait; #[derive(Debug, Clone)] pub enum MoveCursor { @@ -17,8 +18,8 @@ pub enum MoveCursor { pub struct Cursor { pub col: usize, pub row: usize, - anchor_col: usize, // column for anchor, start of selection - anchor_row: usize, // row for anchor, start of selection + anchor_col: usize, // column for anchor, start of selection + anchor_row: usize, // row for anchor, start of selection show_cursor: bool, // show current cursor position selection_enabled: bool, desired_col: usize, // Desired column position, independent of actual line length @@ -182,23 +183,28 @@ impl Cursor { } pub fn get_selection_bounds(&self) -> (usize, usize, usize, usize) { - // Determine the correct order for start and end positions - if self.row < self.anchor_row - || (self.row == self.anchor_row && self.col < self.anchor_col) - { - ( - self.row as usize, - self.col as usize, - self.anchor_row as usize, - self.anchor_col as usize, - ) + // Get the bounds of selected text, position based on unwrapped lines + // (start_row, start_col, end_row, end_col) + if self.selection_enabled() { + if self.row < self.anchor_row + || (self.row == self.anchor_row && self.col < self.anchor_col) + { + ( + self.row as usize, + self.col as usize, + self.anchor_row as usize, + self.anchor_col as usize, + ) + } else { + ( + self.anchor_row as usize, + self.anchor_col as usize, + self.row as usize, + self.col as usize, + ) + } } else { - ( - self.anchor_row as usize, - self.anchor_col as usize, - self.row as usize, - self.col as usize, - ) + (usize::MAX, usize::MAX, usize::MIN, usize::MIN) // No highlighting } } @@ -240,4 +246,4 @@ impl Cursor { } self.real_position = position; } -} \ No newline at end of file +} diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs index 0b6dbf22..23dacc56 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs @@ -1,9 +1,7 @@ mod cursor; mod scroller; -mod text_buffer; mod text_display; mod text_document; -mod text_render; mod text_window; mod window_config; @@ -13,16 +11,17 @@ use lumni::api::error::ApplicationError; use ratatui::layout::Rect; use ratatui::style::{Color, Style}; pub use scroller::Scroller; -pub use text_buffer::TextBuffer; pub use text_display::LineType; pub use text_document::{ ReadDocument, ReadWriteDocument, TextDocumentTrait, TextLine, TextSegment, }; pub use text_window::{TextWindow, TextWindowTrait}; -pub use window_config::{WindowConfig, WindowKind, WindowStatus, WindowContent}; -pub use super::events::WindowEvent; -pub use super::events::KeyTrack; -pub use crate::external as lumni; +pub use window_config::{ + WindowConfig, WindowContent, WindowKind, WindowStatus, +}; + +use super::events::{KeyTrack, WindowEvent}; +use crate::external as lumni; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RectArea { @@ -108,7 +107,6 @@ impl PromptWindow<'_> { self.set_window_status(next_status); return WindowEvent::PromptWindow(None); - } } diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display.rs deleted file mode 100644 index aa31fb08..00000000 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display.rs +++ /dev/null @@ -1,173 +0,0 @@ -use ratatui::style::Color; -use ratatui::text::{Line, Span}; - -use super::cursor::Cursor; - -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -pub struct CodeBlock { - pub start: u16, // start line of the code block - pub end: Option, // end line of the code block (if closed) -} - -impl CodeBlock { - pub fn is_closed(&self) -> bool { - self.end.is_some() - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum CodeBlockLineType { - Start, - End, - Line, -} - -#[derive(Debug, Clone, Copy)] -pub struct CodeBlockLine { - ptr: u16, // refers to the code block itself - r#type: CodeBlockLineType, -} - -impl CodeBlockLine { - pub fn new(ptr: u16, r#type: CodeBlockLineType) -> Self { - CodeBlockLine { ptr, r#type } - } - - pub fn get_ptr(&self) -> u16 { - self.ptr - } - - pub fn get_type(&self) -> CodeBlockLineType { - self.r#type - } - - pub fn is_end(&self) -> bool { - self.r#type == CodeBlockLineType::End - } -} - -#[derive(Debug, Clone, Copy)] -pub enum LineType { - Text, - Code(CodeBlockLine), -} - -#[derive(Debug, Clone)] -pub struct LineSegment<'a> { - pub line: Line<'a>, // wrapped line segment - pub length: usize, // length of the line segment - pub last_segment: bool, // last part of a line - pub line_type: Option, // type of line: Text or Code - pub background: Option, // default background color -} - -impl<'a> LineSegment<'a> { - pub fn new( - line: Line<'a>, - length: usize, - last_segment: bool, - line_type: Option, - background: Option, - ) -> Self { - LineSegment { - line, - length, - last_segment, - line_type, - background, - } - } - - pub fn spans_mut(&mut self) -> &mut Vec> { - &mut self.line.spans - } -} - -#[derive(Debug, Clone)] -pub struct TextDisplay<'a> { - pub wrap_lines: Vec>, // Text (e.g., wrapped, highlighted) for display - pub display_width: usize, // Width of the display area, used for wrapping - pub column: usize, - pub row: usize, -} - -impl<'a> TextDisplay<'a> { - pub fn new(display_width: usize) -> Self { - TextDisplay { - wrap_lines: Vec::new(), - display_width, - column: 0, - row: 0, - } - } - - pub fn update_column_row(&mut self, cursor: &Cursor) -> (usize, usize) { - // Get the current row in the wrapped text display based on the cursor position - let cursor_position = cursor.real_position(); - let mut new_line_position = 0; - - self.column = 0; - self.row = 0; - - let last_line = self.wrap_lines.len().saturating_sub(1); - - for (row, line) in self.wrap_lines.iter().enumerate() { - let line_length = if line.last_segment { - line.length + 1 // account for end of line/ cursor space - } else { - line.length - }; - - // position_newline - if new_line_position + line_length > cursor_position - || row == last_line - { - // Cursor is on this line - let column = cursor_position.saturating_sub(new_line_position); - self.column = column; - self.row = row; - break; - } - new_line_position += line_length; - } - (self.column, self.row) - } - - pub fn wrap_lines(&self) -> &[LineSegment<'a>] { - &self.wrap_lines - } - - pub fn width(&self) -> usize { - self.display_width - } - - pub fn push_line( - &mut self, - line: Line<'a>, - length: usize, - last_segment: bool, - line_type: Option, - background: Option, - ) { - self.wrap_lines.push(LineSegment::new( - line, - length, - last_segment, - line_type, - background, - )); - } - - pub fn set_display_width(&mut self, width: usize) { - self.display_width = width; - } - - pub fn get_column_row(&self) -> (usize, usize) { - (self.column, self.row) - } - - pub fn clear(&mut self) { - self.wrap_lines.clear(); - } -} diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/mod.rs new file mode 100644 index 00000000..a28111f9 --- /dev/null +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/mod.rs @@ -0,0 +1,307 @@ +mod text_render; + +use ratatui::style::{Color, Style}; +use ratatui::text::{Line, Span}; +use text_render::DisplayWindowRenderer; + +use super::cursor::Cursor; +use super::text_document::{TextLine, TextWrapper}; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub struct CodeBlock { + pub start: u16, // start line of the code block + pub end: Option, // end line of the code block (if closed) +} + +impl CodeBlock { + pub fn is_closed(&self) -> bool { + self.end.is_some() + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CodeBlockLineType { + Start, + End, + Line, +} + +#[derive(Debug, Clone, Copy)] +pub struct CodeBlockLine { + ptr: u16, // refers to the code block itself + r#type: CodeBlockLineType, +} + +impl CodeBlockLine { + pub fn new(ptr: u16, r#type: CodeBlockLineType) -> Self { + CodeBlockLine { ptr, r#type } + } + + pub fn get_ptr(&self) -> u16 { + self.ptr + } + + pub fn get_type(&self) -> CodeBlockLineType { + self.r#type + } + + pub fn is_end(&self) -> bool { + self.r#type == CodeBlockLineType::End + } +} + +#[derive(Debug, Clone, Copy)] +pub enum LineType { + Text, + Code(CodeBlockLine), +} + +#[derive(Debug, Clone)] +pub struct LineSegment<'a> { + pub line: Line<'a>, // wrapped line segment + pub length: usize, // length of the line segment + pub last_segment: bool, // last part of a line + pub line_type: Option, // type of line: Text or Code + pub background: Option, // default background color +} + +impl<'a> LineSegment<'a> { + pub fn new( + line: Line<'a>, + length: usize, + last_segment: bool, + line_type: Option, + background: Option, + ) -> Self { + LineSegment { + line, + length, + last_segment, + line_type, + background, + } + } + + pub fn spans_mut(&mut self) -> &mut Vec> { + &mut self.line.spans + } +} + +#[derive(Debug, Clone)] +pub struct TextDisplay<'a> { + pub wrap_lines: Vec>, // Text (e.g., wrapped, highlighted) for display + pub display_width: usize, // Width of the display area, used for wrapping + pub column: usize, + pub row: usize, +} + +impl<'a> TextDisplay<'a> { + pub fn new(display_width: usize) -> Self { + TextDisplay { + wrap_lines: Vec::new(), + display_width, + column: 0, + row: 0, + } + } + + pub fn update(&mut self, text_lines: &Vec, cursor: &Cursor) { + self.clear(); + let text_wrapper = TextWrapper::new(self.width()); + for (idx, line) in text_lines.iter().enumerate() { + let text_str = + line.segments().map(|s| s.text.as_str()).collect::(); + let trailing_spaces = + text_str.len() - text_str.trim_end_matches(' ').len(); + let wrapped_lines = text_wrapper.wrap_text_styled(line, None); + + // length of the wrapped lines content + if wrapped_lines.is_empty() { + self.handle_empty_line(trailing_spaces, line.get_background()); + } else { + // process wrapped lines + self.process_wrapped_lines( + wrapped_lines, + idx, + trailing_spaces, + cursor, + line.get_background(), + ); + } + } + } + + pub fn update_column_row(&mut self, cursor: &Cursor) -> (usize, usize) { + // Get the current row in the wrapped text display based on the cursor position + let cursor_position = cursor.real_position(); + let mut new_line_position = 0; + + self.column = 0; + self.row = 0; + + let last_line = self.wrap_lines.len().saturating_sub(1); + + for (row, line) in self.wrap_lines.iter().enumerate() { + let line_length = if line.last_segment { + line.length + 1 // account for end of line/ cursor space + } else { + line.length + }; + + // position_newline + if new_line_position + line_length > cursor_position + || row == last_line + { + // Cursor is on this line + let column = cursor_position.saturating_sub(new_line_position); + self.column = column; + self.row = row; + break; + } + new_line_position += line_length; + } + (self.column, self.row) + } + + pub fn wrap_lines(&self) -> &[LineSegment<'a>] { + &self.wrap_lines + } + + pub fn width(&self) -> usize { + self.display_width + } + + fn push_line( + &mut self, + line: Line<'a>, + length: usize, + last_segment: bool, + line_type: Option, + background: Option, + ) { + self.wrap_lines.push(LineSegment::new( + line, + length, + last_segment, + line_type, + background, + )); + } + + pub fn set_display_width(&mut self, width: usize) { + self.display_width = width; + } + + pub fn get_column_row(&self) -> (usize, usize) { + (self.column, self.row) + } + + pub fn clear(&mut self) { + self.wrap_lines.clear(); + } + + pub fn select_window_lines(&self, start: usize, end: usize) -> Vec { + let renderer = + DisplayWindowRenderer::new(self.wrap_lines(), self.width()); + renderer.render_lines(start, end) + } + + fn handle_empty_line( + &mut self, + trailing_spaces: usize, + background: Option, + ) { + if trailing_spaces > 0 { + // Add trailing spaces to the line + let spaces = std::iter::repeat(' ') + .take(trailing_spaces) + .collect::(); + + self.push_line( + Line::from(Span::raw(spaces)), + trailing_spaces, + true, + None, + background, + ); + } else { + // add empty row + self.push_line( + Line::from(Span::raw("")), + 0, + true, + None, + background, + ); + } + } + + fn process_wrapped_lines( + &mut self, + wrapped_lines: Vec, + unwrapped_line_index: usize, + // trailing spaces of the unwrapped line are removed during wrapping, + // this is added back to the first and last (wrapped) line respectively + trailing_spaces: usize, + cursor: &Cursor, + background: Option, + ) { + let (start_row, start_col, end_row, end_col) = + cursor.get_selection_bounds(); + let mut char_pos = 0; + + for (idx, line) in wrapped_lines.iter().enumerate() { + let mut spans = Vec::new(); + + // Start character position for this line from the cumulative offset + for segment in line.segments() { + let chars: Vec = segment.text.chars().collect(); + for ch in chars.into_iter() { + // Adjust row based on the index in wrapped lines + let should_select = cursor.should_select( + unwrapped_line_index, + char_pos, + start_row, + start_col, + end_row, + end_col, + ); + + let mut effective_style = + segment.style.unwrap_or(Style::default()); + if should_select { + effective_style = effective_style.bg(Color::Blue); + } + spans.push(Span::styled(ch.to_string(), effective_style)); + char_pos += 1; + } + } + + let mut current_line = Line::from(spans); + + let last_segment = idx == wrapped_lines.len() - 1; + + if last_segment && trailing_spaces > 0 { + // Add trailing spaces back to end of the last line + let spaces = std::iter::repeat(' ') + .take(trailing_spaces) + .collect::(); + current_line.spans.push(Span::raw(spaces)); + } + let current_line_length = current_line + .spans + .iter() + .map(|span| span.content.len()) + .sum::(); + self.push_line( + current_line, + current_line_length, + last_segment, + None, + background, + ); + char_pos += 1; // account for newline character + } + } +} diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_render.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/text_render.rs similarity index 98% rename from lumni/src/apps/builtin/llm/prompt/src/tui/window/text_render.rs rename to lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/text_render.rs index bff68481..2f87ff8a 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_render.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_display/text_render.rs @@ -1,9 +1,7 @@ use ratatui::style::{Color, Style}; use ratatui::text::{Line, Masked, Span}; -use super::text_display::{ - CodeBlockLine, CodeBlockLineType, LineSegment, LineType, -}; +use super::{CodeBlockLine, CodeBlockLineType, LineSegment, LineType}; pub struct DisplayWindowRenderer<'a> { wrap_lines: &'a [LineSegment<'a>], diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/mod.rs index 2b37c8f0..b1bba959 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/mod.rs @@ -11,7 +11,7 @@ pub use read_write_document::ReadWriteDocument; pub use text_line::{TextLine, TextSegment}; pub use text_wrapper::TextWrapper; -pub use crate::external as lumni; +use crate::external as lumni; pub trait TextDocumentTrait { fn from_text(lines: Vec) -> Self; @@ -54,5 +54,4 @@ pub trait TextDocumentTrait { 0 } } - } diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_document.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_document.rs index af43ffca..07642d33 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_document.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_document.rs @@ -3,7 +3,7 @@ use ratatui::style::Style; use super::text_line::TextLine; use super::TextDocumentTrait; -pub use crate::external as lumni; +use crate::external as lumni; #[derive(Debug)] pub struct ReadDocument { diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_write_document.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_write_document.rs index 1e039f46..5099948e 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_write_document.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_document/read_write_document.rs @@ -4,7 +4,7 @@ use ratatui::style::Style; use super::piece_table::PieceTable; use super::text_line::TextLine; use super::TextDocumentTrait; -pub use crate::external as lumni; +use crate::external as lumni; #[derive(Debug)] pub struct ReadWriteDocument { diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window/mod.rs similarity index 63% rename from lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window.rs rename to lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window/mod.rs index cbfd8fbc..d83de579 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/text_window/mod.rs @@ -1,18 +1,28 @@ +mod text_buffer; +mod text_window_trait; + use lumni::api::error::ApplicationError; use ratatui::layout::{Alignment, Rect}; use ratatui::style::{Color, Style}; use ratatui::text::Text; use ratatui::widgets::block::Padding; -use ratatui::widgets::{Block, Borders, Paragraph, ScrollbarState}; +use ratatui::widgets::{Block, Borders, Paragraph}; +pub use text_buffer::TextBuffer; +pub use text_window_trait::TextWindowTrait; -use super::cursor::MoveCursor; +use super::cursor::{Cursor, MoveCursor}; use super::scroller::Scroller; -use super::text_display::{CodeBlock, LineType}; +use super::text_display::{ + CodeBlock, CodeBlockLine, CodeBlockLineType, TextDisplay, +}; use super::text_document::{ - ReadDocument, ReadWriteDocument, TextDocumentTrait, TextLine, + ReadDocument, ReadWriteDocument, TextDocumentTrait, TextLine, TextWrapper, }; -use super::{RectArea, TextBuffer, WindowConfig, WindowContent, WindowKind, WindowStatus}; -pub use crate::external as lumni; +use super::window_config::{ + WindowConfig, WindowContent, WindowKind, WindowStatus, +}; +use super::{LineType, RectArea}; +use crate::external as lumni; #[derive(Debug, Clone)] pub struct TextWindow<'a, T: TextDocumentTrait> { @@ -301,203 +311,3 @@ impl<'a> TextWindow<'a, ReadDocument> { Self::new(window_type, document) } } - -pub trait TextWindowTrait<'a, T: TextDocumentTrait> { - fn base(&mut self) -> &mut TextWindow<'a, T>; - - fn init(&mut self) { - self.base().update_placeholder_text(); - } - - fn current_line_type(&mut self) -> Option { - self.base().current_line_type() - } - - fn get_column_row(&mut self) -> (usize, usize) { - self.base().get_column_row() - } - - fn max_row_idx(&mut self) -> usize { - self.base().max_row_idx() - } - - fn current_code_block(&mut self) -> Option { - self.base().current_code_block() - } - - fn widget<'b>(&'b mut self, area: &Rect) -> Paragraph<'b> - where - 'a: 'b, - T: 'b, - { - let base = self.base(); - base.widget(area) - } - - fn vertical_scroll_bar_state<'b>(&'b mut self) -> &'b mut ScrollbarState - where - 'a: 'b, - T: 'b, - { - self.base().scroller.vertical_scroll_bar_state() - } - - fn set_window_status(&mut self, status: WindowStatus) { - self.set_selection_anchor(false); // disable selection when changing status - self.base().set_window_status(status); - } - - fn is_editable(&mut self) -> bool { - self.base().window_type.is_editable() - } - - fn get_kind(&mut self) -> WindowKind { - self.base().window_type.kind() - } - - fn window_status(&mut self) -> WindowStatus { - self.base().window_status() - } - - fn scroll_up(&mut self) { - self.base().scroll_up(); - } - - fn scroll_down(&mut self) { - self.base().scroll_down(); - } - - fn scroll_to_end(&mut self) { - self.base().scroll_to_end(); - } - - fn move_cursor(&mut self, direction: MoveCursor) { - self.base().move_cursor(direction); - } - - fn text_insert_add( - &mut self, - text: &str, - style: Option