nav for log changes and refactors

[?]
Jul 22, 2025, 1:15 PM
UR4J677RWA3OFG6HQTD46BUUE5YFPSBEFCJAEM5OMT4V5A7SBNNQC

Dependencies

  • [2] WT3GA27P add cursor with selection
  • [3] S2NVIFXR allow to enter record msg
  • [4] W7IUT3ZV start recording impl
  • [5] D7A7MSIH allow to defer or abandon record, add buttons
  • [6] 4WO3ZJM2 show untracked files' contents
  • [7] BJXUYQ2Y show untracked file contents in read-only text editor
  • [8] AMPZ2BXK show changed files diffs (only Edit atm)
  • [9] DCSUCH6R add undecoded diff view, improve decoded view style
  • [10] JE44NYHM display log files diffs
  • [11] L6KSEFQI move cursor related stuff into its module
  • [12] BFN2VHZS refactor file stuff into sub-mod
  • [13] 23SFYK4Q big view refactor into a new crate
  • [14] OPXFZKEB view tests setup
  • [15] 3QVNMRNM test non-empty repo app view
  • [16] MYGIBRRH wip custom theme
  • [17] PKJCFSBM theme improvements
  • [18] XSZZB47U refactor stuff into lib
  • [19] 3BK22XE5 add a test for hover btn and more refactors
  • [20] ACDXXAX2 refactor main's updates into smaller fns
  • [21] TSFQFCB2 test got repo change
  • [22] 7SSBM4UQ view: refactor repo view
  • [23] ZD56BUSU add back +/- bg colors
  • [24] I2AG42PA new cols layout
  • [25] 4PNWU55O replace the circular hor navigation
  • [26] WW36JYLR add iced_nav_scrollable widget crate
  • [27] WIFVLV37 nav-scrollabe: detect size to determine if needs scrolling, msg when ready
  • [28] SASAN2XC use nav-scrollable
  • [29] XZ6D3UUE avoid alloc
  • [30] YKHE3XMW refactor diffs handling
  • [31] KEPKF3WO unify diffs handling, simplify view
  • [32] GOLHUD6R nav-scrollable: set skip-able sections
  • [33] XHWLKCLD auto-scroll past skip sections on load
  • [34] K5YUSV2W auto-scroll to last offset
  • [35] KWTBNTO3 diffs selection and scrolling
  • [36] 5MUEECMJ smooth scrolling nav
  • [37] 3TLPJ57B alt scroll via context and couple fixes
  • [38] BNHJU2DU clippy fixes
  • [39] KQABQCCZ update rust to 1.88
  • [40] AI3IMKC3 refactor stairs
  • [41] WXQBBQ2A update nightly
  • [42] DST3HRZZ fix emoji rendering
  • [43] PTWZYQFR use nav-scrollable for repo status
  • [44] RDRBP7AL auto-scroll status selection
  • [45] 4G6DZDO6 rename diff msg for nav-scrollable
  • [46] A5YBC77V record!
  • [47] MJDGPSHG WIP contents diff
  • [48] ONRCENKT rm unnecessary state from repo's internal state
  • [49] PTFDJ567 add untracked files encoding
  • [50] Y5ATDI2H convert changed file diffs and load src only if any needs it
  • [51] NRCUG4R2 load changed files src when selected
  • [52] FR52XEMW add action for log change file diff
  • [53] ELG3UDT6 allow to rm added files
  • [54] I56UGW7U make record test, fix log update
  • [55] KM5PSZ4A watch repo once loaded
  • [56] ZVI4AWER woot contents_diff
  • [57] FVA36HBV restart repo manager task if it crashes
  • [58] SK3WVX7A add wee spacing for nav back
  • [59] YBJRDOTC make all repo actions async
  • [60] 65DXFP3Y fix status overflow
  • [61] QMAUTRB6 refactor diff
  • [62] 4ELJZGRJ load and store all change diffs at once
  • [63] V55EAIWQ add src file LRU cache
  • [64] S2T7RUKW add nav back placeholder
  • [*] VCNKFNUF app init test
  • [*] 6YZAVBWU Initial commit
  • [*] WGID4LS4 absolutely slayed testing with iced task

Change contents

  • replacement in inflorescence_view/src/diff.rs at line 5
    [19.832][28.166:234]()
    DiffWithoutContents, File, Lines, Section, UndecodableContents,
    [19.832]
    [19.907]
    DiffWithoutContents, File, Lines, Section, State, UndecodableContents,
  • edit in inflorescence_view/src/diff.rs at line 16
    [28.236][28.236:281](),[28.364][28.364:509](),[28.509][13.912:913](),[23.578][13.912:913](),[13.912][13.912:913]()
    #[derive(Debug, Default)]
    pub struct State {
    /// Initialized once the file is loaded and opened in view
    pub nav: Option<NavScrollable>,
    pub state: libflorescence::diff::State,
    }
  • edit in inflorescence_view/src/diff.rs at line 23
    [35.5257]
    [35.5257]
    nav: &'a NavScrollable,
  • replacement in inflorescence_view/src/diff.rs at line 29
    [35.5375][35.5375:5436]()
    view_decoded(state, decoded_file, diff_selected)
    [35.5375]
    [35.5436]
    view_decoded(state, nav, decoded_file, diff_selected)
  • replacement in inflorescence_view/src/diff.rs at line 32
    [13.3689][35.5447:5516]()
    view_undecodable(state, undecodable_file, diff_selected)
    [13.3689]
    [13.3743]
    view_undecodable(state, nav, undecodable_file, diff_selected)
  • replacement in inflorescence_view/src/diff.rs at line 38
    [13.3893][31.165:187]()
    state: &'a State,
    [13.3893]
    [13.3924]
    _state: &'a State,
    nav: &'a NavScrollable,
  • replacement in inflorescence_view/src/diff.rs at line 92
    [13.5499][31.188:247](),[31.247][29.26:74](),[28.691][29.26:74](),[29.74][30.26:134](),[30.134][45.66:92](),[45.92][35.5543:5694](),[30.166][35.5543:5694]()
    let sections = if let Some(nav) = state.nav.as_ref() {
    let children_len = sections_view.len();
    el(iced_nav_scrollable::view(
    nav,
    sections_view,
    children_len,
    Msg::DiffNav,
    )
    .class(if diff_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    }))
    [13.5499]
    [28.898]
    let children_len = sections_view.len();
    let sections = el(iced_nav_scrollable::view(
    nav,
    sections_view,
    children_len,
    Msg::DiffNav,
    )
    .class(if diff_selected {
    theme::Scrollable::Selected
  • replacement in inflorescence_view/src/diff.rs at line 102
    [28.911][13.5542:5576](),[13.5542][13.5542:5576](),[13.5576][28.912:919]()
    el(column(sections_view))
    };
    [28.911]
    [28.919]
    theme::Scrollable::Normal
    }));
  • replacement in inflorescence_view/src/diff.rs at line 118
    [13.5900][31.248:270]()
    state: &'a State,
    [13.5900]
    [13.5931]
    _state: &'a State,
    nav: &'a NavScrollable,
  • replacement in inflorescence_view/src/diff.rs at line 138
    [13.6315][31.271:315](),[31.315][35.5721:5733](),[35.5733][45.93:349](),[45.349][35.6054:6064](),[35.6054][35.6054:6064](),[30.318][28.1614:1671](),[35.6064][28.1614:1671](),[28.1614][28.1614:1671]()
    if let Some(nav) = state.nav.as_ref() {
    el(
    iced_nav_scrollable::view(nav, diffs, diffs_len, Msg::DiffNav)
    .class(if diff_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    }),
    )
    } else {
    el(column(diffs).spacing(10))
    }
    [13.6315]
    [13.6349]
    el(
    iced_nav_scrollable::view(nav, diffs, diffs_len, Msg::DiffNav).class(
    if diff_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    },
    ),
    )
  • replacement in inflorescence_view/src/cursor.rs at line 1
    [18.3142][28.1707:1743]()
    use libflorescence::prelude::pijul;
    [18.3124]
    [36.27]
    use libflorescence::repo;
  • replacement in inflorescence_view/src/cursor.rs at line 52
    [18.3596][18.3596:3623]()
    hash: pijul::Hash,
    [18.3596]
    [18.3623]
    hash: repo::ChangeHash,
  • replacement in inflorescence_view/src/cursor.rs at line 77
    [18.4315][18.4315:4342]()
    hash: pijul::Hash,
    [18.4315]
    [18.4342]
    hash: repo::ChangeHash,
  • replacement in inflorescence_view/src/app.rs at line 11
    [13.12642][18.5659:5749]()
    use iced::widget::{
    button, column, container, row, scrollable, text, text_editor,
    };
    [13.12642]
    [18.5749]
    use iced::widget::{button, column, container, row, text, text_editor};
  • replacement in inflorescence_view/src/app.rs at line 31
    [31.415][31.415:481]()
    pub selected_diff: Option<(&'a diff::File, &'a diff::State)>,
    [31.415]
    [13.12996]
    pub selection: Option<Selection<'a>>,
  • edit in inflorescence_view/src/app.rs at line 40
    [43.1252]
    [43.1252]
    }
    #[derive(Debug)]
    pub enum Selection<'a> {
    UntrackedFile {
    ix: usize,
    path: &'a str,
    diff: Option<Diff<'a>>,
    },
    ChangedFile {
    ix: usize,
    path: &'a str,
    diff: Option<Diff<'a>>,
    },
    Log {
    ix: usize,
    hash: repo::ChangeHash,
    message: &'a str,
    file: Option<LogChangeFileSelection<'a>>,
    nav: Option<&'a NavScrollable>,
    },
  • edit in inflorescence_view/src/app.rs at line 63
    [43.1255]
    [13.12999]
    #[derive(Debug)]
    pub struct LogChangeFileSelection<'a> {
    pub ix: usize,
    pub path: &'a str,
    pub diff: Option<Diff<'a>>,
    }
    #[derive(Debug)]
    pub struct Diff<'a> {
    pub is_selected: bool,
    pub file: &'a diff::File,
    pub state: &'a diff::State,
    pub nav: &'a NavScrollable,
    }
  • edit in inflorescence_view/src/app.rs at line 83
    [43.1297]
    [13.13088]
    LogNav {
    hash: repo::ChangeHash,
    msg: iced_nav_scrollable::Msg,
    },
  • replacement in inflorescence_view/src/app.rs at line 91
    [13.13180][13.13180:13210]()
    FileDiffsContentsAction {
    [13.13180]
    [35.6157]
    /// Message for an untracked or changed file diff
    FileDiff {
  • replacement in inflorescence_view/src/app.rs at line 94
    [35.6188][13.13232:13259](),[13.13232][13.13232:13259]()
    action: diff::Msg,
    [35.6188]
    [13.13259]
    msg: diff::Msg,
  • replacement in inflorescence_view/src/app.rs at line 96
    [13.13266][13.13266:13372]()
    LogChangeFileDiffAction {
    hash: pijul::Hash,
    file: String,
    action: diff::Msg,
    [13.13266]
    [13.13372]
    /// Message for a log change file diff
    LogChangeFileDiff {
    id_hash: file::LogIdHash,
    msg: diff::Msg,
  • replacement in inflorescence_view/src/app.rs at line 113
    [31.531][43.1298:1347](),[43.1347][31.532:564](),[17.2747][31.532:564]()
    let inner = if let Some(repo) = state.repo {
    view_repo(&state, repo)
    [31.531]
    [22.75]
    let State {
    window_size,
    repo_path,
    repo,
    cursor,
    record_msg,
    selection,
    } = state;
    let inner = if let Some(repo) = repo {
    view_repo(window_size, repo_path, repo, cursor, record_msg, selection)
  • replacement in inflorescence_view/src/app.rs at line 132
    [13.15950][43.1348:1429]()
    fn view_repo<'a>(state: &State<'a>, repo: &'a Repo) -> Element<'a, Msg, Theme> {
    [13.15950]
    [43.1429]
    fn view_repo<'a>(
    window_size: iced::Size,
    // TODO show path
    _repo_path: &'a Path,
    repo: &'a Repo,
    cursor: &'a cursor::State,
    record_msg: Option<&'a RecordMsg>,
    selection: Option<Selection<'a>>,
    ) -> Element<'a, Msg, Theme> {
  • replacement in inflorescence_view/src/app.rs at line 142
    [43.1444][43.1444:1465]()
    state: repo,
    [43.1444]
    [43.1465]
    state:
    repo::State {
    dir_name,
    channel,
    untracked_files,
    changed_files,
    log,
    },
  • replacement in inflorescence_view/src/app.rs at line 153
    [31.615][22.436:499](),[43.1500][22.436:499](),[22.436][22.436:499]()
    let repo_info = el(row([
    el(text(&repo.dir_name)),
    [43.1500]
    [22.499]
    let view_repo_info = el(row([
    el(text(dir_name)),
  • replacement in inflorescence_view/src/app.rs at line 157
    [22.535][22.535:650]()
    button(text(&repo.channel)), /* TODO
    * .on_press(Message) */
    [22.535]
    [22.650]
    button(text(channel)), /* TODO
    * .on_press(Message) */
  • replacement in inflorescence_view/src/app.rs at line 162
    [22.671][22.671:702](),[22.702][43.1501:1554]()
    let untracked_files = || {
    repo.untracked_files.iter().enumerate().map(
    [22.671]
    [13.16052]
    let view_untracked_files = || {
    untracked_files.iter().enumerate().map(
  • replacement in inflorescence_view/src/app.rs at line 165
    [13.16079][13.16079:16156]()
    let is_selected = matches!(state.cursor.selection.as_ref() ,
    [13.16079]
    [13.16156]
    let is_selected = matches!(cursor.selection.as_ref() ,
  • replacement in inflorescence_view/src/app.rs at line 181
    [13.16643][22.774:803](),[22.803][43.1566:1617]()
    let changed_files = || {
    repo.changed_files.iter().enumerate().map(
    [13.16643]
    [13.16741]
    let view_changed_files = || {
    changed_files.iter().enumerate().map(
  • replacement in inflorescence_view/src/app.rs at line 184
    [13.16783][13.16783:16859]()
    let is_selected = matches!(state.cursor.selection.as_ref(),
    [13.16783]
    [13.16859]
    let is_selected = matches!(cursor.selection.as_ref(),
  • replacement in inflorescence_view/src/app.rs at line 201
    [13.17369][22.873:892](),[22.892][43.1629:1670]()
    let log = || {
    repo.log.iter().enumerate().map(
    [13.17369]
    [13.17447]
    let view_log = || {
    log.iter().enumerate().map(
  • replacement in inflorescence_view/src/app.rs at line 209
    [13.17641][13.17641:17717]()
    let is_selected = matches!(state.cursor.selection.as_ref(),
    [13.17641]
    [13.17717]
    let is_selected = matches!(cursor.selection.as_ref(),
  • replacement in inflorescence_view/src/app.rs at line 225
    [22.952][22.952:1532]()
    let record_msg_editor = if let Some(RecordMsg::Typing(msg_content)) =
    state.record_msg.as_ref()
    {
    el(column([
    el(text_editor(msg_content)
    .placeholder("Type something here...")
    .on_action(Msg::EditRecordMsg)),
    el(row([
    el(button(text("Save")).on_press(Msg::SaveRecord)),
    el(button(text("Defer")).on_press(Msg::DeferRecord)),
    el(button(text("Abandon")).on_press(Msg::AbandonRecord)),
    ])),
    ]))
    } else {
    el(row([]))
    };
    [22.952]
    [13.18330]
    let record_msg_editor =
    if let Some(RecordMsg::Typing(msg_content)) = record_msg.as_ref() {
    el(column([
    el(text_editor(msg_content)
    .placeholder("Type something here...")
    .on_action(Msg::EditRecordMsg)),
    el(row([
    el(button(text("Save")).on_press(Msg::SaveRecord)),
    el(button(text("Defer")).on_press(Msg::DeferRecord)),
    el(button(text("Abandon")).on_press(Msg::AbandonRecord)),
    ])),
    ]))
    } else {
    el(row([]))
    };
  • replacement in inflorescence_view/src/app.rs at line 241
    [13.18331][22.1533:1601](),[22.1601][35.6189:6317]()
    let selection_details = match state.cursor.selection.as_ref() {
    Some(cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    [13.18331]
    [35.6317]
    let selection_details = match selection.as_ref() {
    Some(Selection::UntrackedFile { ix: _, path, diff }) => {
  • replacement in inflorescence_view/src/app.rs at line 244
    [35.6393][31.616:668](),[22.1802][31.616:668](),[31.668][35.6394:6634]()
    let diffs = match state.selected_diff {
    Some((file, state)) => diff::view(state, file, *diff_selected)
    .map(move |msg| Msg::FileDiffsContentsAction {
    id_hash,
    action: msg,
    }),
    [35.6393]
    [31.771]
    let diffs = match diff {
    Some(Diff {
    is_selected,
    file,
    state,
    nav,
    }) => diff::view(state, nav, file, *is_selected)
    .map(move |msg| Msg::FileDiff { id_hash, msg }),
  • replacement in inflorescence_view/src/app.rs at line 261
    [22.2546][35.6635:6761]()
    Some(cursor::Selection::ChangedFile {
    path,
    ix: _,
    diff_selected,
    }) => {
    [22.2546]
    [35.6761]
    Some(Selection::ChangedFile { path, ix: _, diff }) => {
  • replacement in inflorescence_view/src/app.rs at line 263
    [35.6835][31.825:877](),[22.2743][31.825:877](),[31.877][35.6836:7076]()
    let diffs = match state.selected_diff {
    Some((file, state)) => diff::view(state, file, *diff_selected)
    .map(move |msg| Msg::FileDiffsContentsAction {
    id_hash,
    action: msg,
    }),
    [35.6835]
    [31.980]
    let diffs = match diff {
    Some(Diff {
    is_selected,
    file,
    state,
    nav,
    }) => diff::view(state, nav, file, *is_selected)
    .map(move |msg| Msg::FileDiff { id_hash, msg }),
  • replacement in inflorescence_view/src/app.rs at line 279
    [22.3458][22.3458:3502]()
    Some(cursor::Selection::LogChange {
    [22.3458]
    [22.3502]
    Some(Selection::Log {
  • edit in inflorescence_view/src/app.rs at line 283
    [22.3557]
    [22.3579]
    nav,
  • replacement in inflorescence_view/src/app.rs at line 286
    [22.3613][43.1682:1734]()
    let entry = repo.log.get(*ix).unwrap();
    [22.3613]
    [13.18975]
    let entry = log.get(*ix).unwrap();
  • replacement in inflorescence_view/src/app.rs at line 290
    [13.21516][22.3746:3825](),[22.3825][13.21659:21803](),[13.21659][13.21659:21803]()
    let files = entry.file_paths.iter().enumerate().map(|(ix, path)| {
    let is_selected = matches!(file, Some(cursor::LogChangeFileSelection{ path: selected_path, .. }) if selected_path == path);
    [13.21516]
    [13.21803]
    let view = match nav {
    Some(nav) => {
    let files_len = entry.file_paths.len();
    let files = entry.file_paths.iter().enumerate().map(|(ix, path)| {
    let is_selected = matches!(file, Some(LogChangeFileSelection{ path: selected_path, .. }) if selected_path == path);
  • edit in inflorescence_view/src/app.rs at line 299
    [13.22072]
    [22.3826]
    let hash = *hash;
    el(iced_nav_scrollable::view(
    nav,
    files,
    files_len,
    move |msg| Msg::LogNav { hash, msg },
    )
    .width(Length::Fill)
    .height(Length::Fill))
    }
    None => el(text("Loading...")),
    };
  • replacement in inflorescence_view/src/app.rs at line 314
    [22.3918][42.210:278]()
    el(text(message).shaping(text::Shaping::Advanced)),
    [22.3918]
    [22.3953]
    el(text(*message).shaping(text::Shaping::Advanced)),
  • replacement in inflorescence_view/src/app.rs at line 316
    [22.4017][24.530:597]()
    el(scrollable(column(files).width(Length::Fill))),
    [22.4017]
    [13.23465]
    view,
  • replacement in inflorescence_view/src/app.rs at line 329
    [24.834][24.834:908]()
    state.window_size.width.floor() as usize / DEFAULT_MIN_COL_WIDTH,
    [24.834]
    [24.908]
    window_size.width.floor() as usize / DEFAULT_MIN_COL_WIDTH,
  • replacement in inflorescence_view/src/app.rs at line 332
    [24.916][24.916:996]()
    let depth = if let Some(RecordMsg::Typing(_)) = state.record_msg.as_ref() {
    [24.916]
    [24.996]
    let depth = if let Some(RecordMsg::Typing(_)) = record_msg.as_ref() {
  • replacement in inflorescence_view/src/app.rs at line 334
    [24.1006][24.1006:1076]()
    } else if let Some(selection) = state.cursor.selection.as_ref() {
    [24.1006]
    [24.1076]
    } else if let Some(selection) = cursor.selection.as_ref() {
  • replacement in inflorescence_view/src/app.rs at line 347
    [24.1471][24.1471:1507]()
    // state.window_size.width,
    [24.1471]
    [24.1507]
    // window_size.width,
  • replacement in inflorescence_view/src/app.rs at line 355
    [43.1817][43.1817:1851]()
    .chain(untracked_files())
    [43.1817]
    [43.1851]
    .chain(view_untracked_files())
  • replacement in inflorescence_view/src/app.rs at line 357
    [43.1896][43.1896:1928]()
    .chain(changed_files())
    [43.1896]
    [43.1928]
    .chain(view_changed_files())
  • replacement in inflorescence_view/src/app.rs at line 359
    [43.1974][43.1974:1997]()
    .chain(log());
    [43.1974]
    [43.1997]
    .chain(view_log());
  • replacement in inflorescence_view/src/app.rs at line 361
    [43.1998][43.1998:2064]()
    let status_nav_children_len = repo::nav_contents_count(repo);
    [43.1998]
    [43.2064]
    let status_nav_children_len = repo::nav_contents_count(&repo.state);
  • edit in inflorescence_view/src/app.rs at line 363
    [43.2065]
    [24.1588]
    let status_selected = match selection.as_ref() {
    Some(Selection::UntrackedFile { diff, .. }) => !diff
    .as_ref()
    .map(|diff| diff.is_selected)
    .unwrap_or_default(),
    Some(Selection::ChangedFile { diff, .. }) => !diff
    .as_ref()
    .map(|diff| diff.is_selected)
    .unwrap_or_default(),
    Some(Selection::Log { file, .. }) => file.is_none(),
    None => true,
    };
  • replacement in inflorescence_view/src/app.rs at line 376
    [24.1616][24.1616:1635]()
    repo_info,
    [24.1616]
    [43.2066]
    view_repo_info,
  • replacement in inflorescence_view/src/app.rs at line 382
    [43.2226][43.2226:2238]()
    )),
    [43.2226]
    [24.1831]
    )
    .class(if status_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    })),
  • replacement in inflorescence_view/src/app.rs at line 410
    [24.2381][31.1034:1134]()
    let col_2 = match state.cursor.selection.as_ref() {
    Some(cursor::Selection::LogChange {
    [24.2381]
    [31.1134]
    let col_2 = match selection.as_ref() {
    Some(Selection::Log {
  • replacement in inflorescence_view/src/app.rs at line 415
    [31.1195][35.7077:7257]()
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected,
    }),
    [31.1195]
    [31.1267]
    file: Some(LogChangeFileSelection { ix: _, path, diff }),
    nav: _,
  • replacement in inflorescence_view/src/app.rs at line 423
    [31.1474][31.1474:1563](),[31.1563][35.7258:7337](),[35.7337][31.1626:1685](),[31.1626][31.1626:1685](),[30.1400][24.2887:3024](),[31.1685][24.2887:3024](),[24.2887][24.2887:3024](),[24.3024][31.1686:1743]()
    match state.selected_diff {
    Some((file, state)) => {
    diff::view(state, file, *diff_selected).map(|action| {
    Msg::LogChangeFileDiffAction {
    hash: *hash,
    file: path.clone(),
    action,
    }
    })
    [31.1474]
    [31.1743]
    match diff {
    Some(Diff {
    is_selected,
    file,
    state,
    nav,
    }) => {
    let id_hash = file::log_id_parts_hash(*hash, path);
    diff::view(state, nav, file, *is_selected).map(
    move |action| Msg::LogChangeFileDiff {
    id_hash,
    msg: action,
    },
    )
  • replacement in inflorescence_view/src/app.rs at line 462
    [13.24110][31.2355:2515]()
    Some(cursor::Selection::UntrackedFile { .. })
    | Some(cursor::Selection::ChangedFile { .. })
    | Some(cursor::Selection::LogChange { .. })
    [13.24110]
    [31.2515]
    Some(Selection::UntrackedFile { .. })
    | Some(Selection::ChangedFile { .. })
    | Some(Selection::Log { .. })
  • replacement in inflorescence_view/src/app.rs at line 492
    [13.25114][13.25114:25168]()
    fn display_short_hash(hash: &pijul::Hash) -> String {
    [13.25114]
    [13.25168]
    fn display_short_hash(hash: &repo::ChangeHash) -> String {
  • edit in inflorescence_view/src/app/test.rs at line 6
    [14.4320][30.2321:2357]()
    use libflorescence::prelude::pijul;
  • replacement in inflorescence_view/src/app/test.rs at line 40
    [14.5104][30.2358:2387]()
    selected_diff: None,
    [14.5104]
    [14.5139]
    selection: None,
  • replacement in inflorescence_view/src/app/test.rs at line 68
    [15.254][30.2421:2450]()
    selected_diff: None,
    [15.254]
    [15.289]
    selection: None,
  • replacement in inflorescence_view/src/app/test.rs at line 91
    [15.1422][15.1422:1459]()
    hash: pijul::Hash::None,
    [15.1422]
    [15.1459]
    hash: repo::ChangeHash::None,
  • replacement in inflorescence_view/src/app/test.rs at line 109
    [16.8425][30.2484:2513]()
    selected_diff: None,
    [16.8425]
    [16.8460]
    selection: None,
  • replacement in inflorescence_view/src/app/test.rs at line 123
    [19.1433][30.2547:2576]()
    selected_diff: None,
    [19.1433]
    [19.1468]
    selection: None,
  • replacement in inflorescence_view/src/app/test.rs at line 154
    [14.5787][30.2610:2639]()
    selected_diff: None,
    [14.5787]
    [14.5822]
    selection: None,
  • replacement in inflorescence/src/test.rs at line 721
    [21.1996][35.8624:8653]()
    assert!(task.is_none());
    [21.1996]
    [21.2025]
    assert!(task.is_some());
  • edit in inflorescence/src/main.rs at line 139
    [12.260][35.8996:9041](),[35.9041][28.2531:2562](),[28.2531][28.2531:2562]()
    DiffNav {
    id_hash: file::IdHash,
    msg: diff::Msg,
    },
  • replacement in inflorescence/src/main.rs at line 179
    [32.76][35.9094:9123]()
    id_hash: id,
    [32.76]
    [32.96]
    id_hash,
  • edit in inflorescence/src/main.rs at line 184
    [32.202][38.37:112]()
    let diff_state = state.files_diffs.entry(id).or_default();
  • replacement in inflorescence/src/main.rs at line 188
    [32.360][28.3015:3059](),[28.3015][28.3015:3059](),[28.3059][35.9124:9216](),[35.9216][45.361:411](),[45.411][28.3264:3333](),[28.3264][28.3264:3333](),[28.3333][35.9217:9264](),[35.9264][30.3532:3603](),[28.3376][30.3532:3603](),[30.3603][28.3447:3505](),[28.3447][28.3447:3505](),[28.3505][38.113:148](),[38.148][35.9265:9323](),[28.3548][35.9265:9323](),[35.9323][38.149:188](),[38.188][28.3653:3678](),[35.9370][28.3653:3678](),[28.3653][28.3653:3678]()
    diff_state.nav = Some(nav);
    return tasks.map(move |msg| Msg::DiffNav {
    id_hash: id,
    msg: diff::Msg::DiffNav(msg),
    });
    }
    Task::none()
    }
    Msg::DiffNav { id_hash: id, msg } => {
    if let Some(diff_state) = state.files_diffs.get_mut(&id) {
    let task = diff::update(diff_state, msg);
    let id_clone = id;
    return task.map(move |msg| Msg::DiffNav {
    id_hash: id_clone,
    msg,
    [32.360]
    [28.3678]
    state.files_diffs.diffs_nav = Some((id_hash, nav));
    return tasks.map(move |msg| {
    Msg::View(app::Msg::FileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
  • replacement in inflorescence/src/main.rs at line 198
    [28.3747][35.9371:9532]()
    Msg::LogDiffNav { id_hash: id, msg } => {
    if let Some(diff::FileAndState { file: _, state }) =
    state.logs.diffs.get_mut(&id)
    [28.3747]
    [28.4073]
    Msg::LogDiffNav { id_hash, msg } => {
    if let Some(diff::FileAndState {
    file: _,
    state: diff_state,
    }) = state.logs.diffs.get_mut(&id_hash)
  • replacement in inflorescence/src/main.rs at line 204
    [28.4087][35.9533:9586](),[35.9586][38.189:292]()
    let task = diff::update(state, msg);
    return task
    .map(move |msg| Msg::LogDiffNav { id_hash: id, msg });
    [28.4087]
    [28.4531]
    let nav = state.logs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    );
    let task = diff::update(diff_state, nav, msg);
    return task.map(move |msg| Msg::LogDiffNav { id_hash, msg });
  • replacement in inflorescence/src/main.rs at line 256
    [13.26390][34.83:164](),[34.164][35.9734:9755](),[35.9755][34.164:233](),[34.164][34.164:233](),[34.233][36.727:800](),[36.800][44.37:74](),[44.74][34.291:364](),[43.4633][34.291:364](),[35.9821][34.291:364](),[34.291][34.291:364](),[34.364][35.9822:9917](),[35.9917][34.2256:2341](),[34.2256][34.2256:2341](),[34.2341][35.9918:11029](),[35.11029][45.412:474](),[45.474][35.11097:11197](),[35.11097][35.11097:11197](),[35.11197][34.3150:3172](),[34.3150][34.3150:3172](),[34.3172][35.11198:11788](),[35.11788][34.3172:3190](),[34.3172][34.3172:3190](),[34.3190][35.11789:11839](),[35.11839][34.3421:3437](),[34.3421][34.3421:3437](),[34.3437][35.11840:11914](),[35.11914][34.3489:3499](),[34.3489][34.3489:3499]()
    app::Msg::Cursor(msg) => {
    let cursor_task = cursor::update(
    msg,
    &mut state.cursor,
    &mut state.files,
    &mut state.files_diffs,
    &mut state.logs,
    state.repo.as_mut(),
    )
    .map(|msg| Msg::View(app::Msg::ToRepo(msg)));
    let init_log_nav_task =
    if let Some(cursor::Selection::LogChange {
    ix: _,
    hash,
    message: _,
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected: false,
    }),
    }) = state.cursor.selection.as_ref()
    {
    let id_hash = file::log_id_parts_hash(*hash, path);
    match state.logs.diffs.get_mut(&id_hash) {
    Some(log) if log.state.nav.is_none() => {
    let contents_count =
    diff::contents_count(&log.file);
    let unchanged_sections =
    diff::unchanged_sections(&log.file);
    let (nav, tasks) = iced_nav_scrollable::init(
    contents_count,
    unchanged_sections,
    );
    log.state.nav = Some(nav);
    tasks.map(move |msg| Msg::LogDiffNav {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
    }
    _ => Task::none(),
    }
    } else {
    Task::none()
    };
    let get_diffs_task = if let Some(cursor::Selection::LogChange {
    ix: _,
    hash,
    message: _,
    file: None,
    }) = state.cursor.selection.as_ref()
    {
    if !state.logs.change_hashes.contains(hash) {
    Task::done(Msg::View(app::Msg::ToRepo(
    repo::MsgIn::GetChangeDiffs { hash: *hash },
    )))
    } else {
    Task::none()
    }
    } else {
    Task::none()
    };
    Task::batch([cursor_task, get_diffs_task, init_log_nav_task])
    }
    [13.26390]
    [13.26614]
    app::Msg::Cursor(msg) => cursor::update(
    msg,
    &mut state.cursor,
    &mut state.files,
    &mut state.files_diffs,
    &mut state.logs,
    state.repo.as_mut(),
    ),
  • edit in inflorescence/src/main.rs at line 274
    [43.4908]
    [43.4908]
    } else {
    Task::none()
    }
    }
    app::Msg::LogNav { hash, msg } => {
    if let Some((selected_hash, nav)) = state.logs.changes_nav.as_mut()
    && hash == *selected_hash
    {
    iced_nav_scrollable::update(nav, msg)
    .map(move |msg| Msg::View(app::Msg::LogNav { hash, msg }))
  • replacement in inflorescence/src/main.rs at line 292
    [20.582][35.11915:12045](),[20.702][3.772:782](),[4.1679][3.772:782](),[35.12045][3.772:782](),[3.772][3.772:782](),[3.782][20.703:850]()
    app::Msg::FileDiffsContentsAction { id_hash, action } => {
    file_diffs_contents_action(state, id_hash, action)
    }
    app::Msg::LogChangeFileDiffAction { hash, file, action } => {
    log_change_file_diff_action(state, hash, file, action)
    }
    [20.582]
    [20.850]
    app::Msg::FileDiff {
    id_hash,
    msg: action,
    } => file_diffs_contents_action(state, id_hash, action),
    app::Msg::LogChangeFileDiff {
    id_hash,
    msg: action,
    } => log_change_file_diff_action(state, id_hash, action),
  • edit in inflorescence/src/main.rs at line 349
    [43.5584]
    [43.5584]
    let status_nav_task =
    status_nav_task.map(|msg| Msg::View(app::Msg::StatusNav(msg)));
  • replacement in inflorescence/src/main.rs at line 354
    [41.659][43.5586:5662](),[43.5662][41.729:746](),[41.729][41.729:746]()
    state.cursor.selection = if repo_state.untracked_files.is_empty() {
    None
    [41.659]
    [41.746]
    if repo_state.untracked_files.is_empty() {
    state.cursor.selection = None
  • replacement in inflorescence/src/main.rs at line 358
    [43.5737][43.5737:5788]()
    Some(cursor::untracked_file_selection(
    [43.5737]
    [43.5788]
    let (selection, selection_task) = cursor::untracked_file_selection(
  • edit in inflorescence/src/main.rs at line 360
    [43.5816]
    [43.5816]
    status_nav,
  • replacement in inflorescence/src/main.rs at line 363
    [43.5870][43.5870:5885]()
    ))
    [43.5870]
    [41.910]
    &mut state.files_diffs,
    cursor::VertDir::Down,
    );
    state.cursor.selection = Some(selection);
    return Task::batch([status_nav_task, selection_task]);
  • replacement in inflorescence/src/main.rs at line 371
    [43.5887][43.5887:5966]()
    return status_nav_task.map(|msg| Msg::View(app::Msg::StatusNav(msg)));
    [43.5887]
    [20.2296]
    return status_nav_task;
  • edit in inflorescence/src/main.rs at line 413
    [43.6585]
    [43.6585]
    let status_nav_task =
    status_nav_task.map(|msg| Msg::View(app::Msg::StatusNav(msg)));
  • replacement in inflorescence/src/main.rs at line 418
    [41.1757][43.6587:6665](),[43.6665][41.1829:1850](),[41.1829][41.1829:1850]()
    state.cursor.selection = if repo_state.changed_files.is_empty() {
    None
    [41.1757]
    [41.1850]
    if repo_state.changed_files.is_empty() {
    state.cursor.selection = None
  • replacement in inflorescence/src/main.rs at line 422
    [43.6742][43.6742:6908]()
    Some(cursor::changed_file_selection(
    repo_state,
    ix,
    &mut state.files,
    ))
    [43.6742]
    [41.2022]
    let (selection, selection_task) =
    cursor::changed_file_selection(
    repo_state,
    status_nav,
    ix,
    &mut state.files,
    &mut state.files_diffs,
    cursor::VertDir::Down,
    );
    state.cursor.selection = Some(selection);
    return Task::batch([status_nav_task, selection_task]);
  • replacement in inflorescence/src/main.rs at line 436
    [43.6910][43.6910:7010]()
    return status_nav_task
    .map(|msg| Msg::View(app::Msg::StatusNav(msg)));
    [43.6910]
    [5.1813]
    return status_nav_task;
  • replacement in inflorescence/src/main.rs at line 528
    [20.6475][20.6475:6538](),[20.6538][35.12402:12951](),[7.1089][6.7355:7369](),[28.6825][6.7355:7369](),[20.6853][6.7355:7369](),[35.12951][6.7355:7369](),[6.7355][6.7355:7369](),[6.7369][35.12952:13483](),[28.7104][20.7123:7211](),[35.13483][20.7123:7211](),[20.7123][20.7123:7211](),[20.7211][6.7394:7404](),[6.7394][6.7394:7404](),[6.7404][20.7212:7235]()
    if let Some(selection) = state.cursor.selection.as_mut() {
    match selection {
    cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected: _,
    } => {
    let selection_hash =
    file::id_parts_hash(path, file::Kind::Untracked);
    if id_hash == selection_hash {
    let diffs = state.files_diffs.entry(id_hash).or_default();
    return diff::update(diffs, action)
    .map(move |msg| Msg::DiffNav { id_hash, msg });
    }
    }
    cursor::Selection::ChangedFile {
    ix: _,
    path,
    diff_selected: _,
    } => {
    let selection_hash =
    file::id_parts_hash(path, file::Kind::Changed);
    if id_hash == selection_hash {
    let diffs = state.files_diffs.entry(id_hash).or_default();
    let task = diff::update(diffs, action);
    return task.map(move |msg| Msg::DiffNav { id_hash, msg });
    }
    }
    _ => {
    // Selection has changed
    }
    }
    }
    Task::none()
    [20.6475]
    [20.7235]
    let diffs = state.files_diffs.diffs.entry(id_hash).or_default();
    let nav =
    state
    .files_diffs
    .diffs_nav
    .as_mut()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    });
    diff::update(diffs, nav, action)
    .map(move |msg| Msg::View(app::Msg::FileDiff { id_hash, msg }))
  • replacement in inflorescence/src/main.rs at line 543
    [20.7293][20.7293:7334]()
    hash: pijul::Hash,
    file: String,
    [20.7293]
    [20.7334]
    id_hash: file::LogIdHash,
  • edit in inflorescence/src/main.rs at line 557
    [20.7672][41.2038:2106]()
    && *selected_hash == hash
    && *selected_path == file
  • replacement in inflorescence/src/main.rs at line 558
    [20.7678][41.2107:2275]()
    let id_hash = file::log_id_parts_hash(hash, &file);
    if let Some(diff::FileAndState { file: _, state }) =
    state.logs.diffs.get_mut(&id_hash)
    [20.7678]
    [41.2275]
    let selected_id_hash =
    file::log_id_parts_hash(*selected_hash, selected_path);
    if selected_id_hash == id_hash
    && let Some(diff::FileAndState {
    file: _,
    state: diff_state,
    }) = state.logs.diffs.get_mut(&id_hash)
  • replacement in inflorescence/src/main.rs at line 566
    [41.2285][41.2285:2332]()
    return diff::update(state, action)
    [41.2285]
    [41.2332]
    let nav =
    state
    .logs
    .diffs_nav
    .as_mut()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    });
    return diff::update(diff_state, nav, action)
  • replacement in inflorescence/src/main.rs at line 798
    [20.12819][20.12819:12842]()
    hash: pijul::Hash,
    [20.12819]
    [20.12842]
    hash: repo::ChangeHash,
  • replacement in inflorescence/src/main.rs at line 801
    [20.12890][20.12890:12937]()
    if let Some(cursor::Selection::LogChange {
    [20.12890]
    [20.12937]
    let changed_files_count = diffs.len();
    let task = if let Some(cursor::Selection::LogChange {
  • replacement in inflorescence/src/main.rs at line 807
    [20.13040][20.13040:13057]()
    file: _,
    [20.13001]
    [20.13057]
    file,
  • replacement in inflorescence/src/main.rs at line 811
    [20.13104][41.2896:3536]()
    diffs.into_iter().for_each(|(path, diffs)| {
    // NOTE: using unknown encoding as we don't yet have the file
    // for past changes
    let file = diff::init_file(
    diff::FileContent::UnknownEncoding,
    Some(&diffs),
    );
    let id_hash = file::log_id_parts_hash(hash, &path);
    let log_file_diff = diff::FileAndState {
    file,
    // The nav is initialized only once a file is selected,
    // because its tasks need it to be visible to complete
    state: diff::State::default(),
    };
    [20.13104]
    [30.5852]
    if let Some(file) = file.as_ref() {
    // If a file is selected, init the nav for its diff
    let diff = diffs.get(&file.path).unwrap();
    let diff_contents_count = diff.len();
    let (diffs_nav, nav_task) =
    iced_nav_scrollable::init(diff_contents_count, HashSet::new());
    let id_hash = file::log_id_parts_hash(hash, &file.path);
    state.logs.diffs_nav = Some((id_hash, diffs_nav));
    nav_task.map(move |msg| {
    Msg::View(app::Msg::LogChangeFileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
    })
    } else {
    // Init scrollable nav for log changes
    let (changes_nav, nav_task) =
    iced_nav_scrollable::init(changed_files_count, HashSet::new());
    state.logs.changes_nav = Some((hash, changes_nav));
    nav_task.map(move |msg| Msg::View(app::Msg::LogNav { hash, msg }))
    }
    } else {
    Task::none()
    };
    // Store the changes
    state
    .logs
    .change_file_counts
    .insert(hash, changed_files_count);
    diffs.into_iter().for_each(|(path, diffs)| {
    // NOTE: using unknown encoding as we don't yet have the file
    // for past changes
    let file =
    diff::init_file(diff::FileContent::UnknownEncoding, Some(&diffs));
    let id_hash = file::log_id_parts_hash(hash, &path);
    let log_file_diff = diff::FileAndState {
    file,
    // The nav is initialized only once a file is selected,
    // because its tasks need it to be visible to complete
    state: diff::State::default(),
    };
    state.logs.diffs.insert(id_hash, log_file_diff);
    });
  • replacement in inflorescence/src/main.rs at line 861
    [30.5853][41.3537:3661](),[41.3661][8.2921:2927](),[8.2921][8.2921:2927](),[8.2927][20.13703:13720]()
    state.logs.change_hashes.insert(hash);
    state.logs.diffs.insert(id_hash, log_file_diff);
    });
    }
    Task::none()
    [30.5853]
    [8.4296]
    task
  • edit in inflorescence/src/main.rs at line 1018
    [43.8110]
    [43.8110]
    let log_nav_sub =
    if let Some((hash, _nav)) = state.logs.changes_nav.as_ref() {
    let hash = *hash;
    iced_nav_scrollable::subs()
    .with(hash)
    .map(|(hash, msg)| Msg::View(app::Msg::LogNav { hash, msg }))
    } else {
    Subscription::none()
    };
  • replacement in inflorescence/src/main.rs at line 1038
    [35.16086][35.16086:16168](),[35.16168][45.475:525]()
    .map(|(id_hash, msg)| Msg::DiffNav {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    [35.16086]
    [35.16224]
    .map(|(id_hash, msg)| {
    Msg::View(app::Msg::FileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
  • replacement in inflorescence/src/main.rs at line 1053
    [35.16530][35.16530:16612](),[35.16612][45.526:576]()
    .map(|(id_hash, msg)| Msg::DiffNav {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    [35.16530]
    [35.16668]
    .map(|(id_hash, msg)| {
    Msg::View(app::Msg::FileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
  • edit in inflorescence/src/main.rs at line 1090
    [43.8200]
    [43.8200]
    log_nav_sub,
  • replacement in inflorescence/src/main.rs at line 1107
    [35.17598][35.17598:17623]()
    logs: log_diffs,
    [35.17598]
    [13.28717]
    logs,
  • replacement in inflorescence/src/main.rs at line 1110
    [30.6244][30.6244:6308]()
    let selected_diff = match state.cursor.selection.as_ref() {
    [30.6244]
    [35.17624]
    let selection = match state.cursor.selection.as_ref() {
  • replacement in inflorescence/src/main.rs at line 1112
    [35.17672][35.17672:17691]()
    ix: _,
    [35.17672]
    [35.17691]
    ix,
  • replacement in inflorescence/src/main.rs at line 1114
    [35.17709][35.17709:17739]()
    diff_selected: _,
    [35.17709]
    [35.17739]
    diff_selected,
  • replacement in inflorescence/src/main.rs at line 1117
    [35.17831][35.17831:17894](),[35.17894][31.3223:3248](),[31.3223][31.3223:3248]()
    let file = files.diffs_cache.inner.peek(&id_hash);
    match file {
    [35.17831]
    [31.3248]
    let diff = files.diffs_cache.inner.peek(&id_hash);
    let diff = match diff {
  • replacement in inflorescence/src/main.rs at line 1120
    [31.3300][35.17895:17968]()
    files_diffs.get(&id_hash).map(|state| (file, state))
    [31.3300]
    [31.3368]
    let nav = files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
    .unwrap();
    files_diffs.diffs.get(&id_hash).map(|state| app::Diff {
    is_selected: *diff_selected,
    file,
    state,
    nav,
    })
  • replacement in inflorescence/src/main.rs at line 1135
    [31.3444][31.3444:3458]()
    }
    [31.3444]
    [30.6542]
    };
    Some(app::Selection::UntrackedFile {
    ix: *ix,
    path,
    diff,
    })
  • replacement in inflorescence/src/main.rs at line 1144
    [35.18033][35.18033:18082]()
    ix: _,
    diff_selected: _,
    [35.18033]
    [35.18082]
    ix,
    diff_selected,
  • replacement in inflorescence/src/main.rs at line 1148
    [35.18172][35.18172:18235](),[35.18235][31.3517:3542](),[31.3517][31.3517:3542]()
    let file = files.diffs_cache.inner.peek(&id_hash);
    match file {
    [35.18172]
    [31.3542]
    let diff = files.diffs_cache.inner.peek(&id_hash);
    let diff = match diff {
  • replacement in inflorescence/src/main.rs at line 1151
    [31.3594][35.18236:18309]()
    files_diffs.get(&id_hash).map(|state| (file, state))
    [31.3594]
    [31.3662]
    let nav = files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
    .unwrap();
    files_diffs.diffs.get(&id_hash).map(|state| app::Diff {
    is_selected: *diff_selected,
    file,
    state,
    nav,
    })
  • replacement in inflorescence/src/main.rs at line 1166
    [31.3738][31.3738:3752]()
    }
    [31.3738]
    [30.6782]
    };
    Some(app::Selection::ChangedFile {
    ix: *ix,
    path,
    diff,
    })
  • replacement in inflorescence/src/main.rs at line 1174
    [30.6978][30.6978:6997]()
    ix: _,
    [30.6978]
    [30.6997]
    ix: log_ix,
  • replacement in inflorescence/src/main.rs at line 1176
    [30.7015][30.7015:7039](),[30.7039][35.18310:18493]()
    message: _,
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected: _,
    }),
    [30.7015]
    [30.7111]
    message,
    file,
  • replacement in inflorescence/src/main.rs at line 1179
    [30.7127][35.18494:18558](),[35.18558][30.7240:7262](),[30.7240][30.7240:7262](),[30.7262][35.18559:18686]()
    let id_hash = file::log_id_parts_hash(*hash, path);
    log_diffs
    .diffs
    .get(&id_hash)
    .map(|diff::FileAndState { file, state }| (file, state))
    [30.7127]
    [30.7354]
    let file = file.as_ref().map(
    |cursor::LogChangeFileSelection {
    ix: change_ix,
    path,
    diff_selected,
    }| {
    let id_hash = file::log_id_parts_hash(*hash, path);
    let diff = logs.diffs.get(&id_hash).map(
    |diff::FileAndState { file, state }| {
    let nav = logs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
    .unwrap();
    app::Diff {
    is_selected: *diff_selected,
    file,
    state,
    nav,
    }
    },
    );
    app::LogChangeFileSelection {
    ix: *change_ix,
    path,
    diff,
    }
    },
    );
    let nav = logs
    .changes_nav
    .as_ref()
    .and_then(|(nav_hash, nav)| (nav_hash == hash).then_some(nav));
    Some(app::Selection::Log {
    ix: *log_ix,
    hash: *hash,
    message,
    file,
    nav,
    })
  • replacement in inflorescence/src/main.rs at line 1222
    [30.7364][31.3866:3944]()
    Some(cursor::Selection::LogChange { file: None, .. }) | None => None,
    [30.7364]
    [30.7383]
    None => None,
  • replacement in inflorescence/src/main.rs at line 1225
    [30.7391][13.28732:28768](),[13.28732][13.28732:28768](),[13.28768][24.5792:5831](),[24.5831][13.28768:28889](),[13.28768][13.28768:28889](),[13.28889][30.7392:7419](),[30.7450][13.28914:28944](),[13.28914][13.28914:28944](),[13.28991][13.28991:29017]()
    app::view(
    app::State {
    window_size: *window_size,
    repo_path,
    repo: repo.as_ref(),
    cursor,
    record_msg: record_msg.as_ref(),
    selected_diff,
    },
    window_id,
    )
    .map(Msg::View)
    [30.7391]
    [10.20154]
    let state = app::State {
    window_size: *window_size,
    repo_path,
    repo: repo.as_ref(),
    cursor,
    record_msg: record_msg.as_ref(),
    selection,
    };
    app::view(state, window_id).map(Msg::View)
  • edit in inflorescence/src/file.rs at line 195
    [12.6409]
    [12.6409]
    pub fn try_get_src_file(state: &State, id_hash: IdHash) -> Option<&diff::File> {
    state
    .diffs_cache
    .inner
    .peek(&id_hash)
    .and_then(|diff| match diff {
    Diff::Loading => None,
    Diff::Loaded(file) => Some(file),
    })
    }
  • edit in inflorescence/src/diff.rs at line 13
    [35.19692]
    [28.8470]
    use iced_nav_scrollable::NavScrollable;
  • replacement in inflorescence/src/diff.rs at line 19
    [13.29153][32.1342:1373]()
    use std::collections::HashSet;
    [13.29153]
    [35.19693]
    use std::collections::{HashMap, HashSet};
  • replacement in inflorescence/src/diff.rs at line 21
    [35.19694][35.19694:19736]()
    pub type FilesState = file::IdMap<State>;
    [35.19694]
    [35.19736]
    #[derive(Debug, Default)]
    pub struct FilesState {
    pub diffs: file::IdMap<State>,
    /// Diffs scrollable nav.
    ///
    /// Only the current nav is kept because the sizes of contents have to be
    /// queried everytime the selection changes due to possible container size
    /// changes.
    pub diffs_nav: Option<(file::IdHash, NavScrollable)>,
    }
  • replacement in inflorescence/src/diff.rs at line 34
    [35.19793][35.19793:19898]()
    /// All the hashes in this set have `diffs` loaded
    pub change_hashes: HashSet<repo::ChangeHash>,
    [35.19793]
    [35.19898]
    /// All the hashes in this map have `diffs` loaded. The value is the number
    /// of the changed files in the change (contents count used for
    /// scrollable-nav).
    pub change_file_counts: HashMap<repo::ChangeHash, usize>,
  • edit in inflorescence/src/diff.rs at line 41
    [35.20033]
    [35.20033]
    /// Log changes scrollable nav.
    ///
    /// Only the current nav is kept because the sizes of contents have to be
    /// queried everytime the selection changes due to possible container size
    /// changes.
    pub changes_nav: Option<(repo::ChangeHash, NavScrollable)>,
    /// Log diffs scrollable nav.
    ///
    /// Only the current nav is kept because the sizes of contents have to be
    /// queried everytime the selection changes due to possible container size
    /// changes.
    pub diffs_nav: Option<(file::LogIdHash, NavScrollable)>,
  • replacement in inflorescence/src/diff.rs at line 61
    [35.20124][28.8493:8551](),[9.288][28.8493:8551]()
    pub fn update(state: &mut State, msg: Msg) -> Task<Msg> {
    [35.20124]
    [28.8551]
    pub fn update(
    _state: &mut State,
    nav: Option<&mut NavScrollable>,
    msg: Msg,
    ) -> Task<Msg> {
  • replacement in inflorescence/src/diff.rs at line 68
    [45.661][28.8604:8656](),[28.8604][28.8604:8656]()
    if let Some(nav) = state.nav.as_mut() {
    [45.661]
    [45.662]
    if let Some(nav) = nav {
  • replacement in inflorescence/src/diff.rs at line 235
    [35.20192][35.20192:20214]()
    id: file::IdHash,
    [35.20192]
    [35.20214]
    id_hash: file::IdHash,
  • replacement in inflorescence/src/diff.rs at line 239
    [35.20260][35.20260:20330]()
    .get(&id)
    .and_then(|diff| diff.nav.as_ref())
    [35.20260]
    [35.20330]
    .diffs_nav
    .as_ref()
    .and_then(
    |(nav_id_hash, nav)| (*nav_id_hash == id_hash).then_some(nav)
    )
  • replacement in inflorescence/src/diff.rs at line 254
    [35.20552][35.20552:20652]()
    logs.diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    [35.20552]
    [35.20652]
    logs.diffs_nav
    .as_ref()
    .and_then(
    |(nav_id_hash, nav)| (*nav_id_hash == id_hash).then_some(nav)
    )
  • edit in inflorescence/src/cursor.rs at line 1
    [2.26]
    [44.78]
    use iced_nav_scrollable::NavScrollable;
  • edit in inflorescence/src/cursor.rs at line 15
    [36.4268]
    [12.8577]
    use std::collections::HashSet;
  • replacement in inflorescence/src/cursor.rs at line 17
    [12.8578][35.20755:20773]()
    pub fn update<M>(
    [12.8578]
    [35.20773]
    pub fn update(
  • replacement in inflorescence/src/cursor.rs at line 22
    [36.4309][36.4309:4353]()
    log_diffs: &mut diff::LogFilesAndState,
    [36.4309]
    [44.108]
    logs: &mut diff::LogFilesAndState,
  • replacement in inflorescence/src/cursor.rs at line 24
    [44.142][35.20864:20879](),[35.20864][35.20864:20879]()
    ) -> Task<M> {
    [44.142]
    [11.2479]
    ) -> Task<crate::Msg> {
  • replacement in inflorescence/src/cursor.rs at line 51
    [36.5148][36.5148:5380]()
    Dir::Down => select_down(
    state,
    files,
    repo,
    files_diffs,
    log_diffs,
    delta,
    ),
    [36.5148]
    [36.5380]
    Dir::Down => {
    select_down(state, files, files_diffs, logs, repo, delta)
    }
  • replacement in inflorescence/src/cursor.rs at line 55
    [36.5409][36.5409:5490]()
    select_up(state, files, repo, files_diffs, log_diffs, delta)
    [36.5409]
    [36.5490]
    select_up(state, files, files_diffs, logs, repo, delta)
  • replacement in inflorescence/src/cursor.rs at line 59
    [44.241][44.241:322]()
    select_right(state, repo.as_deref(), files_diffs, log_diffs)
    [44.241]
    [44.322]
    select_right(state, files_diffs, logs, repo.as_deref())
  • replacement in inflorescence/src/cursor.rs at line 63
    [36.5668][36.5668:5745]()
    Msg::ReleaseDir(dir) => release(dir, state, files_diffs, log_diffs),
    [36.5668]
    [37.2886]
    Msg::ReleaseDir(dir) => release(dir, state, files_diffs, logs),
  • replacement in inflorescence/src/cursor.rs at line 89
    [37.3802][37.3802:4005]()
    Dir::Down => {
    alt_select_down(state, files_diffs, log_diffs, delta)
    }
    Dir::Up => alt_select_up(state, files_diffs, log_diffs, delta),
    [37.3802]
    [37.4005]
    Dir::Down => alt_select_down(state, files_diffs, logs, delta),
    Dir::Up => alt_select_up(state, files_diffs, logs, delta),
  • replacement in inflorescence/src/cursor.rs at line 103
    [44.498][44.498:521]()
    log_diffs,
    [44.498]
    [44.521]
    logs,
  • edit in inflorescence/src/cursor.rs at line 105
    [44.532][35.21272:21708](),[35.21272][35.21272:21708]()
    }
    }
    pub fn untracked_file_selection(
    repo: &repo::State,
    ix: usize,
    files: &mut file::State,
    ) -> Selection {
    let path = repo.untracked_files.iter().nth(ix).unwrap().clone();
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    };
    file::load_src_file_if_not_cached(files, id);
    Selection::UntrackedFile {
    ix,
    path,
    diff_selected: false,
  • edit in inflorescence/src/cursor.rs at line 107
    [35.21716][35.21716:22121]()
    pub fn changed_file_selection(
    repo: &repo::State,
    ix: usize,
    files: &mut file::State,
    ) -> Selection {
    let (path, diffs) = repo.changed_files.iter().nth(ix).unwrap();
    if diff::any_diff_has_contents(diffs) {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    file::load_src_file_if_not_cached(files, id);
    }
  • replacement in inflorescence/src/cursor.rs at line 108
    [35.22122][35.22122:22772]()
    Selection::ChangedFile {
    ix,
    path: path.clone(),
    diff_selected: false,
    }
    }
    pub fn log_selection(repo: &repo::State, ix: usize) -> Selection {
    let entry = repo.log.get(ix).unwrap();
    Selection::LogChange {
    ix,
    hash: entry.hash,
    message: entry.message.clone(),
    file: None,
    }
    }
    pub fn log_file_selection(
    log_entry: &repo::LogEntry,
    file_ix: usize,
    ) -> LogChangeFileSelection {
    let path = log_entry.file_paths.get(file_ix).unwrap().clone();
    LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    }
    }
    fn select_down<M>(
    [35.22122]
    [35.22772]
    fn select_down(
  • edit in inflorescence/src/cursor.rs at line 111
    [35.22824][44.533:567]()
    repo: Option<&mut app::Repo>,
  • edit in inflorescence/src/cursor.rs at line 113
    [36.5825]
    [36.5825]
    repo: Option<&mut app::Repo>,
  • replacement in inflorescence/src/cursor.rs at line 115
    [36.5854][35.22927:22942](),[35.22927][35.22927:22942]()
    ) -> Task<M> {
    [36.5854]
    [44.568]
    ) -> Task<crate::Msg> {
  • replacement in inflorescence/src/cursor.rs at line 137
    [40.615][40.615:793]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [40.615]
    [40.793]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 151
    [40.1100][44.854:1235]()
    let selection =
    changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [40.1100]
    [44.1235]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 154
    [44.1271][44.1271:1332]()
    );
    (selection, task)
    [44.1271]
    [44.1332]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 161
    [40.1304][44.1388:1658]()
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [40.1304]
    [44.1658]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 163
    [44.1694][44.1694:1775]()
    );
    (log_selection(repo_state, ix), task)
    [44.1694]
    [35.24638]
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 170
    [40.1397][44.1776:2159]()
    let selection =
    untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [40.1397]
    [44.2159]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 173
    [44.2195][44.2195:2256]()
    );
    (selection, task)
    [44.2195]
    [40.1398]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 181
    [40.1470][44.2257:2590]()
    let selection = untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [40.1470]
    [44.2590]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 184
    [44.2622][44.2622:2675]()
    );
    (selection, task)
    [44.2622]
    [40.1587]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 205
    [40.2080][40.2080:2258]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [40.2080]
    [40.2258]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 219
    [40.2553][44.2850:3120]()
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [40.2553]
    [44.3120]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 221
    [44.3156][44.3156:3237]()
    );
    (log_selection(repo_state, ix), task)
    [44.3156]
    [44.3237]
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 228
    [40.2706][44.3305:3688]()
    let selection =
    untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [40.2706]
    [44.3688]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 231
    [44.3724][44.3724:3785]()
    );
    (selection, task)
    [44.3724]
    [35.26467]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 238
    [40.2864][44.3786:4165]()
    let selection =
    changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [40.2864]
    [44.4165]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 241
    [44.4201][44.4201:4262]()
    );
    (selection, task)
    [44.4201]
    [40.2865]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 249
    [40.2937][44.4263:4592]()
    let selection = changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [40.2937]
    [44.4592]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 252
    [44.4624][44.4624:4677]()
    );
    (selection, task)
    [44.4624]
    [40.3052]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 274
    [40.3574][40.3574:4513]()
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    },
    if let Some(nav) = logs
    .diffs
    .get_mut(&id_hash)
    .and_then(|diff| diff.state.nav.as_mut())
    {
    iced_nav_scrollable::scroll_down(nav, delta)
    } else {
    Task::none()
    },
    )
    [40.3574]
    [40.4513]
    let selection = Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    };
    let task = if let Some(nav) = logs
    .diffs_nav
    .as_mut()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    }) {
    iced_nav_scrollable::scroll_down(nav, delta)
    } else {
    Task::none()
    };
    (selection, task)
  • edit in inflorescence/src/cursor.rs at line 306
    [40.4947]
    [11.7558]
    let (file, selection_task) =
    log_file_selection(logs, log_entry, hash, file_ix);
  • replacement in inflorescence/src/cursor.rs at line 310
    [11.7559][40.4948:5023]()
    let file = log_file_selection(log_entry, file_ix);
    [11.7559]
    [11.7678]
    let scroll_task = if let Some((nav_changes_hash, nav)) =
    logs.changes_nav.as_ref()
    && *nav_changes_hash == hash
    {
    iced_nav_scrollable::scroll_down_to_section(
    file_ix, nav,
    )
    } else {
    Task::none()
    };
  • replacement in inflorescence/src/cursor.rs at line 321
    [11.7679][40.5024:5373]()
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    [11.7679]
    [35.28950]
    let selection = Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    };
    (selection, Task::batch([selection_task, scroll_task]))
  • replacement in inflorescence/src/cursor.rs at line 335
    [44.4983][44.4983:5589]()
    let task =
    iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    status_nav,
    );
    (
    untracked_file_selection(repo_state, ix, files),
    task,
    [44.4983]
    [44.5589]
    untracked_file_selection(
    repo_state,
    status_nav,
    ix,
    files,
    files_diffs,
    VertDir::Up,
  • replacement in inflorescence/src/cursor.rs at line 345
    [44.5732][44.5732:6334]()
    let task =
    iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    status_nav,
    );
    (
    changed_file_selection(repo_state, ix, files),
    task,
    [44.5732]
    [44.6334]
    changed_file_selection(
    repo_state,
    status_nav,
    ix,
    files,
    files_diffs,
    VertDir::Up,
  • replacement in inflorescence/src/cursor.rs at line 355
    [44.6405][44.6405:6922]()
    let task =
    iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    status_nav,
    );
    (log_selection(repo_state, ix), task)
    [44.6405]
    [44.6922]
    log_selection(
    status_nav,
    logs,
    repo_state,
    ix,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 365
    [44.7022][44.7022:7316]()
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [44.7022]
    [44.7316]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 367
    [44.7356][44.7356:7445]()
    );
    (log_selection(repo_state, ix), task)
    [44.7356]
    [44.7445]
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 380
    [40.6334][44.7525:7884]()
    let selection =
    Some(untracked_file_selection(repo_state, ix, files));
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [40.6334]
    [44.7884]
    let (selection, task) = untracked_file_selection(
    repo_state,
  • edit in inflorescence/src/cursor.rs at line 383
    [44.7916]
    [44.7916]
    ix,
    files,
    files_diffs,
    VertDir::Down,
  • replacement in inflorescence/src/cursor.rs at line 388
    [44.7935][44.7935:7969]()
    (selection, task)
    [44.7935]
    [44.7969]
    (Some(selection), task)
  • replacement in inflorescence/src/cursor.rs at line 391
    [40.6540][44.8031:8386]()
    let selection =
    Some(changed_file_selection(repo_state, ix, files));
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [40.6540]
    [44.8386]
    let (selection, task) = changed_file_selection(
    repo_state,
  • edit in inflorescence/src/cursor.rs at line 394
    [44.8418]
    [44.8418]
    ix,
    files,
    files_diffs,
    VertDir::Down,
  • replacement in inflorescence/src/cursor.rs at line 399
    [44.8437][44.8437:8471]()
    (selection, task)
    [44.8437]
    [44.8471]
    (Some(selection), task)
  • replacement in inflorescence/src/cursor.rs at line 402
    [44.8573][44.8573:8787]()
    let selection = log_selection(repo_state, ix);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(repo_state, ix, StatusSectionKind::Log),
    [44.8573]
    [44.8787]
    let (selection, task) = log_selection(
  • edit in inflorescence/src/cursor.rs at line 404
    [44.8819]
    [44.8819]
    logs,
    repo_state,
    ix,
    VertDir::Down,
  • replacement in inflorescence/src/cursor.rs at line 419
    [35.30877][35.30877:30894]()
    fn select_up<M>(
    [35.30877]
    [35.30894]
    fn select_up(
  • edit in inflorescence/src/cursor.rs at line 422
    [35.30946][44.8894:8928]()
    repo: Option<&mut app::Repo>,
  • edit in inflorescence/src/cursor.rs at line 424
    [36.6526]
    [36.6526]
    repo: Option<&mut app::Repo>,
  • replacement in inflorescence/src/cursor.rs at line 426
    [36.6555][35.31049:31064](),[35.31049][35.31049:31064]()
    ) -> Task<M> {
    [36.6555]
    [44.8929]
    ) -> Task<crate::Msg> {
  • replacement in inflorescence/src/cursor.rs at line 448
    [40.7594][40.7594:7772]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [40.7594]
    [40.7772]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 462
    [44.9179][44.9179:9449]()
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [44.9179]
    [44.9449]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 464
    [44.9485][44.9485:9566]()
    );
    (log_selection(repo_state, ix), task)
    [44.9485]
    [44.9566]
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 471
    [44.9696][44.9696:10077]()
    let selection =
    changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [44.9696]
    [44.10077]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 474
    [44.10113][44.10113:10174]()
    );
    (selection, task)
    [44.10113]
    [35.32731]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 481
    [44.10242][44.10242:10627]()
    let selection =
    untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [44.10242]
    [44.10627]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 484
    [44.10663][44.10663:10724]()
    );
    (selection, task)
    [44.10663]
    [40.8405]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 492
    [40.8477][44.10725:11056]()
    let selection = untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [40.8477]
    [44.11056]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 495
    [44.11088][44.11088:11141]()
    );
    (selection, task)
    [44.11088]
    [40.8594]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 516
    [40.9087][40.9087:9265]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [40.9087]
    [40.9265]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 530
    [44.11316][44.11316:11699]()
    let selection =
    untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [44.11316]
    [44.11699]
    untracked_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 533
    [44.11735][44.11735:11796]()
    );
    (selection, task)
    [44.11735]
    [44.11796]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 540
    [44.11906][44.11906:12243]()
    let selection = log_selection(repo_state, ix);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [44.11906]
    [44.12243]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 542
    [44.12279][44.12279:12340]()
    );
    (selection, task)
    [44.12279]
    [35.34478]
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 549
    [44.12406][44.12406:12787]()
    let selection =
    changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [44.12406]
    [44.12787]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 552
    [44.12823][44.12823:12884]()
    );
    (selection, task)
    [44.12823]
    [44.12884]
    ix,
    files,
    files_diffs,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 560
    [40.9995][44.12903:13230]()
    let selection = changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    [40.9995]
    [44.13230]
    changed_file_selection(
    repo_state,
  • replacement in inflorescence/src/cursor.rs at line 563
    [44.13262][44.13262:13315]()
    );
    (selection, task)
    [44.13262]
    [40.10110]
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 585
    [40.10632][40.10632:11430](),[40.11430][35.36450:36487](),[35.36450][35.36450:36487](),[35.36487][40.11431:11533]()
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    },
    if let Some(nav) = logs
    .diffs
    .get_mut(&id_hash)
    .and_then(|diff| diff.state.nav.as_mut())
    {
    iced_nav_scrollable::scroll_up(nav, delta)
    } else {
    Task::none()
    },
    )
    [40.10632]
    [40.11533]
    let selection = Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    };
    let task = if let Some(nav) = logs
    .diffs_nav
    .as_mut()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    }) {
    iced_nav_scrollable::scroll_up(nav, delta)
    } else {
    Task::none()
    };
    (selection, task)
  • replacement in inflorescence/src/cursor.rs at line 615
    [40.11792][40.11792:12216]()
    let file = log_file_selection(log_entry, file_ix);
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    [40.11792]
    [11.15518]
    let (file, selection_task) =
    log_file_selection(logs, log_entry, hash, file_ix);
    let scroll_task = if let Some((nav_changes_hash, nav)) =
    logs.changes_nav.as_ref()
    && *nav_changes_hash == hash
    {
    iced_nav_scrollable::scroll_up_to_section(
    file_ix, nav,
    )
    } else {
    Task::none()
    };
    let selection = Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    };
    (selection, Task::batch([selection_task, scroll_task]))
  • replacement in inflorescence/src/cursor.rs at line 643
    [44.13616][44.13616:14240]()
    let selection =
    changed_file_selection(repo_state, ix, files);
    let task =
    iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Changed,
    ),
    status_nav,
    );
    (selection, task)
    [44.13616]
    [44.14240]
    changed_file_selection(
    repo_state,
    status_nav,
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 653
    [44.14390][44.14390:15018]()
    let selection =
    untracked_file_selection(repo_state, ix, files);
    let task =
    iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    status_nav,
    );
    (selection, task)
    [44.14390]
    [40.12717]
    untracked_file_selection(
    repo_state,
    status_nav,
    ix,
    files,
    files_diffs,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 663
    [44.15082][44.15082:15656]()
    let selection = log_selection(repo_state, ix);
    let task =
    iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    status_nav,
    );
    (selection, task)
    [44.15082]
    [40.12859]
    log_selection(
    status_nav,
    logs,
    repo_state,
    ix,
    VertDir::Down,
    )
  • replacement in inflorescence/src/cursor.rs at line 673
    [40.12959][44.15657:16020]()
    let selection = log_selection(repo_state, ix);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Log,
    ),
    [40.12959]
    [44.16020]
    log_selection(
  • replacement in inflorescence/src/cursor.rs at line 675
    [44.16060][44.16060:16129]()
    );
    (selection, task)
    [44.16060]
    [44.16129]
    logs,
    repo_state,
    ix,
    VertDir::Up,
    )
  • replacement in inflorescence/src/cursor.rs at line 688
    [44.16247][44.16247:16459]()
    let selection = log_selection(repo_state, ix);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(repo_state, ix, StatusSectionKind::Log),
    [44.16247]
    [44.16459]
    let (selection, task) = log_selection(
  • edit in inflorescence/src/cursor.rs at line 690
    [44.16491]
    [44.16491]
    logs,
    repo_state,
    ix,
    VertDir::Up,
  • replacement in inflorescence/src/cursor.rs at line 698
    [44.16672][44.16672:17001]()
    let selection = changed_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [44.16672]
    [44.17001]
    let (selection, task) = changed_file_selection(
    repo_state,
  • edit in inflorescence/src/cursor.rs at line 701
    [44.17033]
    [44.17033]
    ix,
    files,
    files_diffs,
    VertDir::Up,
  • replacement in inflorescence/src/cursor.rs at line 709
    [44.17218][44.17218:17549]()
    let selection = untracked_file_selection(repo_state, ix, files);
    let task = iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(
    repo_state,
    ix,
    StatusSectionKind::Untracked,
    ),
    [44.17218]
    [44.17549]
    let (selection, task) = untracked_file_selection(
    repo_state,
  • edit in inflorescence/src/cursor.rs at line 712
    [44.17581]
    [44.17581]
    ix,
    files,
    files_diffs,
    VertDir::Up,
  • replacement in inflorescence/src/cursor.rs at line 741
    [38.372][37.4583:4715](),[37.4583][37.4583:4715]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    [38.372]
    [37.4715]
    if let Some(nav) =
    files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 760
    [38.447][37.5067:5199](),[37.5067][37.5067:5199]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    [38.447]
    [37.5199]
    if let Some(nav) =
    files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 785
    [38.512][37.5708:5860](),[37.5708][37.5708:5860]()
    if let Some(nav) = logs
    .diffs
    .get_mut(&id_hash)
    .and_then(|diff| diff.state.nav.as_mut())
    [38.512]
    [37.5860]
    if let Some(nav) =
    logs.diffs_nav.as_ref().and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 815
    [38.589][37.6597:6729](),[37.6597][37.6597:6729]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    [38.589]
    [37.6729]
    if let Some(nav) =
    files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 834
    [38.664][37.7079:7211](),[37.7079][37.7079:7211]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    [38.664]
    [37.7211]
    if let Some(nav) =
    files_diffs
    .diffs_nav
    .as_ref()
    .and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 859
    [38.729][37.7718:7870](),[37.7718][37.7718:7870]()
    if let Some(nav) = logs
    .diffs
    .get_mut(&id_hash)
    .and_then(|diff| diff.state.nav.as_mut())
    [38.729]
    [37.7870]
    if let Some(nav) =
    logs.diffs_nav.as_ref().and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    })
  • replacement in inflorescence/src/cursor.rs at line 876
    [35.38748][44.17656:17732](),[44.17732][40.14017:14060](),[35.38826][40.14017:14060]()
    fn select_left<M>(state: &mut State, repo: Option<&app::Repo>) -> Task<M> {
    let Some(_repo) = repo.as_ref() else {
    [35.38748]
    [40.14060]
    fn select_left(
    state: &mut State,
    repo: Option<&app::Repo>,
    ) -> Task<crate::Msg> {
    let Some(app::Repo {
    state: _repo_state,
    status_nav,
    }) = repo.as_ref()
    else {
  • replacement in inflorescence/src/cursor.rs at line 887
    [40.14096][40.14096:14661]()
    let selection: Option<Selection> = match state.selection.take() {
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file:
    Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    }) => {
    if diff_selected {
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    [40.14096]
    [35.39120]
    let (selection, task): (Option<Selection>, Task<crate::Msg>) =
    match state.selection.take() {
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file:
    Some(LogChangeFileSelection {
  • replacement in inflorescence/src/cursor.rs at line 897
    [35.39187][40.14662:14708]()
    diff_selected: false,
    [35.39187]
    [35.39226]
    diff_selected,
  • replacement in inflorescence/src/cursor.rs at line 899
    [35.39250][40.14709:14793]()
    })
    } else {
    Some(Selection::LogChange {
    [35.39250]
    [40.14793]
    }) => {
    if diff_selected {
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    }),
    }),
    Task::none(),
    )
    } else {
    let selection = Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    };
    let task = iced_nav_scrollable::scroll_to_stored_offset(
    status_nav,
    );
    (Some(selection), task)
    }
    }
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: true,
    }) => (
    Some(Selection::UntrackedFile {
  • replacement in inflorescence/src/cursor.rs at line 934
    [40.14817][40.14817:14923](),[40.14923][35.39812:39826](),[35.39812][35.39812:39826](),[35.39826][40.14924:15594]()
    hash,
    message,
    file: None,
    })
    }
    }
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: true,
    }) => Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: false,
    }),
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: true,
    }) => Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: false,
    }),
    selection @ (Some(Selection::UntrackedFile { .. })
    | Some(Selection::ChangedFile { .. })
    | Some(Selection::LogChange { file: None, .. })
    | None) => selection,
    };
    [40.14817]
    [40.15594]
    path,
    diff_selected: false,
    }),
    Task::none(),
    ),
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: true,
    }) => (
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: false,
    }),
    Task::none(),
    ),
    selection @ (Some(Selection::UntrackedFile { .. })
    | Some(Selection::ChangedFile { .. })
    | Some(Selection::LogChange { file: None, .. })
    | None) => (selection, Task::none()),
    };
  • replacement in inflorescence/src/cursor.rs at line 957
    [40.15627][35.40621:40638](),[35.40621][35.40621:40638]()
    Task::none()
    [40.15627]
    [35.40638]
    task
  • replacement in inflorescence/src/cursor.rs at line 960
    [11.16097][35.40641:40661]()
    fn select_right<M>(
    [11.16097]
    [35.40661]
    fn select_right(
  • edit in inflorescence/src/cursor.rs at line 962
    [35.40684][44.17733:17763]()
    repo: Option<&app::Repo>,
  • replacement in inflorescence/src/cursor.rs at line 963
    [35.40752][35.40752:40802]()
    logs: &diff::LogFilesAndState,
    ) -> Task<M> {
    [35.40752]
    [44.17764]
    logs: &mut diff::LogFilesAndState,
    repo: Option<&app::Repo>,
    ) -> Task<crate::Msg> {
  • replacement in inflorescence/src/cursor.rs at line 973
    [40.15706][40.15706:15803](),[40.15803][35.40969:41448](),[35.40969][35.40969:41448](),[35.41448][39.60:155](),[39.155][35.41592:41693](),[35.41592][35.41592:41693](),[35.41693][39.156:244]()
    let (selection, task): (Option<Selection>, Task<M>) =
    match state.selection.take() {
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: false,
    }) => {
    let id_hash = file::id_parts_hash(&path, file::Kind::Untracked);
    let diff_selected =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    [40.15706]
    [35.41877]
    let (selection, task): (Option<Selection>, Task<crate::Msg>) = match state
    .selection
    .take()
    {
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: false,
    }) => {
    let id_hash = file::id_parts_hash(&path, file::Kind::Untracked);
    let diff_selected =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) = files_diffs.diffs_nav.as_ref().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    } else {
    Task::none()
    };
    (
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    }),
    task,
    )
    }
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: false,
    }) => {
    let id_hash = file::id_parts_hash(&path, file::Kind::Changed);
    let diff_selected =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) = files_diffs.diffs_nav.as_ref().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    } else {
    Task::none()
    };
    (
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    }),
    task,
    )
    }
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    }) => {
    let log_entry = repo_state.log.get(ix).unwrap();
    let (file, task) = if let Some(path) = log_entry.file_paths.first()
    {
    let id_hash = file::log_id_parts_hash(log_entry.hash, path);
    let task = if let Some(log) = logs.diffs.get(&id_hash) {
    // Init log diffs nav
    let contents_count = diff::contents_count(&log.file);
    let unchanged_sections =
    diff::unchanged_sections(&log.file);
    let (nav, tasks) = iced_nav_scrollable::init(
    contents_count,
    unchanged_sections,
    );
    logs.diffs_nav = Some((id_hash, nav));
    tasks.map(move |msg| crate::Msg::LogDiffNav {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
  • edit in inflorescence/src/cursor.rs at line 1060
    [35.41902]
    [35.41902]
    // If the log is not loaded yet, the nav will be initialized
    // once it's loaded (`repo::MsgOut::GotChangeDifs`)
  • edit in inflorescence/src/cursor.rs at line 1064
    [35.41954]
    [35.41954]
  • replacement in inflorescence/src/cursor.rs at line 1066
    [35.41972][35.41972:42121]()
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    [35.41972]
    [35.42121]
    Some(LogChangeFileSelection {
    ix: 0,
    path: path.clone(),
    diff_selected: false,
  • replacement in inflorescence/src/cursor.rs at line 1073
    [35.42189][35.42189:42678](),[35.42678][39.245:340](),[39.340][35.42822:42923](),[35.42822][35.42822:42923](),[35.42923][39.341:429](),[39.429][35.43086:43259](),[11.18858][35.43086:43259]()
    }
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: false,
    }) => {
    let id_hash = file::id_parts_hash(&path, file::Kind::Changed);
    let diff_selected =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    } else {
    Task::none()
    };
    (
    Some(Selection::ChangedFile {
    ix,
    [35.42189]
    [35.43259]
    } else {
    (None, Task::none())
    };
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file,
    }),
    task,
    )
    }
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file:
    Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    }),
    }) => {
    let id_hash = file::log_id_parts_hash(hash, &path);
    let diff_selected = diff::log_diff_needs_scrolling(logs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) =
    logs.diffs_nav.as_ref().and_then(|(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    }) {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    } else {
    Task::none()
    };
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
  • replacement in inflorescence/src/cursor.rs at line 1120
    [35.43352][35.43352:44060](),[35.44060][39.430:518](),[39.518][35.44197:44333](),[35.44197][35.44197:44333](),[35.44333][39.519:607](),[39.607][35.44496:44554](),[11.20251][35.44496:44554](),[35.44554][11.20251:20270](),[11.20251][11.20251:20270](),[11.20270][35.44555:44986](),[35.44986][11.20387:20401](),[11.20387][11.20387:20401](),[11.20401][35.44987:45142](),[35.45142][44.17874:17939](),[44.17939][35.45201:45536](),[35.45201][35.45201:45536](),[35.45536][25.765:839](),[25.765][25.765:839](),[25.839][35.45537:45637](),[35.45637][25.1321:1344](),[25.1321][25.1321:1344](),[25.1344][35.45638:45970]()
    task,
    )
    }
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file:
    Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    }),
    }) => {
    let id_hash = file::log_id_parts_hash(hash, &path);
    let diff_selected =
    diff::log_diff_needs_scrolling(logs, id_hash);
    // If the selected file's diff is already loaded (it has an
    // attached state), scroll back to its last offset
    let task = if diff_selected
    && let Some(nav) = logs
    .diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_to_stored_offset(nav)
    } else {
    Task::none()
    };
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    }),
    task,
    )
    }
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    }) => {
    let log_entry = repo_state.log.get(ix).unwrap();
    let (file, task) =
    if let Some(path) = log_entry.file_paths.first() {
    (
    Some(LogChangeFileSelection {
    ix: 0,
    path: path.clone(),
    diff_selected: false,
    }),
    Task::none(),
    )
    } else {
    (None, Task::none())
    };
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file,
    }),
    task,
    )
    }
    selection => (selection, Task::none()),
    };
    [35.43352]
    [40.15804]
    }),
    task,
    )
    }
    selection => (selection, Task::none()),
    };
  • replacement in inflorescence/src/cursor.rs at line 1134
    [36.7242][36.7242:7286]()
    log_diffs: &mut diff::LogFilesAndState,
    [36.7242]
    [36.7286]
    logs: &mut diff::LogFilesAndState,
  • replacement in inflorescence/src/cursor.rs at line 1148
    [38.804][36.7761:7939](),[36.7761][36.7761:7939]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [38.804]
    [36.7939]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 1163
    [38.877][36.8297:8475](),[36.8297][36.8297:8475]()
    if let Some(nav) = files_diffs
    .get_mut(&id_hash)
    .and_then(|state| state.nav.as_mut())
    {
    [38.877]
    [36.8475]
    if let Some(nav) = files_diffs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 1183
    [36.9013][36.9013:9224]()
    if let Some(nav) = log_diffs
    .diffs
    .get_mut(&id_hash)
    .and_then(|diff| diff.state.nav.as_mut())
    {
    [36.9013]
    [36.9224]
    if let Some(nav) = logs.diffs_nav.as_mut().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 1229
    [39.655][39.655:702](),[39.702][35.46979:47072](),[35.46979][35.46979:47072](),[35.47072][39.703:717]()
    && let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    [39.655]
    [39.717]
    && let Some(nav) = files_diffs.diffs_nav.as_ref().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 1263
    [39.947][39.947:994](),[39.994][35.48340:48433](),[35.48340][35.48340:48433](),[35.48433][39.995:1009]()
    && let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    [39.947]
    [39.1009]
    && let Some(nav) = files_diffs.diffs_nav.as_ref().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • replacement in inflorescence/src/cursor.rs at line 1304
    [39.1124][39.1124:1172](),[39.1172][35.49833:49981](),[35.49833][35.49833:49981](),[35.49981][39.1173:1195]()
    && let Some(nav) = logs
    .diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    {
    [39.1124]
    [39.1195]
    && let Some(nav) = logs.diffs_nav.as_ref().and_then(
    |(nav_id_hash, nav)| {
    (*nav_id_hash == id_hash).then_some(nav)
    },
    ) {
  • edit in inflorescence/src/cursor.rs at line 1355
    [44.18659]
    [44.18659]
    }
    #[derive(Debug, Clone, Copy)]
    pub enum VertDir {
    Up,
    Down,
    }
    pub fn untracked_file_selection(
    repo: &repo::State,
    status_nav: &NavScrollable,
    ix: usize,
    files: &mut file::State,
    files_diffs: &mut diff::FilesState,
    dir: VertDir,
    ) -> (Selection, Task<crate::Msg>) {
    let path = repo.untracked_files.iter().nth(ix).unwrap();
    let id_hash = file::id_parts_hash(path, file::Kind::Untracked);
    let nav_task = match file::try_get_src_file(files, id_hash) {
    Some(file_diff) => {
    // If the diff is already loaded init nav for it
    let contents_count = diff::contents_count(file_diff);
    let unchanged_sections = diff::unchanged_sections(file_diff);
    let (nav, nav_task) =
    iced_nav_scrollable::init(contents_count, unchanged_sections);
    files_diffs.diffs_nav = Some((id_hash, nav));
    nav_task.map(move |msg| {
    crate::Msg::View(app::Msg::FileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
    })
    }
    None => {
    // Nav will be initialize once the diff is loaded
    // (`crate::Msg::File`)
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    };
    file::load_src_file_if_not_cached(files, id);
    Task::none()
    }
    };
    let scroll_task = match dir {
    VertDir::Up => iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Untracked),
    status_nav,
    ),
    VertDir::Down => iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Untracked),
    status_nav,
    ),
    };
    let selection = Selection::UntrackedFile {
    ix,
    path: path.clone(),
    diff_selected: false,
    };
    (selection, Task::batch([nav_task, scroll_task]))
    }
    pub fn changed_file_selection(
    repo: &repo::State,
    status_nav: &NavScrollable,
    ix: usize,
    files: &mut file::State,
    files_diffs: &mut diff::FilesState,
    dir: VertDir,
    ) -> (Selection, Task<crate::Msg>) {
    let (path, diffs) = repo.changed_files.iter().nth(ix).unwrap();
    let id_hash = file::id_parts_hash(path, file::Kind::Changed);
    let nav_task = match file::try_get_src_file(files, id_hash) {
    Some(file_diff) => {
    // If the diff is already loaded init nav for it
    let contents_count = diff::contents_count(file_diff);
    let unchanged_sections = diff::unchanged_sections(file_diff);
    let (nav, nav_task) =
    iced_nav_scrollable::init(contents_count, unchanged_sections);
    files_diffs.diffs_nav = Some((id_hash, nav));
    nav_task.map(move |msg| {
    crate::Msg::View(app::Msg::FileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
    })
    }
    None => {
    // Nav will be initialize once the diff is loaded
    // (`crate::Msg::File`)
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    if diff::any_diff_has_contents(diffs) {
    file::load_src_file_if_not_cached(files, id);
    }
    Task::none()
    }
    };
    let scroll_task = match dir {
    VertDir::Up => iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Changed),
    status_nav,
    ),
    VertDir::Down => iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Changed),
    status_nav,
    ),
    };
    let selection = Selection::ChangedFile {
    ix,
    path: path.clone(),
    diff_selected: false,
    };
    (selection, Task::batch([nav_task, scroll_task]))
  • edit in inflorescence/src/cursor.rs at line 1480
    [44.18661]
    fn log_selection(
    status_nav: &NavScrollable,
    logs: &mut diff::LogFilesAndState,
    repo: &repo::State,
    ix: usize,
    dir: VertDir,
    ) -> (Selection, Task<crate::Msg>) {
    let entry = repo.log.get(ix).unwrap();
    let hash = entry.hash;
    let nav_task = match logs.change_file_counts.get(&hash) {
    Some(contents_count) => {
    // Init log changes nav
    let (nav, tasks) =
    iced_nav_scrollable::init(*contents_count, HashSet::new());
    logs.changes_nav = Some((hash, nav));
    tasks.map(move |msg| {
    crate::Msg::View(app::Msg::LogNav { hash, msg })
    })
    }
    None => {
    // If the log is not loaded yet, the nav will be initialized once
    // it's loaded (`repo::MsgOut::GotChangeDifs`)
    Task::done(crate::Msg::View(app::Msg::ToRepo(
    repo::MsgIn::GetChangeDiffs { hash },
    )))
    }
    };
    let scroll_task = match dir {
    VertDir::Up => iced_nav_scrollable::scroll_up_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Log),
    status_nav,
    ),
    VertDir::Down => iced_nav_scrollable::scroll_down_to_section(
    status_section_ix(repo, ix, StatusSectionKind::Log),
    status_nav,
    ),
    };
    let selection = Selection::LogChange {
    ix,
    hash,
    message: entry.message.clone(),
    file: None,
    };
    (selection, Task::batch([nav_task, scroll_task]))
    }
    fn log_file_selection(
    logs: &mut diff::LogFilesAndState,
    log_entry: &repo::LogEntry,
    hash: repo::ChangeHash,
    file_ix: usize,
    ) -> (LogChangeFileSelection, Task<crate::Msg>) {
    let path = log_entry.file_paths.get(file_ix).unwrap().clone();
    let id_hash = file::log_id_parts_hash(hash, &path);
    let nav_task = match (logs.diffs.get(&id_hash), logs.diffs_nav.as_ref()) {
    (Some(diff), Some((nav_id_hash, _nav))) if *nav_id_hash != id_hash => {
    let contents_count = diff::contents_count(&diff.file);
    // Init log change diff nav
    let (nav, tasks) =
    iced_nav_scrollable::init(contents_count, HashSet::new());
    logs.diffs_nav = Some((id_hash, nav));
    tasks.map(move |msg| {
    crate::Msg::View(app::Msg::LogChangeFileDiff {
    id_hash,
    msg: diff::Msg::DiffNav(msg),
    })
    })
    }
    _ => {
    // If the log is not loaded yet, the nav will be initialized once
    // it's loaded (`repo::MsgOut::GotChangeDifs`)
    Task::none()
    }
    };
    (
    LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    },
    nav_task,
    )
    }
  • edit in iced_utils/src/task/wrappers.rs at line 9
    [28.9465]
    [28.9465]
    /// Produces a [`Task`] that scrolls the [`scrollable::Scrollable`] with the
    /// given [`scrollable::Id`] by the provided [`scrollable::AbsoluteOffset`].
    pub fn scroll_by<T>(
    id: impl Into<scrollable::Id>,
    offset: scrollable::AbsoluteOffset,
    ) -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = scrollable::scroll_by(id, offset);
  • edit in iced_utils/src/task/wrappers.rs at line 19
    [28.9466]
    [28.9466]
    #[cfg(any(test, feature = "testing"))]
    let task = {
    let _ = (id, offset);
    Task::none()
    };
    task
    }
  • replacement in iced_nav_scrollable/src/lib.rs at line 32
    [27.98][27.98:119]()
    pub offset: f32,
    [27.98]
    [27.160]
    pub stored_offset: Option<f32>,
  • replacement in iced_nav_scrollable/src/lib.rs at line 123
    [27.1268][27.1268:1293]()
    offset: 0.0,
    [27.1268]
    [27.1343]
    stored_offset: None,
  • replacement in iced_nav_scrollable/src/lib.rs at line 219
    [26.2312][27.3840:3895]()
    nav.offset = viewport.absolute_offset().y;
    [26.2312]
    [26.2847]
    nav.stored_offset = Some(viewport.absolute_offset().y);
  • edit in iced_nav_scrollable/src/lib.rs at line 225
    [26.3439][26.3439:3546]()
    /// NOTE: call `into_iter()` on the children parameter to make it type-check
    /// with `ExactSizeIterator`.
  • replacement in iced_nav_scrollable/src/lib.rs at line 250
    [35.51031][35.51031:51177]()
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: nav.offset,
    },
    )
    [35.51031]
    [36.10853]
    if let Some(y) = nav.stored_offset {
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset { x: 0.0, y },
    )
    } else {
    Task::none()
    }
  • replacement in iced_nav_scrollable/src/lib.rs at line 274
    [35.51678][35.51678:51723]()
    nav.offset + nav.height,
    [35.51678]
    [35.51723]
    nav.stored_offset.unwrap_or_default() + nav.height,
  • replacement in iced_nav_scrollable/src/lib.rs at line 285
    [36.11212][36.11212:11280]()
    Some((nav.offset + offset_delta, Delay::Start))
    [36.11212]
    [37.8229]
    Some((
    nav.stored_offset.unwrap_or_default() + offset_delta,
    Delay::Start,
    ))
  • replacement in iced_nav_scrollable/src/lib.rs at line 352
    [35.52953][35.52953:53022]()
    let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;
    [35.52953]
    [35.53022]
    let top_frame = nav.stored_offset.unwrap_or_default()
    + VISIBLE_CONTEXT_HEIGHT;
  • replacement in iced_nav_scrollable/src/lib.rs at line 363
    [36.12827][36.12827:12893]()
    saturating_sub(nav.offset, offset_delta),
    [36.12827]
    [36.12893]
    saturating_sub(
    nav.stored_offset.unwrap_or_default(),
    offset_delta,
    ),
  • replacement in iced_nav_scrollable/src/lib.rs at line 434
    [37.8431][37.8431:8460]()
    nav: &mut NavScrollable,
    [37.8431]
    [37.8460]
    nav: &NavScrollable,
  • replacement in iced_nav_scrollable/src/lib.rs at line 442
    [37.8649][37.8649:8766]()
    let y = nav.offset + offset_delta;
    task::scroll_to(nav.id.clone(), scrollable::AbsoluteOffset { x: 0.0, y })
    [37.8649]
    [37.8766]
    task::scroll_by(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: offset_delta,
    },
    )
  • replacement in iced_nav_scrollable/src/lib.rs at line 453
    [37.8855][37.8855:8884]()
    nav: &mut NavScrollable,
    [37.8855]
    [37.8884]
    nav: &NavScrollable,
  • replacement in iced_nav_scrollable/src/lib.rs at line 461
    [37.9073][37.9073:9205]()
    let y = saturating_sub(nav.offset, offset_delta);
    task::scroll_to(nav.id.clone(), scrollable::AbsoluteOffset { x: 0.0, y })
    [37.9073]
    [44.18673]
    task::scroll_by(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: -offset_delta,
    },
    )
  • replacement in iced_nav_scrollable/src/lib.rs at line 472
    [44.18733][44.18733:18762]()
    nav: &mut NavScrollable,
    [44.18733]
    [44.18762]
    nav: &NavScrollable,
  • replacement in iced_nav_scrollable/src/lib.rs at line 484
    [44.19110][44.19110:19275]()
    let bottom_frame =
    saturating_sub(nav.offset + nav.height, VISIBLE_CONTEXT_HEIGHT);
    let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;
    [44.19110]
    [44.19275]
    let bottom_frame = saturating_sub(
    nav.stored_offset.unwrap_or_default() + nav.height,
    VISIBLE_CONTEXT_HEIGHT,
    );
    let top_frame =
    nav.stored_offset.unwrap_or_default() + VISIBLE_CONTEXT_HEIGHT;
  • replacement in iced_nav_scrollable/src/lib.rs at line 511
    [44.19815][44.19815:19844]()
    nav: &mut NavScrollable,
    [44.19815]
    [44.19844]
    nav: &NavScrollable,
  • replacement in iced_nav_scrollable/src/lib.rs at line 523
    [44.20184][44.20184:20349]()
    let bottom_frame =
    saturating_sub(nav.offset + nav.height, VISIBLE_CONTEXT_HEIGHT);
    let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;
    [44.20184]
    [44.20349]
    let bottom_frame = saturating_sub(
    nav.stored_offset.unwrap_or_default() + nav.height,
    VISIBLE_CONTEXT_HEIGHT,
    );
    let top_frame =
    nav.stored_offset.unwrap_or_default() + VISIBLE_CONTEXT_HEIGHT;
  • replacement in iced_nav_scrollable/src/lib.rs at line 599
    [34.3710][33.1017:1151](),[33.1017][33.1017:1151]()
    if let Some(y) = nav
    .skip_sections
    .contains(&0)
    .then(|| nav.section_offsets.get(1))
    .flatten()
    [34.3710]
    [33.1151]
    if let Some(y) =
    nav.section_offsets
    .iter()
    .enumerate()
    .find_map(|(ix, offset)| {
    (!nav.skip_sections.contains(&ix)).then_some(*offset)
    })
  • replacement in iced_nav_scrollable/src/lib.rs at line 607
    [33.1157][35.54023:54083]()
    let y = saturating_sub(*y, VISIBLE_CONTEXT_HEIGHT);
    [33.1157]
    [33.1291]
    let y = saturating_sub(y, VISIBLE_CONTEXT_HEIGHT);