Skip to content

Commit 4836e87

Browse files
committed
feat: add +auto_exec attribute to snippets
1 parent b002a97 commit 4836e87

File tree

3 files changed

+119
-24
lines changed

3 files changed

+119
-24
lines changed

src/code/snippet.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ impl SnippetParser {
242242
attributes.execution = SnippetExec::Exec(spec);
243243
}
244244
}
245+
AutoExec(spec) => {
246+
if !matches!(attributes.execution, SnippetExec::AcquireTerminal(_)) {
247+
attributes.execution = SnippetExec::AutoExec(spec);
248+
}
249+
}
245250
ExecReplace(spec) => {
246251
attributes.representation = SnippetRepr::ExecReplace;
247252
attributes.execution = SnippetExec::Exec(spec);
@@ -282,6 +287,7 @@ impl SnippetParser {
282287
let attribute = match token {
283288
"line_numbers" => SnippetAttribute::LineNumbers,
284289
"exec" => SnippetAttribute::Exec(SnippetExecutorSpec::default()),
290+
"auto_exec" => SnippetAttribute::AutoExec(SnippetExecutorSpec::default()),
285291
"exec_replace" => SnippetAttribute::ExecReplace(SnippetExecutorSpec::default()),
286292
"validate" => SnippetAttribute::Validate(SnippetExecutorSpec::default()),
287293
"image" => SnippetAttribute::Image,
@@ -294,6 +300,9 @@ impl SnippetParser {
294300
.ok_or_else(|| SnippetBlockParseError::InvalidToken(Self::next_identifier(input).into()))?;
295301
match attribute {
296302
"exec" => SnippetAttribute::Exec(SnippetExecutorSpec::Alternative(parameter.to_string())),
303+
"auto_exec" => {
304+
SnippetAttribute::AutoExec(SnippetExecutorSpec::Alternative(parameter.to_string()))
305+
}
297306
"exec_replace" => {
298307
SnippetAttribute::ExecReplace(SnippetExecutorSpec::Alternative(parameter.to_string()))
299308
}
@@ -425,6 +434,7 @@ pub enum SnippetBlockParseError {
425434
enum SnippetAttribute {
426435
LineNumbers,
427436
Exec(SnippetExecutorSpec),
437+
AutoExec(SnippetExecutorSpec),
428438
ExecReplace(SnippetExecutorSpec),
429439
Validate(SnippetExecutorSpec),
430440
Image,
@@ -686,6 +696,7 @@ pub(crate) enum SnippetExec {
686696
#[default]
687697
None,
688698
Exec(SnippetExecutorSpec),
699+
AutoExec(SnippetExecutorSpec),
689700
AcquireTerminal(SnippetExecutorSpec),
690701
Validate(SnippetExecutorSpec),
691702
}

src/presentation/builder/mod.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ enum LastElement {
616616
pub(crate) mod utils {
617617
use super::*;
618618
use crate::{
619-
render::{engine::RenderEngine, properties::WindowSize},
619+
render::{engine::RenderEngine, operation::RenderAsyncStartPolicy, properties::WindowSize},
620620
terminal::virt::VirtualTerminal,
621621
};
622622
use std::{path::PathBuf, thread::sleep, time::Duration};
@@ -753,7 +753,7 @@ pub(crate) mod utils {
753753
presentation: Presentation,
754754
columns: Option<u16>,
755755
rows: Option<u16>,
756-
run_async_renders: bool,
756+
run_async_renders: RunAsyncRendersPolicy,
757757
background_maps: Vec<(Color, char)>,
758758
advances: Option<usize>,
759759
}
@@ -764,7 +764,7 @@ pub(crate) mod utils {
764764
presentation,
765765
columns: None,
766766
rows: None,
767-
run_async_renders: true,
767+
run_async_renders: RunAsyncRendersPolicy::All,
768768
background_maps: Default::default(),
769769
advances: None,
770770
}
@@ -785,8 +785,8 @@ pub(crate) mod utils {
785785
self
786786
}
787787

788-
pub(crate) fn run_async_renders(mut self, value: bool) -> Self {
789-
self.run_async_renders = value;
788+
pub(crate) fn run_async_renders(mut self, policy: RunAsyncRendersPolicy) -> Self {
789+
self.run_async_renders = policy;
790790
self
791791
}
792792

@@ -812,13 +812,21 @@ pub(crate) mod utils {
812812
}
813813

814814
let slide = presentation.current_slide_mut();
815-
if run_async_renders {
816-
for operation in slide.iter_operations_mut() {
817-
if let RenderOperation::RenderAsync(operation) = operation {
818-
let mut pollable = operation.pollable();
819-
while !pollable.poll().is_completed() {
820-
sleep(Duration::from_millis(1));
815+
for operation in slide.iter_operations_mut() {
816+
if let RenderOperation::RenderAsync(operation) = operation {
817+
let mut pollable = operation.pollable();
818+
let run = match &run_async_renders {
819+
RunAsyncRendersPolicy::None => false,
820+
RunAsyncRendersPolicy::All => true,
821+
RunAsyncRendersPolicy::OnlyAutomatic => {
822+
matches!(operation.start_policy(), RenderAsyncStartPolicy::Automatic)
821823
}
824+
};
825+
if !run {
826+
continue;
827+
}
828+
while !pollable.poll().is_completed() {
829+
sleep(Duration::from_millis(1));
822830
}
823831
}
824832
}
@@ -850,4 +858,10 @@ pub(crate) mod utils {
850858
(lines, styles)
851859
}
852860
}
861+
862+
pub(crate) enum RunAsyncRendersPolicy {
863+
None,
864+
All,
865+
OnlyAutomatic,
866+
}
853867
}

src/presentation/builder/snippet.rs

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,27 @@ impl PresentationBuilder<'_, '_> {
6767
let block_length = self.push_code_lines(&snippet);
6868
match snippet.attributes.execution.clone() {
6969
SnippetExec::None => Ok(()),
70-
SnippetExec::Exec(_) | SnippetExec::AcquireTerminal(_) if !execution_allowed => {
71-
let exec_type = match snippet.attributes.representation {
70+
SnippetExec::Exec(_) | SnippetExec::AutoExec(_) | SnippetExec::AcquireTerminal(_) if !execution_allowed => {
71+
let mut exec_type = match snippet.attributes.representation {
7272
SnippetRepr::Image => ExecutionType::Image,
7373
SnippetRepr::ExecReplace => ExecutionType::ExecReplace,
7474
SnippetRepr::Render | SnippetRepr::Snippet => ExecutionType::Execute,
7575
};
76+
if matches!(snippet.attributes.execution, SnippetExec::AutoExec(_)) {
77+
exec_type = ExecutionType::ExecReplace;
78+
}
7679
self.push_execution_disabled_operation(exec_type);
7780
Ok(())
7881
}
79-
SnippetExec::Exec(spec) => {
82+
SnippetExec::Exec(spec) | SnippetExec::AutoExec(spec) => {
83+
let policy = if matches!(snippet.attributes.execution, SnippetExec::AutoExec(_)) {
84+
RenderAsyncStartPolicy::Automatic
85+
} else {
86+
RenderAsyncStartPolicy::OnDemand
87+
};
8088
let executor = self.snippet_executor.language_executor(&snippet.language, &spec)?;
8189
let alignment = self.code_style(&snippet).alignment;
82-
let handle = SnippetHandle::new(snippet.clone(), executor, RenderAsyncStartPolicy::OnDemand);
90+
let handle = SnippetHandle::new(snippet.clone(), executor, policy);
8391
self.chunk_operations
8492
.push(RenderOperation::RenderAsync(Rc::new(RunSnippetTrigger::new(handle.clone()))));
8593
self.push_indicator(handle.clone(), block_length, alignment);
@@ -113,7 +121,10 @@ impl PresentationBuilder<'_, '_> {
113121

114122
fn is_execution_allowed(&self, snippet: &Snippet) -> bool {
115123
match snippet.attributes.representation {
116-
SnippetRepr::Snippet => self.options.enable_snippet_execution,
124+
SnippetRepr::Snippet => match snippet.attributes.execution {
125+
SnippetExec::AutoExec(_) => self.options.enable_snippet_execution_replace,
126+
_ => self.options.enable_snippet_execution,
127+
},
117128
SnippetRepr::Image | SnippetRepr::ExecReplace => self.options.enable_snippet_execution_replace,
118129
SnippetRepr::Render => true,
119130
}
@@ -361,7 +372,11 @@ impl AsRenderOperations for Differ {
361372
#[cfg(all(test, target_os = "linux"))]
362373
mod tests {
363374
use super::*;
364-
use crate::{markdown::text_style::Color, presentation::builder::utils::Test, theme::raw};
375+
use crate::{
376+
markdown::text_style::Color,
377+
presentation::builder::utils::{RunAsyncRendersPolicy, Test},
378+
theme::raw,
379+
};
365380
use rstest::rstest;
366381
use std::fs;
367382

@@ -436,7 +451,16 @@ echo hi
436451
```
437452
---";
438453
let lines = Test::new(input).render().rows(7).columns(7).into_lines();
439-
let expected = &[" ", "———————", " ", "echo hi", " ", "———————", " "];
454+
let expected = &[
455+
//
456+
" ",
457+
"———————",
458+
" ",
459+
"echo hi",
460+
" ",
461+
"———————",
462+
" ",
463+
];
440464
assert_eq!(lines, expected);
441465
}
442466

@@ -454,7 +478,14 @@ echo hi
454478
..Default::default()
455479
};
456480
let lines = Test::new(input).theme(theme).render().rows(5).columns(13).into_lines();
457-
let expected = &[" ", " ", " echo hi ", " ", " "];
481+
let expected = &[
482+
//
483+
" ",
484+
" ",
485+
" echo hi ",
486+
" ",
487+
" ",
488+
];
458489
assert_eq!(lines, expected);
459490
}
460491

@@ -464,8 +495,39 @@ echo hi
464495
```bash +exec
465496
echo hi
466497
```";
467-
let lines = Test::new(input).render().rows(4).columns(19).run_async_renders(false).into_lines();
468-
let expected = &[" ", "echo hi ", " ", "—— [not started] ——"];
498+
let lines =
499+
Test::new(input).render().rows(4).columns(19).run_async_renders(RunAsyncRendersPolicy::None).into_lines();
500+
let expected = &[
501+
//
502+
" ",
503+
"echo hi ",
504+
" ",
505+
"—— [not started] ——",
506+
];
507+
assert_eq!(lines, expected);
508+
}
509+
510+
#[test]
511+
fn exec_auto() {
512+
let input = "
513+
```bash +auto_exec
514+
echo hi
515+
```";
516+
let lines = Test::new(input)
517+
.render()
518+
.rows(6)
519+
.columns(19)
520+
.run_async_renders(RunAsyncRendersPolicy::OnlyAutomatic)
521+
.into_lines();
522+
let expected = &[
523+
//
524+
" ",
525+
"echo hi ",
526+
" ",
527+
"——— [finished] ————",
528+
" ",
529+
"hi ",
530+
];
469531
assert_eq!(lines, expected);
470532
}
471533

@@ -475,7 +537,8 @@ echo hi
475537
```bash +validate
476538
echo hi
477539
```";
478-
let lines = Test::new(input).render().rows(4).columns(19).run_async_renders(false).into_lines();
540+
let lines =
541+
Test::new(input).render().rows(4).columns(19).run_async_renders(RunAsyncRendersPolicy::None).into_lines();
479542
let expected = &[" ", "echo hi ", " ", " "];
480543
assert_eq!(lines, expected);
481544
}
@@ -649,9 +712,16 @@ echo hi
649712
echo hi
650713
```
651714
<!-- snippet_output: foo -->";
652-
let lines = Test::new(input).render().rows(4).columns(19).run_async_renders(false).into_lines();
715+
let lines =
716+
Test::new(input).render().rows(4).columns(19).run_async_renders(RunAsyncRendersPolicy::None).into_lines();
653717
// this should look exactly the same as if we hadn't detached the output
654-
let expected = &[" ", "echo hi ", " ", "—— [not started] ——"];
718+
let expected = &[
719+
//
720+
" ",
721+
"echo hi ",
722+
" ",
723+
"—— [not started] ——",
724+
];
655725
assert_eq!(lines, expected);
656726
}
657727

0 commit comments

Comments
 (0)