mirror of
https://github.com/mfontanini/presenterm.git
synced 2025-05-05 15:32:58 +00:00
feat: support hidden code lines (#254)
This PR adds support for hidden code lines for the languages Rust, Python, Shell and Bash using prefixes at the start of the line, particular to each language. Support for more languages can be added in future. The hidden code lines will not be visible in snippets in the presentation, but still be evaluated as normal if executed.
This commit is contained in:
parent
99bafe8aea
commit
480f9475cc
@ -65,11 +65,11 @@ impl CodeExecutor {
|
||||
match &code.language {
|
||||
CodeLanguage::Shell(interpreter) => {
|
||||
let args: &[&str] = &[];
|
||||
Self::execute_shell(interpreter, code.contents.as_bytes(), args)
|
||||
Self::execute_shell(interpreter, code.executable_contents().as_bytes(), args)
|
||||
}
|
||||
lang => {
|
||||
let executor = self.executor(lang).ok_or(CodeExecuteError::UnsupportedExecution)?;
|
||||
Self::execute_lang(executor, code.contents.as_bytes())
|
||||
Self::execute_lang(executor, code.executable_contents().as_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -295,6 +295,32 @@ echo 'hello world'
|
||||
assert_eq!(state.output, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_code_execution_executes_hidden_lines() {
|
||||
let contents = r"
|
||||
/// echo 'this line was hidden'
|
||||
/// echo 'this line was hidden and contains another prefix /// '
|
||||
echo 'hello world'
|
||||
"
|
||||
.into();
|
||||
let code = Code {
|
||||
contents,
|
||||
language: CodeLanguage::Shell("sh".into()),
|
||||
attributes: CodeAttributes { execute: true, ..Default::default() },
|
||||
};
|
||||
let handle = CodeExecutor::default().execute(&code).expect("execution failed");
|
||||
let state = loop {
|
||||
let state = handle.state();
|
||||
if state.status.is_finished() {
|
||||
break state;
|
||||
}
|
||||
};
|
||||
|
||||
let expected_lines =
|
||||
vec!["this line was hidden", "this line was hidden and contains another prefix /// ", "hello world"];
|
||||
assert_eq!(state.output, expected_lines);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_executor() {
|
||||
let dir = tempdir().unwrap();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::style::TextStyle;
|
||||
use std::{convert::Infallible, iter, ops::Range, path::PathBuf, str::FromStr};
|
||||
use std::{convert::Infallible, fmt::Write, iter, ops::Range, path::PathBuf, str::FromStr};
|
||||
use strum::EnumIter;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
@ -185,6 +185,25 @@ pub(crate) struct Code {
|
||||
pub(crate) attributes: CodeAttributes,
|
||||
}
|
||||
|
||||
impl Code {
|
||||
pub(crate) fn visible_lines(&self) -> impl Iterator<Item = &str> {
|
||||
let prefix = self.language.hidden_line_prefix();
|
||||
self.contents.lines().filter(move |line| !prefix.is_some_and(|prefix| line.starts_with(prefix)))
|
||||
}
|
||||
|
||||
pub(crate) fn executable_contents(&self) -> String {
|
||||
if let Some(prefix) = self.language.hidden_line_prefix() {
|
||||
self.contents.lines().fold(String::new(), |mut output, line| {
|
||||
let line = line.strip_prefix(prefix).unwrap_or(line);
|
||||
let _ = writeln!(output, "{line}");
|
||||
output
|
||||
})
|
||||
} else {
|
||||
self.contents.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The language of a piece of code.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, EnumIter, PartialOrd, Ord)]
|
||||
pub enum CodeLanguage {
|
||||
@ -250,6 +269,14 @@ impl CodeLanguage {
|
||||
pub(crate) fn supports_auto_render(&self) -> bool {
|
||||
matches!(self, Self::Latex | Self::Typst | Self::Mermaid)
|
||||
}
|
||||
|
||||
pub(crate) fn hidden_line_prefix(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
CodeLanguage::Rust => Some("# "),
|
||||
CodeLanguage::Python | CodeLanguage::Shell(_) | CodeLanguage::Bash => Some("/// "),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CodeLanguage {
|
||||
@ -395,3 +422,82 @@ impl Table {
|
||||
/// A table row.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct TableRow(pub(crate) Vec<TextBlock>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn code_visible_lines_bash() {
|
||||
let contents = r"echo 'hello world'
|
||||
/// echo 'this was hidden'
|
||||
|
||||
echo '/// is the prefix'
|
||||
/// echo 'the prefix is /// '
|
||||
echo 'hello again'
|
||||
"
|
||||
.to_string();
|
||||
|
||||
let expected = vec!["echo 'hello world'", "", "echo '/// is the prefix'", "echo 'hello again'"];
|
||||
let code = Code { contents, language: CodeLanguage::Bash, attributes: Default::default() };
|
||||
assert_eq!(expected, code.visible_lines().collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_visible_lines_rust() {
|
||||
let contents = r##"# fn main() {
|
||||
println!("Hello world");
|
||||
# // The prefix is # .
|
||||
# }
|
||||
"##
|
||||
.to_string();
|
||||
|
||||
let expected = vec!["println!(\"Hello world\");"];
|
||||
let code = Code { contents, language: CodeLanguage::Rust, attributes: Default::default() };
|
||||
assert_eq!(expected, code.visible_lines().collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_executable_contents_bash() {
|
||||
let contents = r"echo 'hello world'
|
||||
/// echo 'this was hidden'
|
||||
|
||||
echo '/// is the prefix'
|
||||
/// echo 'the prefix is /// '
|
||||
echo 'hello again'
|
||||
"
|
||||
.to_string();
|
||||
|
||||
let expected = r"echo 'hello world'
|
||||
echo 'this was hidden'
|
||||
|
||||
echo '/// is the prefix'
|
||||
echo 'the prefix is /// '
|
||||
echo 'hello again'
|
||||
"
|
||||
.to_string();
|
||||
|
||||
let code = Code { contents, language: CodeLanguage::Bash, attributes: Default::default() };
|
||||
assert_eq!(expected, code.executable_contents());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_executable_contents_rust() {
|
||||
let contents = r##"# fn main() {
|
||||
println!("Hello world");
|
||||
# // The prefix is # .
|
||||
# }
|
||||
"##
|
||||
.to_string();
|
||||
|
||||
let expected = r##"fn main() {
|
||||
println!("Hello world");
|
||||
// The prefix is # .
|
||||
}
|
||||
"##
|
||||
.to_string();
|
||||
|
||||
let code = Code { contents, language: CodeLanguage::Rust, attributes: Default::default() };
|
||||
assert_eq!(expected, code.executable_contents());
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ impl<'a> CodePreparer<'a> {
|
||||
}
|
||||
|
||||
let padding = " ".repeat(horizontal_padding as usize);
|
||||
let padder = NumberPadder::new(code.contents.lines().count());
|
||||
for (index, line) in code.contents.lines().enumerate() {
|
||||
let padder = NumberPadder::new(code.visible_lines().count());
|
||||
for (index, line) in code.visible_lines().enumerate() {
|
||||
let mut line = line.to_string();
|
||||
let mut prefix = padding.clone();
|
||||
if code.attributes.line_numbers {
|
||||
|
Loading…
x
Reference in New Issue
Block a user