navigate log entries

[?]
May 5, 2025, 4:38 PM
AHWWRC73FXLSUDAJBU5UU76MZETHD3DSGJ7OLZPFEHXBDJ733QNAC

Dependencies

  • [2] WT3GA27P add cursor with selection
  • [3] EC3TVL4X add untracked files
  • [4] KT5UYXGK fix selection after adding file, add changed file diffs
  • [5] YBJRDOTC make all repo actions async
  • [6] 4WO3ZJM2 show untracked files' contents
  • [7] W4LFX7IH group diffs by file name
  • [8] V55EAIWQ add src file LRU cache
  • [9] NRCUG4R2 load changed files src when selected
  • [10] Y5ATDI2H convert changed file diffs and load src only if any needs it
  • [11] ZVI4AWER woot contents_diff
  • [12] OQ6HSAWH show record log
  • [13] KM5PSZ4A watch repo once loaded
  • [14] A5YBC77V record!
  • [15] DVKSPF7R track selected file path together with an index
  • [16] SWWE2R6M display basic repo stuff
  • [17] QMAUTRB6 refactor diff
  • [18] D7A7MSIH allow to defer or abandon record, add buttons
  • [19] AMPZ2BXK show changed files diffs (only Edit atm)
  • [*] 6YZAVBWU Initial commit

Change contents

  • replacement in crates/libflorescence/src/repo.rs at line 144
    [5.587][5.587:627]()
    RefreshedChangedAndUntrackedFiles {
    [5.587]
    [5.627]
    RefreshedState {
  • edit in crates/libflorescence/src/repo.rs at line 147
    [7.259]
    [5.716]
    log: Log,
  • edit in crates/libflorescence/src/repo.rs at line 159
    [12.249]
    [12.249]
    pub file_paths: Vec<String>,
  • replacement in crates/libflorescence/src/repo.rs at line 207
    [5.2198][5.2198:2386]()
    let _ =
    msg_out_tx.send(MsgOut::RefreshedChangedAndUntrackedFiles {
    untracked_files,
    changed_files,
    });
    [5.2198]
    [5.2386]
    let log = state.log.clone();
    let _ = msg_out_tx.send(MsgOut::RefreshedState {
    untracked_files,
    changed_files,
    log,
    });
  • edit in crates/libflorescence/src/repo.rs at line 760
    [12.2375]
    [12.2375]
    let files = repo.changes.get_changes(&h).unwrap();
    let file_paths: HashSet<String> = files
    .into_iter()
    .map(|hunk| hunk.path().to_string())
    .collect();
    let file_paths: Vec<String> = file_paths.into_iter().collect();
  • edit in crates/libflorescence/src/repo.rs at line 769
    [12.2448]
    [12.2448]
    file_paths,
  • edit in crates/inflorescence/src/main.rs at line 224
    [11.2918]
    [4.4615]
    // TODO 2nd level cursor, should work much the same
    LogEntryFileSelect {
    path: String,
    },
  • edit in crates/inflorescence/src/main.rs at line 292
    [10.1111]
    [9.755]
    let log_selection = |repo: &repo::State, ix: usize| -> cursor::Selection {
    let entry = repo.log.iter().nth(ix).unwrap();
  • edit in crates/inflorescence/src/main.rs at line 296
    [9.756]
    [5.6702]
    cursor::Selection::LogChange {
    ix,
    hash: entry.hash,
    file: None,
    }
    };
  • replacement in crates/inflorescence/src/main.rs at line 323
    [5.7096][11.4279:5730]()
    state.cursor.selection =
    match state.cursor.selection.as_ref() {
    Some(cursor::Selection::UntrackedFile {
    ix,
    path: _,
    diffs: _,
    }) => {
    let new_selection =
    if repo.untracked_files.len().saturating_sub(1)
    == *ix
    {
    if repo.changed_files.is_empty() {
    let ix = 0;
    untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    } else {
    let ix = 0;
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    }
    [5.7096]
    [11.5730]
    state.cursor.selection = match state.cursor.selection.as_ref() {
    Some(cursor::Selection::UntrackedFile {
    ix,
    path: _,
    diffs: _,
    }) => {
    let new_selection =
    if repo.untracked_files.len().saturating_sub(1)
    == *ix
    {
    if !repo.changed_files.is_empty() {
    let ix = 0;
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    } else if !repo.log.is_empty() {
    let ix = 0;
    log_selection(repo, ix)
  • replacement in crates/inflorescence/src/main.rs at line 345
    [11.5771][11.5771:5824]()
    let ix = ix + 1;
    [11.5771]
    [6.3254]
    let ix = 0;
  • replacement in crates/inflorescence/src/main.rs at line 352
    [6.3591][11.5890:6476]()
    };
    Some(new_selection)
    }
    Some(cursor::Selection::ChangedFile {
    ix,
    path: _,
    diffs: _,
    }) => {
    let new_selection =
    if repo.changed_files.len().saturating_sub(1)
    == *ix
    {
    if repo.untracked_files.is_empty() {
    [6.3591]
    [11.6476]
    }
    } else {
    let ix = ix + 1;
    untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    };
    Some(new_selection)
    }
    Some(cursor::Selection::ChangedFile {
    ix,
    path: _,
    diffs: _,
    }) => {
    let new_selection = 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;
    untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    } else {
    let ix = 0;
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    }
    } else {
    let ix = ix + 1;
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    };
    Some(new_selection)
    }
    Some(cursor::Selection::LogChange {
    ix,
    hash: _,
    file,
    }) => {
    let new_selection = match file {
    Some(_) => todo!(),
    None => {
    if repo.log.len().saturating_sub(1) == *ix {
    if !repo.untracked_files.is_empty() {
  • replacement in crates/inflorescence/src/main.rs at line 417
    [11.6528][11.6528:6592]()
    changed_file_selection(
    [11.6528]
    [11.6592]
    untracked_file_selection(
  • replacement in crates/inflorescence/src/main.rs at line 423
    [11.6869][11.6869:6914]()
    } else {
    [11.6869]
    [11.6914]
    } else if !repo.changed_files.is_empty() {
  • replacement in crates/inflorescence/src/main.rs at line 425
    [11.6966][11.6966:7032]()
    untracked_file_selection(
    [11.6966]
    [11.7032]
    changed_file_selection(
  • edit in crates/inflorescence/src/main.rs at line 431
    [11.7309]
    [11.7309]
    } else {
    let ix = 0;
    log_selection(repo, ix)
  • replacement in crates/inflorescence/src/main.rs at line 437
    [11.7401][9.757:817](),[5.7721][9.757:817](),[9.817][11.7402:7448](),[10.1183][9.863:907](),[11.7448][9.863:907](),[9.863][9.863:907](),[9.907][11.7449:7513](),[11.7513][9.975:1078](),[9.975][9.975:1078](),[9.1078][11.7514:7623](),[11.7623][12.4597:4729](),[12.4729][11.7623:8242](),[11.7623][11.7623:8242]()
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    };
    Some(new_selection)
    }
    Some(cursor::Selection::Change { hash }) => {
    todo!()
    }
    None => {
    if repo.untracked_files.is_empty() {
    if repo.changed_files.is_empty() {
    None
    } else {
    let ix = 0;
    Some(changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
    [11.7401]
    [5.7790]
    log_selection(repo, ix)
  • edit in crates/inflorescence/src/main.rs at line 439
    [5.7824][5.7824:7861](),[5.8395][3.4167:4211](),[3.4167][3.4167:4211](),[3.4211][11.8243:8306](),[11.8306][6.3964:4006](),[6.3964][6.3964:4006](),[10.1383][9.1754:1794](),[9.1754][9.1754:1794](),[9.1794][11.8307:8367](),[11.8367][9.1858:1954](),[9.1858][9.1858:1954]()
    } else {
    let ix = 0;
    Some(untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
  • edit in crates/inflorescence/src/main.rs at line 440
    [3.5091]
    [2.1547]
    };
    Some(new_selection)
    }
    None => {
    if !repo.untracked_files.is_empty() {
    let ix = 0;
    Some(untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
    } else if !repo.changed_files.is_empty() {
    let ix = 0;
    Some(changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
    } else {
    None
  • replacement in crates/inflorescence/src/main.rs at line 463
    [2.1573][11.8368:8391]()
    };
    [2.1573]
    [5.9011]
    }
    };
  • replacement in crates/inflorescence/src/main.rs at line 477
    [5.9299][5.9299:9435](),[5.9435][6.4516:4574]()
    if repo.changed_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(
    [5.9299]
    [6.4574]
    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;
    changed_file_selection(
  • replacement in crates/inflorescence/src/main.rs at line 489
    [5.9539][5.9539:9610](),[5.9610][9.1955:2011]()
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(
    [5.9539]
    [11.8637]
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(
  • replacement in crates/inflorescence/src/main.rs at line 514
    [5.10043][5.10043:10179](),[5.10179][9.2253:2309]()
    if repo.untracked_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(
    [5.10043]
    [11.8980]
    if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(
  • edit in crates/inflorescence/src/main.rs at line 522
    [9.2550]
    [5.10244]
    } else if !repo.log.is_empty() {
    let ix = repo.log.len() - 1;
    log_selection(repo, ix)
  • replacement in crates/inflorescence/src/main.rs at line 526
    [5.10281][5.10281:10354](),[5.10354][6.5120:5178]()
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(
    [5.10281]
    [6.5178]
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(
  • replacement in crates/inflorescence/src/main.rs at line 545
    [12.4752][12.4752:4850]()
    Some(cursor::Selection::Change { hash }) => {
    todo!()
    [12.4752]
    [5.10629]
    Some(cursor::Selection::LogChange { ix, hash, file }) => {
    let new_selection = match file {
    Some(_) => todo!(),
    None => {
    if 0 == *ix {
    if !repo.changed_files.is_empty() {
    let ix = repo.changed_files.len() - 1;
    changed_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    } else if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    )
    } else {
    let ix = repo.log.len() - 1;
    log_selection(repo, ix)
    }
    } else {
    let ix = ix - 1;
    log_selection(repo, ix)
    }
    }
    };
    Some(new_selection)
  • replacement in crates/inflorescence/src/main.rs at line 579
    [5.10681][5.10681:10952](),[5.10952][6.5434:5579](),[6.5579][11.9241:9301](),[11.9301][8.3979:4040](),[8.3979][8.3979:4040](),[8.4040][6.5718:5753](),[6.5718][6.5718:5753](),[6.5753][5.11025:11055](),[5.11025][5.11025:11055](),[5.11055][3.7184:7217](),[3.7184][3.7184:7217]()
    if repo.changed_files.is_empty() {
    if repo.untracked_files.is_empty() {
    None
    } else {
    let ix = repo.untracked_files.len() - 1;
    Some(untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
    }
    } else {
    [5.10681]
    [5.11056]
    if !repo.changed_files.is_empty() {
  • edit in crates/inflorescence/src/main.rs at line 587
    [9.3104]
    [2.2471]
    } else if !repo.untracked_files.is_empty() {
    let ix = repo.untracked_files.len() - 1;
    Some(untracked_file_selection(
    repo,
    ix,
    &mut state.diffs_cache,
    &state.src_file_load_tx,
    ))
    } else {
    None
  • edit in crates/inflorescence/src/main.rs at line 648
    [2.2657]
    [4.6406]
    Message::LogEntryFileSelect { path } => todo!(),
  • replacement in crates/inflorescence/src/main.rs at line 929
    [5.14967][5.14967:15025]()
    repo::MsgOut::RefreshedChangedAndUntrackedFiles {
    [5.14967]
    [5.15025]
    repo::MsgOut::RefreshedState {
  • edit in crates/inflorescence/src/main.rs at line 932
    [5.15081]
    [5.15081]
    log,
  • edit in crates/inflorescence/src/main.rs at line 937
    [5.15249]
    [11.13968]
    repo.log = log;
  • replacement in crates/inflorescence/src/main.rs at line 1003
    [9.4576][12.4912:4979]()
    cursor::Selection::Change { hash } => todo!(),
    [9.4576]
    [5.16407]
    cursor::Selection::LogChange { ix: _, hash, file } => repo
    .log
    .iter()
    .enumerate()
    .find(|(_ix, entry)| &entry.hash == hash)
    .map(|(ix, entry)| {
    let file = file.as_ref().and_then(|file| {
    entry
    .file_paths
    .iter()
    .enumerate()
    .find(|(_ix, path)| *path == &file.path)
    .map(|(ix, path)| {
    cursor::LogChangeFileSelection {
    ix,
    path: path.clone(),
    }
    })
    });
    cursor::Selection::LogChange {
    ix,
    hash: entry.hash,
    file,
    }
    }),
  • replacement in crates/inflorescence/src/main.rs at line 1140
    [3.7859][11.16536:16652]()
    Some(cursor::Selection::UntrackedFile{ix: selected_ix, path:_, diffs: _}) if &ix == selected_ix
    [3.7859]
    [3.7965]
    Some(cursor::Selection::UntrackedFile{ ix: selected_ix, .. }) if &ix == selected_ix
  • replacement in crates/inflorescence/src/main.rs at line 1155
    [7.4563][2.3660:3737](),[4.7626][2.3660:3737](),[2.3660][2.3660:3737](),[2.3737][11.16737:16850]()
    let is_selected = matches!(state.cursor.selection.as_ref() ,
    Some(cursor::Selection::ChangedFile{ix: selected_ix, path:_, diffs:_}) if &ix == selected_ix
    [7.4563]
    [2.3822]
    let is_selected = matches!(state.cursor.selection.as_ref(),
    Some(cursor::Selection::ChangedFile{ ix: selected_ix, .. }) if &ix == selected_ix
  • replacement in crates/inflorescence/src/main.rs at line 1169
    [2.4169][12.4980:5078]()
    let log = el(column(repo.log.iter().map(
    |repo::LogEntry { hash, message }| {
    [2.4169]
    [12.5078]
    let log = el(column(repo.log.iter().enumerate().map(
    |(ix, repo::LogEntry {
    hash,
    message,
    file_paths: _,
    })| {
  • replacement in crates/inflorescence/src/main.rs at line 1177
    [12.5173][12.5173:5238]()
    // TODO
    let is_selected = false;
    [12.5173]
    [12.5238]
    let is_selected = matches!(state.cursor.selection.as_ref(),
    Some(cursor::Selection::LogChange { ix: selected_ix, .. }) if &ix == selected_ix
    );
  • replacement in crates/inflorescence/src/main.rs at line 1261
    [12.5692][12.5692:5774]()
    Some(cursor::Selection::Change { hash }) => {
    todo!()
    [12.5692]
    [6.9691]
    Some(cursor::Selection::LogChange { ix, hash, file }) => {
    let entry =
    state.repo.as_ref().unwrap().log.iter().nth(*ix).unwrap();
    let mut short_hash = hash.to_base32();
    short_hash.truncate(8);
    let files = entry.file_paths.iter().map(|path| {
    let is_selected = matches!(file, Some(cursor::LogChangeFileSelection{ix: _, path: selected_path}) if selected_path == path);
    el(button(text(path)).on_press_with(|| {
    Message::LogEntryFileSelect { path: path.clone() }
    }).style(selectable_button_style(is_selected)))
    });
    el(column([
    view_diff_header(format!("{} changed files:", short_hash)),
    el(scrollable(column(files))),
    ]))
  • replacement in crates/inflorescence/src/cursor.rs at line 22
    [12.5879][12.5879:5892]()
    Change {
    [12.5879]
    [12.5892]
    LogChange {
    ix: usize,
  • edit in crates/inflorescence/src/cursor.rs at line 25
    [12.5919]
    [11.19105]
    file: Option<LogChangeFileSelection>,
  • edit in crates/inflorescence/src/cursor.rs at line 27
    [11.19112]
    [11.19112]
    }
    #[derive(Debug)]
    pub struct LogChangeFileSelection {
    pub ix: usize,
    pub path: String,