mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
fix left arrow closing popup (#1121)
* fix left arrow closing popup * fix pageup/down being borked * adhere to global diff options * fix build for rust 1.50 * show revision count in header * allow blaming any specific file revision * show blame command * allow opening history from blame
This commit is contained in:
parent
750b45a6c4
commit
4f3ecfcd7c
14 changed files with 181 additions and 101 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Result,
|
error::Result,
|
||||||
hash,
|
hash,
|
||||||
sync::{self, FileBlame, RepoPath},
|
sync::{self, CommitId, FileBlame, RepoPath},
|
||||||
AsyncGitNotification,
|
AsyncGitNotification,
|
||||||
};
|
};
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
|
|
@ -18,6 +18,8 @@ use std::{
|
||||||
pub struct BlameParams {
|
pub struct BlameParams {
|
||||||
/// path to the file to blame
|
/// path to the file to blame
|
||||||
pub file_path: String,
|
pub file_path: String,
|
||||||
|
/// blame at a specific revision
|
||||||
|
pub commit_id: Option<CommitId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Request<R, A>(R, Option<A>);
|
struct Request<R, A>(R, Option<A>);
|
||||||
|
|
@ -145,8 +147,11 @@ impl AsyncBlame {
|
||||||
arc_current: &Arc<Mutex<Request<u64, FileBlame>>>,
|
arc_current: &Arc<Mutex<Request<u64, FileBlame>>>,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let file_blame =
|
let file_blame = sync::blame::blame_file(
|
||||||
sync::blame::blame_file(repo_path, ¶ms.file_path)?;
|
repo_path,
|
||||||
|
¶ms.file_path,
|
||||||
|
params.commit_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut notify = false;
|
let mut notify = false;
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,13 @@ impl AsyncDiff {
|
||||||
repo_path,
|
repo_path,
|
||||||
id,
|
id,
|
||||||
params.path.clone(),
|
params.path.clone(),
|
||||||
|
Some(params.options),
|
||||||
)?,
|
)?,
|
||||||
DiffType::Commits(ids) => sync::diff::get_diff_commits(
|
DiffType::Commits(ids) => sync::diff::get_diff_commits(
|
||||||
repo_path,
|
repo_path,
|
||||||
ids,
|
ids,
|
||||||
params.path.clone(),
|
params.path.clone(),
|
||||||
|
Some(params.options),
|
||||||
)?,
|
)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ impl AsyncLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn count(&mut self) -> Result<usize> {
|
pub fn count(&self) -> Result<usize> {
|
||||||
Ok(self.current.lock()?.len())
|
Ok(self.current.lock()?.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
sync::{get_commits_info, repository::repo},
|
sync::{get_commits_info, repository::repo},
|
||||||
};
|
};
|
||||||
|
use git2::BlameOptions;
|
||||||
use scopetime::scope_time;
|
use scopetime::scope_time;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
@ -56,12 +57,17 @@ fn fixup_windows_path(path: &str) -> String {
|
||||||
pub fn blame_file(
|
pub fn blame_file(
|
||||||
repo_path: &RepoPath,
|
repo_path: &RepoPath,
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
|
commit_id: Option<CommitId>,
|
||||||
) -> Result<FileBlame> {
|
) -> Result<FileBlame> {
|
||||||
scope_time!("blame_file");
|
scope_time!("blame_file");
|
||||||
|
|
||||||
let repo = repo(repo_path)?;
|
let repo = repo(repo_path)?;
|
||||||
|
|
||||||
let commit_id = utils::get_head_repo(&repo)?;
|
let commit_id = if let Some(commit_id) = commit_id {
|
||||||
|
commit_id
|
||||||
|
} else {
|
||||||
|
utils::get_head_repo(&repo)?
|
||||||
|
};
|
||||||
|
|
||||||
let spec = format!(
|
let spec = format!(
|
||||||
"{}:{}",
|
"{}:{}",
|
||||||
|
|
@ -76,7 +82,11 @@ pub fn blame_file(
|
||||||
return Err(Error::NoBlameOnBinaryFile);
|
return Err(Error::NoBlameOnBinaryFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
let blame = repo.blame_file(Path::new(file_path), None)?;
|
let mut opts = BlameOptions::new();
|
||||||
|
opts.newest_commit(commit_id.into());
|
||||||
|
|
||||||
|
let blame =
|
||||||
|
repo.blame_file(Path::new(file_path), Some(&mut opts))?;
|
||||||
|
|
||||||
let reader = BufReader::new(blob.content());
|
let reader = BufReader::new(blob.content());
|
||||||
|
|
||||||
|
|
@ -160,7 +170,10 @@ mod tests {
|
||||||
let repo_path: &RepoPath =
|
let repo_path: &RepoPath =
|
||||||
&root.as_os_str().to_str().unwrap().into();
|
&root.as_os_str().to_str().unwrap().into();
|
||||||
|
|
||||||
assert!(matches!(blame_file(&repo_path, "foo"), Err(_)));
|
assert!(matches!(
|
||||||
|
blame_file(&repo_path, "foo", None),
|
||||||
|
Err(_)
|
||||||
|
));
|
||||||
|
|
||||||
File::create(&root.join(file_path))?
|
File::create(&root.join(file_path))?
|
||||||
.write_all(b"line 1\n")?;
|
.write_all(b"line 1\n")?;
|
||||||
|
|
@ -168,7 +181,7 @@ mod tests {
|
||||||
stage_add_file(repo_path, file_path)?;
|
stage_add_file(repo_path, file_path)?;
|
||||||
commit(repo_path, "first commit")?;
|
commit(repo_path, "first commit")?;
|
||||||
|
|
||||||
let blame = blame_file(&repo_path, "foo")?;
|
let blame = blame_file(&repo_path, "foo", None)?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
blame.lines.as_slice(),
|
blame.lines.as_slice(),
|
||||||
|
|
@ -192,7 +205,7 @@ mod tests {
|
||||||
stage_add_file(repo_path, file_path)?;
|
stage_add_file(repo_path, file_path)?;
|
||||||
commit(repo_path, "second commit")?;
|
commit(repo_path, "second commit")?;
|
||||||
|
|
||||||
let blame = blame_file(&repo_path, "foo")?;
|
let blame = blame_file(&repo_path, "foo", None)?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
blame.lines.as_slice(),
|
blame.lines.as_slice(),
|
||||||
|
|
@ -219,14 +232,14 @@ mod tests {
|
||||||
|
|
||||||
file.write(b"line 3\n")?;
|
file.write(b"line 3\n")?;
|
||||||
|
|
||||||
let blame = blame_file(&repo_path, "foo")?;
|
let blame = blame_file(&repo_path, "foo", None)?;
|
||||||
|
|
||||||
assert_eq!(blame.lines.len(), 2);
|
assert_eq!(blame.lines.len(), 2);
|
||||||
|
|
||||||
stage_add_file(repo_path, file_path)?;
|
stage_add_file(repo_path, file_path)?;
|
||||||
commit(repo_path, "third commit")?;
|
commit(repo_path, "third commit")?;
|
||||||
|
|
||||||
let blame = blame_file(&repo_path, "foo")?;
|
let blame = blame_file(&repo_path, "foo", None)?;
|
||||||
|
|
||||||
assert_eq!(blame.lines.len(), 3);
|
assert_eq!(blame.lines.len(), 3);
|
||||||
|
|
||||||
|
|
@ -251,6 +264,6 @@ mod tests {
|
||||||
stage_add_file(repo_path, file_path).unwrap();
|
stage_add_file(repo_path, file_path).unwrap();
|
||||||
commit(repo_path, "first commit").unwrap();
|
commit(repo_path, "first commit").unwrap();
|
||||||
|
|
||||||
assert!(blame_file(&repo_path, "bar\\foo").is_ok());
|
assert!(blame_file(&repo_path, "bar\\foo", None).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
//! Functions for getting infos about files in commits
|
//! Functions for getting infos about files in commits
|
||||||
|
|
||||||
use super::{stash::is_stash_commit, CommitId, RepoPath};
|
use super::{
|
||||||
|
diff::DiffOptions, stash::is_stash_commit, CommitId, RepoPath,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Result, sync::repository::repo, StatusItem, StatusItemType,
|
error::Result, sync::repository::repo, StatusItem, StatusItemType,
|
||||||
};
|
};
|
||||||
use git2::{Diff, DiffOptions, Repository};
|
use git2::{Diff, Repository};
|
||||||
use scopetime::scope_time;
|
use scopetime::scope_time;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
|
@ -19,9 +21,9 @@ pub fn get_commit_files(
|
||||||
let repo = repo(repo_path)?;
|
let repo = repo(repo_path)?;
|
||||||
|
|
||||||
let diff = if let Some(other) = other {
|
let diff = if let Some(other) = other {
|
||||||
get_compare_commits_diff(&repo, (id, other), None)?
|
get_compare_commits_diff(&repo, (id, other), None, None)?
|
||||||
} else {
|
} else {
|
||||||
get_commit_diff(repo_path, &repo, id, None)?
|
get_commit_diff(repo_path, &repo, id, None, None)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = diff
|
let res = diff
|
||||||
|
|
@ -49,6 +51,7 @@ pub fn get_compare_commits_diff(
|
||||||
repo: &Repository,
|
repo: &Repository,
|
||||||
ids: (CommitId, CommitId),
|
ids: (CommitId, CommitId),
|
||||||
pathspec: Option<String>,
|
pathspec: Option<String>,
|
||||||
|
options: Option<DiffOptions>,
|
||||||
) -> Result<Diff<'_>> {
|
) -> Result<Diff<'_>> {
|
||||||
// scope_time!("get_compare_commits_diff");
|
// scope_time!("get_compare_commits_diff");
|
||||||
|
|
||||||
|
|
@ -67,7 +70,12 @@ pub fn get_compare_commits_diff(
|
||||||
|
|
||||||
let trees = (commits.0.tree()?, commits.1.tree()?);
|
let trees = (commits.0.tree()?, commits.1.tree()?);
|
||||||
|
|
||||||
let mut opts = DiffOptions::new();
|
let mut opts = git2::DiffOptions::new();
|
||||||
|
if let Some(options) = options {
|
||||||
|
opts.context_lines(options.context);
|
||||||
|
opts.ignore_whitespace(options.ignore_whitespace);
|
||||||
|
opts.interhunk_lines(options.interhunk_lines);
|
||||||
|
}
|
||||||
if let Some(p) = &pathspec {
|
if let Some(p) = &pathspec {
|
||||||
opts.pathspec(p.clone());
|
opts.pathspec(p.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +96,7 @@ pub fn get_commit_diff<'a>(
|
||||||
repo: &'a Repository,
|
repo: &'a Repository,
|
||||||
id: CommitId,
|
id: CommitId,
|
||||||
pathspec: Option<String>,
|
pathspec: Option<String>,
|
||||||
|
options: Option<DiffOptions>,
|
||||||
) -> Result<Diff<'a>> {
|
) -> Result<Diff<'a>> {
|
||||||
// scope_time!("get_commit_diff");
|
// scope_time!("get_commit_diff");
|
||||||
|
|
||||||
|
|
@ -102,7 +111,12 @@ pub fn get_commit_diff<'a>(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut opts = DiffOptions::new();
|
let mut opts = git2::DiffOptions::new();
|
||||||
|
if let Some(options) = options {
|
||||||
|
opts.context_lines(options.context);
|
||||||
|
opts.ignore_whitespace(options.ignore_whitespace);
|
||||||
|
opts.interhunk_lines(options.interhunk_lines);
|
||||||
|
}
|
||||||
if let Some(p) = &pathspec {
|
if let Some(p) = &pathspec {
|
||||||
opts.pathspec(p.clone());
|
opts.pathspec(p.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -121,6 +135,7 @@ pub fn get_commit_diff<'a>(
|
||||||
repo,
|
repo,
|
||||||
CommitId::new(untracked_commit),
|
CommitId::new(untracked_commit),
|
||||||
pathspec,
|
pathspec,
|
||||||
|
options,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
diff.merge(&untracked_diff)?;
|
diff.merge(&untracked_diff)?;
|
||||||
|
|
|
||||||
|
|
@ -214,12 +214,14 @@ pub fn get_diff_commit(
|
||||||
repo_path: &RepoPath,
|
repo_path: &RepoPath,
|
||||||
id: CommitId,
|
id: CommitId,
|
||||||
p: String,
|
p: String,
|
||||||
|
options: Option<DiffOptions>,
|
||||||
) -> Result<FileDiff> {
|
) -> Result<FileDiff> {
|
||||||
scope_time!("get_diff_commit");
|
scope_time!("get_diff_commit");
|
||||||
|
|
||||||
let repo = repo(repo_path)?;
|
let repo = repo(repo_path)?;
|
||||||
let work_dir = work_dir(&repo)?;
|
let work_dir = work_dir(&repo)?;
|
||||||
let diff = get_commit_diff(repo_path, &repo, id, Some(p))?;
|
let diff =
|
||||||
|
get_commit_diff(repo_path, &repo, id, Some(p), options)?;
|
||||||
|
|
||||||
raw_diff_to_file_diff(&diff, work_dir)
|
raw_diff_to_file_diff(&diff, work_dir)
|
||||||
}
|
}
|
||||||
|
|
@ -229,13 +231,18 @@ pub fn get_diff_commits(
|
||||||
repo_path: &RepoPath,
|
repo_path: &RepoPath,
|
||||||
ids: (CommitId, CommitId),
|
ids: (CommitId, CommitId),
|
||||||
p: String,
|
p: String,
|
||||||
|
options: Option<DiffOptions>,
|
||||||
) -> Result<FileDiff> {
|
) -> Result<FileDiff> {
|
||||||
scope_time!("get_diff_commits");
|
scope_time!("get_diff_commits");
|
||||||
|
|
||||||
let repo = repo(repo_path)?;
|
let repo = repo(repo_path)?;
|
||||||
let work_dir = work_dir(&repo)?;
|
let work_dir = work_dir(&repo)?;
|
||||||
let diff =
|
let diff = get_compare_commits_diff(
|
||||||
get_compare_commits_diff(&repo, (ids.0, ids.1), Some(p))?;
|
&repo,
|
||||||
|
(ids.0, ids.1),
|
||||||
|
Some(p),
|
||||||
|
options,
|
||||||
|
)?;
|
||||||
|
|
||||||
raw_diff_to_file_diff(&diff, work_dir)
|
raw_diff_to_file_diff(&diff, work_dir)
|
||||||
}
|
}
|
||||||
|
|
@ -649,7 +656,8 @@ mod tests {
|
||||||
let id = commit(repo_path, "").unwrap();
|
let id = commit(repo_path, "").unwrap();
|
||||||
|
|
||||||
let diff =
|
let diff =
|
||||||
get_diff_commit(repo_path, id, String::new()).unwrap();
|
get_diff_commit(repo_path, id, String::new(), None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
dbg!(&diff);
|
dbg!(&diff);
|
||||||
assert_eq!(diff.sizes, (1, 2));
|
assert_eq!(diff.sizes, (1, 2));
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ pub fn diff_contains_file(
|
||||||
repo,
|
repo,
|
||||||
*commit_id,
|
*commit_id,
|
||||||
Some(file_path.clone()),
|
Some(file_path.clone()),
|
||||||
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let contains_file = diff.deltas().len() > 0;
|
let contains_file = diff.deltas().len() > 0;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ impl App {
|
||||||
sender,
|
sender,
|
||||||
theme.clone(),
|
theme.clone(),
|
||||||
key_config.clone(),
|
key_config.clone(),
|
||||||
|
options.clone(),
|
||||||
),
|
),
|
||||||
revision_files_popup: RevisionFilesPopup::new(
|
revision_files_popup: RevisionFilesPopup::new(
|
||||||
repo.clone(),
|
repo.clone(),
|
||||||
|
|
@ -714,8 +715,8 @@ impl App {
|
||||||
InternalEvent::TagCommit(id) => {
|
InternalEvent::TagCommit(id) => {
|
||||||
self.tag_commit_popup.open(id)?;
|
self.tag_commit_popup.open(id)?;
|
||||||
}
|
}
|
||||||
InternalEvent::BlameFile(path) => {
|
InternalEvent::BlameFile(path, commit_id) => {
|
||||||
self.blame_file_popup.open(&path)?;
|
self.blame_file_popup.open(&path, commit_id)?;
|
||||||
flags
|
flags
|
||||||
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub struct BlameFileComponent {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
async_blame: AsyncBlame,
|
async_blame: AsyncBlame,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
file_path: Option<String>,
|
params: Option<BlameParams>,
|
||||||
file_blame: Option<FileBlame>,
|
file_blame: Option<FileBlame>,
|
||||||
table_state: std::cell::Cell<TableState>,
|
table_state: std::cell::Cell<TableState>,
|
||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
|
|
@ -175,6 +175,16 @@ impl Component for BlameFileComponent {
|
||||||
)
|
)
|
||||||
.order(1),
|
.order(1),
|
||||||
);
|
);
|
||||||
|
out.push(
|
||||||
|
CommandInfo::new(
|
||||||
|
strings::commands::open_file_history(
|
||||||
|
&self.key_config,
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
self.file_blame.is_some(),
|
||||||
|
)
|
||||||
|
.order(1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
visibility_blocking(self)
|
visibility_blocking(self)
|
||||||
|
|
@ -205,19 +215,23 @@ impl Component for BlameFileComponent {
|
||||||
} else if key == self.key_config.keys.page_up {
|
} else if key == self.key_config.keys.page_up {
|
||||||
self.move_selection(ScrollType::PageUp);
|
self.move_selection(ScrollType::PageUp);
|
||||||
} else if key == self.key_config.keys.focus_right {
|
} else if key == self.key_config.keys.focus_right {
|
||||||
self.hide();
|
if let Some(id) = self.selected_commit() {
|
||||||
|
self.hide();
|
||||||
return self.selected_commit().map_or(
|
self.queue.push(
|
||||||
Ok(EventState::NotConsumed),
|
InternalEvent::InspectCommit(id, None),
|
||||||
|id| {
|
);
|
||||||
self.queue.push(
|
}
|
||||||
InternalEvent::InspectCommit(
|
} else if key == self.key_config.keys.file_history {
|
||||||
id, None,
|
if let Some(filepath) = self
|
||||||
),
|
.params
|
||||||
);
|
.as_ref()
|
||||||
Ok(EventState::Consumed)
|
.map(|p| p.file_path.clone())
|
||||||
},
|
{
|
||||||
);
|
self.hide();
|
||||||
|
self.queue.push(
|
||||||
|
InternalEvent::OpenFileRevlog(filepath),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(EventState::Consumed);
|
return Ok(EventState::Consumed);
|
||||||
|
|
@ -261,7 +275,7 @@ impl BlameFileComponent {
|
||||||
),
|
),
|
||||||
queue: queue.clone(),
|
queue: queue.clone(),
|
||||||
visible: false,
|
visible: false,
|
||||||
file_path: None,
|
params: None,
|
||||||
file_blame: None,
|
file_blame: None,
|
||||||
table_state: std::cell::Cell::new(TableState::default()),
|
table_state: std::cell::Cell::new(TableState::default()),
|
||||||
key_config,
|
key_config,
|
||||||
|
|
@ -270,8 +284,15 @@ impl BlameFileComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn open(&mut self, file_path: &str) -> Result<()> {
|
pub fn open(
|
||||||
self.file_path = Some(file_path.into());
|
&mut self,
|
||||||
|
file_path: &str,
|
||||||
|
commit_id: Option<CommitId>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.params = Some(BlameParams {
|
||||||
|
file_path: file_path.into(),
|
||||||
|
commit_id,
|
||||||
|
});
|
||||||
self.file_blame = None;
|
self.file_blame = None;
|
||||||
self.table_state.get_mut().select(Some(0));
|
self.table_state.get_mut().select(Some(0));
|
||||||
self.show()?;
|
self.show()?;
|
||||||
|
|
@ -300,24 +321,20 @@ impl BlameFileComponent {
|
||||||
|
|
||||||
fn update(&mut self) -> Result<()> {
|
fn update(&mut self) -> Result<()> {
|
||||||
if self.is_visible() {
|
if self.is_visible() {
|
||||||
if let Some(file_path) = &self.file_path {
|
if let Some(params) = &self.params {
|
||||||
let blame_params = BlameParams {
|
|
||||||
file_path: file_path.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some((
|
if let Some((
|
||||||
previous_blame_params,
|
previous_blame_params,
|
||||||
last_file_blame,
|
last_file_blame,
|
||||||
)) = self.async_blame.last()?
|
)) = self.async_blame.last()?
|
||||||
{
|
{
|
||||||
if previous_blame_params == blame_params {
|
if previous_blame_params == *params {
|
||||||
self.file_blame = Some(last_file_blame);
|
self.file_blame = Some(last_file_blame);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.async_blame.request(blame_params)?;
|
self.async_blame.request(params.clone())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,27 +345,27 @@ impl BlameFileComponent {
|
||||||
fn get_title(&self) -> String {
|
fn get_title(&self) -> String {
|
||||||
match (
|
match (
|
||||||
self.any_work_pending(),
|
self.any_work_pending(),
|
||||||
self.file_path.as_ref(),
|
self.params.as_ref(),
|
||||||
self.file_blame.as_ref(),
|
self.file_blame.as_ref(),
|
||||||
) {
|
) {
|
||||||
(true, Some(file_path), _) => {
|
(true, Some(params), _) => {
|
||||||
format!(
|
format!(
|
||||||
"{} -- {} -- <calculating.. (who is to blame?)>",
|
"{} -- {} -- <calculating.. (who is to blame?)>",
|
||||||
self.title, file_path
|
self.title, params.file_path
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, Some(file_path), Some(file_blame)) => {
|
(false, Some(params), Some(file_blame)) => {
|
||||||
format!(
|
format!(
|
||||||
"{} -- {} -- {}",
|
"{} -- {} -- {}",
|
||||||
self.title,
|
self.title,
|
||||||
file_path,
|
params.file_path,
|
||||||
file_blame.commit_id.get_short_string()
|
file_blame.commit_id.get_short_string()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, Some(file_path), None) => {
|
(false, Some(params), None) => {
|
||||||
format!(
|
format!(
|
||||||
"{} -- {} -- <no blame available>",
|
"{} -- {} -- <no blame available>",
|
||||||
self.title, file_path
|
self.title, params.file_path
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => format!("{} -- <no blame available>", self.title),
|
_ => format!("{} -- <no blame available>", self.title),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::utils::logitems::ItemBatch;
|
|
||||||
use super::visibility_blocking;
|
use super::visibility_blocking;
|
||||||
|
use super::{utils::logitems::ItemBatch, SharedOptions};
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{
|
components::{
|
||||||
event_pump, CommandBlocking, CommandInfo, Component,
|
event_pump, CommandBlocking, CommandInfo, Component,
|
||||||
|
|
@ -13,8 +13,7 @@ use crate::{
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use asyncgit::{
|
use asyncgit::{
|
||||||
sync::{
|
sync::{
|
||||||
diff::DiffOptions, diff_contains_file, get_commits_info,
|
diff_contains_file, get_commits_info, CommitId, RepoPathRef,
|
||||||
CommitId, RepoPathRef,
|
|
||||||
},
|
},
|
||||||
AsyncDiff, AsyncGitNotification, AsyncLog, DiffParams, DiffType,
|
AsyncDiff, AsyncGitNotification, AsyncLog, DiffParams, DiffType,
|
||||||
FetchStatus,
|
FetchStatus,
|
||||||
|
|
@ -47,6 +46,7 @@ pub struct FileRevlogComponent {
|
||||||
items: ItemBatch,
|
items: ItemBatch,
|
||||||
count_total: usize,
|
count_total: usize,
|
||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
|
options: SharedOptions,
|
||||||
current_width: std::cell::Cell<usize>,
|
current_width: std::cell::Cell<usize>,
|
||||||
current_height: std::cell::Cell<usize>,
|
current_height: std::cell::Cell<usize>,
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +59,7 @@ impl FileRevlogComponent {
|
||||||
sender: &Sender<AsyncGitNotification>,
|
sender: &Sender<AsyncGitNotification>,
|
||||||
theme: SharedTheme,
|
theme: SharedTheme,
|
||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
|
options: SharedOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
theme: theme.clone(),
|
theme: theme.clone(),
|
||||||
|
|
@ -85,6 +86,7 @@ impl FileRevlogComponent {
|
||||||
key_config,
|
key_config,
|
||||||
current_width: std::cell::Cell::new(0),
|
current_width: std::cell::Cell::new(0),
|
||||||
current_height: std::cell::Cell::new(0),
|
current_height: std::cell::Cell::new(0),
|
||||||
|
options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +171,7 @@ impl FileRevlogComponent {
|
||||||
let diff_params = DiffParams {
|
let diff_params = DiffParams {
|
||||||
path: file_path.clone(),
|
path: file_path.clone(),
|
||||||
diff_type: DiffType::Commit(commit_id),
|
diff_type: DiffType::Commit(commit_id),
|
||||||
options: DiffOptions::default(),
|
options: self.options.borrow().diff,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((params, last)) =
|
if let Some((params, last)) =
|
||||||
|
|
@ -243,10 +245,20 @@ impl FileRevlogComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_title(&self) -> String {
|
fn get_title(&self) -> String {
|
||||||
|
let selected = {
|
||||||
|
let table = self.table_state.take();
|
||||||
|
let res = table.selected().unwrap_or_default();
|
||||||
|
self.table_state.set(table);
|
||||||
|
res
|
||||||
|
};
|
||||||
|
let revisions = self.get_max_selection();
|
||||||
|
|
||||||
self.file_path.as_ref().map_or(
|
self.file_path.as_ref().map_or(
|
||||||
"<no history available>".into(),
|
"<no history available>".into(),
|
||||||
|file_path| {
|
|file_path| {
|
||||||
strings::file_log_title(&self.key_config, file_path)
|
strings::file_log_title(
|
||||||
|
file_path, selected, revisions,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -282,8 +294,8 @@ impl FileRevlogComponent {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_selection(&mut self) -> usize {
|
fn get_max_selection(&self) -> usize {
|
||||||
self.git_log.as_mut().map_or(0, |log| {
|
self.git_log.as_ref().map_or(0, |log| {
|
||||||
log.count().unwrap_or(0).saturating_sub(1)
|
log.count().unwrap_or(0).saturating_sub(1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -293,6 +305,7 @@ impl FileRevlogComponent {
|
||||||
|
|
||||||
let old_selection = table_state.selected().unwrap_or(0);
|
let old_selection = table_state.selected().unwrap_or(0);
|
||||||
let max_selection = self.get_max_selection();
|
let max_selection = self.get_max_selection();
|
||||||
|
let height_in_items = self.current_height.get() / 2;
|
||||||
|
|
||||||
let new_selection = match scroll_type {
|
let new_selection = match scroll_type {
|
||||||
ScrollType::Up => old_selection.saturating_sub(1),
|
ScrollType::Up => old_selection.saturating_sub(1),
|
||||||
|
|
@ -301,13 +314,10 @@ impl FileRevlogComponent {
|
||||||
}
|
}
|
||||||
ScrollType::Home => 0,
|
ScrollType::Home => 0,
|
||||||
ScrollType::End => max_selection,
|
ScrollType::End => max_selection,
|
||||||
ScrollType::PageUp => old_selection.saturating_sub(
|
ScrollType::PageUp => old_selection
|
||||||
self.current_height.get().saturating_sub(2),
|
.saturating_sub(height_in_items.saturating_sub(2)),
|
||||||
),
|
|
||||||
ScrollType::PageDown => old_selection
|
ScrollType::PageDown => old_selection
|
||||||
.saturating_add(
|
.saturating_add(height_in_items.saturating_sub(2))
|
||||||
self.current_height.get().saturating_sub(2),
|
|
||||||
)
|
|
||||||
.min(max_selection),
|
.min(max_selection),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -418,53 +428,46 @@ impl Component for FileRevlogComponent {
|
||||||
if let Event::Key(key) = event {
|
if let Event::Key(key) = event {
|
||||||
if key == self.key_config.keys.exit_popup {
|
if key == self.key_config.keys.exit_popup {
|
||||||
self.hide();
|
self.hide();
|
||||||
|
|
||||||
return Ok(EventState::Consumed);
|
|
||||||
} else if key == self.key_config.keys.focus_right
|
} else if key == self.key_config.keys.focus_right
|
||||||
&& self.can_focus_diff()
|
&& self.can_focus_diff()
|
||||||
{
|
{
|
||||||
self.diff.focus(true);
|
self.diff.focus(true);
|
||||||
return Ok(EventState::Consumed);
|
|
||||||
} else if key == self.key_config.keys.focus_left {
|
} else if key == self.key_config.keys.focus_left {
|
||||||
if self.diff.focused() {
|
if self.diff.focused() {
|
||||||
self.diff.focus(false);
|
self.diff.focus(false);
|
||||||
} else {
|
|
||||||
self.hide();
|
|
||||||
}
|
}
|
||||||
return Ok(EventState::Consumed);
|
|
||||||
} else if key == self.key_config.keys.enter {
|
} else if key == self.key_config.keys.enter {
|
||||||
self.hide();
|
if let Some(id) = self.selected_commit() {
|
||||||
|
self.hide();
|
||||||
return self.selected_commit().map_or(
|
self.queue.push(
|
||||||
Ok(EventState::NotConsumed),
|
InternalEvent::InspectCommit(id, None),
|
||||||
|id| {
|
);
|
||||||
self.queue.push(
|
};
|
||||||
InternalEvent::InspectCommit(
|
} else if key == self.key_config.keys.blame {
|
||||||
id, None,
|
if let Some(file) = self.file_path.clone() {
|
||||||
),
|
self.hide();
|
||||||
);
|
self.queue.push(InternalEvent::BlameFile(
|
||||||
Ok(EventState::Consumed)
|
file,
|
||||||
},
|
self.selected_commit(),
|
||||||
);
|
));
|
||||||
|
}
|
||||||
} else if key == self.key_config.keys.move_up {
|
} else if key == self.key_config.keys.move_up {
|
||||||
self.move_selection(ScrollType::Up)
|
self.move_selection(ScrollType::Up);
|
||||||
} else if key == self.key_config.keys.move_down {
|
} else if key == self.key_config.keys.move_down {
|
||||||
self.move_selection(ScrollType::Down)
|
self.move_selection(ScrollType::Down);
|
||||||
} else if key == self.key_config.keys.shift_up
|
} else if key == self.key_config.keys.shift_up
|
||||||
|| key == self.key_config.keys.home
|
|| key == self.key_config.keys.home
|
||||||
{
|
{
|
||||||
self.move_selection(ScrollType::Home)
|
self.move_selection(ScrollType::Home);
|
||||||
} else if key == self.key_config.keys.shift_down
|
} else if key == self.key_config.keys.shift_down
|
||||||
|| key == self.key_config.keys.end
|
|| key == self.key_config.keys.end
|
||||||
{
|
{
|
||||||
self.move_selection(ScrollType::End)
|
self.move_selection(ScrollType::End);
|
||||||
} else if key == self.key_config.keys.page_up {
|
} else if key == self.key_config.keys.page_up {
|
||||||
self.move_selection(ScrollType::PageUp)
|
self.move_selection(ScrollType::PageUp);
|
||||||
} else if key == self.key_config.keys.page_down {
|
} else if key == self.key_config.keys.page_down {
|
||||||
self.move_selection(ScrollType::PageDown)
|
self.move_selection(ScrollType::PageDown);
|
||||||
} else {
|
}
|
||||||
false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(EventState::Consumed);
|
return Ok(EventState::Consumed);
|
||||||
|
|
@ -497,6 +500,14 @@ impl Component for FileRevlogComponent {
|
||||||
)
|
)
|
||||||
.order(1),
|
.order(1),
|
||||||
);
|
);
|
||||||
|
out.push(
|
||||||
|
CommandInfo::new(
|
||||||
|
strings::commands::blame_file(&self.key_config),
|
||||||
|
true,
|
||||||
|
self.selected_commit().is_some(),
|
||||||
|
)
|
||||||
|
.order(1),
|
||||||
|
);
|
||||||
|
|
||||||
out.push(CommandInfo::new(
|
out.push(CommandInfo::new(
|
||||||
strings::commands::diff_focus_right(&self.key_config),
|
strings::commands::diff_focus_right(&self.key_config),
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,8 @@ impl RevisionFilesComponent {
|
||||||
|
|
||||||
fn blame(&self) -> bool {
|
fn blame(&self) -> bool {
|
||||||
self.selected_file_path().map_or(false, |path| {
|
self.selected_file_path().map_or(false, |path| {
|
||||||
self.queue.push(InternalEvent::BlameFile(path));
|
self.queue
|
||||||
|
.push(InternalEvent::BlameFile(path, self.revision));
|
||||||
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -418,8 +418,10 @@ impl Component for StatusTreeComponent {
|
||||||
return if e == self.key_config.keys.blame {
|
return if e == self.key_config.keys.blame {
|
||||||
match (&self.queue, self.selection_file()) {
|
match (&self.queue, self.selection_file()) {
|
||||||
(Some(queue), Some(status_item)) => {
|
(Some(queue), Some(status_item)) => {
|
||||||
|
//TODO: use correct revision here
|
||||||
queue.push(InternalEvent::BlameFile(
|
queue.push(InternalEvent::BlameFile(
|
||||||
status_item.path,
|
status_item.path,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(EventState::Consumed)
|
Ok(EventState::Consumed)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ pub enum InternalEvent {
|
||||||
///
|
///
|
||||||
Tags,
|
Tags,
|
||||||
///
|
///
|
||||||
BlameFile(String),
|
BlameFile(String, Option<CommitId>),
|
||||||
///
|
///
|
||||||
OpenFileRevlog(String),
|
OpenFileRevlog(String),
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -270,10 +270,14 @@ pub fn log_title(_key_config: &SharedKeyConfig) -> String {
|
||||||
"Commit".to_string()
|
"Commit".to_string()
|
||||||
}
|
}
|
||||||
pub fn file_log_title(
|
pub fn file_log_title(
|
||||||
_key_config: &SharedKeyConfig,
|
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
|
selected: usize,
|
||||||
|
revisions: usize,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!("Commits for file {}", file_path)
|
format!(
|
||||||
|
"Revisions of '{}' ({}/{})",
|
||||||
|
file_path, selected, revisions
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn blame_title(_key_config: &SharedKeyConfig) -> String {
|
pub fn blame_title(_key_config: &SharedKeyConfig) -> String {
|
||||||
"Blame".to_string()
|
"Blame".to_string()
|
||||||
|
|
@ -1057,7 +1061,7 @@ pub mod commands {
|
||||||
key_config.get_hint(key_config.keys.blame),
|
key_config.get_hint(key_config.keys.blame),
|
||||||
),
|
),
|
||||||
"open blame view of selected file",
|
"open blame view of selected file",
|
||||||
CMD_GROUP_LOG,
|
CMD_GROUP_GENERAL,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn open_file_history(
|
pub fn open_file_history(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue