mirror of
https://github.com/gitui-org/gitui
synced 2026-05-22 16:38:28 +00:00
support options for the way we calculate the status (#849)
This commit is contained in:
parent
923323bd71
commit
7cc19f673a
24 changed files with 638 additions and 75 deletions
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
**options**
|
||||
|
||||

|
||||
|
||||
**drop multiple stashes**
|
||||
|
||||

|
||||
|
|
@ -16,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||

|
||||
|
||||
## Added
|
||||
- new options popup (show untracked files, diff settings) ([#849](https://github.com/extrawurst/gitui/issues/849))
|
||||
- mark and drop multiple stashes ([#854](https://github.com/extrawurst/gitui/issues/854))
|
||||
- check branch name validity while typing ([#559](https://github.com/extrawurst/gitui/issues/559))
|
||||
- support deleting remote branch [[@zcorniere](https://github.com/zcorniere)] ([#622](https://github.com/extrawurst/gitui/issues/622))
|
||||
|
|
|
|||
1
assets/options.drawio
Normal file
1
assets/options.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2021-08-17T21:58:53.216Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" etag="DR-vNI6rA-1d9_EpYLQC" version="14.9.7" type="device"><diagram id="cIq82w5ce00BbVejkL92" name="Page-1">5Zldc6IwFIZ/DZfrEJCvS0Xb7exHZ9bd6WxvdlIIkGlMmBir3V+/iQQVg+12Bqm7eqHwkkB43nMCJ1puPF9fc1gWX1iKiOXY6dpyJ5bjABCG8kcpz5Xi+0El5BynutFOmOHfSIu2Vpc4RYtGQ8EYEbhsigmjFCWioUHO2arZLGOkedUS5sgQZgkkpnqHU1FUaugEO/0jwnlRXxn4UXVkDuvG+k4WBUzZak9yp5Ybc8ZEtTVfx4goeDWXqt/VkaPbgXFExd90KLNx+X10/wl4v+6/5fx28jMVH/RZniBZ6hvWgxXPNQHOljRF6iS25Y5XBRZoVsJEHV1Jz6VWiDmRe0Bu6tMhLtD66DjB9u5l2CA2R4I/yyZ1h0B30REDvGDgVcpq54AbaazFHn0n1CLUrufbs+/AyA3N5g2cnPPj5Hh+g5Njm5S2wbdPCYTeiSi5BqVZIUPesX9QwWHyKOkcUpP3L4c1hgTnVCoEZWpXgcEyCUdanuM0VT3GTcgZJiRmhPHNudxs8+mIrn9A17UNutE2MBtBeKoYHBp0JyiDSyKOQd2Lt4Xg7BHVrCij6ACflmofEokJ8ZecaAvwpjsduDC03RrxCz74bVPBqVzwDBfuFInFhoR8DhWQ5vKB1b0lr6VGL4Y4fjBwDhwJTUcA6Dc1fMOUm5wyjv7nzBgOgteN6DU1AsOFmNENccf+jKnKCh/OFQ76sCi3HC4iS1zbNCfs05zQMGd4UdnRZkCv2REZBmDFqljSRymTKj8uJx+clvepPu2oC8w9P+wT8D/jhGhxoNeEAGbxORNQLM8zDTL5KJvpIQ07cKTtTQq8e40BzEJ3gmVNdamO+O/viFlU35YCM3r+aaL2CXxAZMx4ivjBNbtwzIsOZzUQvb9jZoloWPWmxaIDdzoB11zdcD2TWuva0fBk0MwSLt6U0moAkKby/SiXm0xORfZyt5pky2BW7032DZVf9ePDtrzxNRYJoxmWvWJ1aLMQpba+oWTJF/gJWd7kWAp1uxzVZm8nJgaDenl962PLGiDwt9PavpXuyaxsqQMJWxwtxs9u7Q8EzewYAq9tBbrXOcUs32YrGeFFFezW1LGiqTUC1vTKGodWeKWUUWyF3k7pK9xPMD+1OxBEfTpg1m9f4VOFfzD4p+DWL94vh3c3cOXu7i+uzbG9Pwrd6R8=</diagram></mxfile>
|
||||
BIN
assets/options.gif
Normal file
BIN
assets/options.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
hash,
|
||||
sync::{self, CommitId},
|
||||
sync::{self, diff::DiffOptions, CommitId},
|
||||
AsyncGitNotification, FileDiff, CWD,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
|
|
@ -14,7 +14,7 @@ use std::{
|
|||
};
|
||||
|
||||
///
|
||||
#[derive(Hash, Clone, PartialEq)]
|
||||
#[derive(Debug, Hash, Clone, PartialEq)]
|
||||
pub enum DiffType {
|
||||
/// diff in a given commit
|
||||
Commit(CommitId),
|
||||
|
|
@ -25,12 +25,14 @@ pub enum DiffType {
|
|||
}
|
||||
|
||||
///
|
||||
#[derive(Hash, Clone, PartialEq)]
|
||||
#[derive(Debug, Hash, Clone, PartialEq)]
|
||||
pub struct DiffParams {
|
||||
/// path to the file to diff
|
||||
pub path: String,
|
||||
/// what kind of diff
|
||||
pub diff_type: DiffType,
|
||||
/// diff options
|
||||
pub options: DiffOptions,
|
||||
}
|
||||
|
||||
struct Request<R, A>(R, Option<A>);
|
||||
|
|
@ -87,7 +89,7 @@ impl AsyncDiff {
|
|||
&mut self,
|
||||
params: DiffParams,
|
||||
) -> Result<Option<FileDiff>> {
|
||||
log::trace!("request");
|
||||
log::trace!("request {:?}", params);
|
||||
|
||||
let hash = hash(¶ms);
|
||||
|
||||
|
|
@ -148,12 +150,18 @@ impl AsyncDiff {
|
|||
hash: u64,
|
||||
) -> Result<bool> {
|
||||
let res = match params.diff_type {
|
||||
DiffType::Stage => {
|
||||
sync::diff::get_diff(CWD, ¶ms.path, true)?
|
||||
}
|
||||
DiffType::WorkDir => {
|
||||
sync::diff::get_diff(CWD, ¶ms.path, false)?
|
||||
}
|
||||
DiffType::Stage => sync::diff::get_diff(
|
||||
CWD,
|
||||
¶ms.path,
|
||||
true,
|
||||
Some(params.options),
|
||||
)?,
|
||||
DiffType::WorkDir => sync::diff::get_diff(
|
||||
CWD,
|
||||
¶ms.path,
|
||||
false,
|
||||
Some(params.options),
|
||||
)?,
|
||||
DiffType::Commit(id) => sync::diff::get_diff_commit(
|
||||
CWD,
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
hash,
|
||||
sync::{self, status::StatusType},
|
||||
sync::{self, status::StatusType, ShowUntrackedFilesConfig},
|
||||
AsyncGitNotification, StatusItem, CWD,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
|
|
@ -31,14 +31,19 @@ pub struct Status {
|
|||
pub struct StatusParams {
|
||||
tick: u128,
|
||||
status_type: StatusType,
|
||||
config: Option<ShowUntrackedFilesConfig>,
|
||||
}
|
||||
|
||||
impl StatusParams {
|
||||
///
|
||||
pub fn new(status_type: StatusType) -> Self {
|
||||
pub fn new(
|
||||
status_type: StatusType,
|
||||
config: Option<ShowUntrackedFilesConfig>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tick: current_tick(),
|
||||
status_type,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,12 +114,14 @@ impl AsyncStatus {
|
|||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
let status_type = params.status_type;
|
||||
let config = params.config;
|
||||
|
||||
self.pending.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
rayon_core::spawn(move || {
|
||||
let ok = Self::fetch_helper(
|
||||
status_type,
|
||||
config,
|
||||
hash_request,
|
||||
&arc_current,
|
||||
&arc_last,
|
||||
|
|
@ -135,11 +142,12 @@ impl AsyncStatus {
|
|||
|
||||
fn fetch_helper(
|
||||
status_type: StatusType,
|
||||
config: Option<ShowUntrackedFilesConfig>,
|
||||
hash_request: u64,
|
||||
arc_current: &Arc<Mutex<Request<u64, Status>>>,
|
||||
arc_last: &Arc<Mutex<Status>>,
|
||||
) -> Result<()> {
|
||||
let res = Self::get_status(status_type)?;
|
||||
let res = Self::get_status(status_type, config)?;
|
||||
log::trace!(
|
||||
"status fetched: {} (type: {:?})",
|
||||
hash_request,
|
||||
|
|
@ -161,9 +169,16 @@ impl AsyncStatus {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_status(status_type: StatusType) -> Result<Status> {
|
||||
fn get_status(
|
||||
status_type: StatusType,
|
||||
config: Option<ShowUntrackedFilesConfig>,
|
||||
) -> Result<Status> {
|
||||
Ok(Status {
|
||||
items: sync::status::get_status(CWD, status_type)?,
|
||||
items: sync::status::get_status(
|
||||
CWD,
|
||||
status_type,
|
||||
config,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use scopetime::scope_time;
|
|||
|
||||
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-statusshowUntrackedFiles
|
||||
/// represents the `status.showUntrackedFiles` git config state
|
||||
#[derive(Hash, Copy, Clone, PartialEq)]
|
||||
pub enum ShowUntrackedFilesConfig {
|
||||
///
|
||||
No,
|
||||
|
|
@ -14,19 +15,25 @@ pub enum ShowUntrackedFilesConfig {
|
|||
All,
|
||||
}
|
||||
|
||||
impl Default for ShowUntrackedFilesConfig {
|
||||
fn default() -> Self {
|
||||
Self::No
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowUntrackedFilesConfig {
|
||||
///
|
||||
pub const fn include_none(&self) -> bool {
|
||||
pub const fn include_none(self) -> bool {
|
||||
matches!(self, Self::No)
|
||||
}
|
||||
|
||||
///
|
||||
pub const fn include_untracked(&self) -> bool {
|
||||
pub const fn include_untracked(self) -> bool {
|
||||
matches!(self, Self::Normal | Self::All)
|
||||
}
|
||||
|
||||
///
|
||||
pub const fn recurse_untracked_dirs(&self) -> bool {
|
||||
pub const fn recurse_untracked_dirs(self) -> bool {
|
||||
matches!(self, Self::All)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ use super::{
|
|||
use crate::{error::Error, error::Result, hash};
|
||||
use easy_cast::Conv;
|
||||
use git2::{
|
||||
Delta, Diff, DiffDelta, DiffFormat, DiffHunk, DiffOptions, Patch,
|
||||
Repository,
|
||||
Delta, Diff, DiffDelta, DiffFormat, DiffHunk, Patch, Repository,
|
||||
};
|
||||
use scopetime::scope_time;
|
||||
use std::{cell::RefCell, fs, path::Path, rc::Rc};
|
||||
|
|
@ -125,18 +124,41 @@ pub struct FileDiff {
|
|||
pub size_delta: i64,
|
||||
}
|
||||
|
||||
/// see <https://libgit2.org/libgit2/#HEAD/type/git_diff_options>
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq)]
|
||||
pub struct DiffOptions {
|
||||
/// see <https://libgit2.org/libgit2/#HEAD/type/git_diff_options>
|
||||
pub ignore_whitespace: bool,
|
||||
/// see <https://libgit2.org/libgit2/#HEAD/type/git_diff_options>
|
||||
pub context: u32,
|
||||
/// see <https://libgit2.org/libgit2/#HEAD/type/git_diff_options>
|
||||
pub interhunk_lines: u32,
|
||||
}
|
||||
|
||||
impl Default for DiffOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ignore_whitespace: false,
|
||||
context: 3,
|
||||
interhunk_lines: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_diff_raw<'a>(
|
||||
repo: &'a Repository,
|
||||
p: &str,
|
||||
stage: bool,
|
||||
reverse: bool,
|
||||
context: Option<u32>,
|
||||
options: Option<DiffOptions>,
|
||||
) -> Result<Diff<'a>> {
|
||||
// scope_time!("get_diff_raw");
|
||||
|
||||
let mut opt = DiffOptions::new();
|
||||
if let Some(context) = context {
|
||||
opt.context_lines(context);
|
||||
let mut opt = git2::DiffOptions::new();
|
||||
if let Some(options) = options {
|
||||
opt.context_lines(options.context);
|
||||
opt.ignore_whitespace(options.ignore_whitespace);
|
||||
opt.interhunk_lines(options.interhunk_lines);
|
||||
}
|
||||
opt.pathspec(p);
|
||||
opt.reverse(reverse);
|
||||
|
|
@ -173,12 +195,13 @@ pub fn get_diff(
|
|||
repo_path: &str,
|
||||
p: &str,
|
||||
stage: bool,
|
||||
options: Option<DiffOptions>,
|
||||
) -> Result<FileDiff> {
|
||||
scope_time!("get_diff");
|
||||
|
||||
let repo = utils::repo(repo_path)?;
|
||||
let work_dir = work_dir(&repo)?;
|
||||
let diff = get_diff_raw(&repo, p, stage, false, None)?;
|
||||
let diff = get_diff_raw(&repo, p, stage, false, options)?;
|
||||
|
||||
raw_diff_to_file_diff(&diff, work_dir)
|
||||
}
|
||||
|
|
@ -386,7 +409,8 @@ mod tests {
|
|||
|
||||
assert_eq!(get_statuses(repo_path), (1, 0));
|
||||
|
||||
let diff = get_diff(repo_path, "foo/bar.txt", false).unwrap();
|
||||
let diff =
|
||||
get_diff(repo_path, "foo/bar.txt", false, None).unwrap();
|
||||
|
||||
assert_eq!(diff.hunks.len(), 1);
|
||||
assert_eq!(diff.hunks[0].lines[1].content, "test\n");
|
||||
|
|
@ -412,9 +436,13 @@ mod tests {
|
|||
|
||||
assert_eq!(get_statuses(repo_path), (0, 1));
|
||||
|
||||
let diff =
|
||||
get_diff(repo_path, file_path.to_str().unwrap(), true)
|
||||
.unwrap();
|
||||
let diff = get_diff(
|
||||
repo_path,
|
||||
file_path.to_str().unwrap(),
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(diff.hunks.len(), 1);
|
||||
}
|
||||
|
|
@ -462,8 +490,8 @@ mod tests {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
let res =
|
||||
get_status(repo_path, StatusType::WorkingDir).unwrap();
|
||||
let res = get_status(repo_path, StatusType::WorkingDir, None)
|
||||
.unwrap();
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0].path, "bar.txt");
|
||||
|
||||
|
|
@ -480,7 +508,8 @@ mod tests {
|
|||
|
||||
assert_eq!(get_statuses(repo_path), (1, 1));
|
||||
|
||||
let res = get_diff(repo_path, "bar.txt", false).unwrap();
|
||||
let res =
|
||||
get_diff(repo_path, "bar.txt", false, None).unwrap();
|
||||
|
||||
assert_eq!(res.hunks.len(), 2)
|
||||
}
|
||||
|
|
@ -503,6 +532,7 @@ mod tests {
|
|||
sub_path.to_str().unwrap(),
|
||||
file_path.to_str().unwrap(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
@ -525,9 +555,13 @@ mod tests {
|
|||
File::create(&root.join(file_path))?
|
||||
.write_all(b"\x00\x02")?;
|
||||
|
||||
let diff =
|
||||
get_diff(repo_path, file_path.to_str().unwrap(), false)
|
||||
.unwrap();
|
||||
let diff = get_diff(
|
||||
repo_path,
|
||||
file_path.to_str().unwrap(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
dbg!(&diff);
|
||||
assert_eq!(diff.sizes, (1, 2));
|
||||
|
|
@ -546,9 +580,13 @@ mod tests {
|
|||
File::create(&root.join(file_path))?
|
||||
.write_all(b"\x00\xc7")?;
|
||||
|
||||
let diff =
|
||||
get_diff(repo_path, file_path.to_str().unwrap(), false)
|
||||
.unwrap();
|
||||
let diff = get_diff(
|
||||
repo_path,
|
||||
file_path.to_str().unwrap(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
dbg!(&diff);
|
||||
assert_eq!(diff.sizes, (0, 2));
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ mod tests {
|
|||
sub_path.to_str().unwrap(),
|
||||
file_path.to_str().unwrap(),
|
||||
false,
|
||||
None,
|
||||
)?;
|
||||
|
||||
assert!(reset_hunk(
|
||||
|
|
|
|||
|
|
@ -255,10 +255,12 @@ mod tests {
|
|||
/// helper returning amount of files with changes in the (wd,stage)
|
||||
pub fn get_statuses(repo_path: &str) -> (usize, usize) {
|
||||
(
|
||||
get_status(repo_path, StatusType::WorkingDir)
|
||||
get_status(repo_path, StatusType::WorkingDir, None)
|
||||
.unwrap()
|
||||
.len(),
|
||||
get_status(repo_path, StatusType::Stage, None)
|
||||
.unwrap()
|
||||
.len(),
|
||||
get_status(repo_path, StatusType::Stage).unwrap().len(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::diff::{get_diff_raw, HunkHeader};
|
||||
use super::diff::{get_diff_raw, DiffOptions, HunkHeader};
|
||||
use crate::error::{Error, Result};
|
||||
use git2::{Diff, DiffLine, Patch, Repository};
|
||||
|
||||
|
|
@ -15,7 +15,16 @@ pub(crate) fn get_file_diff_patch_and_hunklines<'a>(
|
|||
is_staged: bool,
|
||||
reverse: bool,
|
||||
) -> Result<(Patch<'a>, Vec<HunkLines<'a>>)> {
|
||||
let diff = get_diff_raw(repo, file, is_staged, reverse, Some(1))?;
|
||||
let diff = get_diff_raw(
|
||||
repo,
|
||||
file,
|
||||
is_staged,
|
||||
reverse,
|
||||
Some(DiffOptions {
|
||||
context: 1,
|
||||
..DiffOptions::default()
|
||||
}),
|
||||
)?;
|
||||
let patches = get_patches(&diff)?;
|
||||
if patches.len() > 1 {
|
||||
return Err(Error::Generic(String::from("patch error")));
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ mod tests {
|
|||
let root = repo.path().parent().unwrap();
|
||||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
let res =
|
||||
get_status(repo_path, StatusType::WorkingDir).unwrap();
|
||||
let res = get_status(repo_path, StatusType::WorkingDir, None)
|
||||
.unwrap();
|
||||
assert_eq!(res.len(), 0);
|
||||
|
||||
let file_path = root.join("bar.txt");
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ mod test {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let diff = get_diff(path, "test.txt", true).unwrap();
|
||||
let diff = get_diff(path, "test.txt", true, None).unwrap();
|
||||
|
||||
assert_eq!(diff.lines, 3);
|
||||
assert_eq!(
|
||||
|
|
@ -139,7 +139,7 @@ c = 4";
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let diff = get_diff(path, "test.txt", true).unwrap();
|
||||
let diff = get_diff(path, "test.txt", true, None).unwrap();
|
||||
|
||||
assert_eq!(diff.lines, 5);
|
||||
assert_eq!(
|
||||
|
|
@ -172,7 +172,8 @@ c = 4";
|
|||
|
||||
assert_eq!(get_statuses(path), (0, 1));
|
||||
|
||||
let diff_before = get_diff(path, "test.txt", true).unwrap();
|
||||
let diff_before =
|
||||
get_diff(path, "test.txt", true, None).unwrap();
|
||||
|
||||
assert_eq!(diff_before.lines, 5);
|
||||
|
||||
|
|
@ -189,7 +190,7 @@ c = 4";
|
|||
|
||||
assert_eq!(get_statuses(path), (1, 1));
|
||||
|
||||
let diff = get_diff(path, "test.txt", true).unwrap();
|
||||
let diff = get_diff(path, "test.txt", true, None).unwrap();
|
||||
|
||||
assert_eq!(diff.lines, 4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ use git2::{Delta, Status, StatusOptions, StatusShow};
|
|||
use scopetime::scope_time;
|
||||
use std::path::Path;
|
||||
|
||||
use super::ShowUntrackedFilesConfig;
|
||||
|
||||
///
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Debug)]
|
||||
pub enum StatusItemType {
|
||||
|
|
@ -96,12 +98,17 @@ impl From<StatusType> for StatusShow {
|
|||
pub fn get_status(
|
||||
repo_path: &str,
|
||||
status_type: StatusType,
|
||||
show_untracked: Option<ShowUntrackedFilesConfig>,
|
||||
) -> Result<Vec<StatusItem>> {
|
||||
scope_time!("get_status");
|
||||
|
||||
let repo = utils::repo(repo_path)?;
|
||||
|
||||
let show_untracked = untracked_files_config_repo(&repo)?;
|
||||
let show_untracked = if let Some(config) = show_untracked {
|
||||
config
|
||||
} else {
|
||||
untracked_files_config_repo(&repo)?
|
||||
};
|
||||
|
||||
let mut options = StatusOptions::default();
|
||||
options
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ mod tests {
|
|||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
let status_count = |s: StatusType| -> usize {
|
||||
get_status(repo_path, s).unwrap().len()
|
||||
get_status(repo_path, s, None).unwrap().len()
|
||||
};
|
||||
|
||||
fs::create_dir_all(&root.join("a/d"))?;
|
||||
|
|
@ -329,7 +329,8 @@ mod tests {
|
|||
assert_eq!(get_statuses(repo_path), (0, 1));
|
||||
|
||||
// And that file is test.txt
|
||||
let diff = get_diff(repo_path, "test.txt", true).unwrap();
|
||||
let diff =
|
||||
get_diff(repo_path, "test.txt", true, None).unwrap();
|
||||
assert_eq!(
|
||||
diff.hunks[0].lines[0].content,
|
||||
String::from("@@ -1 +1 @@\n")
|
||||
|
|
@ -371,7 +372,7 @@ mod tests {
|
|||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
let status_count = |s: StatusType| -> usize {
|
||||
get_status(repo_path, s).unwrap().len()
|
||||
get_status(repo_path, s, None).unwrap().len()
|
||||
};
|
||||
|
||||
let full_path = &root.join(file_path);
|
||||
|
|
@ -405,7 +406,7 @@ mod tests {
|
|||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
let status_count = |s: StatusType| -> usize {
|
||||
get_status(repo_path, s).unwrap().len()
|
||||
get_status(repo_path, s, None).unwrap().len()
|
||||
};
|
||||
|
||||
let sub = &root.join("sub");
|
||||
|
|
|
|||
51
src/app.rs
51
src/app.rs
|
|
@ -2,14 +2,15 @@ use crate::{
|
|||
accessors,
|
||||
cmdbar::CommandBar,
|
||||
components::{
|
||||
event_pump, BlameFileComponent, BranchListComponent,
|
||||
CommandBlocking, CommandInfo, CommitComponent, Component,
|
||||
ConfirmComponent, CreateBranchComponent, DrawableComponent,
|
||||
event_pump, AppOption, BlameFileComponent,
|
||||
BranchListComponent, CommandBlocking, CommandInfo,
|
||||
CommitComponent, Component, ConfirmComponent,
|
||||
CreateBranchComponent, DrawableComponent,
|
||||
ExternalEditorComponent, HelpComponent,
|
||||
InspectCommitComponent, MsgComponent, PullComponent,
|
||||
PushComponent, PushTagsComponent, RenameBranchComponent,
|
||||
RevisionFilesPopup, StashMsgComponent, TagCommitComponent,
|
||||
TagListComponent,
|
||||
InspectCommitComponent, MsgComponent, OptionsPopupComponent,
|
||||
PullComponent, PushComponent, PushTagsComponent,
|
||||
RenameBranchComponent, RevisionFilesPopup, SharedOptions,
|
||||
StashMsgComponent, TagCommitComponent, TagListComponent,
|
||||
},
|
||||
input::{Input, InputEvent, InputState},
|
||||
keys::{KeyConfig, SharedKeyConfig},
|
||||
|
|
@ -56,6 +57,7 @@ pub struct App {
|
|||
create_branch_popup: CreateBranchComponent,
|
||||
rename_branch_popup: RenameBranchComponent,
|
||||
select_branch_popup: BranchListComponent,
|
||||
options_popup: OptionsPopupComponent,
|
||||
tags_popup: TagListComponent,
|
||||
cmdbar: RefCell<CommandBar>,
|
||||
tab: usize,
|
||||
|
|
@ -88,6 +90,7 @@ impl App {
|
|||
let queue = Queue::new();
|
||||
let theme = Rc::new(theme);
|
||||
let key_config = Rc::new(key_config);
|
||||
let options = SharedOptions::default();
|
||||
|
||||
Self {
|
||||
input,
|
||||
|
|
@ -173,6 +176,12 @@ impl App {
|
|||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
options_popup: OptionsPopupComponent::new(
|
||||
&queue,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
options.clone(),
|
||||
),
|
||||
do_quit: false,
|
||||
cmdbar: RefCell::new(CommandBar::new(
|
||||
theme.clone(),
|
||||
|
|
@ -195,6 +204,7 @@ impl App {
|
|||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
options,
|
||||
),
|
||||
stashing_tab: Stashing::new(
|
||||
sender,
|
||||
|
|
@ -291,6 +301,9 @@ impl App {
|
|||
} else if k == self.key_config.cmd_bar_toggle {
|
||||
self.cmdbar.borrow_mut().toggle_more();
|
||||
NeedsUpdate::empty()
|
||||
} else if k == self.key_config.open_options {
|
||||
self.options_popup.show()?;
|
||||
NeedsUpdate::ALL
|
||||
} else {
|
||||
NeedsUpdate::empty()
|
||||
};
|
||||
|
|
@ -426,6 +439,7 @@ impl App {
|
|||
select_branch_popup,
|
||||
revision_files_popup,
|
||||
tags_popup,
|
||||
options_popup,
|
||||
help,
|
||||
revlog,
|
||||
status_tab,
|
||||
|
|
@ -453,6 +467,7 @@ impl App {
|
|||
push_popup,
|
||||
push_tags_popup,
|
||||
pull_popup,
|
||||
options_popup,
|
||||
reset,
|
||||
msg
|
||||
]
|
||||
|
|
@ -665,6 +680,20 @@ impl App {
|
|||
flags
|
||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||
}
|
||||
InternalEvent::OptionSwitched(o) => {
|
||||
match o {
|
||||
AppOption::StatusShowUntracked => {
|
||||
self.status_tab.update()?;
|
||||
}
|
||||
AppOption::DiffContextLines
|
||||
| AppOption::DiffIgnoreWhitespaces
|
||||
| AppOption::DiffInterhunkLines => {
|
||||
self.status_tab.update_diff()?;
|
||||
}
|
||||
}
|
||||
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(flags)
|
||||
|
|
@ -784,6 +813,14 @@ impl App {
|
|||
)
|
||||
.order(order::NAV),
|
||||
);
|
||||
res.push(
|
||||
CommandInfo::new(
|
||||
strings::commands::options_popup(&self.key_config),
|
||||
true,
|
||||
!self.any_popup_visible(),
|
||||
)
|
||||
.order(order::NAV),
|
||||
);
|
||||
|
||||
res.push(
|
||||
CommandInfo::new(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
};
|
||||
use anyhow::Result;
|
||||
use asyncgit::{
|
||||
sync::{CommitId, CommitTags},
|
||||
sync::{diff::DiffOptions, CommitId, CommitTags},
|
||||
AsyncDiff, AsyncGitNotification, DiffParams, DiffType,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
|
|
@ -245,6 +245,7 @@ impl InspectCommitComponent {
|
|||
let diff_params = DiffParams {
|
||||
path: f.path.clone(),
|
||||
diff_type: DiffType::Commit(id),
|
||||
options: DiffOptions::default(),
|
||||
};
|
||||
|
||||
if let Some((params, last)) =
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ mod filetree;
|
|||
mod help;
|
||||
mod inspect_commit;
|
||||
mod msg;
|
||||
mod options_popup;
|
||||
mod pull;
|
||||
mod push;
|
||||
mod push_tags;
|
||||
|
|
@ -41,6 +42,9 @@ pub use externaleditor::ExternalEditorComponent;
|
|||
pub use help::HelpComponent;
|
||||
pub use inspect_commit::InspectCommitComponent;
|
||||
pub use msg::MsgComponent;
|
||||
pub use options_popup::{
|
||||
AppOption, OptionsPopupComponent, SharedOptions,
|
||||
};
|
||||
pub use pull::PullComponent;
|
||||
pub use push::PushComponent;
|
||||
pub use push_tags::PushTagsComponent;
|
||||
|
|
|
|||
381
src/components/options_popup.rs
Normal file
381
src/components/options_popup.rs
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use super::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent, EventState,
|
||||
};
|
||||
use crate::{
|
||||
components::utils::string_width_align,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, Queue},
|
||||
strings::{self},
|
||||
ui::{self, style::SharedTheme},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use asyncgit::sync::{diff::DiffOptions, ShowUntrackedFilesConfig};
|
||||
use crossterm::event::Event;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
style::{Modifier, Style},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Clear, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum AppOption {
|
||||
StatusShowUntracked,
|
||||
DiffIgnoreWhitespaces,
|
||||
DiffContextLines,
|
||||
DiffInterhunkLines,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Options {
|
||||
pub status_show_untracked: Option<ShowUntrackedFilesConfig>,
|
||||
pub diff: DiffOptions,
|
||||
}
|
||||
|
||||
pub type SharedOptions = Rc<RefCell<Options>>;
|
||||
|
||||
pub struct OptionsPopupComponent {
|
||||
selection: AppOption,
|
||||
queue: Queue,
|
||||
visible: bool,
|
||||
key_config: SharedKeyConfig,
|
||||
options: SharedOptions,
|
||||
theme: SharedTheme,
|
||||
}
|
||||
|
||||
impl OptionsPopupComponent {
|
||||
///
|
||||
pub fn new(
|
||||
queue: &Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
options: SharedOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
selection: AppOption::StatusShowUntracked,
|
||||
queue: queue.clone(),
|
||||
visible: false,
|
||||
key_config,
|
||||
options,
|
||||
theme,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text(&self, width: u16) -> Vec<Spans> {
|
||||
let mut txt: Vec<Spans> = Vec::with_capacity(10);
|
||||
|
||||
self.add_status(&mut txt, width);
|
||||
|
||||
txt
|
||||
}
|
||||
|
||||
fn add_status(&self, txt: &mut Vec<Spans>, width: u16) {
|
||||
Self::add_header(txt, "Status");
|
||||
|
||||
self.add_entry(
|
||||
txt,
|
||||
width,
|
||||
"Show untracked",
|
||||
match self.options.borrow().status_show_untracked {
|
||||
None => "Gitconfig",
|
||||
Some(ShowUntrackedFilesConfig::No) => "No",
|
||||
Some(ShowUntrackedFilesConfig::Normal) => "Normal",
|
||||
Some(ShowUntrackedFilesConfig::All) => "All",
|
||||
},
|
||||
self.is_select(AppOption::StatusShowUntracked),
|
||||
);
|
||||
Self::add_header(txt, "");
|
||||
|
||||
Self::add_header(txt, "Diff");
|
||||
self.add_entry(
|
||||
txt,
|
||||
width,
|
||||
"Ignore whitespaces",
|
||||
&self.options.borrow().diff.ignore_whitespace.to_string(),
|
||||
self.is_select(AppOption::DiffIgnoreWhitespaces),
|
||||
);
|
||||
self.add_entry(
|
||||
txt,
|
||||
width,
|
||||
"Context lines",
|
||||
&self.options.borrow().diff.context.to_string(),
|
||||
self.is_select(AppOption::DiffContextLines),
|
||||
);
|
||||
self.add_entry(
|
||||
txt,
|
||||
width,
|
||||
"Inter hunk lines",
|
||||
&self.options.borrow().diff.interhunk_lines.to_string(),
|
||||
self.is_select(AppOption::DiffInterhunkLines),
|
||||
);
|
||||
}
|
||||
|
||||
fn is_select(&self, kind: AppOption) -> bool {
|
||||
self.selection == kind
|
||||
}
|
||||
|
||||
fn add_header(txt: &mut Vec<Spans>, header: &'static str) {
|
||||
txt.push(Spans::from(vec![Span::styled(
|
||||
header,
|
||||
//TODO:
|
||||
Style::default().add_modifier(Modifier::UNDERLINED),
|
||||
)]));
|
||||
}
|
||||
|
||||
fn add_entry(
|
||||
&self,
|
||||
txt: &mut Vec<Spans>,
|
||||
width: u16,
|
||||
entry: &'static str,
|
||||
value: &str,
|
||||
selected: bool,
|
||||
) {
|
||||
let half = usize::from(width / 2);
|
||||
txt.push(Spans::from(vec![
|
||||
Span::styled(
|
||||
string_width_align(entry, half),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
Span::styled(
|
||||
format!("{:^w$}", value, w = half),
|
||||
self.theme.text(true, selected),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
fn move_selection(&mut self, up: bool) {
|
||||
if up {
|
||||
self.selection = match self.selection {
|
||||
AppOption::StatusShowUntracked => {
|
||||
AppOption::DiffInterhunkLines
|
||||
}
|
||||
AppOption::DiffIgnoreWhitespaces => {
|
||||
AppOption::StatusShowUntracked
|
||||
}
|
||||
AppOption::DiffContextLines => {
|
||||
AppOption::DiffIgnoreWhitespaces
|
||||
}
|
||||
AppOption::DiffInterhunkLines => {
|
||||
AppOption::DiffContextLines
|
||||
}
|
||||
};
|
||||
} else {
|
||||
self.selection = match self.selection {
|
||||
AppOption::StatusShowUntracked => {
|
||||
AppOption::DiffIgnoreWhitespaces
|
||||
}
|
||||
AppOption::DiffIgnoreWhitespaces => {
|
||||
AppOption::DiffContextLines
|
||||
}
|
||||
AppOption::DiffContextLines => {
|
||||
AppOption::DiffInterhunkLines
|
||||
}
|
||||
AppOption::DiffInterhunkLines => {
|
||||
AppOption::StatusShowUntracked
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_option(&mut self, right: bool) {
|
||||
if right {
|
||||
match self.selection {
|
||||
AppOption::StatusShowUntracked => {
|
||||
let untracked =
|
||||
self.options.borrow().status_show_untracked;
|
||||
|
||||
let untracked = match untracked {
|
||||
None => {
|
||||
Some(ShowUntrackedFilesConfig::Normal)
|
||||
}
|
||||
Some(ShowUntrackedFilesConfig::Normal) => {
|
||||
Some(ShowUntrackedFilesConfig::All)
|
||||
}
|
||||
Some(ShowUntrackedFilesConfig::All) => {
|
||||
Some(ShowUntrackedFilesConfig::No)
|
||||
}
|
||||
Some(ShowUntrackedFilesConfig::No) => None,
|
||||
};
|
||||
|
||||
self.options.borrow_mut().status_show_untracked =
|
||||
untracked;
|
||||
}
|
||||
AppOption::DiffIgnoreWhitespaces => {
|
||||
let old =
|
||||
self.options.borrow().diff.ignore_whitespace;
|
||||
self.options
|
||||
.borrow_mut()
|
||||
.diff
|
||||
.ignore_whitespace = !old;
|
||||
}
|
||||
AppOption::DiffContextLines => {
|
||||
let old = self.options.borrow().diff.context;
|
||||
self.options.borrow_mut().diff.context =
|
||||
old.saturating_add(1);
|
||||
}
|
||||
AppOption::DiffInterhunkLines => {
|
||||
let old =
|
||||
self.options.borrow().diff.interhunk_lines;
|
||||
self.options.borrow_mut().diff.interhunk_lines =
|
||||
old.saturating_add(1);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
match self.selection {
|
||||
AppOption::StatusShowUntracked => {
|
||||
let untracked =
|
||||
self.options.borrow().status_show_untracked;
|
||||
|
||||
let untracked = match untracked {
|
||||
None => Some(ShowUntrackedFilesConfig::No),
|
||||
Some(ShowUntrackedFilesConfig::No) => {
|
||||
Some(ShowUntrackedFilesConfig::All)
|
||||
}
|
||||
Some(ShowUntrackedFilesConfig::All) => {
|
||||
Some(ShowUntrackedFilesConfig::Normal)
|
||||
}
|
||||
Some(ShowUntrackedFilesConfig::Normal) => {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
self.options.borrow_mut().status_show_untracked =
|
||||
untracked;
|
||||
}
|
||||
AppOption::DiffIgnoreWhitespaces => {
|
||||
let old =
|
||||
self.options.borrow().diff.ignore_whitespace;
|
||||
self.options
|
||||
.borrow_mut()
|
||||
.diff
|
||||
.ignore_whitespace = !old;
|
||||
}
|
||||
AppOption::DiffContextLines => {
|
||||
let old = self.options.borrow().diff.context;
|
||||
self.options.borrow_mut().diff.context =
|
||||
old.saturating_sub(1);
|
||||
}
|
||||
AppOption::DiffInterhunkLines => {
|
||||
let old =
|
||||
self.options.borrow().diff.interhunk_lines;
|
||||
self.options.borrow_mut().diff.interhunk_lines =
|
||||
old.saturating_sub(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.queue
|
||||
.push(InternalEvent::OptionSwitched(self.selection));
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawableComponent for OptionsPopupComponent {
|
||||
fn draw<B: Backend>(
|
||||
&self,
|
||||
f: &mut Frame<B>,
|
||||
area: Rect,
|
||||
) -> Result<()> {
|
||||
if self.is_visible() {
|
||||
const SIZE: (u16, u16) = (50, 10);
|
||||
let area =
|
||||
ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
|
||||
|
||||
let width = area.width;
|
||||
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_text(width))
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(Span::styled(
|
||||
"Options",
|
||||
self.theme.title(true),
|
||||
))
|
||||
.border_style(self.theme.block(true)),
|
||||
)
|
||||
.alignment(Alignment::Left),
|
||||
area,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for OptionsPopupComponent {
|
||||
fn commands(
|
||||
&self,
|
||||
out: &mut Vec<CommandInfo>,
|
||||
force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
if self.is_visible() || force_all {
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.order(1),
|
||||
);
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
strings::commands::navigate_tree(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.order(1),
|
||||
);
|
||||
}
|
||||
|
||||
visibility_blocking(self)
|
||||
}
|
||||
|
||||
fn event(
|
||||
&mut self,
|
||||
event: crossterm::event::Event,
|
||||
) -> Result<EventState> {
|
||||
if self.is_visible() {
|
||||
if let Event::Key(key) = &event {
|
||||
if *key == self.key_config.exit_popup {
|
||||
self.hide();
|
||||
} else if *key == self.key_config.move_up {
|
||||
self.move_selection(true);
|
||||
} else if *key == self.key_config.move_down {
|
||||
self.move_selection(false);
|
||||
} else if *key == self.key_config.move_right {
|
||||
self.switch_option(true);
|
||||
} else if *key == self.key_config.move_left {
|
||||
self.switch_option(false);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
|
||||
Ok(EventState::NotConsumed)
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> bool {
|
||||
self.visible
|
||||
}
|
||||
|
||||
fn hide(&mut self) {
|
||||
self.visible = false;
|
||||
}
|
||||
|
||||
fn show(&mut self) -> Result<()> {
|
||||
self.visible = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ pub struct KeyConfig {
|
|||
pub open_commit: KeyEvent,
|
||||
pub open_commit_editor: KeyEvent,
|
||||
pub open_help: KeyEvent,
|
||||
pub open_options: KeyEvent,
|
||||
pub move_left: KeyEvent,
|
||||
pub move_right: KeyEvent,
|
||||
pub tree_collapse_recursive: KeyEvent,
|
||||
|
|
@ -108,6 +109,7 @@ impl Default for KeyConfig {
|
|||
open_commit: KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::empty()},
|
||||
open_commit_editor: KeyEvent { code: KeyCode::Char('e'), modifiers:KeyModifiers::CONTROL},
|
||||
open_help: KeyEvent { code: KeyCode::Char('h'), modifiers: KeyModifiers::empty()},
|
||||
open_options: KeyEvent { code: KeyCode::Char('o'), modifiers: KeyModifiers::empty()},
|
||||
move_left: KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::empty()},
|
||||
move_right: KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::empty()},
|
||||
tree_collapse_recursive: KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::SHIFT},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::tabs::StashingOptions;
|
||||
use crate::{components::AppOption, tabs::StashingOptions};
|
||||
use asyncgit::sync::{diff::DiffLinePosition, CommitId, CommitTags};
|
||||
use bitflags::bitflags;
|
||||
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||
|
|
@ -83,6 +83,8 @@ pub enum InternalEvent {
|
|||
PushTags,
|
||||
///
|
||||
OpenFileTree(CommitId),
|
||||
///
|
||||
OptionSwitched(AppOption),
|
||||
}
|
||||
|
||||
/// single threaded simple queue for components to communicate with each other
|
||||
|
|
|
|||
|
|
@ -373,6 +373,18 @@ pub mod commands {
|
|||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn options_popup(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Options [{}]",
|
||||
key_config.get_hint(key_config.open_options),
|
||||
),
|
||||
"open options popup",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn help_open(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ impl Stashing {
|
|||
pub fn update(&mut self) -> Result<()> {
|
||||
if self.is_visible() {
|
||||
self.git_status
|
||||
.fetch(&StatusParams::new(StatusType::Both))?;
|
||||
//TODO: support options
|
||||
.fetch(&StatusParams::new(StatusType::Both, None))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
command_pump, event_pump, visibility_blocking,
|
||||
ChangesComponent, CommandBlocking, CommandInfo, Component,
|
||||
DiffComponent, DrawableComponent, EventState,
|
||||
FileTreeItemKind,
|
||||
FileTreeItemKind, SharedOptions,
|
||||
},
|
||||
keys::SharedKeyConfig,
|
||||
queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem},
|
||||
|
|
@ -70,6 +70,7 @@ pub struct Status {
|
|||
git_branch_name: cached::BranchName,
|
||||
queue: Queue,
|
||||
git_action_executed: bool,
|
||||
options: SharedOptions,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +135,7 @@ impl Status {
|
|||
sender: &Sender<AsyncGitNotification>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
options: SharedOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
queue: queue.clone(),
|
||||
|
|
@ -169,6 +171,7 @@ impl Status {
|
|||
git_branch_state: None,
|
||||
git_branch_name: cached::BranchName::new(CWD),
|
||||
key_config,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -317,11 +320,17 @@ impl Status {
|
|||
self.git_branch_name.lookup().map(Some).unwrap_or(None);
|
||||
|
||||
if self.is_visible() {
|
||||
let config = self.options.borrow().status_show_untracked;
|
||||
|
||||
self.git_diff.refresh()?;
|
||||
self.git_status_workdir
|
||||
.fetch(&StatusParams::new(StatusType::WorkingDir))?;
|
||||
self.git_status_stage
|
||||
.fetch(&StatusParams::new(StatusType::Stage))?;
|
||||
self.git_status_workdir.fetch(&StatusParams::new(
|
||||
StatusType::WorkingDir,
|
||||
config,
|
||||
))?;
|
||||
self.git_status_stage.fetch(&StatusParams::new(
|
||||
StatusType::Stage,
|
||||
config,
|
||||
))?;
|
||||
|
||||
self.branch_compare();
|
||||
}
|
||||
|
|
@ -394,6 +403,7 @@ impl Status {
|
|||
let diff_params = DiffParams {
|
||||
path: path.clone(),
|
||||
diff_type,
|
||||
options: self.options.borrow().diff,
|
||||
};
|
||||
|
||||
if self.diff.current() == (path.clone(), is_stage) {
|
||||
|
|
@ -401,18 +411,20 @@ impl Status {
|
|||
// maybe the diff changed (outside file change)
|
||||
if let Some((params, last)) = self.git_diff.last()? {
|
||||
if params == diff_params {
|
||||
// all params match, so we might need to update
|
||||
self.diff.update(path, is_stage, last);
|
||||
} else {
|
||||
// params changed, we need to request the right diff
|
||||
self.request_diff(
|
||||
diff_params,
|
||||
path,
|
||||
is_stage,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we dont show the right diff right now, so we need to request
|
||||
if let Some(diff) =
|
||||
self.git_diff.request(diff_params)?
|
||||
{
|
||||
self.diff.update(path, is_stage, diff);
|
||||
} else {
|
||||
self.diff.clear(true);
|
||||
}
|
||||
self.request_diff(diff_params, path, is_stage)?;
|
||||
}
|
||||
} else {
|
||||
self.diff.clear(false);
|
||||
|
|
@ -421,6 +433,21 @@ impl Status {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn request_diff(
|
||||
&mut self,
|
||||
diff_params: DiffParams,
|
||||
path: String,
|
||||
is_stage: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
if let Some(diff) = self.git_diff.request(diff_params)? {
|
||||
self.diff.update(path, is_stage, diff);
|
||||
} else {
|
||||
self.diff.clear(true);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// called after confirmation
|
||||
pub fn reset(&mut self, item: &ResetItem) -> bool {
|
||||
if let Err(e) = sync::reset_workdir(CWD, item.path.as_str()) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
focus_below: ( code: Char('j'), modifiers: ( bits: 0,),),
|
||||
|
||||
open_help: ( code: F(1), modifiers: ( bits: 0,),),
|
||||
open_options: ( code: Char('o'), modifiers: ( bits: 0,),),
|
||||
|
||||
exit: ( code: Char('c'), modifiers: ( bits: 2,),),
|
||||
quit: ( code: Char('q'), modifiers: ( bits: 0,),),
|
||||
|
|
|
|||
Loading…
Reference in a new issue