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