bb7f8ae79d
Explorer: foldable calendar, hand-laid toolbar, no empty Bookmarks block, Move to... picker, folder notes, Trash tab. Menu bar as flat text, title bar drawn whole each frame. New Outline pane, scrolling Related tab, performance overlay (fps, frame time, memory, CPU), first-run UI scale from the desktop on Linux. Editor: Extract to a new note, Merge into..., reading mode, indent settings, table row/column controls, tags in notes searchable, search order and collapse, excluded search folders, user themes in the settings folder. Engine: note.extract, note.merge, trash.list/restore/empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
86 lines
4.9 KiB
Rust
86 lines
4.9 KiB
Rust
//! The note composer: `note.extract` and `note.merge`.
|
|
|
|
mod common;
|
|
|
|
use common::{temp_config, temp_vault};
|
|
use notescraft_engine::{Registry, Session, register_core};
|
|
use serde_json::json;
|
|
|
|
fn open(name: &str, files: &[(&str, &str)]) -> (Registry<Session>, Session, common::TempVault, common::TempVault) {
|
|
let vault = temp_vault(name);
|
|
for (path, text) in files {
|
|
std::fs::write(vault.path().join(path), text).expect("seed");
|
|
}
|
|
let mut registry = Registry::new();
|
|
register_core(&mut registry).expect("register");
|
|
let mut session = Session::open(vault.path()).expect("open");
|
|
let config = temp_config(name);
|
|
session.use_settings_dir(config.path());
|
|
(registry, session, vault, config)
|
|
}
|
|
|
|
#[test]
|
|
fn extract_moves_the_passage_into_a_new_note_and_leaves_a_link() {
|
|
let (registry, mut session, vault, _config) = open("extract", &[("src.md", "intro\nthe passage\noutro\n")]);
|
|
let out = registry.run("note.extract", &mut session, &json!({"path": "src.md", "start": 6, "end": 18, "to": "Passage.md"})).expect("extract");
|
|
assert_eq!(out["link"], "[[Passage]]");
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("Passage.md")).expect("new"), "the passage\n");
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("src.md")).expect("src"), "intro\n[[Passage]]\noutro\n");
|
|
assert!(session.index().resolve("src.md", "Passage").is_resolved(), "the link resolves");
|
|
}
|
|
|
|
#[test]
|
|
fn extract_refuses_bad_ranges_and_taken_names_without_changing_anything() {
|
|
let (registry, mut session, vault, _config) = open("extract-bad", &[("src.md", "héllo world\n"), ("taken.md", "x\n")]);
|
|
for params in [
|
|
json!({"path": "src.md", "start": 2, "end": 5, "to": "n.md"}),
|
|
json!({"path": "src.md", "start": 3, "end": 3, "to": "n.md"}),
|
|
json!({"path": "src.md", "start": 0, "end": 999, "to": "n.md"}),
|
|
json!({"path": "src.md", "start": 0, "end": 5, "to": "taken.md"}),
|
|
json!({"path": "src.md", "start": 0, "end": 5, "to": "../escape.md"}),
|
|
] {
|
|
assert!(registry.run("note.extract", &mut session, ¶ms).is_err(), "{params}");
|
|
}
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("src.md")).expect("src"), "héllo world\n");
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("taken.md")).expect("taken"), "x\n");
|
|
assert!(!vault.path().join("n.md").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn extract_goes_through_an_open_buffer() {
|
|
let (registry, mut session, _vault, _config) = open("extract-buffer", &[("src.md", "keep this\ncut me\n")]);
|
|
session.open_buffer("src.md").expect("buffer");
|
|
registry.run("note.extract", &mut session, &json!({"path": "src.md", "start": 10, "end": 17, "to": "Cut.md"})).expect("extract");
|
|
assert_eq!(session.buffer("src.md").map(|buffer| buffer.text().to_owned()).as_deref(), Some("keep this\n[[Cut]]\n"));
|
|
assert!(session.buffer("src.md").is_some_and(notescraft_engine::Buffer::can_undo), "one undo step");
|
|
}
|
|
|
|
#[test]
|
|
fn merge_appends_the_note_and_trashes_it() {
|
|
let (registry, mut session, vault, _config) = open("merge", &[("a.md", "# A\nbody\n"), ("b.md", "# B\n")]);
|
|
registry.run("note.merge", &mut session, &json!({"from": "b.md", "into": "a.md"})).expect("merge");
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("a.md")).expect("a"), "# A\nbody\n\n# B\n");
|
|
assert!(!vault.path().join("b.md").exists());
|
|
assert!(registry.run("note.merge", &mut session, &json!({"from": "a.md", "into": "a.md"})).is_err(), "not into itself");
|
|
assert!(registry.run("note.merge", &mut session, &json!({"from": "nope.md", "into": "a.md"})).is_err());
|
|
assert_eq!(std::fs::read_to_string(vault.path().join("a.md")).expect("a"), "# A\nbody\n\n# B\n", "a failed merge changes nothing");
|
|
}
|
|
|
|
#[test]
|
|
fn a_deleted_note_comes_back_from_the_trash_and_is_indexed_again() {
|
|
let (registry, mut session, vault, _config) = open("trash-cmd", &[("keep.md", "# Keep\n[[gone]]\n"), ("gone.md", "# Gone\n")]);
|
|
registry.run("note.delete", &mut session, &json!({"path": "gone.md"})).expect("delete");
|
|
assert!(!session.index().resolve("keep.md", "gone").is_resolved());
|
|
let listed = registry.run("trash.list", &mut session, &json!({})).expect("list");
|
|
let item = &listed["items"][0];
|
|
assert_eq!(item["from"], "gone.md");
|
|
let id = item["id"].as_str().expect("id").to_owned();
|
|
assert_eq!(registry.run("trash.restore", &mut session, &json!({"id": id})).expect("restore")["path"], "gone.md");
|
|
assert!(vault.path().join("gone.md").exists());
|
|
assert!(session.index().resolve("keep.md", "gone").is_resolved(), "the link resolves again");
|
|
assert!(registry.run("trash.restore", &mut session, &json!({"id": "1/nothing.md"})).is_err());
|
|
registry.run("note.delete", &mut session, &json!({"path": "gone.md"})).expect("delete again");
|
|
registry.run("trash.empty", &mut session, &json!({})).expect("empty");
|
|
assert_eq!(registry.run("trash.list", &mut session, &json!({})).expect("list")["items"].as_array().map(Vec::len), Some(0));
|
|
}
|