diff --git a/src/processing/builder.rs b/src/processing/builder.rs index 427da91..951e6d5 100644 --- a/src/processing/builder.rs +++ b/src/processing/builder.rs @@ -1,6 +1,6 @@ use super::{ code::{CodeBlockParser, CodeLine, ExternalFile, Highlight, HighlightGroup, Snippet, SnippetLanguage}, - execution::{DisplaySeparator, RunAcquireTerminalCodeSnippet, SnippetExecutionDisabledOperation}, + execution::{DisplaySeparator, RunAcquireTerminalSnippet, SnippetExecutionDisabledOperation}, modals::KeyBindingsModalBuilder, }; use crate::{ @@ -880,10 +880,16 @@ impl<'a> PresentationBuilder<'a> { return Err(BuildError::UnsupportedExecution(code.language)); } if code.attributes.acquire_terminal { - let operation = RunAcquireTerminalCodeSnippet::new( + let block_length = block_length as u16; + let block_length = match self.theme.code.alignment.clone().unwrap_or_default() { + Alignment::Left { .. } | Alignment::Right { .. } => block_length, + Alignment::Center { minimum_size, .. } => block_length.max(minimum_size), + }; + let operation = RunAcquireTerminalSnippet::new( code, self.code_executor.clone(), - self.theme.execution_output.status.failure, + self.theme.execution_output.status.clone(), + block_length, ); let operation = RenderOperation::RenderAsync(Rc::new(operation)); self.chunk_operations.push(operation); diff --git a/src/processing/execution.rs b/src/processing/execution.rs index fcc3912..86f9484 100644 --- a/src/processing/execution.rs +++ b/src/processing/execution.rs @@ -246,21 +246,45 @@ impl RenderAsync for SnippetExecutionDisabledOperation { } } +#[derive(Default, Clone)] +enum AcquireTerminalSnippetState { + #[default] + NotStarted, + Success, + Failure(Vec), +} + +impl std::fmt::Debug for AcquireTerminalSnippetState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NotStarted => write!(f, "NotStarted"), + Self::Success => write!(f, "Success"), + Self::Failure(_) => write!(f, "Failure"), + } + } +} + #[derive(Clone, Debug)] -pub(crate) struct RunAcquireTerminalCodeSnippet { +pub(crate) struct RunAcquireTerminalSnippet { snippet: Snippet, + block_length: u16, executor: Rc, - error_message: RefCell>>, - error_colors: Colors, + colors: ExecutionStatusBlockStyle, + state: RefCell, } -impl RunAcquireTerminalCodeSnippet { - pub(crate) fn new(snippet: Snippet, executor: Rc, error_colors: Colors) -> Self { - Self { snippet, executor, error_message: Default::default(), error_colors } +impl RunAcquireTerminalSnippet { + pub(crate) fn new( + snippet: Snippet, + executor: Rc, + colors: ExecutionStatusBlockStyle, + block_length: u16, + ) -> Self { + Self { snippet, block_length, executor, colors, state: Default::default() } } } -impl RunAcquireTerminalCodeSnippet { +impl RunAcquireTerminalSnippet { fn invoke(&self) -> Result<(), String> { let mut stdout = io::stdout(); stdout @@ -282,35 +306,53 @@ impl RunAcquireTerminalCodeSnippet { } } -impl AsRenderOperations for RunAcquireTerminalCodeSnippet { +impl AsRenderOperations for RunAcquireTerminalSnippet { fn as_render_operations(&self, _dimensions: &WindowSize) -> Vec { - let error_message = self.error_message.borrow(); - match error_message.deref() { - Some(lines) => { - let mut ops = vec![RenderOperation::RenderLineBreak]; - for line in lines { - ops.extend([ - RenderOperation::RenderText { - line: vec![Text::new(line, TextStyle::default().colors(self.error_colors))].into(), - alignment: Alignment::Left { margin: Margin::Percent(25) }, - }, - RenderOperation::RenderLineBreak, - ]); - } - ops + let state = self.state.borrow(); + let separator_text = match state.deref() { + AcquireTerminalSnippetState::NotStarted => { + Text::new("not started", TextStyle::colored(self.colors.not_started)) + } + AcquireTerminalSnippetState::Success => Text::new("finished", TextStyle::colored(self.colors.success)), + AcquireTerminalSnippetState::Failure(_) => { + Text::new("finished with error", TextStyle::colored(self.colors.failure)) + } + }; + + let heading = TextBlock(vec![" [".into(), separator_text, "] ".into()]); + let separator_width = SeparatorWidth::Fixed(self.block_length.max(MINIMUM_SEPARATOR_WIDTH)); + let separator = RenderSeparator::new(heading, separator_width); + let mut ops = vec![ + RenderOperation::RenderLineBreak, + RenderOperation::RenderDynamic(Rc::new(separator)), + RenderOperation::RenderLineBreak, + ]; + if let AcquireTerminalSnippetState::Failure(lines) = state.deref() { + ops.push(RenderOperation::RenderLineBreak); + for line in lines { + ops.extend([ + RenderOperation::RenderText { + line: vec![Text::new(line, TextStyle::default().colors(self.colors.failure))].into(), + alignment: Alignment::Left { margin: Margin::Percent(25) }, + }, + RenderOperation::RenderLineBreak, + ]); } - None => Vec::new(), } + ops } } -impl RenderAsync for RunAcquireTerminalCodeSnippet { +impl RenderAsync for RunAcquireTerminalSnippet { fn start_render(&self) -> bool { + if !matches!(*self.state.borrow(), AcquireTerminalSnippetState::NotStarted) { + return false; + } if let Err(e) = self.invoke() { let lines = e.lines().map(ToString::to_string).collect(); - *self.error_message.borrow_mut() = Some(lines); + *self.state.borrow_mut() = AcquireTerminalSnippetState::Failure(lines); } else { - *self.error_message.borrow_mut() = None; + *self.state.borrow_mut() = AcquireTerminalSnippetState::Success; } true } diff --git a/src/style.rs b/src/style.rs index 77f9a00..32c5b41 100644 --- a/src/style.rs +++ b/src/style.rs @@ -15,6 +15,10 @@ pub(crate) struct TextStyle { } impl TextStyle { + pub(crate) fn colored(colors: Colors) -> Self { + Self { flags: Default::default(), colors } + } + /// Add bold to this style. pub(crate) fn bold(self) -> Self { self.add_flag(TextFormatFlags::Bold)