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}