mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Slide: leading tabs in a new slide's body set the outline level (#370)
This commit is contained in:
@@ -108,7 +108,12 @@ fn new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if let Some(b) = str_param(p, "body")
|
||||
&& let Some(sh) = slide.shapes.iter_mut().find(|x| x.ph_type().is_some_and(|k| !k.is_title()))
|
||||
{
|
||||
sh.text = Some(TextBody::from_text(b));
|
||||
let mut body = TextBody::from_text(b);
|
||||
// Leading tabs set the outline level, as in text.set.
|
||||
for para in &mut body.paragraphs {
|
||||
super::text::apply_leading_tabs(para);
|
||||
}
|
||||
sh.text = Some(body);
|
||||
}
|
||||
let at = usize_param(p, "at").unwrap_or(if doc.slides.is_empty() { 0 } else { sel.slide + 1 }).min(doc.slides.len());
|
||||
let id = slide.id;
|
||||
@@ -584,3 +589,24 @@ fn from_outline(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"slides": n}))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use crate::Session;
|
||||
|
||||
#[test]
|
||||
fn new_slide_body_leading_tabs_become_levels() {
|
||||
let mut s = Session::with_new();
|
||||
let text = "Level one\n\tLevel two\n\t\tLevel three\nLevel one again";
|
||||
s.execute("slide.new", &json!({"layout": "titleAndContent", "title": "Indent", "body": text})).expect("new slide");
|
||||
let st = s.doc().expect("doc");
|
||||
let slide = st.doc.slides.get(st.selection.slide).expect("slide");
|
||||
let body = slide.shapes.iter().find(|x| x.ph_type().is_some_and(|k| !k.is_title())).expect("body");
|
||||
let paras = &body.text.as_ref().expect("text").paragraphs;
|
||||
let got: Vec<(u8, String)> = paras.iter().map(|p| (p.level, p.text())).collect();
|
||||
let want = [(0, "Level one"), (1, "Level two"), (2, "Level three"), (0, "Level one again")];
|
||||
assert_eq!(got, want.iter().map(|(l, t)| (*l, t.to_string())).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,6 +687,20 @@ fn select_para(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
ok()
|
||||
}
|
||||
|
||||
/// Turn leading tabs of a paragraph into its outline level (capped at 8) and strip them.
|
||||
/// Returns whether any tab was found.
|
||||
pub(crate) fn apply_leading_tabs(para: &mut deckcraft_model::text::Paragraph) -> bool {
|
||||
let tabs = para.runs.first().map(|r| r.text.chars().take_while(|c| *c == '\t').count()).unwrap_or(0);
|
||||
if tabs == 0 {
|
||||
return false;
|
||||
}
|
||||
if let Some(r) = para.runs.first_mut() {
|
||||
r.text = r.text.trim_start_matches('\t').to_string();
|
||||
}
|
||||
para.level = tabs.min(8) as u8;
|
||||
true
|
||||
}
|
||||
|
||||
fn set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let text = str_param(p, "text").ok_or_else(|| bad("text.set", "missing `text`"))?.to_string();
|
||||
let id = match id_param(p, "id") {
|
||||
@@ -706,13 +720,9 @@ fn set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut nb = TextBody::from_text(&text);
|
||||
for (i, para) in nb.paragraphs.iter_mut().enumerate() {
|
||||
// Leading tabs set the outline level.
|
||||
let tabs = para.runs.first().map(|r| r.text.chars().take_while(|c| *c == '\t').count()).unwrap_or(0);
|
||||
if tabs > 0
|
||||
&& let Some(r) = para.runs.first_mut()
|
||||
if !apply_leading_tabs(para)
|
||||
&& let Some((lv, pp)) = levels.get(i)
|
||||
{
|
||||
r.text = r.text.trim_start_matches('\t').to_string();
|
||||
para.level = tabs.min(8) as u8;
|
||||
} else if let Some((lv, pp)) = levels.get(i) {
|
||||
para.level = *lv;
|
||||
para.props = pp.clone();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user