Files
Marcos Paulo 6d3ba3096c
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat: tags.rename and tags.merge commands, paste URL over selection (MiniMax M3.1 Flash)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 05:11:38 -03:00

160 lines
4.7 KiB
Rust

//! Pasting a URL over selected text makes a markdown link; every other paste
//! stays a plain paste.
use notescraft_editor::keys::{self, Edit};
fn applied(text: &str, edit: &Edit) -> String {
let mut out = String::from(text);
out.replace_range(edit.range.clone(), &edit.insert);
out
}
fn link(text: &str, range: std::ops::Range<usize>, pasted: &str) -> Option<String> {
keys::on_paste_url(text, range, pasted).map(|edit| applied(text, &edit))
}
const URL: &str = "https://example.com";
#[test]
fn a_plain_word_becomes_a_link() {
let text = "hello world";
assert_eq!(link(text, 6..11, URL), Some(String::from("hello [world](https://example.com)")));
}
#[test]
fn a_phrase_becomes_a_link() {
let text = "the quick brown fox";
assert_eq!(link(text, 4..15, URL), Some(String::from("the [quick brown](https://example.com) fox")));
}
#[test]
fn the_cursor_ends_after_the_closing_paren() {
let text = "hello world";
let edit = keys::on_paste_url(text, 6..11, URL).unwrap();
assert_eq!(edit.sel, 34..34);
assert_eq!(edit.range, 6..11);
}
#[test]
fn brackets_in_the_selection_are_escaped() {
let text = "a [b] c";
assert_eq!(link(text, 0..7, URL), Some(String::from("[a \\[b\\] c](https://example.com)")));
}
#[test]
fn a_wrapped_selection_keeps_its_label() {
let text = "[label](old)";
assert_eq!(link(text, 0..12, URL), Some(String::from("[label](https://example.com)")));
let text = "[label]";
assert_eq!(link(text, 0..7, URL), Some(String::from("[label](https://example.com)")));
}
#[test]
fn an_angle_bracketed_url_is_unwrapped() {
let text = "hello world";
assert_eq!(link(text, 6..11, "<https://example.com>"), Some(String::from("hello [world](https://example.com)")));
}
#[test]
fn other_schemes_are_refused() {
let text = "hello";
assert!(link(text, 0..5, "javascript:alert(1)").is_none());
assert!(link(text, 0..5, "file:///etc/passwd").is_none());
assert!(link(text, 0..5, "mailto:me@example.com").is_none());
}
#[test]
fn a_url_with_spaces_is_refused() {
let text = "hello";
assert!(link(text, 0..5, "https://example.com/a b").is_none());
}
#[test]
fn a_host_without_a_dot_is_refused() {
let text = "hello";
assert!(link(text, 0..5, "http://example").is_none());
assert!(link(text, 0..5, "http://localhost:8080/x").is_some());
}
#[test]
fn the_scheme_is_case_insensitive() {
let text = "hello";
assert!(link(text, 0..5, "HTTPS://EXAMPLE.COM").is_some());
}
#[test]
fn a_url_over_2048_bytes_is_refused() {
let text = "hello";
let fits = format!("https://example.com/{}", "x".repeat(2028));
assert_eq!(fits.len(), 2048);
assert!(link(text, 0..5, &fits).is_some());
let too_long = format!("https://example.com/{}", "x".repeat(2029));
assert!(link(text, 0..5, &too_long).is_none());
}
#[test]
fn an_empty_selection_is_refused() {
let text = "hello world";
assert!(link(text, 6..6, URL).is_none());
}
#[test]
fn a_selection_inside_a_fenced_block_is_refused() {
let text = "```rust\nfn main() {}\n```";
let start = text.find("fn main").unwrap();
assert!(link(text, start..start + 9, URL).is_none());
}
#[test]
fn a_selection_inside_inline_code_is_refused() {
let text = "a `code` b";
let start = text.find("code").unwrap();
assert!(link(text, start..start + 4, URL).is_none());
}
#[test]
fn a_selection_inside_a_link_is_refused() {
let text = "[label](https://example.com)";
let start = text.find("label").unwrap();
assert!(link(text, start..start + 5, URL).is_none());
}
#[test]
fn a_multi_line_selection_is_refused() {
let text = "one\ntwo";
assert!(link(text, 0..7, URL).is_none());
}
#[test]
fn a_selection_over_500_bytes_is_refused() {
let text = "x".repeat(501);
assert!(link(&text, 0..501, URL).is_none());
let text = "x".repeat(500);
assert!(link(&text, 0..500, URL).is_some());
}
#[test]
fn a_selection_that_is_a_url_pastes_normally() {
let text = "https://example.com";
assert!(link(text, 0..19, URL).is_none());
}
#[test]
fn unicode_offsets_are_kept() {
let text = "caf\u{e9} na\u{ef}ve";
let start = text.find("na\u{ef}ve").unwrap();
let (head, tail) = text.split_at_checked(start).unwrap();
assert_eq!(head, "caf\u{e9} ");
assert_eq!(tail, "na\u{ef}ve");
assert_eq!(link(text, start..start + tail.len(), URL), Some(String::from("caf\u{e9} [na\u{ef}ve](https://example.com)")));
}
#[test]
fn a_second_paste_changes_nothing() {
let text = "hello world";
let edit = keys::on_paste_url(text, 6..11, URL).unwrap();
let linked = applied(text, &edit);
assert_eq!(keys::on_paste_url(text, 6..11, URL), Some(edit.clone()));
assert!(keys::on_paste_url(&linked, edit.sel.clone(), URL).is_none());
}