diffs selection and scrolling

[?]
Jul 10, 2025, 8:48 PM
KWTBNTO3QUUE2YADF6SYW6G6ZOKYEWRJQKIWDGZXR33S3YNDVIZQC

Dependencies

  • [2] 6YZAVBWU Initial commit
  • [3] IQDCHWCP load a pijul repo
  • [4] SWWE2R6M display basic repo stuff
  • [5] WT3GA27P add cursor with selection
  • [6] UB2ITZJS refresh changed files on FS changes
  • [7] KT5UYXGK fix selection after adding file, add changed file diffs
  • [8] ELG3UDT6 allow to rm added files
  • [9] S2NVIFXR allow to enter record msg
  • [10] W7IUT3ZV start recording impl
  • [11] YBJRDOTC make all repo actions async
  • [12] A5YBC77V record!
  • [13] D7A7MSIH allow to defer or abandon record, add buttons
  • [14] 4WO3ZJM2 show untracked files' contents
  • [15] BJXUYQ2Y show untracked file contents in read-only text editor
  • [16] W4LFX7IH group diffs by file name
  • [17] AMPZ2BXK show changed files diffs (only Edit atm)
  • [18] FDDPOH5R add arrow controls
  • [19] V55EAIWQ add src file LRU cache
  • [20] ZVI4AWER woot contents_diff
  • [21] OQ6HSAWH show record log
  • [22] WI2BVQ6J rm client lib crate
  • [23] DCSUCH6R add undecoded diff view, improve decoded view style
  • [24] JE44NYHM display log files diffs
  • [25] ONRCENKT rm unnecessary state from repo's internal state
  • [26] 4ELJZGRJ load and store all change diffs at once
  • [27] FR52XEMW add action for log change file diff
  • [28] L6KSEFQI move cursor related stuff into its module
  • [29] BFN2VHZS refactor file stuff into sub-mod
  • [30] VJNWIGSX clippy
  • [31] GWZGYNIB add view crate
  • [32] 23SFYK4Q big view refactor into a new crate
  • [33] MYGIBRRH wip custom theme
  • [34] PKJCFSBM theme improvements
  • [35] XSZZB47U refactor stuff into lib
  • [36] XIASAP3G clippy
  • [37] 3BK22XE5 add a test for hover btn and more refactors
  • [38] WGID4LS4 absolutely slayed testing with iced task
  • [39] VCNKFNUF app init test
  • [40] ACDXXAX2 refactor main's updates into smaller fns
  • [41] I56UGW7U make record test, fix log update
  • [42] X6AK4QPX finish recording test
  • [43] 5CYU7UT7 test: rm added file
  • [44] ESMM3FEL test selection reindexing
  • [45] TSFQFCB2 test got repo change
  • [46] UF5NJKAS test load repo
  • [47] 7SSBM4UQ view: refactor repo view
  • [48] S2T7RUKW add nav back placeholder
  • [49] I2AG42PA new cols layout
  • [50] 4PNWU55O replace the circular hor navigation
  • [51] WW36JYLR add iced_nav_scrollable widget crate
  • [52] WIFVLV37 nav-scrollabe: detect size to determine if needs scrolling, msg when ready
  • [53] SASAN2XC use nav-scrollable
  • [54] YKHE3XMW refactor diffs handling
  • [55] KEPKF3WO unify diffs handling, simplify view
  • [56] GOLHUD6R nav-scrollable: set skip-able sections
  • [57] XHWLKCLD auto-scroll past skip sections on load
  • [58] K5YUSV2W auto-scroll to last offset
  • [59] KM5PSZ4A watch repo once loaded
  • [60] Y5ATDI2H convert changed file diffs and load src only if any needs it
  • [61] F542TMBE test log
  • [62] B4RMW5AE add syntax highlighter to untracked files contents
  • [63] SWDPAGF6 test channel name
  • [64] QMAUTRB6 refactor diff
  • [65] XZ6D3UUE avoid alloc
  • [66] AHWWRC73 navigate log entries
  • [67] NWJD6VM6 mv libflowers libflorescence
  • [68] Z2CJPWZE focus record message text_editor on spawn
  • [69] HC7ROIBC move main diffs state out of cursor
  • [70] PTFDJ567 add untracked files encoding
  • [71] YYKXNBFL test: add untracked file
  • [72] UMO6U2ZT partition the change files diffs on whether they have content
  • [73] SGOJEZNO fix
  • [*] OPXFZKEB view tests setup
  • [*] MJDGPSHG WIP contents diff

Change contents

  • edit in libflorescence/src/repo.rs at line 4
    [46.908]
    [12.7]
    #[doc(inline)]
    pub use pijul::Hash as ChangeHash;
  • replacement in libflorescence/src/repo.rs at line 17
    [22.194][22.194:306]()
    working_copy, ChannelMutTxnT, ChannelTxnT, Encoding, Hash, HashSet,
    MutTxnT, MutTxnTExt, TxnT, TxnTExt,
    [22.194]
    [22.306]
    working_copy, ChannelMutTxnT, ChannelTxnT, Encoding, HashSet, MutTxnT,
    MutTxnTExt, TxnT, TxnTExt,
  • replacement in libflorescence/src/repo.rs at line 143
    [26.28][26.28:55]()
    hash: pijul::Hash,
    [26.28]
    [12.546]
    hash: ChangeHash,
  • replacement in libflorescence/src/repo.rs at line 160
    [26.77][26.77:104]()
    hash: pijul::Hash,
    [26.77]
    [26.104]
    hash: ChangeHash,
  • replacement in libflorescence/src/repo.rs at line 165
    [11.726][11.726:790]()
    pub type Diff = LocalChange<Hunk<Option<Hash>, Local>, Author>;
    [11.726]
    [21.119]
    pub type Diff = LocalChange<Hunk<Option<ChangeHash>, Local>, Author>;
  • replacement in libflorescence/src/repo.rs at line 171
    [21.197][21.197:224]()
    pub hash: pijul::Hash,
    [21.197]
    [21.224]
    pub hash: ChangeHash,
  • edit in libflorescence/src/repo.rs at line 176
    [21.252][24.136:204](),[24.204][55.6:64](),[55.64][24.238:263](),[24.238][24.238:263]()
    #[derive(Debug, Clone, Hash, PartialEq, Eq)]
    pub struct LogFileId {
    /// Hash of the log change
    pub hash: pijul::Hash,
    pub path: String,
    }
  • edit in libflorescence/src/repo.rs at line 286
    [25.1569]
    [25.1569]
    pub fn hash_bytes(bytes: &[u8]) -> ChangeHash {
    let mut hasher = pijul::pristine::Hasher::default();
    hasher.update(bytes);
    hasher.finish()
    }
  • replacement in libflorescence/src/repo.rs at line 605
    [16.428][16.428:457]()
    match dbg!(change) {
    [16.428]
    [7.1758]
    // match dbg!(change) {
    match change {
  • replacement in libflorescence/src/repo.rs at line 838
    [22.484][24.1030:1103]()
    let entry = mk_log_entry(repo, pijul::Hash::from(hash));
    [22.484]
    [21.2025]
    let entry = mk_log_entry(repo, ChangeHash::from(hash));
  • replacement in libflorescence/src/repo.rs at line 852
    [21.2241][24.1104:1179]()
    fn mk_log_entry(repo: &pijul::Repository, hash: pijul::Hash) -> LogEntry {
    [21.2241]
    [24.1179]
    fn mk_log_entry(repo: &pijul::Repository, hash: ChangeHash) -> LogEntry {
  • edit in libflorescence/src/lib.rs at line 10
    [39.2186]
    #[doc(inline)]
    pub use std::hash::DefaultHasher;
    pub type AHash = u64;
    pub fn hasher() -> DefaultHasher {
    DefaultHasher::new()
    }
    pub fn hash_one<T>(x: T) -> u64
    where
    T: std::hash::Hash,
    {
    let mut hasher = hasher();
    x.hash(&mut hasher);
    std::hash::Hasher::finish(&hasher)
    }
    pub type NoHashMap<K, V> = std::collections::HashMap<K, V, BuildNoHashHasher>;
    pub type BuildNoHashHasher = std::hash::BuildHasherDefault<NoHashHasher>;
    #[derive(Debug, Default)]
    pub struct NoHashHasher(u64);
    impl std::hash::Hasher for NoHashHasher {
    fn write(&mut self, _: &[u8]) {
    unimplemented!("Invalid use of NoHashHasher")
    }
    fn write_u8(&mut self, n: u8) {
    self.0 = u64::from(n)
    }
    fn write_u16(&mut self, n: u16) {
    self.0 = u64::from(n)
    }
    fn write_u32(&mut self, n: u32) {
    self.0 = u64::from(n)
    }
    fn write_u64(&mut self, n: u64) {
    self.0 = n
    }
    fn write_usize(&mut self, n: usize) {
    self.0 = n as u64
    }
    fn write_i8(&mut self, n: i8) {
    self.0 = n as u64
    }
    fn write_i16(&mut self, n: i16) {
    self.0 = n as u64
    }
    fn write_i32(&mut self, n: i32) {
    self.0 = n as u64
    }
    fn write_i64(&mut self, n: i64) {
    self.0 = n as u64
    }
    fn write_isize(&mut self, n: isize) {
    self.0 = n as u64
    }
    fn finish(&self) -> u64 {
    self.0
    }
    }
  • replacement in libflorescence/src/file.rs at line 1
    [35.58][35.59:76]()
    use crate::diff;
    [35.58]
    [35.76]
    use crate::{diff, hash_one, repo, AHash, NoHashMap};
  • edit in libflorescence/src/file.rs at line 6
    [35.160]
    [35.160]
    pub file_kind: Kind,
    }
    #[derive(Debug, Clone, Hash, PartialEq, Eq)]
    pub struct IdRef<'a> {
    pub path: &'a str,
  • edit in libflorescence/src/file.rs at line 13
    [35.185]
    [35.185]
    }
    #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
    pub struct IdHash(pub AHash);
    pub type IdMap<V> = NoHashMap<IdHash, V>;
    #[derive(Debug, Clone, Hash, PartialEq, Eq)]
    pub struct LogId {
    /// Hash of the log change
    pub hash: repo::ChangeHash,
    pub path: String,
    }
    #[derive(Debug, Clone, Hash, PartialEq, Eq)]
    pub struct LogIdRef<'a> {
    pub hash: repo::ChangeHash,
    pub path: &'a str,
  • edit in libflorescence/src/file.rs at line 32
    [35.187]
    [35.187]
    #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
    pub struct LogIdHash(pub AHash);
    pub type LogIdMap<V> = NoHashMap<LogIdHash, V>;
  • edit in libflorescence/src/file.rs at line 48
    [35.356]
    [35.356]
    }
    pub fn id_hash(id: &Id) -> IdHash {
    IdHash(hash_one(id))
    }
    pub fn id_ref_hash(id: &IdRef<'_>) -> IdHash {
    IdHash(hash_one(id))
    }
    pub fn id_parts_hash(path: &str, file_kind: Kind) -> IdHash {
    id_ref_hash(&IdRef { path, file_kind })
    }
    pub fn log_id_hash(id: &LogId) -> LogIdHash {
    LogIdHash(hash_one(id))
    }
    pub fn log_id_ref_hash(id: &LogIdRef<'_>) -> LogIdHash {
    LogIdHash(hash_one(id))
    }
    pub fn log_id_parts_hash(hash: repo::ChangeHash, path: &str) -> LogIdHash {
    log_id_ref_hash(&LogIdRef { path, hash })
    }
    #[cfg(test)]
    mod test {
    use super::*;
    #[test]
    fn id_hash_eq_id_ref_hash() {
    let path = "some random path";
    let file_kind = Kind::Untracked;
    assert_eq!(
    id_ref_hash(&IdRef { path, file_kind }),
    id_hash(&Id {
    path: path.to_string(),
    file_kind
    })
    );
    assert_eq!(
    id_ref_hash(&IdRef { path, file_kind }),
    id_parts_hash(path, file_kind),
    );
    assert_ne!(
    id_ref_hash(&IdRef { path, file_kind }),
    id_hash(&Id {
    path: path.to_string(),
    file_kind: Kind::Changed
    })
    );
    }
    #[test]
    fn log_id_hash_eq_log_id_ref_hash() {
    let path = "some change file path";
    let hash = repo::hash_bytes(&[0, 1, 2]);
    assert_eq!(
    log_id_ref_hash(&LogIdRef { path, hash }),
    log_id_hash(&LogId {
    path: path.to_string(),
    hash
    })
    );
    assert_eq!(
    log_id_ref_hash(&LogIdRef { path, hash }),
    log_id_parts_hash(hash, path),
    );
    assert_ne!(
    log_id_ref_hash(&LogIdRef { path, hash }),
    log_id_hash(&LogId {
    path: path.to_string(),
    hash: repo::hash_bytes(&[255])
    })
    );
    }
  • edit in inflorescence_view/src/theme.rs at line 91
    [36.87]
    [34.1363]
    Selected,
  • replacement in inflorescence_view/src/theme.rs at line 218
    [33.3596][33.3596:3693]()
    let background =
    palette::Background::new(PALETTE.background, PALETTE.text);
    [33.3596]
    [33.3693]
    let background = palette::Background::new(
    match class {
    Scrollable::Normal => PALETTE.background,
    Scrollable::Selected => MAGENTA_DARKEST,
    },
    PALETTE.text,
    );
  • replacement in inflorescence_view/src/theme.rs at line 230
    [33.3905][33.3905:3953]()
    color: background.strong.color,
    [33.3905]
    [33.3953]
    color: match class {
    Scrollable::Normal => background.strong.color,
    Scrollable::Selected => MAGENTA_LIGHTEST,
    },
  • replacement in inflorescence_view/src/diff.rs at line 28
    [32.2803][55.85:164]()
    pub fn view<'a>(state: &'a State, file: &'a File) -> Element<'a, Msg, Theme> {
    [32.2803]
    [32.3549]
    pub fn view<'a>(
    state: &'a State,
    file: &'a File,
    diff_selected: bool,
    ) -> Element<'a, Msg, Theme> {
  • replacement in inflorescence_view/src/diff.rs at line 34
    [32.3566][32.3566:3640]()
    File::Decoded(decoded_file) => view_decoded(state, decoded_file),
    [32.3566]
    [32.3640]
    File::Decoded(decoded_file) => {
    view_decoded(state, decoded_file, diff_selected)
    }
  • replacement in inflorescence_view/src/diff.rs at line 38
    [32.3689][32.3689:3743]()
    view_undecodable(state, undecodable_file)
    [32.3689]
    [32.3743]
    view_undecodable(state, undecodable_file, diff_selected)
  • edit in inflorescence_view/src/diff.rs at line 46
    [32.3951]
    [33.6447]
    diff_selected: bool,
  • replacement in inflorescence_view/src/diff.rs at line 104
    [54.166][54.166:177]()
    ))
    [54.166]
    [53.898]
    )
    .class(if diff_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    }))
  • edit in inflorescence_view/src/diff.rs at line 129
    [32.5962]
    [33.6479]
    diff_selected: bool,
  • replacement in inflorescence_view/src/diff.rs at line 147
    [55.315][54.178:318](),[53.1403][54.178:318]()
    el(iced_nav_scrollable::view(
    nav,
    diffs,
    diffs_len,
    Msg::NavScrollable,
    ))
    [55.315]
    [53.1614]
    el(
    iced_nav_scrollable::view(
    nav,
    diffs,
    diffs_len,
    Msg::NavScrollable,
    )
    .class(if diff_selected {
    theme::Scrollable::Selected
    } else {
    theme::Scrollable::Normal
    }),
    )
  • edit in inflorescence_view/src/cursor.rs at line 22
    [35.3488]
    [35.3488]
    diff_selected: bool,
  • edit in inflorescence_view/src/cursor.rs at line 27
    [35.3554]
    [35.3554]
    diff_selected: bool,
  • edit in inflorescence_view/src/cursor.rs at line 41
    [35.4101]
    [35.4101]
    pub diff_selected: bool,
  • replacement in inflorescence_view/src/app.rs at line 44
    [32.13210][32.13210:13232]()
    id: file::Id,
    [32.13210]
    [32.13232]
    id_hash: file::IdHash,
  • replacement in inflorescence_view/src/app.rs at line 169
    [47.1601][47.1601:1802]()
    Some(cursor::Selection::UntrackedFile { ix: _, path }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    };
    [47.1601]
    [55.616]
    Some(cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Untracked);
  • replacement in inflorescence_view/src/app.rs at line 176
    [55.668][55.668:770](),[54.629][47.2046:2253](),[55.770][47.2046:2253](),[47.2046][47.2046:2253]()
    Some((file, state)) => {
    diff::view(state, file).map(move |msg| {
    Msg::FileDiffsContentsAction {
    id: id.clone(),
    action: msg,
    }
    })
    }
    [55.668]
    [55.771]
    Some((file, state)) => diff::view(state, file, *diff_selected)
    .map(move |msg| Msg::FileDiffsContentsAction {
    id_hash,
    action: msg,
    }),
  • replacement in inflorescence_view/src/app.rs at line 190
    [47.2546][47.2546:2743]()
    Some(cursor::Selection::ChangedFile { path, ix: _ }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    [47.2546]
    [55.825]
    Some(cursor::Selection::ChangedFile {
    path,
    ix: _,
    diff_selected,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Changed);
  • replacement in inflorescence_view/src/app.rs at line 197
    [55.877][55.877:979](),[54.705][47.2987:3194](),[55.979][47.2987:3194](),[47.2987][47.2987:3194]()
    Some((file, state)) => {
    diff::view(state, file).map(move |msg| {
    Msg::FileDiffsContentsAction {
    id: id.clone(),
    action: msg,
    }
    })
    }
    [55.877]
    [55.980]
    Some((file, state)) => diff::view(state, file, *diff_selected)
    .map(move |msg| Msg::FileDiffsContentsAction {
    id_hash,
    action: msg,
    }),
  • replacement in inflorescence_view/src/app.rs at line 299
    [55.1195][55.1195:1267]()
    file: Some(cursor::LogChangeFileSelection { ix: _, path }),
    [55.1195]
    [55.1267]
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected,
    }),
  • replacement in inflorescence_view/src/app.rs at line 313
    [55.1563][55.1563:1626]()
    diff::view(state, file).map(|action| {
    [55.1563]
    [55.1626]
    diff::view(state, file, *diff_selected).map(|action| {
  • replacement in inflorescence_view/src/app.rs at line 324
    [48.314][48.314:347](),[48.347][49.3179:3213]()
    .width(Length::Fill)
    .height(Length::Fill)
    [48.314]
    [55.1822]
    // .width(Length::Fill)
    // .height(Length::Fill)
  • edit in inflorescence_view/src/app/test.rs at line 131
    [33.8883]
    [33.8883]
    diff_selected: false,
  • edit in inflorescence/src/test.rs at line 7
    [41.1488][44.107:143]()
    use libflorescence::prelude::pijul;
  • edit in inflorescence/src/test.rs at line 184
    [42.1025]
    [42.1025]
    diff_selected: false,
  • edit in inflorescence/src/test.rs at line 380
    [43.347][43.347:402]()
    let _msg = task::await_next_msg(&mut tasks).await;
  • edit in inflorescence/src/test.rs at line 461
    [44.2141]
    [44.2141]
    diff_selected: false,
  • replacement in inflorescence/src/test.rs at line 474
    [44.2424][53.2119:2173](),[53.2173][44.2494:2546](),[44.2494][44.2494:2546]()
    cursor::Selection::UntrackedFile { ix, path }
    if *ix == 1 && path == "untracked_1.rs"
    [44.2424]
    [44.2546]
    cursor::Selection::UntrackedFile { ix, path, diff_selected }
    if *ix == 1 && path == "untracked_1.rs" && !diff_selected
  • replacement in inflorescence/src/test.rs at line 481
    [44.2660][44.2660:2820]()
    state.files.diffs_cache.inner.peek(&file::Id {
    path: "untracked_1.rs".to_string(),
    file_kind: file::Kind::Untracked
    }),
    [44.2660]
    [44.2820]
    state.files.diffs_cache.inner.peek(&file::id_parts_hash(
    "untracked_1.rs",
    file::Kind::Untracked
    )),
  • edit in inflorescence/src/test.rs at line 496
    [44.3224]
    [44.3224]
    diff_selected: false,
  • edit in inflorescence/src/test.rs at line 527
    [44.4142]
    [44.4142]
    diff_selected: false,
  • replacement in inflorescence/src/test.rs at line 540
    [44.4426][53.2174:2226](),[53.2226][44.4494:4544](),[44.4494][44.4494:4544]()
    cursor::Selection::ChangedFile { ix, path }
    if *ix == 0 && path == "changed_0.rs"
    [44.4426]
    [44.4544]
    cursor::Selection::ChangedFile { ix, path, diff_selected }
    if *ix == 0 && path == "changed_0.rs" && !diff_selected
  • replacement in inflorescence/src/test.rs at line 547
    [44.4658][44.4658:4814]()
    state.files.diffs_cache.inner.peek(&file::Id {
    path: "changed_0.rs".to_string(),
    file_kind: file::Kind::Changed
    }),
    [44.4658]
    [44.4814]
    state
    .files
    .diffs_cache
    .inner
    .peek(&file::id_parts_hash("changed_0.rs", file::Kind::Changed)),
  • edit in inflorescence/src/test.rs at line 564
    [44.5268]
    [44.5268]
    diff_selected: false,
  • replacement in inflorescence/src/test.rs at line 577
    [44.5552][53.2227:2279](),[53.2279][44.5620:5670](),[44.5620][44.5620:5670]()
    cursor::Selection::ChangedFile { ix, path }
    if *ix == 1 && path == "changed_1.rs"
    [44.5552]
    [44.5670]
    cursor::Selection::ChangedFile { ix, path, diff_selected }
    if *ix == 1 && path == "changed_1.rs" && !diff_selected
  • edit in inflorescence/src/test.rs at line 590
    [44.6034]
    [44.6034]
    diff_selected: false,
  • replacement in inflorescence/src/test.rs at line 608
    [44.6625][44.6625:6754]()
    let change_hash_0 = change_hash(&[0]);
    let change_hash_1 = change_hash(&[1]);
    let change_hash_2 = change_hash(&[2]);
    [44.6464]
    [44.6754]
    let change_hash_0 = repo::hash_bytes(&[0]);
    let change_hash_1 = repo::hash_bytes(&[1]);
    let change_hash_2 = repo::hash_bytes(&[2]);
  • edit in inflorescence/src/test.rs at line 659
    [44.8137]
    [44.8137]
    diff_selected: false,
  • replacement in inflorescence/src/test.rs at line 687
    [45.498][45.498:584]()
    let change_hash_0 = change_hash(&[0]);
    let change_hash_1 = change_hash(&[1]);
    [45.498]
    [45.584]
    let change_hash_0 = repo::hash_bytes(&[0]);
    let change_hash_1 = repo::hash_bytes(&[1]);
  • replacement in inflorescence/src/test.rs at line 724
    [45.1996][54.2818:2892]()
    // Initializes nav-scrollables for diffs
    assert!(task.is_some());
    [45.1996]
    [45.2025]
    assert!(task.is_none());
  • edit in inflorescence/src/test.rs at line 771
    [41.7441]
    [41.7441]
    /// Tasks to open window, set icon, tasks replies from repo, load user id
    /// and files
  • edit in inflorescence/src/test.rs at line 780
    [45.2216][45.2216:2366](),[45.2366][41.7650:7652](),[41.7650][41.7650:7652]()
    fn change_hash(bytes: &[u8]) -> pijul::Hash {
    let mut hasher = pijul::pristine::Hasher::default();
    hasher.update(bytes);
    hasher.finish()
    }
  • replacement in inflorescence/src/main.rs at line 14
    [3.1025][58.37:82]()
    use iced::widget::{scrollable, text_editor};
    [3.1025]
    [38.5279]
    use iced::widget::text_editor;
  • replacement in inflorescence/src/main.rs at line 26
    [38.5338][44.8591:8634]()
    use std::collections::{BTreeSet, HashMap};
    [38.5338]
    [38.5369]
    use std::collections::BTreeSet;
  • replacement in inflorescence/src/main.rs at line 102
    [29.164][54.2894:2974]()
    files_diffs: HashMap::new(),
    log_diffs: HashMap::new(),
    [29.164]
    [6.1520]
    files_diffs: diff::FilesState::default(),
    logs: diff::LogFilesAndState::default(),
  • replacement in inflorescence/src/main.rs at line 122
    [54.3085][54.3085:3134]()
    files_diffs: HashMap<file::Id, diff::State>,
    [54.3085]
    [54.3134]
    files_diffs: diff::FilesState,
  • replacement in inflorescence/src/main.rs at line 124
    [54.3200][55.2908:2962]()
    log_diffs: HashMap<repo::LogFileId, LogFileDiff>,
    [54.3200]
    [19.1025]
    logs: diff::LogFilesAndState,
  • replacement in inflorescence/src/main.rs at line 139
    [29.260][53.2490:2531]()
    DiffStateNav {
    id: file::Id,
    [29.260]
    [53.2531]
    DiffNav {
    id_hash: file::IdHash,
  • replacement in inflorescence/src/main.rs at line 143
    [53.2562][53.2562:2631]()
    LogChangeDiff {
    hash: pijul::Hash,
    file: String,
    [53.2562]
    [53.2631]
    LogDiffNav {
    id_hash: file::LogIdHash,
  • edit in inflorescence/src/main.rs at line 149
    [54.3368][54.3368:3460](),[15.122][5.735:737](),[27.134][5.735:737](),[13.216][5.735:737](),[9.240][5.735:737](),[8.649][5.735:737](),[10.1205][5.735:737](),[17.1921][5.735:737](),[53.2662][5.735:737](),[14.2792][5.735:737](),[54.3460][5.735:737](),[7.4637][5.735:737](),[5.735][5.735:737](),[5.737][2.3098:3099](),[4.5117][2.3098:3099](),[2.3098][2.3098:3099]()
    #[derive(Debug)]
    struct LogFileDiff {
    pub diff: diff::File,
    pub state: diff::State,
    }
  • replacement in inflorescence/src/main.rs at line 180
    [56.76][56.76:96]()
    id,
    [56.76]
    [56.96]
    id_hash: id,
  • replacement in inflorescence/src/main.rs at line 192
    [53.3059][53.3059:3208]()
    let id_clone = id.clone();
    return tasks.map(move |msg| Msg::DiffStateNav {
    id: id_clone.clone(),
    [53.3059]
    [53.3208]
    return tasks.map(move |msg| Msg::DiffNav {
    id_hash: id,
  • replacement in inflorescence/src/main.rs at line 199
    [53.3333][53.3333:3376]()
    Msg::DiffStateNav { id, msg } => {
    [53.3333]
    [54.3532]
    Msg::DiffNav { id_hash: id, msg } => {
  • replacement in inflorescence/src/main.rs at line 203
    [53.3548][53.3548:3653]()
    return task.map(move |msg| Msg::DiffStateNav {
    id: id_clone.clone(),
    [53.3548]
    [53.3653]
    return task.map(move |msg| Msg::DiffNav {
    id_hash: id_clone.clone(),
  • replacement in inflorescence/src/main.rs at line 210
    [53.3747][53.3747:3799](),[53.3799][55.2963:3018](),[55.3018][53.3874:3963](),[53.3874][53.3874:3963](),[53.3963][54.3604:3680](),[54.3680][53.4024:4073](),[53.4024][53.4024:4073]()
    Msg::LogChangeDiff { hash, file, msg } => {
    if let Some(cursor::Selection::LogChange {
    ix: _,
    hash: selection_hash,
    message: _,
    file: Some(cursor::LogChangeFileSelection { ix: _, path }),
    }) = state.cursor.selection.as_mut()
    [53.3747]
    [53.4073]
    Msg::LogDiffNav { id_hash: id, msg } => {
    if let Some(diff::FileAndState { file: _, state }) =
    state.logs.diffs.get_mut(&id)
  • replacement in inflorescence/src/main.rs at line 214
    [53.4087][53.4087:4132](),[53.4132][55.3019:3066](),[55.3066][54.3722:4021](),[54.3722][54.3722:4021](),[54.4021][53.4276:4531](),[53.4276][53.4276:4531]()
    if selection_hash == &hash {
    let id = repo::LogFileId {
    hash,
    path: path.clone(),
    };
    if let Some(LogFileDiff { diff: _, state }) =
    state.log_diffs.get_mut(&id)
    {
    let task = diff::update(state, msg);
    return task.map(move |msg| Msg::LogChangeDiff {
    hash,
    file: file.clone(),
    msg,
    });
    }
    }
    [53.4087]
    [53.4531]
    let task = diff::update(state, msg);
    return task.map(move |msg| Msg::LogDiffNav {
    id_hash: id.clone(),
    msg,
    });
  • edit in inflorescence/src/main.rs at line 266
    [58.164]
    [58.164]
    msg,
  • replacement in inflorescence/src/main.rs at line 270
    [58.270][58.270:291]()
    msg,
    [58.270]
    [58.291]
    &state.files_diffs,
    &state.logs,
  • replacement in inflorescence/src/main.rs at line 275
    [58.364][58.364:2256]()
    // If the selected file's diff is already loaded, scroll back to its
    // last offset
    let scroll_task = match state.cursor.selection.as_ref() {
    Some(cursor::Selection::UntrackedFile { ix: _, path }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    };
    if let Some(nav) = state
    .files_diffs
    .get(&id)
    .and_then(|state| state.nav.as_ref())
    {
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: nav.offset,
    },
    )
    } else {
    Task::none()
    }
    }
    Some(cursor::Selection::ChangedFile { ix: _, path }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    if let Some(nav) = state
    .files_diffs
    .get(&id)
    .and_then(|state| state.nav.as_ref())
    {
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: nav.offset,
    },
    )
    } else {
    Task::none()
    }
    }
    Some(cursor::Selection::LogChange {
    [58.364]
    [58.2256]
    let init_log_nav_task =
    if let Some(cursor::Selection::LogChange {
  • replacement in inflorescence/src/main.rs at line 280
    [58.2341][58.2341:3150]()
    file: Some(cursor::LogChangeFileSelection { ix: _, path }),
    }) => {
    let id = repo::LogFileId {
    hash: *hash,
    path: path.clone(),
    };
    if let Some(nav) = state
    .log_diffs
    .get(&id)
    .and_then(|diff| diff.state.nav.as_ref())
    {
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: nav.offset,
    },
    )
    } else {
    Task::none()
    [58.2341]
    [58.3150]
    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::NavScrollable(msg),
    })
    }
    _ => Task::none(),
  • edit in inflorescence/src/main.rs at line 307
    [58.3172]
    [58.3172]
    } 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()
  • replacement in inflorescence/src/main.rs at line 325
    [58.3190][58.3190:3421]()
    Some(cursor::Selection::LogChange {
    ix: _,
    hash: _,
    message: _,
    file: None,
    })
    | None => Task::none(),
    [58.3190]
    [58.3421]
    } else {
    Task::none()
  • replacement in inflorescence/src/main.rs at line 329
    [58.3437][58.3437:3489]()
    Task::batch([cursor_task, scroll_task])
    [58.3437]
    [58.3489]
    Task::batch([cursor_task, get_diffs_task, init_log_nav_task])
  • replacement in inflorescence/src/main.rs at line 339
    [40.582][40.582:702]()
    app::Msg::FileDiffsContentsAction { id, action } => {
    file_diffs_contents_action(state, id, action)
    [40.582]
    [9.772]
    app::Msg::FileDiffsContentsAction { id_hash, action } => {
    file_diffs_contents_action(state, id_hash, action)
  • replacement in inflorescence/src/main.rs at line 363
    [40.1311][40.1311:1424]()
    if let Some(cursor::Selection::UntrackedFile { ix, path }) =
    state.cursor.selection.as_ref()
    [40.1311]
    [40.1424]
    if let Some(cursor::Selection::UntrackedFile {
    ix,
    path,
    diff_selected: _,
    }) = state.cursor.selection.as_ref()
  • replacement in inflorescence/src/main.rs at line 398
    [40.2419][40.2419:2530]()
    if let Some(cursor::Selection::ChangedFile { ix, path }) =
    state.cursor.selection.as_ref()
    [40.2419]
    [40.2530]
    if let Some(cursor::Selection::ChangedFile {
    ix,
    path,
    diff_selected: _,
    }) = state.cursor.selection.as_ref()
  • replacement in inflorescence/src/main.rs at line 524
    [40.6417][40.6417:6435]()
    id: file::Id,
    [40.6417]
    [40.6435]
    id_hash: file::IdHash,
  • replacement in inflorescence/src/main.rs at line 528
    [40.6538][40.6538:6738](),[40.6738][54.4247:4325](),[54.4325][53.6606:6825](),[53.6606][53.6606:6825]()
    match (selection, id.file_kind) {
    (
    cursor::Selection::UntrackedFile { ix: _, path },
    file::Kind::Untracked,
    ) if path == &id.path => {
    let diffs = state.files_diffs.entry(id.clone()).or_default();
    return diff::update(diffs, action).map(move |msg| {
    Msg::DiffStateNav {
    id: id.clone(),
    msg,
    }
    });
    [40.6538]
    [14.7355]
    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 });
    }
  • replacement in inflorescence/src/main.rs at line 542
    [14.7369][40.6854:7008](),[40.7008][54.4326:4404](),[54.4404][53.6904:7104](),[53.6904][53.6904:7104]()
    (
    cursor::Selection::ChangedFile { ix: _, path },
    file::Kind::Changed,
    ) if path == &id.path => {
    let diffs = state.files_diffs.entry(id.clone()).or_default();
    let task = diff::update(diffs, action);
    return task.map(move |msg| Msg::DiffStateNav {
    id: id.clone(),
    msg,
    });
    [14.7369]
    [40.7123]
    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 });
    }
  • edit in inflorescence/src/main.rs at line 577
    [40.7615]
    [40.7615]
    diff_selected: _,
  • replacement in inflorescence/src/main.rs at line 582
    [40.7740][55.3067:3106](),[55.3106][54.4438:4614](),[54.4438][54.4438:4614]()
    let id = repo::LogFileId {
    hash,
    path: file.clone(),
    };
    if let Some(LogFileDiff { diff: _, state }) =
    state.log_diffs.get_mut(&id)
    [40.7740]
    [54.4614]
    let id_hash = file::log_id_parts_hash(hash, &file);
    if let Some(diff::FileAndState { file: _, state }) =
    state.logs.diffs.get_mut(&id_hash)
  • replacement in inflorescence/src/main.rs at line 586
    [54.4628][54.4628:4882]()
    return diff::update(state, action).map(move |msg| {
    Msg::LogChangeDiff {
    hash,
    file: file.clone(),
    msg,
    }
    });
    [54.4628]
    [54.4882]
    return diff::update(state, action)
    .map(move |msg| Msg::LogDiffNav { id_hash, msg });
  • replacement in inflorescence/src/main.rs at line 668
    [40.9691][40.9691:9757]()
    cursor::Selection::UntrackedFile { ix: _, path } => {
    [40.9691]
    [40.10022]
    cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected,
    } => {
  • edit in inflorescence/src/main.rs at line 680
    [44.9210]
    [40.10329]
    diff_selected,
  • replacement in inflorescence/src/main.rs at line 695
    [40.10409][40.10409:10473]()
    cursor::Selection::ChangedFile { ix: _, path } => {
    [40.10409]
    [40.10682]
    cursor::Selection::ChangedFile {
    ix: _,
    path,
    diff_selected,
    } => {
  • edit in inflorescence/src/main.rs at line 708
    [40.11062]
    [40.11062]
    diff_selected,
  • replacement in inflorescence/src/main.rs at line 739
    [40.11737][40.11737:12196](),[29.1165][20.15057:15117](),[40.12196][20.15057:15117](),[20.15057][20.15057:15117](),[20.15117][40.12197:12298]()
    let file = file.and_then(|file| {
    entry
    .file_paths
    .iter()
    .enumerate()
    .find(|(_ix, path)| *path == &file.path)
    .map(|(ix, path)| {
    cursor::LogChangeFileSelection {
    ix,
    path: path.clone(),
    }
    })
    });
    [40.11737]
    [40.12298]
    let file = file.and_then(
    |cursor::LogChangeFileSelection {
    ix: _,
    path: selected_path,
    diff_selected,
    }| {
    entry
    .file_paths
    .iter()
    .enumerate()
    .find(|(_ix, path)| *path == &selected_path)
    .map(|(ix, path)| {
    cursor::LogChangeFileSelection {
    ix,
    path: path.clone(),
    diff_selected,
    }
    })
    },
    );
  • edit in inflorescence/src/main.rs at line 801
    [40.13140][54.4897:4958]()
    let mut tasks = Vec::with_capacity(diffs.len());
  • replacement in inflorescence/src/main.rs at line 804
    [54.5129][54.5129:5173]()
    let diff = diff::init_file(
    [54.5129]
    [54.5173]
    let file = diff::init_file(
  • replacement in inflorescence/src/main.rs at line 808
    [54.5282][54.5282:5348](),[54.5348][56.361:596](),[56.596][54.5450:5601](),[54.5450][54.5450:5601]()
    let contents_count = diff::contents_count(&diff);
    let unchanged_sections = diff::unchanged_sections(&diff);
    let (nav, nav_tasks) = iced_nav_scrollable::init(
    contents_count,
    unchanged_sections,
    );
    let diff_state = diff::State {
    nav: Some(nav),
    state: libflorescence::diff::State::default(),
    [54.5282]
    [54.5601]
    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(),
  • edit in inflorescence/src/main.rs at line 815
    [54.5620][54.5620:5667](),[54.5667][55.3107:3164](),[55.3164][54.5718:5852](),[54.5718][54.5718:5852]()
    let path_clone = path.clone();
    let id = repo::LogFileId { hash, path };
    let log_file_diff = LogFileDiff {
    diff,
    state: diff_state,
    };
  • replacement in inflorescence/src/main.rs at line 816
    [54.5853][54.5853:6133]()
    state.log_diffs.insert(id, log_file_diff);
    tasks.push(nav_tasks.map(move |msg| Msg::LogChangeDiff {
    hash,
    file: path_clone.clone(),
    msg: diff::Msg::NavScrollable(msg),
    }))
    [54.5853]
    [54.6133]
    state.logs.change_hashes.insert(hash);
    state.logs.diffs.insert(id_hash, log_file_diff);
  • edit in inflorescence/src/main.rs at line 819
    [54.6149][54.6149:6188]()
    return Task::batch(tasks);
  • replacement in inflorescence/src/main.rs at line 824
    [17.4299][32.27331:27378]()
    fn subs(_state: &State) -> Subscription<Msg> {
    [17.4299]
    [18.22]
    fn subs(state: &State) -> Subscription<Msg> {
  • replacement in inflorescence/src/main.rs at line 877
    [49.5720][49.5720:5769]()
    Subscription::batch([key_subs, window_subs])
    [49.5720]
    [5.4172]
    let nav_subs = match state.cursor.selection.as_ref() {
    Some(cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected: true,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Untracked);
    iced_nav_scrollable::subs()
    .with(id_hash)
    .map(|(id_hash, msg)| Msg::DiffNav {
    id_hash,
    msg: diff::Msg::NavScrollable(msg),
    })
    }
    Some(cursor::Selection::ChangedFile {
    ix: _,
    path,
    diff_selected: true,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Changed);
    iced_nav_scrollable::subs()
    .with(id_hash)
    .map(|(id_hash, msg)| Msg::DiffNav {
    id_hash,
    msg: diff::Msg::NavScrollable(msg),
    })
    }
    Some(cursor::Selection::LogChange {
    ix: _,
    hash,
    message: _,
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected: true,
    }),
    }) => {
    let id_hash = file::log_id_parts_hash(*hash, path);
    iced_nav_scrollable::subs()
    .with(id_hash)
    .map(|(id_hash, msg)| Msg::LogDiffNav {
    id_hash,
    msg: diff::Msg::NavScrollable(msg),
    })
    }
    Some(cursor::Selection::UntrackedFile { .. })
    | Some(cursor::Selection::ChangedFile { .. })
    | Some(cursor::Selection::LogChange { .. })
    | None => Subscription::none(),
    };
    Subscription::batch([key_subs, window_subs, nav_subs])
  • replacement in inflorescence/src/main.rs at line 943
    [32.28696][54.6189:6242]()
    files_diffs: diffs_state,
    log_diffs,
    [32.28696]
    [32.28717]
    files_diffs,
    logs: log_diffs,
  • replacement in inflorescence/src/main.rs at line 948
    [54.6308][54.6308:6509](),[54.6509][55.3165:3223]()
    Some(cursor::Selection::UntrackedFile { ix: _, path }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    };
    let file = files.diffs_cache.inner.peek(&id);
    [54.6308]
    [55.3223]
    Some(cursor::Selection::UntrackedFile {
    ix: _,
    path,
    diff_selected: _,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Untracked);
    let file = files.diffs_cache.inner.peek(&id_hash);
  • replacement in inflorescence/src/main.rs at line 957
    [55.3300][55.3300:3368]()
    diffs_state.get(&id).map(|state| (file, state))
    [55.3300]
    [55.3368]
    files_diffs.get(&id_hash).map(|state| (file, state))
  • replacement in inflorescence/src/main.rs at line 962
    [54.6552][54.6552:6749](),[54.6749][55.3459:3517]()
    Some(cursor::Selection::ChangedFile { path, ix: _ }) => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    let file = files.diffs_cache.inner.peek(&id);
    [54.6552]
    [55.3517]
    Some(cursor::Selection::ChangedFile {
    path,
    ix: _,
    diff_selected: _,
    }) => {
    let id_hash = file::id_parts_hash(path, file::Kind::Changed);
    let file = files.diffs_cache.inner.peek(&id_hash);
  • replacement in inflorescence/src/main.rs at line 971
    [55.3594][55.3594:3662]()
    diffs_state.get(&id).map(|state| (file, state))
    [55.3594]
    [55.3662]
    files_diffs.get(&id_hash).map(|state| (file, state))
  • replacement in inflorescence/src/main.rs at line 980
    [54.7039][54.7039:7111]()
    file: Some(cursor::LogChangeFileSelection { ix: _, path }),
    [54.7039]
    [54.7111]
    file:
    Some(cursor::LogChangeFileSelection {
    ix: _,
    path,
    diff_selected: _,
    }),
  • replacement in inflorescence/src/main.rs at line 987
    [54.7127][55.3753:3792](),[55.3792][54.7160:7240](),[54.7160][54.7160:7240]()
    let id = repo::LogFileId {
    hash: *hash,
    path: path.clone(),
    };
    [54.7127]
    [54.7240]
    let id_hash = file::log_id_parts_hash(*hash, path);
  • replacement in inflorescence/src/main.rs at line 989
    [54.7262][54.7262:7288](),[54.7288][55.3793:3865]()
    .get(&id)
    .map(|LogFileDiff { diff: file, state }| (file, state))
    [54.7262]
    [54.7354]
    .diffs
    .get(&id_hash)
    .map(|diff::FileAndState { file, state }| (file, state))
  • replacement in inflorescence/src/file.rs at line 4
    [37.1678][35.6102:6150](),[32.29020][35.6102:6150]()
    pub use libflorescence::file::{Diff, Id, Kind};
    [37.1678]
    [29.1869]
    pub use libflorescence::file::{
    id_hash, id_parts_hash, log_id_parts_hash, Diff, Id, IdHash, IdMap, Kind,
    LogIdHash, LogIdMap,
    };
  • replacement in inflorescence/src/file.rs at line 53
    [29.2831][29.2831:2898]()
    CLruCache<Id, Diff, std::hash::RandomState, DiffsCacheWeight>;
    [29.2831]
    [29.3180]
    CLruCache<IdHash, Diff, std::hash::RandomState, DiffsCacheWeight>;
  • replacement in inflorescence/src/file.rs at line 97
    [53.7756][53.7756:7772]()
    pub id: Id,
    [53.7756]
    [53.7772]
    pub id_hash: IdHash,
  • edit in inflorescence/src/file.rs at line 122
    [29.4904]
    [29.4904]
    let id_hash = id_hash(&id);
  • replacement in inflorescence/src/file.rs at line 133
    [29.5192][53.7994:8034]()
    id.clone(),
    [29.5192]
    [29.5224]
    id_hash,
  • replacement in inflorescence/src/file.rs at line 138
    [56.836][56.836:868]()
    id,
    [56.836]
    [56.868]
    id_hash,
  • replacement in inflorescence/src/file.rs at line 156
    [29.5732][53.8222:8266]()
    id.clone(),
    [29.5732]
    [29.5768]
    id_hash,
  • replacement in inflorescence/src/file.rs at line 161
    [56.1157][56.1157:1193]()
    id,
    [56.1157]
    [56.1193]
    id_hash,
  • replacement in inflorescence/src/file.rs at line 196
    [29.6473][29.6473:6597]()
    if !state.diffs_cache.inner.contains(&id) {
    diffs_cache_put(&mut state.diffs_cache, id.clone(), Diff::Loading);
    [29.6473]
    [29.6597]
    let id_hash = id_hash(&id);
    if !state.diffs_cache.inner.contains(&id_hash) {
    diffs_cache_put(&mut state.diffs_cache, id_hash, Diff::Loading);
  • replacement in inflorescence/src/file.rs at line 211
    [29.6850][29.6850:6917]()
    fn diffs_cache_put(cache: &mut DiffsCache, key: Id, value: Diff) {
    [29.6850]
    [29.6917]
    fn diffs_cache_put(cache: &mut DiffsCache, key: IdHash, value: Diff) {
  • replacement in inflorescence/src/file.rs at line 214
    [29.7053][29.7053:7165]()
    info!("Source file cache is too small to hold {}. Resizing cache to to {kv_weight} fit it.", key.path);
    [29.7053]
    [29.7165]
    info!("Source file cache is too small to fit new key. Resizing cache to {kv_weight} fit it.");
  • replacement in inflorescence/src/file.rs at line 221
    [29.7325][29.7325:7472]()
    impl WeightScale<Id, Diff> for DiffsCacheWeight {
    fn weight(&self, key: &Id, value: &Diff) -> usize {
    let key_weight = key.path.len();
    [29.7325]
    [29.7472]
    impl WeightScale<IdHash, Diff> for DiffsCacheWeight {
    fn weight(&self, _key: &IdHash, value: &Diff) -> usize {
    const KEY_WEIGHT: usize = mem::size_of::<IdHash>();
  • replacement in inflorescence/src/file.rs at line 248
    [29.8419][29.8419:8451]()
    key_weight + val_weight
    [29.8419]
    [29.8451]
    KEY_WEIGHT + val_weight
  • edit in inflorescence/src/diff.rs at line 12
    [31.732]
    [53.8470]
    use crate::file;
  • edit in inflorescence/src/diff.rs at line 19
    [56.1373]
    [23.287]
    pub type FilesState = file::IdMap<State>;
    #[derive(Debug, Default)]
    pub struct LogFilesAndState {
    /// All the hashes in this set have `diffs` loaded
    pub change_hashes: HashSet<repo::ChangeHash>,
    /// All the diffs in this map have the change hash present in
    /// `change_hashes`
    pub diffs: file::LogIdMap<FileAndState>,
    }
  • edit in inflorescence/src/diff.rs at line 31
    [23.288]
    [53.8493]
    #[derive(Debug)]
    pub struct FileAndState {
    pub file: File,
    pub state: State,
    }
  • edit in inflorescence/src/diff.rs at line 203
    [56.1917]
    [56.1917]
    }
    pub fn file_diff_needs_scrolling(
    files_diffs: &FilesState,
    id: file::IdHash,
    ) -> bool {
    matches!(
    files_diffs
    .get(&id)
    .and_then(|diff| diff.nav.as_ref())
    .and_then(|nav| nav.ready),
    Some(iced_nav_scrollable::NeedsScrolling::Yes)
    )
    }
    pub fn log_diff_needs_scrolling(
    logs: &LogFilesAndState,
    id_hash: file::LogIdHash,
    ) -> bool {
    matches!(
    logs.diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    .and_then(|nav| nav.ready),
    Some(iced_nav_scrollable::NeedsScrolling::Yes)
    )
  • replacement in inflorescence/src/cursor.rs at line 10
    [28.2281][28.2281:2296]()
    pub fn update(
    [29.8578]
    [28.2296]
    pub fn update<M>(
    msg: Msg,
  • replacement in inflorescence/src/cursor.rs at line 15
    [28.2440][28.2440:2479]()
    msg: Msg,
    ) -> Task<repo::MsgIn> {
    [28.2385]
    [28.2479]
    files_diffs: &diff::FilesState,
    log_diffs: &diff::LogFilesAndState,
    ) -> Task<M> {
  • replacement in inflorescence/src/cursor.rs at line 19
    [28.2495][30.939:962](),[30.962][28.2524:3023](),[28.2524][28.2524:3023](),[28.3023][29.8609:8742](),[29.8742][28.3339:3741](),[28.3339][28.3339:3741](),[28.3741][29.8743:8800](),[29.8800][28.3942:4163](),[28.3942][28.3942:4163](),[28.4163][29.8801:8928](),[29.8928][28.4457:4594](),[28.4457][28.4457:4594]()
    Msg::Down => {
    if let Some(repo) = repo.as_ref() {
    let (selection, task) = match state.selection.take() {
    Some(Selection::UntrackedFile { ix, path: _ }) => {
    let (selection, task) =
    if repo.untracked_files.len().saturating_sub(1)
    == ix
    {
    if !repo.changed_files.is_empty() {
    let ix = 0;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
    } else if !repo.log.is_empty() {
    let ix = 0;
    log_selection(repo, ix)
    } else {
    let ix = 0;
    let selection = untracked_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    }
    } else {
    let ix = ix + 1;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
    [28.2495]
    [28.4594]
    Msg::Down => select_down(state, files, repo, files_diffs, log_diffs),
    Msg::Up => select_up(state, files, repo, files_diffs, log_diffs),
    Msg::Left => select_left(state, repo),
    Msg::Right => select_right(state, repo, files_diffs, log_diffs),
    Msg::Select(select) => {
    select_exact(select, state, files, repo, files_diffs, log_diffs)
    }
    }
    }
    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,
    }
    }
    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);
    }
    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>(
    state: &mut State,
    files: &mut file::State,
    repo: Option<&repo::State>,
    files_diffs: &diff::FilesState,
    logs: &diff::LogFilesAndState,
    ) -> Task<M> {
    // TODO stair
    if let Some(repo) = repo.as_ref() {
    let (selection, task) = match state.selection.take() {
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    }) => {
    let (selection, task) = if diff_selected {
    let id_hash =
    file::id_parts_hash(&path, file::Kind::Untracked);
    (
    Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    },
    if let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_down(nav)
    } else {
    Task::none()
    },
    )
    } else if repo.untracked_files.len().saturating_sub(1) == ix {
    if !repo.changed_files.is_empty() {
    let ix = 0;
    let selection = changed_file_selection(repo, ix, files);
    (selection, Task::none())
    } else if !repo.log.is_empty() {
    let ix = 0;
    (log_selection(repo, ix), Task::none())
    } else {
    let ix = 0;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 140
    [28.4616][28.4616:5213](),[28.5213][29.8929:8986](),[29.8986][28.5414:5604](),[28.5414][28.5414:5604](),[28.5604][29.8987:9120](),[29.9120][28.5920:6102](),[28.5920][28.5920:6102](),[28.6102][29.9121:9246](),[29.9246][28.6394:6531](),[28.6394][28.6394:6531]()
    Some(Selection::ChangedFile { ix, path: _ }) => {
    let (selection, task) =
    if repo.changed_files.len().saturating_sub(1) == ix
    {
    if !repo.log.is_empty() {
    let ix = 0;
    log_selection(repo, ix)
    } else if !repo.untracked_files.is_empty() {
    let ix = 0;
    let selection = untracked_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    } else {
    let ix = 0;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
    }
    } else {
    let ix = ix + 1;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
    [28.4616]
    [28.6531]
    } else {
    let ix = ix + 1;
    let selection = untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
    }
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    }) => {
    let (selection, task) = if diff_selected {
    let id_hash =
    file::id_parts_hash(&path, file::Kind::Changed);
    (
    Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    },
    if let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_down(nav)
    } else {
    Task::none()
    },
    )
    } else if repo.changed_files.len().saturating_sub(1) == ix {
    if !repo.log.is_empty() {
    let ix = 0;
    (log_selection(repo, ix), Task::none())
    } else if !repo.untracked_files.is_empty() {
    let ix = 0;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    } else {
    let ix = 0;
    let selection = changed_file_selection(repo, ix, files);
    (selection, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 184
    [28.6553][28.6553:6700](),[28.6731][28.6731:6761]()
    Some(Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file,
    [28.6553]
    [28.6761]
    } else {
    let ix = ix + 1;
    let selection = changed_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
    }
    Some(Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file,
    }) => {
    let (selection, task) = match file {
    Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
  • replacement in inflorescence/src/cursor.rs at line 203
    [28.6789][28.6789:7109]()
    let (selection, task) = match file {
    Some(LogChangeFileSelection {
    ix: file_ix,
    path: _,
    }) => {
    let log_entry = repo.log.get(log_ix).unwrap();
    [28.6789]
    [28.7109]
    if diff_selected {
    let id_hash = file::log_id_parts_hash(hash, &path);
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    },
    if let Some(nav) = logs
    .diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_down(nav)
    } else {
    Task::none()
    },
    )
    } else {
    let log_entry = repo.log.get(log_ix).unwrap();
  • replacement in inflorescence/src/cursor.rs at line 229
    [28.7110][28.7110:7315]()
    let file_ix = if log_entry
    .file_paths
    .len()
    .saturating_sub(1)
    [28.7110]
    [28.7315]
    let file_ix =
    if log_entry.file_paths.len().saturating_sub(1)
  • replacement in inflorescence/src/cursor.rs at line 238
    [28.7559][28.7559:7678]()
    let file =
    log_file_selection(log_entry, file_ix);
    [28.7559]
    [28.7678]
    let file = log_file_selection(log_entry, file_ix);
  • replacement in inflorescence/src/cursor.rs at line 240
    [28.7679][28.7679:7919](),[28.7966][28.7966:8750](),[28.8750][29.9247:9312](),[29.9312][28.8983:9307](),[28.8983][28.8983:9307](),[28.9307][29.9313:9374](),[29.9374][28.9524:9832](),[28.9524][28.9524:9832]()
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    }
    None => {
    let (selection, task) = if repo
    .log
    .len()
    .saturating_sub(1)
    == log_ix
    {
    if !repo.untracked_files.is_empty() {
    let ix = 0;
    let selection =
    untracked_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    } else if !repo.changed_files.is_empty() {
    let ix = 0;
    let selection = changed_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    } else {
    let ix = 0;
    log_selection(repo, ix)
    }
    [28.7679]
    [28.9832]
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    }
    }
    None => {
    let selection =
    if repo.log.len().saturating_sub(1) == log_ix {
    if !repo.untracked_files.is_empty() {
    let ix = 0;
    untracked_file_selection(repo, ix, files)
    } else if !repo.changed_files.is_empty() {
    let ix = 0;
    changed_file_selection(repo, ix, files)
  • replacement in inflorescence/src/cursor.rs at line 261
    [28.9873][28.9873:9930]()
    let ix = log_ix + 1;
    [28.9873]
    [28.9930]
    let ix = 0;
  • replacement in inflorescence/src/cursor.rs at line 263
    [28.9990][28.9990:10180]()
    };
    (selection, task)
    }
    };
    (Some(selection), task)
    [28.9990]
    [28.10180]
    }
    } else {
    let ix = log_ix + 1;
    log_selection(repo, ix)
    };
    (selection, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 270
    [28.10202][28.10202:10232](),[28.10232][29.9375:9517]()
    None => {
    let (selection, task) = if !repo
    .untracked_files
    .is_empty()
    [28.10202]
    [29.9517]
    };
    (Some(selection), task)
    }
    None => {
    let (selection, task) = if !repo.untracked_files.is_empty() {
    let ix = 0;
    let selection =
    Some(untracked_file_selection(repo, ix, files));
    (selection, Task::none())
    } else if !repo.changed_files.is_empty() {
    let ix = 0;
    let selection =
    Some(changed_file_selection(repo, ix, files));
    (selection, Task::none())
    } else if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    let selection = log_selection(repo, ix);
    (Some(selection), Task::none())
    } else {
    (None, Task::none())
    };
    (selection, task)
    }
    };
    state.selection = selection;
    task
    } else {
    Task::none()
    }
    }
    fn select_up<M>(
    state: &mut State,
    files: &mut file::State,
    repo: Option<&repo::State>,
    files_diffs: &diff::FilesState,
    logs: &diff::LogFilesAndState,
    ) -> Task<M> {
    // TODO stair
    if let Some(repo) = repo.as_ref() {
    let (selection, task) = match state.selection.take() {
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    }) => {
    let (selection, task) = if diff_selected {
    let id_hash =
    file::id_parts_hash(&path, file::Kind::Untracked);
    (
    Selection::UntrackedFile {
    ix,
    path,
    diff_selected,
    },
    if let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
  • replacement in inflorescence/src/cursor.rs at line 329
    [29.9543][29.9543:10289]()
    let ix = 0;
    let selection =
    Some(untracked_file_selection(repo, ix, files));
    (selection, Task::none())
    } else if !repo.changed_files.is_empty() {
    let ix = 0;
    let selection =
    Some(changed_file_selection(repo, ix, files));
    (selection, Task::none())
    } else if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    let (selection, task) = log_selection(repo, ix);
    (Some(selection), task)
    [29.9543]
    [29.10289]
    iced_nav_scrollable::scroll_up(nav)
  • replacement in inflorescence/src/cursor.rs at line 331
    [29.10322][29.10322:10398](),[29.10398][28.11599:11641](),[28.11599][28.11599:11641]()
    (None, Task::none())
    };
    (selection, task)
    [29.10322]
    [28.11641]
    Task::none()
    },
    )
    } else if 0 == ix {
    if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    (log_selection(repo, ix), Task::none())
    } else if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    let selection = changed_file_selection(repo, ix, files);
    (selection, Task::none())
    } else {
    let ix = repo.untracked_files.len() - 1;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
  • edit in inflorescence/src/cursor.rs at line 348
    [28.11663]
    [28.11663]
    } else {
    let ix = ix - 1;
    let selection = untracked_file_selection(repo, ix, files);
    (selection, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 353
    [28.11682][28.11682:11798]()
    state.selection = selection;
    task
    } else {
    Task::none()
    [28.11682]
    [28.11798]
    (Some(selection), task)
    }
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    }) => {
    let (selection, task) = if diff_selected {
    let id_hash =
    file::id_parts_hash(&path, file::Kind::Changed);
    (
    Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    },
    if let Some(nav) = files_diffs
    .get(&id_hash)
    .and_then(|state| state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_up(nav)
    } else {
    Task::none()
    },
    )
    } else if 0 == ix {
    let selection = if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(repo, ix, files)
    } else if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    log_selection(repo, ix)
    } else {
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(repo, ix, files)
    };
    (selection, Task::none())
    } else {
    let ix = ix - 1;
    let selection = changed_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
  • replacement in inflorescence/src/cursor.rs at line 397
    [28.11812][28.11812:11822](),[28.11822][30.963:984](),[30.984][28.11849:12414](),[28.11849][28.11849:12414](),[28.12414][29.10399:10524](),[29.10524][28.12706:12874](),[28.12706][28.12706:12874](),[28.12874][29.10525:10652](),[29.10652][28.13168:13256](),[28.13168][28.13168:13256]()
    }
    Msg::Up => {
    if let Some(repo) = repo.as_ref() {
    let (selection, task) = match state.selection.take() {
    Some(Selection::UntrackedFile { ix, path: _ }) => {
    let (selection, task) = if 0 == ix {
    if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    log_selection(repo, ix)
    } else if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
    } else {
    let ix = repo.untracked_files.len() - 1;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    }
    [28.11812]
    [28.13256]
    Some(Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file,
    }) => {
    let (selection, task) = match file {
    Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }) => {
    if diff_selected {
    let id_hash = file::log_id_parts_hash(hash, &path);
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    },
    if let Some(nav) = logs
    .diffs
    .get(&id_hash)
    .and_then(|diff| diff.state.nav.as_ref())
    {
    iced_nav_scrollable::scroll_up(nav)
    } else {
    Task::none()
    },
    )
  • replacement in inflorescence/src/cursor.rs at line 433
    [28.13289][28.13289:13334](),[28.13334][29.10653:10772](),[29.10772][28.13604:13733](),[28.13604][28.13604:13733]()
    let ix = ix - 1;
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    };
    (Some(selection), task)
    [28.13289]
    [28.13733]
    let log_entry = repo.log.get(log_ix).unwrap();
    let file_ix = if 0 == file_ix {
    log_entry.file_paths.len() - 1
    } else {
    file_ix - 1
    };
    let file = log_file_selection(log_entry, file_ix);
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    }
  • replacement in inflorescence/src/cursor.rs at line 453
    [28.13755][28.13755:13952]()
    Some(Selection::ChangedFile { ix, path: _ }) => {
    let (selection, task) = if 0 == ix {
    if !repo.untracked_files.is_empty() {
    [28.13755]
    [28.13952]
    None => {
    let selection = if 0 == log_ix {
    if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(repo, ix, files)
    } else if !repo.untracked_files.is_empty() {
  • replacement in inflorescence/src/cursor.rs at line 460
    [28.14025][29.10773:10900](),[29.10900][28.14319:14438](),[28.14319][28.14319:14438]()
    let selection =
    untracked_file_selection(repo, ix, files);
    (selection, Task::none())
    } else if !repo.log.is_empty() {
    [28.14025]
    [28.14438]
    untracked_file_selection(repo, ix, files)
    } else {
  • edit in inflorescence/src/cursor.rs at line 464
    [28.14555][28.14555:14663](),[28.14663][29.10901:11026](),[29.11026][28.14955:15013](),[28.14955][28.14955:15013]()
    } else {
    let ix = repo.changed_files.len() - 1;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 466
    [28.15076][28.15076:15121](),[28.15121][29.11027:11144](),[29.11144][28.15389:15443](),[28.15389][28.15389:15443]()
    let ix = ix - 1;
    let selection =
    changed_file_selection(repo, ix, files);
    (selection, Task::none())
    [28.15076]
    [28.15443]
    let ix = log_ix - 1;
    log_selection(repo, ix)
  • replacement in inflorescence/src/cursor.rs at line 469
    [28.15470][28.15470:15518]()
    (Some(selection), task)
    [28.15470]
    [28.15518]
    (selection, Task::none())
  • edit in inflorescence/src/cursor.rs at line 471
    [28.15540]
    [28.15540]
    };
    (Some(selection), task)
    }
    None => {
    let (selection, task) = if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    let selection = log_selection(repo, ix);
    (Some(selection), Task::none())
    } else if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    let selection = changed_file_selection(repo, ix, files);
    (Some(selection), Task::none())
    } else if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    let selection = untracked_file_selection(repo, ix, files);
    (Some(selection), Task::none())
    } else {
    (None, Task::none())
    };
    (selection, task)
    }
    };
    state.selection = selection;
    task
    } else {
    Task::none()
    }
    }
    fn select_left<M>(state: &mut State, repo: Option<&repo::State>) -> Task<M> {
    if let Some(_repo) = repo.as_ref() {
    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 {
    ix: file_ix,
    path,
    diff_selected: false,
    }),
    })
    } else {
  • replacement in inflorescence/src/cursor.rs at line 528
    [28.15588][28.15588:15624]()
    ix: log_ix,
    [28.15588]
    [28.15624]
    ix,
  • replacement in inflorescence/src/cursor.rs at line 531
    [28.15718][28.15718:16096]()
    file,
    }) => {
    let (selection, task) = match file {
    Some(LogChangeFileSelection {
    ix: file_ix,
    path: _,
    }) => {
    let log_entry = repo.log.get(log_ix).unwrap();
    [28.15687]
    [28.16096]
    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,
    };
    state.selection = selection;
    }
    Task::none()
    }
  • replacement in inflorescence/src/cursor.rs at line 563
    [28.16097][28.16097:16712](),[28.16759][28.16759:17312](),[28.17312][29.11145:11206](),[29.11206][28.17529:17926](),[28.17529][28.17529:17926](),[28.17926][29.11207:11272](),[29.11272][28.18159:18836](),[28.18159][28.18159:18836]()
    let file_ix = if 0 == file_ix {
    log_entry.file_paths.len() - 1
    } else {
    file_ix - 1
    };
    let file =
    log_file_selection(log_entry, file_ix);
    (
    Selection::LogChange {
    ix: log_ix,
    hash,
    message,
    file: Some(file),
    },
    Task::none(),
    )
    }
    None => {
    let (selection, task) = if 0 == log_ix {
    if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    let selection = changed_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    } else if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    let selection =
    untracked_file_selection(
    repo, ix, files,
    );
    (selection, Task::none())
    } else {
    let ix = repo.log.len() - 1;
    log_selection(repo, ix)
    }
    } else {
    let ix = log_ix - 1;
    log_selection(repo, ix)
    };
    (selection, task)
    }
    };
    (Some(selection), task)
    [28.16097]
    [28.18836]
    fn select_right<M>(
    state: &mut State,
    repo: Option<&repo::State>,
    files_diffs: &diff::FilesState,
    logs: &diff::LogFilesAndState,
    ) -> Task<M> {
    if let Some(repo) = repo.as_ref() {
    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
    // TODO: simplify in rust 1.88
    let task = if diff_selected {
    if 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()
    }
    } 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
    // TODO: simplify in rust 1.88
    let task = if diff_selected {
    if 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()
  • replacement in inflorescence/src/cursor.rs at line 626
    [28.18858][28.18858:19282](),[28.19282][29.11273:11390](),[29.11390][28.19550:19748](),[28.19550][28.19550:19748](),[28.19748][29.11391:11510](),[29.11510][28.20018:20229](),[28.20018][28.20018:20229]()
    None => {
    let (selection, task) = if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    let (selection, task) = log_selection(repo, ix);
    (Some(selection), task)
    } else if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    let selection =
    changed_file_selection(repo, ix, files);
    (Some(selection), Task::none())
    } else if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    let selection =
    untracked_file_selection(repo, ix, files);
    (Some(selection), Task::none())
    } else {
    (None, Task::none())
    };
    (selection, task)
    [28.18858]
    [28.20229]
    } else {
    Task::none()
    };
    (
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected,
    }),
    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
    // TODO: simplify in rust 1.88
    let task = if diff_selected {
    if 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()
  • edit in inflorescence/src/cursor.rs at line 665
    [28.20251]
    [28.20251]
    } else {
    Task::none()
  • replacement in inflorescence/src/cursor.rs at line 668
    [28.20270][28.20270:20387]()
    state.selection = selection;
    task
    } else {
    Task::none()
    [28.20270]
    [28.20387]
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected,
    }),
    }),
    task,
    )
  • replacement in inflorescence/src/cursor.rs at line 682
    [28.20401][28.20401:20411](),[28.20411][50.43:401](),[50.436][50.436:682](),[50.721][50.721:765]()
    }
    Msg::Left => {
    if let Some(_repo) = repo.as_ref() {
    let (selection, task): (Option<Selection>, Task<repo::MsgIn>) =
    match state.selection.take() {
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: Some(_),
    }) => (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    [28.20401]
    [50.765]
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    }) => {
    let log_entry = repo.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,
  • replacement in inflorescence/src/cursor.rs at line 698
    [50.839][50.839:1321]()
    ),
    selection @ (Some(Selection::UntrackedFile {
    ..
    })
    | Some(Selection::ChangedFile {
    ..
    })
    | Some(Selection::LogChange {
    file: None,
    ..
    })
    | None) => (selection, Task::none()),
    [50.839]
    [50.1321]
    )
    } else {
    (None, Task::none())
  • replacement in inflorescence/src/cursor.rs at line 702
    [50.1344][50.1344:1410]()
    state.selection = selection;
    task
    [50.1344]
    [50.1410]
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file,
    }),
    task,
    )
    }
    selection => (selection, Task::none()),
    };
    state.selection = selection;
    task
    } else {
    Task::none()
    }
    }
    fn select_exact<M>(
    select: Select,
    state: &mut State,
    files: &mut file::State,
    repo: Option<&repo::State>,
    files_diffs: &diff::FilesState,
    logs: &diff::LogFilesAndState,
    ) -> Task<M> {
    let (selection, task) = match select {
    Select::UntrackedFile { ix, path } => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    let id_hash = file::id_hash(&id);
    file::load_src_file_if_not_cached(files, id);
    // If the selected file's diff is already loaded (it has an attached
    // state), scroll back to its last offset
    // TODO: simplify in rust 1.88
    let is_diff_scrollable =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    let task = if is_diff_scrollable {
    if 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()
    }
  • edit in inflorescence/src/cursor.rs at line 753
    [50.1460]
    [50.1460]
    };
    (
    Some(Selection::UntrackedFile {
    ix,
    path,
    diff_selected: false,
    }),
    task,
    )
    }
    Select::ChangedFile { ix, path } => {
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    let id_hash = file::id_hash(&id);
    if let Some(diffs) =
    repo.as_ref().and_then(|repo| repo.changed_files.get(&path))
    {
    if diff::any_diff_has_contents(diffs) {
    file::load_src_file_if_not_cached(files, id);
    }
  • replacement in inflorescence/src/cursor.rs at line 776
    [50.1474][50.1474:1508](),[30.1021][28.20459:20793](),[50.1508][28.20459:20793](),[28.20459][28.20459:20793](),[28.20828][50.1509:1549](),[50.1549][28.20862:20894](),[28.20862][28.20862:20894](),[28.20894][50.1550:1779](),[50.1779][28.21679:21713](),[28.21679][28.21679:21713](),[28.21713][50.1780:1953](),[50.1953][28.22009:22049](),[28.22009][28.22009:22049](),[28.22049][50.1954:2004](),[50.2004][28.22091:22162](),[28.22091][28.22091:22162](),[28.22162][50.2005:2306](),[50.2349][50.2349:2495](),[50.2495][28.22652:22861](),[28.22652][28.22652:22861](),[28.22861][50.2496:2651](),[50.2651][28.22861:23070](),[28.22861][28.22861:23070]()
    }
    Msg::Right => {
    if let Some(repo) = repo.as_ref() {
    let (selection, task): (Option<Selection>, Task<repo::MsgIn>) =
    match state.selection.take() {
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    }) => {
    let log_entry = repo.log.get(ix).unwrap();
    let (file, task) = if let Some(path) =
    log_entry.file_paths.first()
    {
    (
    Some(LogChangeFileSelection {
    ix: 0,
    path: path.clone(),
    }),
    Task::none(),
    )
    } else {
    (None, Task::none())
    };
    (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file,
    }),
    task,
    )
    }
    selection @ (Some(Selection::UntrackedFile {
    ..
    })
    | Some(Selection::ChangedFile {
    ..
    })
    | Some(Selection::LogChange {
    file: Some(_),
    ..
    })
    | None) => (selection, Task::none()),
    };
    state.selection = selection;
    task
    [50.1474]
    [28.23070]
    // If the selected file's diff is already loaded (it has an attached
    // state), scroll back to its last offset
    // TODO: simplify in rust 1.88
    let is_diff_scrollable =
    diff::file_diff_needs_scrolling(files_diffs, id_hash);
    let task = if is_diff_scrollable {
    if 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()
    }
  • replacement in inflorescence/src/cursor.rs at line 792
    [28.23120][28.23120:23134]()
    }
    [28.23120]
    [28.23134]
    };
    (
    Some(Selection::ChangedFile {
    ix,
    path,
    diff_selected: false,
    }),
    task,
    )
  • replacement in inflorescence/src/cursor.rs at line 802
    [28.23144][30.1022:1055](),[30.1055][28.23183:23290](),[28.23183][28.23183:23290](),[28.23290][29.11511:11632](),[29.11632][28.23451:23499](),[28.23451][28.23451:23499](),[28.23499][29.11633:11695](),[29.11695][28.23559:23999](),[28.23559][28.23559:23999](),[28.23999][29.11696:11841](),[29.11841][28.24192:24248](),[28.24192][28.24192:24248](),[28.24248][29.11842:11910](),[29.11910][28.24314:24380](),[28.24314][28.24314:24380]()
    Msg::Select(select) => {
    let (selection, task) = match select {
    Select::UntrackedFile { ix, path } => {
    file::load_src_file_if_not_cached(
    files,
    file::Id {
    path: path.clone(),
    file_kind: file::Kind::Untracked,
    },
    );
    (Some(Selection::UntrackedFile { ix, path }), Task::none())
    }
    Select::ChangedFile { ix, path } => {
    if let Some(diffs) = repo
    .as_ref()
    .and_then(|repo| repo.changed_files.get(&path))
    {
    if diff::any_diff_has_contents(diffs) {
    file::load_src_file_if_not_cached(
    files,
    file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    },
    );
    [28.23144]
    [28.24380]
    Select::LogChange { ix, hash, message } => (
    Some(Selection::LogChange {
    ix,
    hash,
    message,
    file: None,
    }),
    Task::none(),
    ),
    Select::LogChangeFile { ix: file_ix, path } => {
    match state.selection.take() {
    Some(Selection::LogChange {
    ix: change_ix,
    hash,
    message,
    file: _,
    }) => {
    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
    // TODO: simplify in rust 1.88
    let task = if diff_selected {
    if 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()
  • replacement in inflorescence/src/cursor.rs at line 835
    [28.24406][28.24406:24714]()
    }
    (Some(Selection::ChangedFile { ix, path }), Task::none())
    }
    Select::LogChange { ix, hash, message } => {
    // Request to get the diffs
    let task = Task::done(repo::MsgIn::GetChangeDiffs { hash });
    [28.24406]
    [28.24714]
    } else {
    Task::none()
    };
    let file = LogChangeFileSelection {
    ix: file_ix,
    path,
    diff_selected: false,
    };
  • replacement in inflorescence/src/cursor.rs at line 845
    [28.24788][28.24788:24820]()
    ix,
    [28.24788]
    [28.24820]
    ix: change_ix,
  • replacement in inflorescence/src/cursor.rs at line 848
    [28.24932][28.24932:24972]()
    file: None,
    [28.24891]
    [28.24972]
    file: Some(file),
  • edit in inflorescence/src/cursor.rs at line 852
    [28.25052][28.25052:25352](),[28.25387][28.25387:25801](),[28.25844][28.25844:26122]()
    }
    Select::LogChangeFile { ix: file_ix, path } => {
    match state.selection.take() {
    Some(Selection::LogChange {
    ix: change_ix,
    hash,
    message,
    file: _,
    }) => {
    let file =
    LogChangeFileSelection { ix: file_ix, path };
    (
    Some(Selection::LogChange {
    ix: change_ix,
    hash,
    message,
    file: Some(file),
    }),
    Task::none(),
    )
    }
    selection => (selection, Task::none()),
    }
  • replacement in inflorescence/src/cursor.rs at line 853
    [28.26140][28.26140:26213]()
    };
    state.selection = selection;
    task
    [28.26140]
    [28.26213]
    selection => (selection, Task::none()),
    }
  • edit in inflorescence/src/cursor.rs at line 856
    [28.26223][28.26223:26304](),[28.26304][29.11911:11940](),[29.11940][28.26393:26480](),[28.26393][28.26393:26480](),[28.26480][29.11941:11965](),[29.11965][28.26502:26530](),[28.26502][28.26502:26530](),[28.26530][29.11966:12008]()
    }
    }
    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,
  • replacement in inflorescence/src/cursor.rs at line 857
    [28.26577][29.12009:12059](),[29.12059][28.26645:26761](),[28.26645][28.26645:26761](),[28.26761][29.12060:12089](),[29.12089][28.26850:26980](),[28.26850][28.26850:26980](),[28.26980][29.12090:12118](),[29.12118][28.27006:27038](),[28.27006][28.27006:27038](),[28.27038][29.12119:12163](),[29.12163][28.27080:27091](),[28.27080][28.27080:27091](),[28.27091][29.12164:12218](),[29.12218][28.27163:27245](),[28.27163][28.27163:27245]()
    file::load_src_file_if_not_cached(files, id);
    Selection::UntrackedFile { ix, path }
    }
    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);
    }
    Selection::ChangedFile {
    ix,
    path: path.clone(),
    }
    [28.26577]
    [28.27245]
    state.selection = selection;
    task
  • edit in inflorescence/src/cursor.rs at line 860
    [28.27247][28.27247:27628](),[28.27653][28.27653:27938]()
    pub fn log_selection(
    repo: &repo::State,
    ix: usize,
    ) -> (Selection, Task<repo::MsgIn>) {
    let entry = repo.log.get(ix).unwrap();
    // Request to get the diffs
    let task = Task::done(repo::MsgIn::GetChangeDiffs { hash: entry.hash });
    (
    Selection::LogChange {
    ix,
    hash: entry.hash,
    message: entry.message.clone(),
    file: None,
    },
    task,
    )
    }
    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 }
    }
  • replacement in iced_nav_scrollable/src/lib.rs at line 8
    [51.190][51.190:239]()
    use iced::widget::{self, container, scrollable};
    [51.190]
    [53.11770]
    use iced::widget::{self, container, scrollable, Scrollable};
  • edit in iced_nav_scrollable/src/lib.rs at line 43
    [51.681][51.681:717]()
    ScrollToPrev,
    ScrollToNext,
  • edit in iced_nav_scrollable/src/lib.rs at line 185
    [52.3895][51.2369:2435](),[51.2369][51.2369:2435](),[51.2435][52.3896:4101](),[52.4101][51.2614:2632](),[51.2614][51.2614:2632](),[51.2632][53.13381:13425](),[53.13425][51.2682:2847](),[51.2682][51.2682:2847]()
    Task::none()
    }
    Msg::ScrollToNext => {
    if nav.pending_tasks.is_none() {
    if let Some(y) = nav
    .section_offsets
    .iter()
    .find(|offset| *offset > &nav.offset)
    {
    return task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset { x: 0.0, y: *y },
    );
    }
    }
  • edit in iced_nav_scrollable/src/lib.rs at line 187
    [51.2882][51.2882:2913](),[51.2913][52.4102:4147](),[52.4147][51.2981:3018](),[51.2981][51.2981:3018](),[51.3018][52.4148:4185](),[52.4185][51.3047:3102](),[51.3047][51.3047:3102](),[51.3102][52.4186:4244](),[52.4244][51.3162:3180](),[51.3162][51.3162:3180](),[51.3180][53.13426:13470](),[53.13470][51.3230:3430](),[51.3230][51.3230:3430]()
    Msg::ScrollToPrev => {
    if nav.pending_tasks.is_none() {
    if let Some(y) = nav
    .section_offsets
    .iter()
    .rev()
    .find(|offset| *offset < &nav.offset)
    {
    return task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset { x: 0.0, y: *y },
    );
    }
    }
    Task::none()
    }
  • replacement in iced_nav_scrollable/src/lib.rs at line 197
    [52.4316][51.3740:3783](),[51.3740][51.3740:3783]()
    ) -> Element<'a, Message, Theme, Renderer>
    [52.4316]
    [51.3783]
    ) -> Scrollable<'a, Message, Theme, Renderer>
  • replacement in iced_nav_scrollable/src/lib.rs at line 210
    [51.4371][53.13471:13500](),[53.13500][51.4390:4549](),[51.4390][51.4390:4549](),[51.4549][53.13501:13531]()
    let nav = Element::from(
    widget::scrollable(widget::column(children))
    .id(nav.id.clone())
    .on_scroll(move |viewport| map_msg(Msg::Scrolled(viewport))),
    );
    Element::from(nav)
    [51.4371]
    [51.4555]
    widget::scrollable(widget::column(children))
    .id(nav.id.clone())
    .on_scroll(move |viewport| map_msg(Msg::Scrolled(viewport)))
    }
    /// A task to scroll to the last stored offset
    pub fn scroll_to_stored_offset<M>(nav: &NavScrollable) -> Task<M> {
    task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset {
    x: 0.0,
    y: nav.offset,
    },
    )
    }
    // TODO: time delta
    const DELTA: f32 = 10.0;
    /// Scroll down, skipping any sections that are marked to skip
    pub fn scroll_down<M>(nav: &NavScrollable) -> Task<M> {
    if let Some(y) = nav
    .section_offsets
    .iter()
    .zip(nav.section_heights.values())
    .enumerate()
    .find_map(|(ix, (offset, height))| {
    if !nav.skip_sections.contains(&ix) {
    // dbg!(ix, offset, height, nav.offset);
    let bottom_frame = saturating_sub(
    nav.offset + nav.height,
    VISIBLE_CONTEXT_HEIGHT,
    );
    if *offset < bottom_frame && offset + height > bottom_frame {
    // The top edge of the section is visible, but the bottom
    // edge of is below the bottom edge of nav, scroll down
    Some(nav.offset + DELTA)
    } else if *offset > nav.offset + nav.height {
    // Scroll to the next section
    Some(saturating_sub(*offset, VISIBLE_CONTEXT_HEIGHT))
    } else {
    None
    }
    } else {
    None
    }
    })
    {
    return task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset { x: 0.0, y },
    );
    }
    Task::none()
    }
    /// Scroll up, skipping any sections that are marked to skip
    pub fn scroll_up<M>(nav: &NavScrollable) -> Task<M> {
    if let Some(y) = nav
    .section_offsets
    .iter()
    .zip(nav.section_heights.values())
    .enumerate()
    .rev()
    .find_map(|(ix, (offset, height))| {
    if !nav.skip_sections.contains(&ix) {
    // dbg!(ix, offset, height, nav.offset);
    let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;
    if offset + height > top_frame && *offset < top_frame {
    // The bottom edge of the section is visible, but the top
    // edge of is above the top edge of nav, scroll up
    Some(saturating_sub(nav.offset, DELTA))
    } else if
    // offset + height < nav.offset &&
    offset + height
    < saturating_sub(nav.offset, VISIBLE_CONTEXT_HEIGHT)
    {
    // Scroll to the bottom of the prev section
    Some(saturating_sub(
    offset + height + VISIBLE_CONTEXT_HEIGHT,
    nav.height,
    ))
    } else {
    None
    }
    } else {
    None
    }
    })
    {
    return task::scroll_to(
    nav.id.clone(),
    scrollable::AbsoluteOffset { x: 0.0, y },
    );
    }
    Task::none()
  • replacement in iced_nav_scrollable/src/lib.rs at line 360
    [57.1157][57.1157:1291]()
    let y = if *y > -VISIBLE_CONTEXT_HEIGHT {
    *y - VISIBLE_CONTEXT_HEIGHT
    } else {
    0.0
    };
    [57.1157]
    [57.1291]
    let y = saturating_sub(*y, VISIBLE_CONTEXT_HEIGHT);
  • edit in iced_nav_scrollable/src/lib.rs at line 453
    [51.6289]
    fn saturating_sub(left: f32, right: f32) -> f32 {
    if left > right {
    left - right
    } else {
    0.0
    }
    }