diff --git a/src/documents/document/lists.rs b/src/documents/document/lists.rs index 8c418a8..d6641a0 100644 --- a/src/documents/document/lists.rs +++ b/src/documents/document/lists.rs @@ -50,10 +50,14 @@ pub(super) fn enter(source: &str, at: usize) -> Option<(Range, String)> { "" => " ", written => written, }; - let task = matches!(item, mdast::Node::ListItem(item) if item.checked.is_some()); let body = source[start + after..span(item).end].trim_start(); - let body = match task { - true => body.get(3..).unwrap_or_default(), + // A bare box has no `checked`: it is still a task. + let boxed = ["[ ]", "[x]", "[X]"] + .iter() + .any(|task| body.starts_with(task)); + let task = boxed || matches!(item, mdast::Node::ListItem(item) if item.checked.is_some()); + let body = match boxed { + true => &body[3..], false => body, }; let end = source[at..] @@ -101,6 +105,8 @@ pub(super) fn shift(text: &str, range: Range, deeper: bool) -> (String, R let spaces = line.len() - line.trim_start_matches(' ').len(); match deeper { true => changes.push((at, STEP as isize)), + // A leading tab is one step out. + false if line.starts_with('\t') => changes.push((at, -1)), false if spaces > 0 => changes.push((at, -(spaces.min(STEP) as isize))), false => {} } diff --git a/src/documents/document/tables.rs b/src/documents/document/tables.rs index aef523f..d523047 100644 --- a/src/documents/document/tables.rs +++ b/src/documents/document/tables.rs @@ -214,10 +214,16 @@ impl MarkdownDocument { let at = self.editor.read(cx).primary().range().start; let end = text[at..].find('\n').map_or(text.len(), |found| at + found); let start = text[..at].rfind('\n').map_or(0, |found| found + 1); - let before = if text[start..end].trim().is_empty() { - "" - } else { - "\n\n" + // A blank line gives way; a written one keeps. + let blank = text[start..end].trim().is_empty(); + let above = text[..start.saturating_sub(1)] + .rsplit('\n') + .next() + .unwrap_or_default(); + let (from, before) = match (blank, above.trim().is_empty()) { + (true, true) => (start, ""), + (true, false) => (start, "\n"), + (false, _) => (end, "\n\n"), }; let after = if text[end..].starts_with("\n\n") || end == text.len() { "" @@ -225,10 +231,10 @@ impl MarkdownDocument { "\n" }; let written = format!("{before}{NEW}{after}"); - let caret = end + before.len() + 2; + let caret = from + before.len() + 2; log::info!("markdown document: a table goes in"); self.editor.update(cx, |editor, cx| { - editor.edit([(end..end, written)], cx); + editor.edit([(from..end, written)], cx); editor.select(std::iter::once(caret..caret), cx); }); Ok(()) diff --git a/src/documents/document/tests/tools.rs b/src/documents/document/tests/tools.rs index e5a799a..6be0fea 100644 --- a/src/documents/document/tests/tools.rs +++ b/src/documents/document/tests/tools.rs @@ -201,3 +201,43 @@ fn a_newer_match_takes_the_place_of_a_waiting_one(cx: &mut TestAppContext) { cx.simulate_input("z"); assert_eq!(text(&document, cx), "a b z\n"); } + +#[gpui::test] +fn an_empty_task_ends_the_list(cx: &mut TestAppContext) { + let (document, cx) = open("- [x] done", MarkdownMode::Source, cx); + document.update(cx, |document, cx| document.set_caret(0, 10, cx)); + let editor = document.read_with(cx, |document, _| document.editor().clone()); + let focus = editor.read_with(cx, gpui::Focusable::focus_handle); + cx.update(|window, cx| window.focus(&focus, cx)); + cx.simulate_keystrokes("enter"); + cx.run_until_parked(); + cx.simulate_keystrokes("enter"); + assert_eq!(text(&document, cx), "- [x] done\n"); +} + +#[gpui::test] +fn shift_tab_takes_a_tab_indent_out(cx: &mut TestAppContext) { + let (document, cx) = open("- a\n\t- b\n", MarkdownMode::Visual, cx); + press(0, Modifiers::none(), cx); + cx.simulate_keystrokes("shift-tab"); + assert_eq!(opened(&document, cx), "- a\n- b"); +} + +#[gpui::test] +fn a_table_on_an_indented_blank_line_is_a_table(cx: &mut TestAppContext) { + let (document, cx) = open( + " +", + MarkdownMode::Source, + cx, + ); + document.update(cx, |document, cx| { + document.set_caret(0, 4, cx); + document.insert_table(cx).expect("inserted"); + }); + let column = document.update(cx, |document, cx| { + document.edit_table(crate::documents::TableEdit::ColumnRight, cx) + }); + assert_eq!(column, Ok(()), "{}", text(&document, cx)); + assert!(text(&document, cx).starts_with("| ")); +}