gnostr_asyncgit/gitui/mod.rs
1mod bindings;
2pub mod cli;
3mod cmd_log;
4pub mod config;
5mod file_watcher;
6mod git;
7mod git2_opts;
8pub mod gitu_diff;
9pub mod gitui_error;
10mod highlight;
11mod items;
12mod key_parser;
13mod menu;
14mod ops;
15mod prompt;
16mod screen;
17pub mod state;
18mod syntax_parser;
19pub mod term;
20#[cfg(test)]
21mod tests;
22mod ui;
23
24use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventState, KeyModifiers};
25use git2::Repository;
26use gitui_error::Error;
27use items::Item;
28//use ops::Action;
29use std::{
30 path::{Path, PathBuf},
31 process::Command,
32 rc::Rc,
33 time::Duration,
34};
35use term::Term;
36
37pub const LOG_FILE_NAME: &str = "gitu.log";
38
39// An overview of Gitu's ui and terminology:
40//
41// Screen (src/screen/*)
42// │
43// ▼
44// ┌──────────────────────────────────────────────────────────────────┐
45// Item───┬─► On branch master │
46// Item └─► Your branch is up to date with 'origin/master'. │
47// ... │ │
48// │ Untracked files │
49// │ src/tests/rebase.rs │
50// │ │
51// │ Unstaged changes (4) │
52// │▌modified src/keybinds.rs… │
53// │ modified src/ops/mod.rs… │
54// │ modified src/ops/rebase.rs… │
55// │ modified src/tests/mod.rs… │
56// │ │
57// │ Stashes │
58// │ stash@0 On master: scroll │
59// │ stash@1 WIP on fix/run-cmd-error-on-bad-exit: 098d14a feat: prom…│
60// │ │
61// │ Recent commits │
62// Ops (src/ops/*) ├──────────────────────────────────────────────────────────────────┤
63// │ │Help Submenu modified src/keybinds.r│
64// └─────┬───►g Refresh h Help ret Show │
65// └───►tab Toggle section b Branch K Discard │
66// │k p ↑ Move up c Commit s Stage │
67// │j n ↓ Move down f Fetch u Unstage │
68// Submenu ───────►│C-k C-p C-↑ Move up line l Log │
69// │C-j C-n C-↓ Move down line F Pull │
70// │C-u Half page up P Push │
71// │C-d Half page down r Rebase │
72// │y Show refs X Reset │
73// │ z Stash │
74// └──────────────────────────────────────────────────────────────────┘
75
76pub type Res<T> = Result<T, Error>;
77
78pub fn run(args: &cli::Args, term: &mut Term) -> Res<()> {
79 let dir = find_git_dir()?;
80 let repo = open_repo(&dir)?;
81 let config = Rc::new(config::init_config()?);
82
83 let mut state = state::State::create(
84 Rc::new(repo),
85 term.size().map_err(Error::Term)?,
86 args,
87 config.clone(),
88 true,
89 )?;
90
91 if let Some(keys_string) = &args.keys {
92 let ("", keys) = key_parser::parse_keys(keys_string).expect("Couldn't parse keys") else {
93 panic!("Couldn't parse keys");
94 };
95
96 for event in keys_to_events(&keys) {
97 state.handle_event(term, event)?;
98 }
99 }
100
101 state.redraw_now(term)?;
102
103 if args.print {
104 return Ok(());
105 }
106
107 state.run(term, Duration::from_millis(100))?;
108
109 Ok(())
110}
111
112fn open_repo(dir: &Path) -> Res<Repository> {
113 log::debug!("Opening repo");
114 let repo = open_repo_from_env()?;
115 repo.set_workdir(dir, false).map_err(Error::OpenRepo)?;
116 Ok(repo)
117}
118
119fn find_git_dir() -> Res<PathBuf> {
120 log::debug!("Finding git dir");
121 let dir = PathBuf::from(
122 String::from_utf8(
123 Command::new("git")
124 .args(["rev-parse", "--show-toplevel"])
125 .output()
126 .map_err(Error::FindGitDir)?
127 .stdout,
128 )
129 .map_err(Error::GitDirUtf8)?
130 .trim_end(),
131 );
132 Ok(dir)
133}
134
135fn open_repo_from_env() -> Res<Repository> {
136 match Repository::open_from_env() {
137 Ok(repo) => Ok(repo),
138 Err(err) => Err(Error::OpenRepo(err)),
139 }
140}
141
142fn keys_to_events(keys: &[(KeyModifiers, KeyCode)]) -> Vec<Event> {
143 keys.iter()
144 .map(|(mods, key)| {
145 Event::Key(KeyEvent {
146 code: *key,
147 modifiers: *mods,
148 kind: event::KeyEventKind::Press,
149 state: KeyEventState::NONE,
150 })
151 })
152 .collect::<Vec<_>>()
153}