use crate::code_nav::{Answer, Ask, Place, Want};
use crate::diff::anchor::Side;
use crate::route::code;
use crate::ui::diff_doc::Pos;
use super::*;
const LIB_RS: &str = "use std::io;\n\nfn main() {\n helper(io::stdin());\n other();\n}\n\nfn helper(input: io::Stdin) {}\n\nfn ioctl() {}\n";
fn with_code() -> State {
let mut s = with_repo();
let route = Route::blob(ghtui(), "main".into(), "src/lib.rs".into());
let blob = crate::fixtures::file("src/lib.rs", LIB_RS);
open(&mut s, route, Data::Blob(Box::new(blob)));
s
}
fn at(s: &State) -> (Option<u32>, Option<String>) {
let Screen::Page(p) = s.screen() else {
panic!("not on a page")
};
let word = (p.word_cursor()).map(|(_, code, w)| code.get(w.bytes).unwrap().to_owned());
(p.selected.and_then(|i| p.line_of(i)), word)
}
fn said(s: &State) -> String {
format!("{:?}", s.notices.shown())
}
#[test]
fn symbols_go_to_their_line() {
let mut s = with_code();
act(&mut s, Action::Symbols);
let Some(Overlay::Picker(p)) = &s.overlay else {
panic!("no picker")
};
let labels: Vec<String> = (s.picker_rows(p).into_iter())
.map(|(item, _)| item.label)
.collect();
assert_eq!(labels, ["fn main", "fn helper", "fn ioctl"]);
press(&mut s, "help<Enter>");
assert_eq!(at(&s).0, Some(8));
}
#[test]
fn gd_finds_a_definition_in_the_file() {
let mut s = with_code();
press(&mut s, ":4<Enter>");
assert_eq!(at(&s), (Some(4), Some("helper".into())));
press(&mut s, "g");
assert_eq!(at(&s), (Some(8), Some("helper".into())), "{}", said(&s));
let mut s = with_code();
press(&mut s, "<Home><C-Right><C-Right><C-Right>");
assert_eq!(at(&s).1.as_deref(), Some("::"));
press(&mut s, "g");
assert!(
said(&s).contains("No name under the cursor"),
"{}",
said(&s)
);
}
#[test]
fn gd_asks_the_repository_for_one_outside_the_file() {
let mut s = with_code();
press(&mut s, ":5<Enter>");
let ask = Ask {
repo: ghtui(),
rev: "main".into(),
path: "src/lib.rs".into(),
want: Want::Definition("other".into()),
};
assert_eq!(press(&mut s, "g"), [Cmd::Git(Git::Code(ask.clone()))]);
let answer = Answer {
places: vec![Place {
path: "src/other.rs".into(),
line: 12,
label: "fn other".into(),
}],
note: Some("Still indexing".into()),
none: String::new(),
};
update(&mut s, Msg::Code(ask, Ok(answer)));
assert_eq!(
s.route(),
Some(&Route::Code(code::Blob {
repo: ghtui(),
rev: "main".into(),
path: "src/other.rs".into(),
lines: Some((12, 12)),
}))
);
assert!(said(&s).contains("Still indexing"), "{}", said(&s));
}
#[test]
fn repository_symbols_and_references_are_listed() {
let mut s = with_code();
press(&mut s, ":4<Enter>");
let ask = |want| Ask {
repo: ghtui(),
rev: "main".into(),
path: "src/lib.rs".into(),
want,
};
let place = |path: &str, line, label: &str| Place {
path: path.into(),
line,
label: label.into(),
};
let symbols = ask(Want::Symbols);
assert_eq!(
act(&mut s, Action::RepoSymbols),
[Cmd::Git(Git::Code(symbols.clone()))]
);
let answer = Answer {
places: vec![
place("src/other.rs", 12, "fn other"),
place("src/lib.rs", 10, "fn ioctl"),
],
..Answer::default()
};
update(&mut s, Msg::Code(symbols, Ok(answer)));
press(&mut s, "ioctl<Enter>");
assert_eq!(at(&s).0, Some(10));
press(&mut s, ":4<Enter>");
let refs = ask(Want::References("helper".into()));
assert_eq!(
act(&mut s, Action::References),
[Cmd::Git(Git::Code(refs.clone()))]
);
let answer = Answer {
places: vec![
place("src/lib.rs", 4, "helper(io::stdin());"),
place("src/main.rs", 2, "lib::helper(x)"),
],
..Answer::default()
};
update(&mut s, Msg::Code(refs.clone(), Ok(answer)));
let Some(Overlay::Picker(p)) = &s.overlay else {
panic!("no picker")
};
let rows: Vec<(String, String)> = (s.picker_rows(p).into_iter())
.map(|(item, _)| (item.label, item.hint))
.collect();
assert_eq!(rows[1], ("lib::helper(x)".into(), "src/main.rs:2".into()));
press(&mut s, "lib::<Enter>");
assert_eq!(
s.route(),
Some(&Route::Code(code::Blob {
repo: ghtui(),
rev: "main".into(),
path: "src/main.rs".into(),
lines: Some((2, 2)),
}))
);
let mut s = with_code();
let none = Answer {
note: Some("Still indexing".into()),
none: "“helper” isn't used".into(),
..Answer::default()
};
update(&mut s, Msg::Code(refs, Ok(none)));
assert!(
said(&s).contains("isn't used · Still indexing"),
"{}",
said(&s)
);
}
#[test]
fn star_matches_whole_words() {
let mut s = with_code();
press(&mut s, "<Home><C-Right><C-Right><C-Right><C-Right>");
act(&mut s, Action::SearchWord);
assert_eq!(at(&s), (Some(4), Some("io".into())));
act(&mut s, Action::SearchWord);
act(&mut s, Action::SearchWord);
assert_eq!(at(&s), (Some(1), Some("io".into())), "around, past ioctl");
assert!(said(&s).contains("Match 1 of 3"), "{}", said(&s));
}
#[test]
fn symbols_and_definitions_in_the_diff() {
let at = Pos { file: 0, row: 0 };
let mut s = screen_state(at, Pane::Diff);
let line = |s: &State| {
let Screen::Diff(screen) = s.screen() else {
panic!("not the diff")
};
let doc = &s.diff().unwrap().doc;
doc.line_at(screen.cursor, Side::Right).map(|l| l.line)
};
act(&mut s, Action::Symbols);
press(&mut s, "origin<Enter>");
assert_eq!(line(&s), Some(14));
press(&mut s, "<Down><C-Right><C-Right><C-Right>g");
assert_eq!(line(&s), Some(10), "{}", said(&s));
}
#[test]
fn a_search_match_is_the_word_gd_reads() {
let mut s = with_code();
press(&mut s, "/other<Enter>");
assert_eq!(at(&s), (Some(5), Some("other".into())), "{}", said(&s));
press(&mut s, "<Home>/helper<Enter>");
assert_eq!(at(&s), (Some(4), Some("helper".into())), "{}", said(&s));
let mut long = "fn pad() {}\n".repeat(1500);
long.push_str("fn last() {\n valid_login(login).then(|| x);\n}\n");
let mut s = with_repo();
let route = Route::blob(ghtui(), "main".into(), "src/lib.rs".into());
let blob = crate::fixtures::file("src/lib.rs", &long);
open(&mut s, route, Data::Blob(Box::new(blob)));
press(&mut s, "gOlast<Enter>ww/valid_login<Enter>");
assert_eq!(
at(&s),
(Some(1502), Some("valid_login".into())),
"{}",
said(&s)
);
}