Fix glp backward cycle and gl<Space> current-list scope
CI / cargo fmt --check (push) Successful in 16s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 37s
CI / cargo fmt --check (pull_request) Successful in 34s
CI / cargo clippy (pull_request) Successful in 36s
CI / cargo test (pull_request) Successful in 38s
CI / editor keymaps (push) Successful in 1m23s
CI / editor keymaps (pull_request) Successful in 1m31s

Two documented checkbox-list mappings did not match their spec:

- `glp` dispatched the same forward-only cycleCheckbox as `gln`, so it
  could never "cycle the checkbox state backward". Add `ops::cycle_state_back`
  and thread a `reverse` flag through the dispatcher and both clients.

- `gl<Space>` and `gL<Space>` both swept the whole buffer, making them
  identical. `gl<Space>` now passes the cursor position and the server
  scopes deletion to the contiguous list block under the cursor (current
  list, cascading into sublists); `gL<Space>` stays whole-buffer.

Adds Rust unit tests, plus behavioral and full-registration coverage for
the documented mapping surface in the Neovim and Vim keymap harnesses, and
a command-coverage suite mirroring the doc's command groups.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 13:56:28 -03:00
parent 93725501dc
commit 21ac9deb23
12 changed files with 1016 additions and 25 deletions
+35 -5
View File
@@ -86,10 +86,10 @@ fn remove_done_returns_none_when_no_lists() {
}
#[test]
fn remove_done_scoped_by_position_to_one_item() {
// With `position` set, only items whose line range contains the
// cursor line are considered. So a done item on line 1 is removed
// by cursor on line 1, but a done item on line 3 is left alone.
fn remove_done_scoped_by_position_to_current_list() {
// With `position` set, the scope is the contiguous list block the
// cursor sits in — every done item in that list is removed, not just
// the one under the cursor.
let src = "- [X] done one\n- [ ] todo\n- [X] done two\n";
let ast = parse(src);
let pos = Some(LspPosition {
@@ -98,7 +98,37 @@ fn remove_done_scoped_by_position_to_one_item() {
});
let edit = ops::remove_done_edit(src, &ast, &uri(), pos, true).expect("edit");
let edits = &edit.changes.unwrap()[&uri()];
assert_eq!(edits.len(), 1, "only the line-0 item should match");
assert_eq!(edits.len(), 2, "both done items in the current list match");
}
#[test]
fn remove_done_position_leaves_other_lists_untouched() {
// Two separate list blocks split by a paragraph. Cursor in the first
// list removes only that list's done items; the second list's done
// item survives.
let src = "- [X] done a\n- [ ] todo a\n\nparagraph\n\n- [ ] todo b\n- [X] done b\n";
let ast = parse(src);
let pos = Some(LspPosition {
line: 0,
character: 0,
});
let edit = ops::remove_done_edit(src, &ast, &uri(), pos, true).expect("edit");
let edits = &edit.changes.unwrap()[&uri()];
assert_eq!(edits.len(), 1, "only the first list's done item is removed");
}
#[test]
fn remove_done_position_cascades_into_sublists() {
// The current-list scope includes nested sublists of the block.
let src = "- [ ] parent\n - [X] done child\n - [ ] open child\n";
let ast = parse(src);
let pos = Some(LspPosition {
line: 0,
character: 0,
});
let edit = ops::remove_done_edit(src, &ast, &uri(), pos, true).expect("edit");
let edits = &edit.changes.unwrap()[&uri()];
assert_eq!(edits.len(), 1, "the nested done child is removed");
}
// ===== renumber =====