#[doc(inline)]
pub use inflorescence_model::selection::{
unify, Channel, CompareRemote, Dir, HeldKey, LogChange,
LogChangeFileSelection, Msg, Primary, Select, State, Status, UnfilteredMsg,
Unified,
};
use crate::{diff, file, log};
use inflorescence_iced_widget::nav_scrollable;
use inflorescence_model::model;
use inflorescence_model::model::{Log, Logs, Navigation};
use libflorescence::repo;
use iced::time::{Duration, Instant};
use iced_utils::Task;
use std::cmp;
pub struct Ctx<'a> {
pub state: &'a mut State,
pub files: &'a mut file::State,
pub navigation: &'a mut Navigation,
pub repo: &'a repo::State,
pub logs: &'a Logs,
pub record_dichotomy: Option<&'a repo::RecordDichotomy>,
}
pub fn update(
msg: Msg,
state: &mut State,
files: &mut file::State,
navigation: &mut Navigation,
repo: &repo::State,
logs: &Logs,
record_dichotomy: Option<&repo::RecordDichotomy>,
) -> Task<crate::ManagingRepoMsg> {
let mut ctx = Ctx {
state,
files,
navigation,
repo,
logs,
record_dichotomy,
};
let mctx = &mut ctx;
match msg {
Msg::PressDir(dir) => {
let delta = {
mctx.state.held_key.as_ref().and_then(
|HeldKey {
dir: held_dir,
last_tick,
}| {
(dir == *held_dir).then(|| {
cmp::min(
Duration::from_millis(50),
Instant::now() - *last_tick,
)
})
},
)
};
mctx.state.held_key = Some(HeldKey {
dir,
last_tick: Instant::now(),
});
match dir {
Dir::Down => select_down(mctx, delta),
Dir::Up => select_up(mctx, delta),
Dir::Left => select_left(mctx),
Dir::Right => select_right(mctx),
}
}
Msg::AltPressDir(dir) => {
let delta = {
mctx.state.held_key.as_ref().and_then(
|HeldKey {
dir: held_dir,
last_tick,
}| {
(dir == *held_dir).then(|| {
cmp::min(
Duration::from_millis(50),
Instant::now() - *last_tick,
)
})
},
)
};
mctx.state.held_key = Some(HeldKey {
dir,
last_tick: Instant::now(),
});
match dir {
Dir::Down => alt_select_down(mctx, delta),
Dir::Up => alt_select_up(mctx, delta),
Dir::Left | Dir::Right => {
Task::none()
}
}
}
}
}
pub fn update_unfiltered(
msg: UnfilteredMsg,
state: &mut State,
files: &mut file::State,
navigation: &mut Navigation,
repo: &repo::State,
logs: &Logs,
record_dichotomy: Option<&repo::RecordDichotomy>,
) -> Task<crate::ManagingRepoMsg> {
let mut ctx = Ctx {
state,
files,
navigation,
repo,
logs,
record_dichotomy,
};
let mctx = &mut ctx;
match msg {
UnfilteredMsg::ReleaseDir(dir) => release(dir, mctx),
UnfilteredMsg::Select(select) => select_exact(select, mctx),
}
}
fn select_down(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
match ctx.state.primary {
Primary::Status => select_down_status(ctx, delta),
Primary::Channel => select_down_channel(ctx, delta),
Primary::EntireLog => select_down_entire_log(ctx, delta),
Primary::CompareRemote => select_down_compare_remote(ctx, delta),
}
}
fn select_down_status(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
let (selection, task) = match ctx.state.status.take() {
Some(Status::UntrackedFile {
ix,
path,
diff_selected,
}) => {
let (selection, task) = if diff_selected {
nav_scrollable::alt_scroll_down(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
(
Status::UntrackedFile {
ix,
path,
diff_selected,
},
Task::none(),
)
} else if ctx.repo.untracked_files.len().saturating_sub(1) == ix {
if !ctx.repo.changed_files.is_empty() {
let ix = 0;
changed_file_selection(ix, VDir::Down, ctx)
} else if !ctx.repo.short_log.is_empty() {
let ix = 0;
status_log_selection(ix, VDir::Down, ctx)
} else {
let ix = 0;
untracked_file_selection(ix, VDir::Up, ctx)
}
} else {
let ix = ix + 1;
untracked_file_selection(ix, VDir::Down, ctx)
};
(Some(selection), task)
}
Some(Status::ChangedFile {
ix,
path,
diff_selected,
}) => {
let (selection, task) = if diff_selected {
nav_scrollable::scroll_down(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
(
Status::ChangedFile {
ix,
path,
diff_selected,
},
Task::none(),
)
} else if ctx.repo.changed_files.len().saturating_sub(1) == ix {
if !ctx.repo.short_log.is_empty() {
let ix = 0;
status_log_selection(ix, VDir::Down, ctx)
} else if !ctx.repo.untracked_files.is_empty() {
let ix = 0;
untracked_file_selection(ix, VDir::Up, ctx)
} else {
let ix = 0;
changed_file_selection(ix, VDir::Up, ctx)
}
} else {
let ix = ix + 1;
changed_file_selection(ix, VDir::Down, ctx)
};
(Some(selection), task)
}
Some(Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file,
})) => {
let (selection, task) = match file {
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) => {
if diff_selected {
let selection = Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
});
nav_scrollable::scroll_down(
&mut ctx.navigation.status_logs_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = ctx.repo.short_log.get(log_ix).unwrap();
let (file_ix, dir) =
if log_entry.file_paths.len().saturating_sub(1)
== file_ix
{
(0, VDir::Up)
} else {
(file_ix + 1, VDir::Down)
};
let (file, selection_task) = status_log_file_selection(
file_ix,
hash,
dir,
ctx.navigation,
log_entry,
);
let selection = Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: Some(file),
});
(selection, selection_task)
}
}
None => {
if ctx.repo.short_log.len().saturating_sub(1) == log_ix {
if !ctx.repo.untracked_files.is_empty() {
let ix = 0;
untracked_file_selection(ix, VDir::Up, ctx)
} else if !ctx.repo.changed_files.is_empty() {
let ix = 0;
changed_file_selection(ix, VDir::Up, ctx)
} else {
let ix = 0;
status_log_selection(ix, VDir::Up, ctx)
}
} else {
let ix = log_ix + 1;
status_log_selection(ix, VDir::Down, ctx)
}
}
};
(Some(selection), task)
}
None => {
if !ctx.repo.untracked_files.is_empty() {
let ix = 0;
let (selection, task) =
untracked_file_selection(ix, VDir::Down, ctx);
(Some(selection), task)
} else if !ctx.repo.changed_files.is_empty() {
let ix = 0;
let (selection, task) =
changed_file_selection(ix, VDir::Down, ctx);
(Some(selection), task)
} else if !ctx.repo.short_log.is_empty() {
let ix = ctx.repo.short_log.len() - 1;
let (selection, task) =
status_log_selection(ix, VDir::Down, ctx);
(Some(selection), task)
} else {
(None, Task::none())
}
}
};
ctx.state.status = selection;
task
}
fn select_down_channel(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
let (selection, task) = match ctx.state.channel.take() {
Some(Channel {
ix,
name: _,
log: None,
}) => {
let ix = if ix == ctx.repo.other_channels.len().saturating_sub(1) {
0
} else {
ix + 1
};
channel_selection(ix, VDir::Down, ctx)
}
None => {
let ix = 0;
channel_selection(ix, VDir::Down, ctx)
}
Some(Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
}) => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
}),
};
nav_scrollable::scroll_down(
&mut ctx
.navigation
.other_channel_logs_navs
.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = log.get(change_ix).unwrap();
let file_ix =
if log_entry.file_paths.len().saturating_sub(1)
== file_ix
{
0
} else {
file_ix + 1
};
let (file, selection_task) = channel_log_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(file),
}),
};
(selection, selection_task)
}
} else {
let (selection, task) =
if log.len().saturating_sub(1) == change_ix {
let ix = 0;
channel_log_selection(ix, VDir::Up, ctx, log)
} else {
let ix = change_ix + 1;
channel_log_selection(ix, VDir::Down, ctx, log)
};
let selection = Channel {
ix: channel_ix,
name,
log: Some(selection),
};
(selection, task)
}
} else {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
};
(selection, Task::none())
}
}
};
ctx.state.channel = Some(selection);
task
}
fn select_down_entire_log(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
if let Some(Log::Loaded { log }) = ctx.logs.entire_log.as_ref() {
let (selection, task) = if let Some(LogChange {
ix: change_ix,
hash,
message,
file,
}) = ctx.state.entire_log.take()
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = LogChange {
ix: change_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
};
nav_scrollable::scroll_down(
&mut ctx.navigation.entire_logs_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = log.get(change_ix).unwrap();
let file_ix =
if log_entry.file_paths.len().saturating_sub(1)
== file_ix
{
0
} else {
file_ix + 1
};
let (file, selection_task) = entire_log_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
let selection = LogChange {
ix: change_ix,
hash,
message,
file: Some(file),
};
(selection, selection_task)
}
} else if log.len().saturating_sub(1) == change_ix {
let ix = 0;
entire_log_selection(ix, VDir::Up, ctx, log)
} else {
let ix = change_ix + 1;
entire_log_selection(ix, VDir::Down, ctx, log)
}
} else {
let ix = 0;
entire_log_selection(ix, VDir::Down, ctx, log)
};
ctx.state.entire_log = Some(selection);
return task;
}
Task::none()
}
fn select_down_compare_remote(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
if let Some(record_dichotomy) = ctx.record_dichotomy {
let len = record_dichotomy.len();
if len > 0
&& let Some(CompareRemote {
ix,
hash,
file,
remote,
remote_channel: channel,
}) = ctx.state.compare_remote.take()
{
let (selection, task) = if let (Some(change_ix), Some(hash)) =
(ix, hash)
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
remote,
remote_channel: channel,
};
nav_scrollable::scroll_down(
&mut ctx.navigation.compare_remote_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry =
record_dichotomy.get(change_ix).unwrap();
let file_ix =
if log_entry.file_paths.len().saturating_sub(1)
== file_ix
{
0
} else {
file_ix + 1
};
let (file, selection_task) =
compare_remote_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
let selection = CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file: Some(file),
remote,
remote_channel: channel,
};
(selection, selection_task)
}
} else {
if change_ix + 1 == len {
let ix = 0;
compare_remote_selection(
ix,
VDir::Up,
ctx,
record_dichotomy,
remote,
channel,
)
} else {
let ix = change_ix + 1;
compare_remote_selection(
ix,
VDir::Down,
ctx,
record_dichotomy,
remote,
channel,
)
}
}
} else {
let ix = 0;
compare_remote_selection(
ix,
VDir::Down,
ctx,
record_dichotomy,
remote,
channel,
)
};
ctx.state.compare_remote = Some(selection);
return task;
}
}
Task::none()
}
fn select_up(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
match ctx.state.primary {
Primary::Status => select_up_status(ctx, delta),
Primary::Channel => select_up_channel(ctx, delta),
Primary::EntireLog => select_up_entire_log(ctx, delta),
Primary::CompareRemote => select_up_compare_remote(ctx, delta),
}
}
fn select_up_status(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
let (selection, task) = match ctx.state.status.take() {
Some(Status::UntrackedFile {
ix,
path,
diff_selected,
}) => {
let (selection, task) = if diff_selected {
nav_scrollable::alt_scroll_up(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
(
Status::UntrackedFile {
ix,
path,
diff_selected,
},
Task::none(),
)
} else if 0 == ix {
if !ctx.repo.short_log.is_empty() {
let ix = ctx.repo.short_log.len() - 1;
status_log_selection(ix, VDir::Down, ctx)
} else if !ctx.repo.changed_files.is_empty() {
let ix = ctx.repo.changed_files.len() - 1;
changed_file_selection(ix, VDir::Down, ctx)
} else {
let ix = ctx.repo.untracked_files.len() - 1;
untracked_file_selection(ix, VDir::Down, ctx)
}
} else {
let ix = ix - 1;
untracked_file_selection(ix, VDir::Up, ctx)
};
(Some(selection), task)
}
Some(Status::ChangedFile {
ix,
path,
diff_selected,
}) => {
let (selection, task) = if diff_selected {
nav_scrollable::scroll_up(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
(
Status::ChangedFile {
ix,
path,
diff_selected,
},
Task::none(),
)
} else if 0 == ix {
if !ctx.repo.untracked_files.is_empty() {
let ix = ctx.repo.untracked_files.len() - 1;
untracked_file_selection(ix, VDir::Up, ctx)
} else if !ctx.repo.short_log.is_empty() {
let ix = ctx.repo.short_log.len() - 1;
status_log_selection(ix, VDir::Down, ctx)
} else {
let ix = ctx.repo.changed_files.len() - 1;
changed_file_selection(ix, VDir::Down, ctx)
}
} else {
let ix = ix - 1;
changed_file_selection(ix, VDir::Up, ctx)
};
(Some(selection), task)
}
Some(Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file,
})) => {
let (selection, task) = match file {
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) => {
if diff_selected {
let selection = Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
});
nav_scrollable::scroll_up(
&mut ctx.navigation.status_logs_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = ctx.repo.short_log.get(log_ix).unwrap();
let (file_ix, dir) = if 0 == file_ix {
(log_entry.file_paths.len() - 1, VDir::Down)
} else {
(file_ix - 1, VDir::Up)
};
let (file, selection_task) = status_log_file_selection(
file_ix,
hash,
dir,
ctx.navigation,
log_entry,
);
let selection = Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: Some(file),
});
(selection, selection_task)
}
}
None => {
if 0 == log_ix {
if !ctx.repo.changed_files.is_empty() {
let ix = ctx.repo.changed_files.len() - 1;
changed_file_selection(ix, VDir::Up, ctx)
} else if !ctx.repo.untracked_files.is_empty() {
let ix = ctx.repo.untracked_files.len() - 1;
untracked_file_selection(ix, VDir::Up, ctx)
} else {
let ix = ctx.repo.short_log.len() - 1;
status_log_selection(ix, VDir::Down, ctx)
}
} else {
let ix = log_ix - 1;
status_log_selection(ix, VDir::Up, ctx)
}
}
};
(Some(selection), task)
}
None => {
if !ctx.repo.short_log.is_empty() {
let ix = ctx.repo.short_log.len() - 1;
let (selection, task) = status_log_selection(ix, VDir::Up, ctx);
(Some(selection), task)
} else if !ctx.repo.changed_files.is_empty() {
let ix = ctx.repo.changed_files.len() - 1;
let (selection, task) =
changed_file_selection(ix, VDir::Up, ctx);
(Some(selection), task)
} else if !ctx.repo.untracked_files.is_empty() {
let ix = ctx.repo.untracked_files.len() - 1;
let (selection, task) =
untracked_file_selection(ix, VDir::Up, ctx);
(Some(selection), task)
} else {
(None, Task::none())
}
}
};
ctx.state.status = selection;
task
}
fn select_up_channel(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
let (selection, task) = match ctx.state.channel.take() {
Some(Channel {
ix,
name: _,
log: None,
}) => {
let ix = if ix == 0 {
ctx.repo.other_channels.len().saturating_sub(1)
} else {
ix - 1
};
channel_selection(ix, VDir::Up, ctx)
}
None => {
let ix = ctx.repo.other_channels.len().saturating_sub(1);
channel_selection(ix, VDir::Up, ctx)
}
Some(Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
}) => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
}),
};
nav_scrollable::scroll_up(
&mut ctx
.navigation
.other_channel_logs_navs
.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = log.get(change_ix).unwrap();
let file_ix = if 0 == file_ix {
log_entry.file_paths.len().saturating_sub(1)
} else {
file_ix - 1
};
let (file, selection_task) = channel_log_file_selection(
file_ix,
hash,
VDir::Up,
ctx.navigation,
log_entry,
);
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(file),
}),
};
(selection, selection_task)
}
} else {
let (selection, task) = if 0 == change_ix {
let ix = log.len().saturating_sub(1);
channel_log_selection(ix, VDir::Down, ctx, log)
} else {
let ix = change_ix - 1;
channel_log_selection(ix, VDir::Up, ctx, log)
};
let selection = Channel {
ix: channel_ix,
name,
log: Some(selection),
};
(selection, task)
}
} else {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
};
(selection, Task::none())
}
}
};
ctx.state.channel = Some(selection);
task
}
fn select_up_entire_log(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
if let Some(Log::Loaded { log }) = ctx.logs.entire_log.as_ref() {
let (selection, task) = if let Some(LogChange {
ix: log_ix,
hash,
message,
file,
}) = ctx.state.entire_log.take()
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = LogChange {
ix: log_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
};
nav_scrollable::scroll_up(
&mut ctx.navigation.entire_logs_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry = log.get(log_ix).unwrap();
let file_ix = if 0 == file_ix {
log_entry.file_paths.len().saturating_sub(1)
} else {
file_ix - 1
};
let (file, selection_task) = entire_log_file_selection(
file_ix,
hash,
VDir::Up,
ctx.navigation,
log_entry,
);
let selection = LogChange {
ix: log_ix,
hash,
message,
file: Some(file),
};
(selection, selection_task)
}
} else if 0 == log_ix {
let ix = log.len().saturating_sub(1);
entire_log_selection(ix, VDir::Down, ctx, log)
} else {
let ix = log_ix - 1;
entire_log_selection(ix, VDir::Up, ctx, log)
}
} else {
let ix = 0;
entire_log_selection(ix, VDir::Down, ctx, log)
};
ctx.state.entire_log = Some(selection);
return task;
}
Task::none()
}
fn select_up_compare_remote(
ctx: &mut Ctx<'_>,
delta: Option<Duration>,
) -> Task<crate::ManagingRepoMsg> {
if let Some(record_dichotomy) = ctx.record_dichotomy {
let len = record_dichotomy.len();
if len > 0
&& let Some(CompareRemote {
ix,
hash,
file,
remote,
remote_channel: channel,
}) = ctx.state.compare_remote.take()
{
let (selection, task) = if let (Some(change_ix), Some(hash)) =
(ix, hash)
{
if let Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}) = file
{
if diff_selected {
let selection = CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
remote,
remote_channel: channel,
};
nav_scrollable::scroll_up(
&mut ctx.navigation.compare_remote_navs.diffs_nav,
delta,
);
(selection, Task::none())
} else {
let log_entry =
record_dichotomy.get(change_ix).unwrap();
let file_ix = if 0 == file_ix {
log_entry.file_paths.len().saturating_sub(1)
} else {
file_ix - 1
};
let (file, selection_task) =
compare_remote_file_selection(
file_ix,
hash,
VDir::Up,
ctx.navigation,
log_entry,
);
let selection = CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file: Some(file),
remote,
remote_channel: channel,
};
(selection, selection_task)
}
} else {
if change_ix == 0 {
let ix = len.saturating_sub(1);
compare_remote_selection(
ix,
VDir::Up,
ctx,
record_dichotomy,
remote,
channel,
)
} else {
let ix = change_ix - 1;
compare_remote_selection(
ix,
VDir::Up,
ctx,
record_dichotomy,
remote,
channel,
)
}
}
} else {
let ix = len.saturating_sub(1);
compare_remote_selection(
ix,
VDir::Down,
ctx,
record_dichotomy,
remote,
channel,
)
};
ctx.state.compare_remote = Some(selection);
return task;
}
}
Task::none()
}
fn alt_select_down<M>(ctx: &mut Ctx<'_>, delta: Option<Duration>) -> Task<M> {
match ctx.state.status.as_mut() {
Some(Status::UntrackedFile {
diff_selected: true,
..
}) => {
nav_scrollable::alt_scroll_down(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::ChangedFile {
diff_selected: true,
..
}) => {
nav_scrollable::alt_scroll_down(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::LogChange(LogChange {
file:
Some(LogChangeFileSelection {
diff_selected: true,
..
}),
..
})) => {
nav_scrollable::alt_scroll_down(
&mut ctx.navigation.status_logs_navs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::UntrackedFile { .. })
| Some(Status::ChangedFile { .. })
| Some(Status::LogChange(LogChange { .. }))
| None => Task::none(),
}
}
fn alt_select_up<M>(ctx: &mut Ctx<'_>, delta: Option<Duration>) -> Task<M> {
match ctx.state.status.as_mut() {
Some(Status::UntrackedFile {
diff_selected: true,
..
}) => {
nav_scrollable::alt_scroll_up(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::ChangedFile {
diff_selected: true,
..
}) => {
nav_scrollable::alt_scroll_up(
&mut ctx.navigation.files_diffs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::LogChange(LogChange {
file:
Some(LogChangeFileSelection {
diff_selected: true,
..
}),
..
})) => {
nav_scrollable::alt_scroll_up(
&mut ctx.navigation.status_logs_navs.diffs_nav,
delta,
);
Task::none()
}
Some(Status::UntrackedFile { .. })
| Some(Status::ChangedFile { .. })
| Some(Status::LogChange(LogChange { .. }))
| None => Task::none(),
}
}
fn select_left(ctx: &mut Ctx<'_>) -> Task<crate::ManagingRepoMsg> {
match ctx.state.primary {
Primary::Status => {
let (selection, task): (
Option<Status>,
Task<crate::ManagingRepoMsg>,
) = match ctx.state.status.take() {
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
})) => {
if diff_selected {
(
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
})),
Task::none(),
)
} else {
let selection = Status::LogChange(LogChange {
ix,
hash,
message,
file: None,
});
(Some(selection), Task::none())
}
}
Some(Status::UntrackedFile {
ix,
path,
diff_selected: true,
}) => (
Some(Status::UntrackedFile {
ix,
path,
diff_selected: false,
}),
Task::none(),
),
Some(Status::ChangedFile {
ix,
path,
diff_selected: true,
}) => (
Some(Status::ChangedFile {
ix,
path,
diff_selected: false,
}),
Task::none(),
),
selection @ (Some(Status::UntrackedFile { .. })
| Some(Status::ChangedFile { .. })
| Some(Status::LogChange(LogChange {
file: None,
..
}))
| None) => (selection, Task::none()),
};
ctx.state.status = selection;
task
}
Primary::Channel => {
let (selection, task) = match ctx.state.channel.take() {
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange { file: None, .. }),
}) => {
let selection = Channel {
ix: channel_ix,
name,
log: None,
};
(Some(selection), Task::none())
}
Some(Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix: change_ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
}),
}) => {
if diff_selected {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
}),
};
(Some(selection), Task::none())
} else {
let selection = Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: None,
}),
};
(Some(selection), Task::none())
}
}
selection @ (Some(Channel { .. }) | None) => {
(selection, Task::none())
}
};
ctx.state.channel = selection;
task
}
Primary::EntireLog => {
let (selection, task) = match ctx.state.entire_log.take() {
Some(LogChange {
ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
}) => {
if diff_selected {
(
Some(LogChange {
ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
}),
Task::none(),
)
} else {
let selection = LogChange {
ix,
hash,
message,
file: None,
};
(Some(selection), Task::none())
}
}
selection @ (Some(LogChange { file: None, .. }) | None) => {
(selection, Task::none())
}
};
ctx.state.entire_log = selection;
task
}
Primary::CompareRemote => {
let (selection, task) = match ctx.state.compare_remote.take() {
Some(CompareRemote {
ix,
hash,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected,
}),
remote,
remote_channel: channel,
}) => {
if diff_selected {
(
Some(CompareRemote {
ix,
hash,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
remote,
remote_channel: channel,
}),
Task::none(),
)
} else {
let selection = CompareRemote {
ix,
hash,
file: None,
remote,
remote_channel: channel,
};
(Some(selection), Task::none())
}
}
selection @ (Some(CompareRemote { file: None, .. }) | None) => {
(selection, Task::none())
}
};
ctx.state.compare_remote = selection;
task
}
}
}
fn select_right(ctx: &mut Ctx<'_>) -> Task<crate::ManagingRepoMsg> {
match ctx.state.primary {
Primary::Status => select_right_status(ctx),
Primary::Channel => select_right_channel(ctx),
Primary::EntireLog => select_right_entire_log(ctx),
Primary::CompareRemote => select_right_compare_remote(ctx),
}
}
fn select_right_status(ctx: &mut Ctx<'_>) -> Task<crate::ManagingRepoMsg> {
let (selection, task): (Option<Status>, Task<crate::ManagingRepoMsg>) =
match ctx.state.status.take() {
Some(Status::UntrackedFile {
ix,
path,
diff_selected: false,
}) => {
let diff_selected = diff::file_diff_needs_scrolling(
&ctx.navigation.files_diffs,
);
(
Some(Status::UntrackedFile {
ix,
path,
diff_selected,
}),
Task::none(),
)
}
Some(Status::ChangedFile {
ix,
path,
diff_selected: false,
}) => {
let diff_selected = true;
(
Some(Status::ChangedFile {
ix,
path,
diff_selected,
}),
Task::none(),
)
}
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file: None,
})) => {
let log_entry = ctx.repo.short_log.get(ix).unwrap();
let (file, task) = if let Some(path) =
log_entry.file_paths.first()
{
let file_id = file::log_id_parts_hash(log_entry.hash, path);
if let Some(log) =
ctx.navigation.log_diffs.diffs.get(&file_id)
{
let unchanged_sections =
diff::unchanged_sections(&log.file);
log::init_diffs_nav(
&mut ctx.navigation.status_logs_navs,
file_id,
)
.set_skip_sections(unchanged_sections);
};
let (selection, task) = status_log_file_selection(
0,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
(Some(selection), task)
} else {
(None, Task::none())
};
(
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file,
})),
task,
)
}
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
})) => {
let is_diff_scrollable =
log::diff_needs_scrolling(&ctx.navigation.status_logs_navs);
(
Some(Status::LogChange(LogChange {
ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: is_diff_scrollable,
}),
})),
Task::none(),
)
}
selection => (selection, Task::none()),
};
ctx.state.status = selection;
task
}
fn select_right_channel(ctx: &mut Ctx<'_>) -> Task<crate::ManagingRepoMsg> {
if let Some(channel) = ctx.state.channel.take() {
let (selection, task) = match channel {
Channel {
ix: channel_ix,
name,
log: None,
} => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
{
let change_ix = 0;
let (log_selection, task) =
channel_log_selection(change_ix, VDir::Down, ctx, log);
let selection = Some(Channel {
ix: channel_ix,
name,
log: Some(log_selection),
});
(selection, task)
} else {
(
Some(Channel {
ix: channel_ix,
name,
log: None,
}),
Task::none(),
)
}
}
Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix: change_ix,
hash,
message,
file: None,
}),
} => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
{
let log_entry = log.get(change_ix).unwrap();
let (file, task) =
if let Some(path) = log_entry.file_paths.first() {
let file_id =
file::log_id_parts_hash(log_entry.hash, path);
if let Some(log) =
ctx.navigation.log_diffs.diffs.get(&file_id)
{
let unchanged_sections =
diff::unchanged_sections(&log.file);
log::init_diffs_nav(
&mut ctx.navigation.other_channel_logs_navs,
file_id,
)
.set_skip_sections(unchanged_sections);
};
let (file, task) = channel_log_file_selection(
0,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
(Some(file), task)
} else {
(None, Task::none())
};
(
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
}),
task,
)
} else {
(
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: None,
}),
}),
Task::none(),
)
}
}
Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
}),
} => {
let navs = &ctx.navigation.other_channel_logs_navs;
let is_diff_scrollable = log::diff_needs_scrolling(navs);
(
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: is_diff_scrollable,
}),
}),
}),
Task::none(),
)
}
selection @ Channel { .. } => (Some(selection), Task::none()),
};
ctx.state.channel = selection;
return task;
}
Task::none()
}
fn select_right_entire_log(ctx: &mut Ctx<'_>) -> Task<crate::ManagingRepoMsg> {
if let Some(Log::Loaded { log }) = ctx.logs.entire_log.as_ref()
&& let Some(entire_log) = ctx.state.entire_log.take()
{
let (selection, task) = match entire_log {
LogChange {
ix,
hash,
message,
file: None,
} => {
let log_entry = log.get(ix).unwrap();
let (file, task) = if let Some(path) =
log_entry.file_paths.first()
{
let file_id = file::log_id_parts_hash(log_entry.hash, path);
if let Some(log) =
ctx.navigation.log_diffs.diffs.get(&file_id)
{
let unchanged_sections =
diff::unchanged_sections(&log.file);
log::init_diffs_nav(
&mut ctx.navigation.entire_logs_navs,
file_id,
)
.set_skip_sections(unchanged_sections);
};
(
Some(LogChangeFileSelection {
ix: 0,
path: path.clone(),
diff_selected: false,
}),
Task::none(),
)
} else {
(None, Task::none())
};
(
Some(LogChange {
ix,
hash,
message,
file,
}),
task,
)
}
LogChange {
ix,
hash,
message,
file:
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}),
} => {
let is_diff_scrollable =
log::diff_needs_scrolling(&ctx.navigation.entire_logs_navs);
(
Some(LogChange {
ix,
hash,
message,
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: is_diff_scrollable,
}),
}),
Task::none(),
)
}
selection @ LogChange { .. } => (Some(selection), Task::none()),
};
ctx.state.entire_log = selection;
return task;
}
Task::none()
}
fn select_right_compare_remote(
ctx: &mut Ctx<'_>,
) -> Task<crate::ManagingRepoMsg> {
if let Some(record_dichotomy) = ctx.record_dichotomy {
let len = record_dichotomy.len();
if len > 0
&& let Some(CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file,
remote,
remote_channel: channel,
}) = ctx.state.compare_remote.take()
{
let (selection, task) = match file {
None => {
let log_entry = record_dichotomy.get(change_ix).unwrap();
let (file, task) =
if let Some(path) = log_entry.file_paths.first() {
let file_id =
file::log_id_parts_hash(log_entry.hash, path);
if let Some(log) =
ctx.navigation.log_diffs.diffs.get(&file_id)
{
let unchanged_sections =
diff::unchanged_sections(&log.file);
log::init_diffs_nav(
&mut ctx.navigation.compare_remote_navs,
file_id,
)
.set_skip_sections(unchanged_sections);
};
(
Some(LogChangeFileSelection {
ix: 0,
path: path.clone(),
diff_selected: false,
}),
Task::none(),
)
} else {
(None, Task::none())
};
(
Some(CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file,
remote,
remote_channel: channel,
}),
task,
)
}
Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: false,
}) => {
let is_diff_scrollable = log::diff_needs_scrolling(
&ctx.navigation.compare_remote_navs,
);
(
Some(CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file: Some(LogChangeFileSelection {
ix: file_ix,
path,
diff_selected: is_diff_scrollable,
}),
remote,
remote_channel: channel,
}),
Task::none(),
)
}
file => (
Some(CompareRemote {
ix: Some(change_ix),
hash: Some(hash),
file,
remote,
remote_channel: channel,
}),
Task::none(),
),
};
ctx.state.compare_remote = selection;
return task;
}
}
Task::none()
}
fn release<M>(dir: Dir, ctx: &mut Ctx<'_>) -> Task<M> {
let Ctx {
state,
files: _,
navigation,
repo: _,
logs: _,
record_dichotomy: _,
} = ctx;
if state.held_key.as_ref().map(|key| key.dir) == Some(dir) {
state.held_key = None;
if let Dir::Down | Dir::Up = dir {
match state.status.as_mut() {
Some(Status::UntrackedFile {
diff_selected: true,
..
}) => {
let nav = &mut navigation.files_diffs.diffs_nav;
nav_scrollable::reset_skip_delay(nav);
}
Some(Status::ChangedFile {
diff_selected: true,
..
}) => {
let nav = &mut navigation.files_diffs.diffs_nav;
nav_scrollable::reset_skip_delay(nav);
}
Some(Status::LogChange(LogChange {
file:
Some(LogChangeFileSelection {
diff_selected: true,
..
}),
..
})) => {
let nav = &mut navigation.status_logs_navs.diffs_nav;
nav_scrollable::reset_skip_delay(nav);
}
Some(Status::UntrackedFile { .. })
| Some(Status::ChangedFile { .. })
| Some(Status::LogChange(LogChange { .. }))
| None => {}
}
}
}
Task::none()
}
fn select_exact(
select: Select,
ctx: &mut Ctx<'_>,
) -> Task<crate::ManagingRepoMsg> {
match ctx.state.primary {
Primary::Status => {
let (selection, task) = match select {
Select::UntrackedFile { ix, path } => {
let id = file::Id {
path: path.clone(),
file_kind: file::Kind::Untracked,
};
file::load_src_file_if_not_cached(ctx.files, id);
let (selection, selection_task) =
untracked_file_selection(ix, VDir::Down, ctx);
(Some(selection), selection_task)
}
Select::ChangedFile { ix, path } => {
let id = file::Id {
path: path.clone(),
file_kind: file::Kind::Changed,
};
let file_task = if let Some(diffs) =
ctx.repo.changed_files.get(&path)
{
if diff::should_file_exist(diffs) {
file::load_src_file_if_not_cached(ctx.files, id);
Task::none()
} else {
file::src_file_doesnt_exist(ctx.files, id, diffs)
.map(crate::ManagingRepoMsg::File)
}
} else {
Task::none()
};
let (selection, selection_task) =
changed_file_selection(ix, VDir::Down, ctx);
(Some(selection), Task::batch([selection_task, file_task]))
}
Select::LogChange {
ix,
hash: _,
message: _,
} => {
let (selection, task) =
status_log_selection(ix, VDir::Down, ctx);
(Some(selection), task)
}
Select::LogChangeFile {
ix: file_ix,
path: _,
} => match ctx.state.status.take() {
Some(Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: _,
})) => {
let log_entry = ctx.repo.short_log.get(log_ix).unwrap();
let (file, selection_task) = status_log_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
let selection = Some(Status::LogChange(LogChange {
ix: log_ix,
hash,
message,
file: Some(file),
}));
(selection, selection_task)
}
selection => (selection, Task::none()),
},
Select::Channel { .. }
| Select::CompareRemote { .. }
| Select::CompareRemoteFile { .. } => {
unreachable!()
}
};
ctx.state.status = selection;
return task;
}
Primary::Channel => {
let (selection, task) = match select {
Select::Channel { ix, name: _ } => {
let (selection, task) =
channel_selection(ix, VDir::Down, ctx);
(Some(selection), task)
}
Select::LogChange {
ix,
hash: _,
message: _,
} => match ctx.state.channel.take() {
Some(Channel {
ix: channel_ix,
name,
log,
}) => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
{
let (selection, task) =
channel_log_selection(ix, VDir::Down, ctx, log);
(
Some(Channel {
ix: channel_ix,
name,
log: Some(selection),
}),
task,
)
} else {
(
Some(Channel {
ix: channel_ix,
name,
log,
}),
Task::none(),
)
}
}
selection => (selection, Task::none()),
},
Select::LogChangeFile {
ix: file_ix,
path: _,
} => match ctx.state.channel.take() {
Some(Channel {
ix: channel_ix,
name,
log:
Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
}) => {
if let Some(Log::Loaded { log }) =
ctx.logs.other_channels_logs.get(&name)
&& let Some(log_entry) = log.get(change_ix)
{
let (selection, task) = channel_log_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
(
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file: Some(selection),
}),
}),
task,
)
} else {
(
Some(Channel {
ix: channel_ix,
name,
log: Some(LogChange {
ix: change_ix,
hash,
message,
file,
}),
}),
Task::none(),
)
}
}
selection => (selection, Task::none()),
},
Select::UntrackedFile { .. }
| Select::ChangedFile { .. }
| Select::CompareRemote { .. }
| Select::CompareRemoteFile { .. } => {
unreachable!()
}
};
ctx.state.channel = selection;
return task;
}
Primary::EntireLog => {
if let Some(Log::Loaded { log }) = ctx.logs.entire_log.as_ref() {
let (selection, task) = match select {
Select::LogChange {
ix,
hash: _,
message: _,
} => {
let (selection, task) =
entire_log_selection(ix, VDir::Down, ctx, log);
(Some(selection), task)
}
Select::LogChangeFile {
ix: file_ix,
path: _,
} => match ctx.state.entire_log.take() {
Some(LogChange {
ix: log_ix,
hash,
message,
file: _,
}) => {
let log_entry = log.get(log_ix).unwrap();
let (file, selection_task) =
entire_log_file_selection(
file_ix,
hash,
VDir::Down,
ctx.navigation,
log_entry,
);
let selection = Some(LogChange {
ix: log_ix,
hash,
message,
file: Some(file),
});
(selection, selection_task)
}
selection => (selection, Task::none()),
},
Select::UntrackedFile { .. }
| Select::ChangedFile { .. }
| Select::Channel { .. }
| Select::CompareRemote { .. }
| Select::CompareRemoteFile { .. } => unreachable!(),
};
ctx.state.entire_log = selection;
return task;
}
}
Primary::CompareRemote => {
if let Some((
record_dichotomy,
CompareRemote {
ix: record_ix,
hash,
file: _,
remote,
remote_channel: channel,
},
)) = ctx.record_dichotomy.zip(ctx.state.compare_remote.take())
{
let (selection, task) = match select {
Select::CompareRemote { ix } => {
let (selection, task) = compare_remote_selection(
ix,
VDir::Down,
ctx,
record_dichotomy,
remote,
channel,
);
(Some(selection), task)
}
Select::CompareRemoteFile {
ix: file_ix,
path: _,
} => {
let record_ix = record_ix.unwrap();
let entry = record_dichotomy.get(record_ix).unwrap();
let (file, task) = compare_remote_file_selection(
file_ix,
hash.unwrap(),
VDir::Down,
ctx.navigation,
entry,
);
let selection = Some(CompareRemote {
ix: Some(record_ix),
hash,
file: Some(file),
remote,
remote_channel: channel,
});
(selection, task)
}
Select::UntrackedFile { .. }
| Select::ChangedFile { .. }
| Select::LogChange { .. }
| Select::LogChangeFile { .. }
| Select::Channel { .. } => unreachable!(),
};
ctx.state.compare_remote = selection;
return task;
}
}
}
Task::none()
}
enum StatusSectionKind {
Untracked,
Changed,
Log,
}
fn status_section_ix(
repo: &repo::State,
ix: usize,
kind: StatusSectionKind,
) -> usize {
match kind {
StatusSectionKind::Untracked => 1 + ix,
StatusSectionKind::Changed => 2 + repo.untracked_files.len() + ix,
StatusSectionKind::Log => {
3 + repo.untracked_files.len() + repo.changed_files.len() + ix
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum VDir {
Up,
Down,
}
pub fn untracked_file_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
) -> (Status, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files,
navigation,
repo,
logs: _,
record_dichotomy: _,
} = ctx;
let path = repo.untracked_files.iter().nth(ix).unwrap();
let file_id = file::id_parts_hash(path, file::Kind::Untracked);
match file::try_get_src_file(files, file_id) {
Some(file_diff) => {
let unchanged_sections = diff::unchanged_sections(file_diff);
diff::init_diffs_nav(
&mut navigation.files_diffs,
#[cfg(debug_assertions)]
file_id,
)
.set_skip_sections(unchanged_sections);
}
None => {
let id = file::Id {
path: path.clone(),
file_kind: file::Kind::Untracked,
};
file::load_src_file_if_not_cached(files, id);
}
}
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Untracked),
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Untracked),
),
}
let selection = Status::UntrackedFile {
ix,
path: path.clone(),
diff_selected: false,
};
(selection, Task::none())
}
pub fn changed_file_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
) -> (Status, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files,
navigation,
repo,
logs: _,
record_dichotomy: _,
} = ctx;
let (path, diffs) = repo.changed_files.iter().nth(ix).unwrap();
let file_id = file::id_parts_hash(path, file::Kind::Changed);
let task = match file::try_get_src_file(files, file_id) {
Some(file_diff) => {
let unchanged_sections = diff::unchanged_sections(file_diff);
diff::init_diffs_nav(
&mut navigation.files_diffs,
#[cfg(debug_assertions)]
file_id,
)
.set_skip_sections(unchanged_sections);
Task::none()
}
None => {
let id = file::Id {
path: path.clone(),
file_kind: file::Kind::Changed,
};
if diff::should_file_exist(diffs) {
file::load_src_file_if_not_cached(files, id);
Task::none()
} else {
file::src_file_doesnt_exist(files, id, diffs)
.map(crate::ManagingRepoMsg::File)
}
}
};
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Changed),
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Changed),
),
};
let selection = Status::ChangedFile {
ix,
path: path.clone(),
diff_selected: false,
};
(selection, task)
}
fn status_log_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
) -> (Status, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files: _,
navigation,
repo,
logs: _,
record_dichotomy: _,
} = ctx;
let entry = repo.short_log.get(ix).unwrap();
let hash = entry.hash;
let task = if !navigation
.log_diffs
.changes_with_loaded_diffs
.contains(&hash)
{
Task::done(crate::ManagingRepoMsg::ToRepo(
repo::MsgIn::GetChangeDiffs { hash },
))
} else {
Task::none()
};
log::init_files_nav(&mut navigation.status_logs_navs, hash);
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Log),
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.status_nav,
status_section_ix(repo, ix, StatusSectionKind::Log),
),
}
let selection = Status::LogChange(LogChange {
ix,
hash,
message: entry.message.clone(),
file: None,
});
(selection, task)
}
fn status_log_file_selection(
ix: usize,
hash: repo::ChangeHash,
dir: VDir,
navigation: &mut Navigation,
log_entry: &repo::LogEntry,
) -> (LogChangeFileSelection, Task<crate::ManagingRepoMsg>) {
let path = log_entry.file_paths.get(ix).unwrap().clone();
log::init_diffs_nav(
&mut navigation.status_logs_navs,
file::log_id_parts_hash(hash, &path),
);
let nav = &mut navigation.status_logs_navs.files_nav;
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(nav, ix),
VDir::Down => nav_scrollable::scroll_down_to_section(nav, ix),
}
(
LogChangeFileSelection {
ix,
path,
diff_selected: false,
},
Task::none(),
)
}
fn channel_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
) -> (Channel, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files: _,
navigation,
repo,
logs,
record_dichotomy: _,
} = ctx;
let name = repo.other_channels.iter().nth(ix).unwrap().clone();
let task = if !logs.other_channels_logs.contains_key(&name) {
Task::done(crate::ManagingRepoMsg::ToRepo(
repo::MsgIn::LoadOtherChannelLog(name.clone()),
))
} else {
Task::none()
};
model::init_channel_nav(
navigation,
#[cfg(debug_assertions)]
name.clone(),
);
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.other_channels_nav,
ix,
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.other_channels_nav,
ix,
),
}
let selection = Channel {
ix,
name,
log: None,
};
(selection, task)
}
fn channel_log_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
log: &repo::Log,
) -> (LogChange, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files: _,
navigation,
repo: _,
logs: _,
record_dichotomy: _,
} = ctx;
let entry = log.get(ix).unwrap();
let hash = entry.hash;
let task = if !navigation
.log_diffs
.changes_with_loaded_diffs
.contains(&hash)
{
Task::done(crate::ManagingRepoMsg::ToRepo(
repo::MsgIn::GetChangeDiffs { hash },
))
} else {
Task::none()
};
log::init_files_nav(&mut navigation.other_channel_logs_navs, hash);
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.other_channel_log_nav,
ix,
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.other_channel_log_nav,
ix,
),
}
let selection = LogChange {
ix,
hash,
message: entry.message.clone(),
file: None,
};
(selection, task)
}
fn channel_log_file_selection(
ix: usize,
hash: repo::ChangeHash,
dir: VDir,
navigation: &mut Navigation,
log_entry: &repo::LogEntry,
) -> (LogChangeFileSelection, Task<crate::ManagingRepoMsg>) {
let path = log_entry.file_paths.get(ix).unwrap().clone();
log::init_diffs_nav(
&mut navigation.other_channel_logs_navs,
file::log_id_parts_hash(hash, &path),
);
let nav = &mut navigation.other_channel_logs_navs.files_nav;
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(nav, ix),
VDir::Down => nav_scrollable::scroll_down_to_section(nav, ix),
}
(
LogChangeFileSelection {
ix,
path,
diff_selected: false,
},
Task::none(),
)
}
fn entire_log_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
log: &repo::Log,
) -> (LogChange, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files: _,
navigation,
repo: _,
logs: _,
record_dichotomy: _,
} = ctx;
let entry = log.get(ix).unwrap();
let hash = entry.hash;
let task = if !navigation
.log_diffs
.changes_with_loaded_diffs
.contains(&hash)
{
Task::done(crate::ManagingRepoMsg::ToRepo(
repo::MsgIn::GetChangeDiffs { hash },
))
} else {
Task::none()
};
log::init_files_nav(&mut navigation.entire_logs_navs, hash);
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.entire_log_nav,
ix,
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.entire_log_nav,
ix,
),
}
let selection = LogChange {
ix,
hash,
message: entry.message.clone(),
file: None,
};
(selection, task)
}
fn entire_log_file_selection(
ix: usize,
hash: repo::ChangeHash,
dir: VDir,
navigation: &mut Navigation,
log_entry: &repo::LogEntry,
) -> (LogChangeFileSelection, Task<crate::ManagingRepoMsg>) {
let path = log_entry.file_paths.get(ix).unwrap().clone();
log::init_diffs_nav(
&mut navigation.entire_logs_navs,
file::log_id_parts_hash(hash, &path),
);
let nav = &mut navigation.entire_logs_navs.files_nav;
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(nav, ix),
VDir::Down => nav_scrollable::scroll_down_to_section(nav, ix),
}
(
LogChangeFileSelection {
ix,
path,
diff_selected: false,
},
Task::none(),
)
}
fn compare_remote_selection(
ix: usize,
dir: VDir,
ctx: &mut Ctx<'_>,
record_dichotomy: &repo::RecordDichotomy,
remote: String,
channel: String,
) -> (CompareRemote, Task<crate::ManagingRepoMsg>) {
let Ctx {
state: _,
files: _,
navigation,
repo: _,
logs: _,
record_dichotomy: _,
} = ctx;
let entry = record_dichotomy.get(ix).unwrap();
let hash = entry.hash;
let task = if !navigation
.log_diffs
.changes_with_loaded_diffs
.contains(&hash)
{
Task::done(crate::ManagingRepoMsg::ToRepo(
repo::MsgIn::GetChangeDiffs { hash },
))
} else {
Task::none()
};
log::init_files_nav(&mut navigation.compare_remote_navs, hash);
let repo::RecordDichotomy {
local_records,
remote_records,
remote_unrecords: _,
} = record_dichotomy;
let scroll_section_ix = if ix >= local_records.len() + remote_records.len()
{
ix + 3
} else if ix >= local_records.len() {
ix + 2
} else {
ix + 1
};
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(
&mut navigation.compare_remote_nav,
scroll_section_ix,
),
VDir::Down => nav_scrollable::scroll_down_to_section(
&mut navigation.compare_remote_nav,
scroll_section_ix,
),
}
let selection = CompareRemote {
ix: Some(ix),
hash: Some(hash),
file: None,
remote,
remote_channel: channel,
};
(selection, task)
}
fn compare_remote_file_selection(
ix: usize,
hash: repo::ChangeHash,
dir: VDir,
navigation: &mut Navigation,
log_entry: &repo::LogEntry,
) -> (LogChangeFileSelection, Task<crate::ManagingRepoMsg>) {
let path = log_entry.file_paths.get(ix).unwrap().clone();
log::init_diffs_nav(
&mut navigation.compare_remote_navs,
file::log_id_parts_hash(hash, &path),
);
let nav = &mut navigation.compare_remote_navs.files_nav;
match dir {
VDir::Up => nav_scrollable::scroll_up_to_section(nav, ix),
VDir::Down => nav_scrollable::scroll_down_to_section(nav, ix),
}
(
LogChangeFileSelection {
ix,
path,
diff_selected: false,
},
Task::none(),
)
}