fix moved tracked file view

[?]
Nov 16, 2025, 8:36 PM
HPSOAD4RXHXU7TSVX2AD5ZDGHMS7HLYNRARWHJCXGGH5RWW7LH6QC

Dependencies

  • [2] SWWE2R6M display basic repo stuff
  • [3] KT5UYXGK fix selection after adding file, add changed file diffs
  • [4] A5YBC77V record!
  • [5] 4WO3ZJM2 show untracked files' contents
  • [6] W4LFX7IH group diffs by file name
  • [7] MJDGPSHG WIP contents diff
  • [8] ZVI4AWER woot contents_diff
  • [9] QMAUTRB6 refactor diff
  • [10] WI2BVQ6J rm client lib crate
  • [11] L6KSEFQI move cursor related stuff into its module
  • [12] BFN2VHZS refactor file stuff into sub-mod
  • [13] 23SFYK4Q big view refactor into a new crate
  • [14] XSZZB47U refactor stuff into lib
  • [15] ACDXXAX2 refactor main's updates into smaller fns
  • [16] I56UGW7U make record test, fix log update
  • [17] ESMM3FEL test selection reindexing
  • [18] TSFQFCB2 test got repo change
  • [19] SASAN2XC use nav-scrollable
  • [20] GOLHUD6R nav-scrollable: set skip-able sections
  • [21] WXQBBQ2A update nightly
  • [22] UR4J677R nav for log changes and refactors
  • [23] JZXYSIYD channel selection!
  • [24] 3XRG4BB6 rewritten nav-scrollable!
  • [25] WAOGSCOJ very nice refactor, wip adding channels logs
  • [26] WH57EHNM update tests
  • [27] YK3MOJJL chonky refactor, wip other channels logs & diffs
  • [28] 5O4FWCFP add tests to_record selection and improve it
  • [29] VCNKFNUF app init test
  • [30] SWDPAGF6 test channel name
  • [31] KWTBNTO3 diffs selection and scrolling
  • [32] MYGIBRRH wip custom theme
  • [33] UF5NJKAS test load repo
  • [34] 3BK22XE5 add a test for hover btn and more refactors
  • [35] RDRBP7AL auto-scroll status selection
  • [36] 2LWMGRUM test diff
  • [37] A6Z4O6RC actions menu
  • [38] OJPGHVC3 entire log!
  • [39] EJPSD5XO shared allowed actions conditions between update and view
  • [40] ZIUHKVJK update tests
  • [41] UUB7SHLR more re-use in diffs
  • [42] AMPZ2BXK show changed files diffs (only Edit atm)
  • [43] LFEMJYYD start of to_record selection
  • [44] OC6DLIZ3 test record when nothing to record
  • [*] WT3GA27P add cursor with selection
  • [*] 6YZAVBWU Initial commit

Change contents

  • replacement in libflorescence/src/repo.rs at line 15
    [5.55][4.75:112](),[4.75][4.75:112]()
    use pijul::changestore::ChangeStore;
    [5.55]
    [10.181]
    use pijul::changestore::{ChangeStore, FileMetadata};
  • replacement in libflorescence/src/repo.rs at line 113
    [3.362][3.362:381]()
    Move,
    Del,
    [3.362]
    [3.381]
    Move {
    old_path: String,
    },
    Del {
    contents: Option<Contents>,
    },
  • replacement in libflorescence/src/repo.rs at line 161
    [7.666][7.666:743]()
    ChangedFileDiff::Move => None,
    ChangedFileDiff::Del => None,
    [7.666]
    [7.743]
    ChangedFileDiff::Move { .. } => None,
    ChangedFileDiff::Del { .. } => None,
  • replacement in libflorescence/src/repo.rs at line 809
    [3.1815][3.1815:1861](),[3.1861][6.458:642]()
    add: _,
    path,
    } => {
    changes
    .entry(path.clone())
    .or_default()
    .insert(ChangedFileDiff::Move);
    }
    [3.1815]
    [2.3081]
    add,
    path: old_path,
    } => match add {
    change::Atom::NewVertex(add) => {
    let FileMetadata {
    basename: new_path,
    metadata: _,
    ..
    } = FileMetadata::read(
    &diff.contents[add.start.0.into()..add.end.0.into()],
    );
    changes.entry(new_path.to_string()).or_default().insert(
    ChangedFileDiff::Move {
    old_path: old_path.clone(),
    },
    );
    }
    change::Atom::EdgeMap(_edge_map) => todo!(),
    },
  • replacement in libflorescence/src/repo.rs at line 830
    [3.2013][3.2013:2042]()
    contents: _,
    [3.2013]
    [2.3160]
    contents,
  • replacement in libflorescence/src/repo.rs at line 832
    [2.3182][3.2043:2072]()
    encoding: _,
    [2.3182]
    [6.643]
    encoding,
  • edit in libflorescence/src/repo.rs at line 834
    [6.662]
    [6.662]
    let contents = if let Some(contents) = contents.as_ref() {
    let raw_contents = get_change_contents(
    repo_changes,
    contents,
    &diff.contents,
    )?;
    Some(try_decode_contents(raw_contents, encoding))
    } else {
    None
    };
  • replacement in libflorescence/src/repo.rs at line 848
    [6.761][6.761:812]()
    .insert(ChangedFileDiff::Del);
    [6.761]
    [6.812]
    .insert(ChangedFileDiff::Del { contents });
  • replacement in libflorescence/src/diff.rs at line 49
    [14.1600][14.1600:1609]()
    Del,
    [14.1600]
    [14.1609]
    // Also present in `DiffWithoutContents` to show that the whole file is
    // deleted
    Del {
    contents: Option<String>,
    },
  • edit in libflorescence/src/diff.rs at line 55
    [14.1620]
    [14.1620]
    /// Also present in `DiffWithoutContents` to show the old path
    Move,
  • replacement in libflorescence/src/diff.rs at line 63
    [14.1791][14.1791:1801]()
    Move,
    [14.1791]
    [14.1801]
    Move {
    old_path: String,
    },
    Del,
  • replacement in inflorescence_view/src/diff.rs at line 226
    [13.8590][13.8590:8644]()
    DiffWithContents::Del => el(text("Deleted")),
    [13.8590]
    [13.8644]
    DiffWithContents::Del { contents } => {
    if let Some(contents) = contents {
    let line_num = 1;
    let lines = contents_to_lines(contents);
    let max_line_num = line_num + lines.len();
    let line_num_digits = max_line_num.to_string().len();
    let lines_view =
    lines.into_iter().enumerate().map(|(ix, line)| {
    line_view(
    LineKind::Deleted,
    line_num + ix,
    line_num_digits,
    line,
    )
    });
    el(column(lines_view))
    } else {
    el(text("Deleted"))
    }
    }
  • edit in inflorescence_view/src/diff.rs at line 248
    [13.8700]
    [13.8700]
    DiffWithContents::Move => el(text(format!("Moved"))),
  • edit in inflorescence_view/src/diff.rs at line 260
    [13.8857][13.8857:8912]()
    DiffWithoutContents::Move => el(text("Move")),
  • edit in inflorescence_view/src/diff.rs at line 309
    [13.10576]
    [13.10576]
    DiffWithoutContents::Move { old_path } => {
    el(text(format!("Moved from {old_path}")))
    }
    DiffWithoutContents::Del => el(text("Deleted file")),
  • replacement in inflorescence_model/src/to_record/test.rs at line 146
    [28.9962][28.9962:10012]()
    let change_a_1 = repo::ChangedFileDiff::Move;
    [28.9962]
    [28.10012]
    let change_a_1 = repo::ChangedFileDiff::SolveNameConflict;
  • replacement in inflorescence_model/src/to_record/test.rs at line 148
    [28.10013][28.10013:10062]()
    let change_b_1 = repo::ChangedFileDiff::Del;
    [28.10013]
    [28.10062]
    let change_b_1 = repo::ChangedFileDiff::SolveNameConflict;
  • replacement in inflorescence/src/test.rs at line 2
    [18.47][23.10449:10536]()
    init, reindex_selection, repo_got_change_diffs, selection, update, Msg,
    State,
    [18.47]
    [18.137]
    diff, file, init, reindex_selection, repo, repo_got_change_diffs,
    selection, update, Msg, State,
  • edit in inflorescence/src/test.rs at line 12
    [16.1581][17.144:178]()
    use libflorescence::{file, repo};
  • replacement in inflorescence/src/test.rs at line 776
    [17.5276][17.5276:5310]()
    let task = reindex_selection(
    [17.5276]
    [26.8109]
    // // Add a diff that would make `reindex_selection` expect that the file
    // doesn't exist
    {
    let changed_file = BTreeSet::from_iter([repo::ChangedFileDiff::Del {
    contents: None,
    }]);
    assert!(!diff::should_file_exist(&changed_file));
    ready_state
    .repo
    .changed_files
    .insert("changed_1.rs".to_string(), changed_file);
    }
    let mut task = reindex_selection(
  • replacement in inflorescence/src/test.rs at line 802
    [17.5678][17.5678:5762]()
    assert!(task.is_none());
    assert!(state.files.diffs_cache.inner.is_empty());
    [17.5678]
    [17.5762]
    assert!(task.is_some());
    let msg = await_next_msg(&mut task).await;
    assert_matches!(
    &msg,
    crate::Msg::File(file::Msg::SrcFileDoesntExist { .. })
    );
    // The file will be assembled from the diff
    assert_matches!(
    state
    .files
    .diffs_cache
    .inner
    .peek(&file::id_parts_hash("changed_1.rs", file::Kind::Changed)),
    Some(file::Diff::Loaded(_))
    );
    // Clear the cache for next test case
    state.files.diffs_cache.inner.clear();
  • replacement in inflorescence/src/selection.rs at line 1648
    [25.70981][25.70981:71118]()
    if let Some(diffs) = ctx.repo.changed_files.get(&path)
    && diff::any_diff_has_contents(diffs)
    [25.70981]
    [25.71118]
    let file_task = if let Some(diffs) =
    ctx.repo.changed_files.get(&path)
  • replacement in inflorescence/src/selection.rs at line 1651
    [25.71140][25.71140:71236]()
    file::load_src_file_if_not_cached(ctx.files, id);
    }
    [25.71140]
    [25.71236]
    if diff::should_file_exist(diffs) {
    file::load_src_file_if_not_cached(ctx.files, id);
    Task::none()
    } else {
    file::src_file_doesnt_exist(ctx.files, id, diffs)
    .map(crate::Msg::File)
    }
    } else {
    Task::none()
    };
  • replacement in inflorescence/src/selection.rs at line 1663
    [25.71359][25.71359:71413]()
    (Some(selection), selection_task)
    [25.71359]
    [25.71413]
    (Some(selection), Task::batch([selection_task, file_task]))
  • replacement in inflorescence/src/selection.rs at line 1971
    [27.54195][27.54195:54246]()
    match file::try_get_src_file(files, file_id) {
    [27.54195]
    [22.46480]
    let task = match file::try_get_src_file(files, file_id) {
  • edit in inflorescence/src/selection.rs at line 1981
    [27.54458]
    [22.47086]
    Task::none()
  • replacement in inflorescence/src/selection.rs at line 1990
    [25.75664][22.47343:47395](),[22.47343][22.47343:47395]()
    if diff::any_diff_has_contents(diffs) {
    [22.47343]
    [22.47395]
    if diff::should_file_exist(diffs) {
  • edit in inflorescence/src/selection.rs at line 1992
    [22.47457]
    [22.47457]
    Task::none()
    } else {
    file::src_file_doesnt_exist(files, id, diffs)
    .map(crate::Msg::File)
  • replacement in inflorescence/src/selection.rs at line 1998
    [22.47506][24.16701:16707]()
    }
    [22.47506]
    [22.47513]
    };
  • replacement in inflorescence/src/selection.rs at line 2017
    [22.48023][24.16946:16976]()
    (selection, Task::none())
    [22.48023]
    [27.54459]
    (selection, task)
  • replacement in inflorescence/src/main.rs at line 1168
    [17.9541][21.2400:2439]()
    if selection.is_some()
    [17.9541]
    [25.92966]
    let task = if selection.is_some()
  • edit in inflorescence/src/main.rs at line 1170
    [25.93037][21.2505:2563](),[21.2505][21.2505:2563]()
    && diff::any_diff_has_contents(diffs)
  • replacement in inflorescence/src/main.rs at line 1171
    [21.2581][21.2581:2860](),[21.2860][17.10097:10115](),[17.10097][17.10097:10115]()
    file::load_src_file_if_not_cached(
    files,
    file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    },
    );
    }
    [21.2581]
    [17.10115]
    let id = file::Id {
    path: path.clone(),
    file_kind: file::Kind::Changed,
    };
    if diff::should_file_exist(diffs) {
    file::load_src_file_if_not_cached(files, id);
    Task::none()
    } else {
    file::src_file_doesnt_exist(files, id, diffs)
    .map(Msg::File)
    }
    } else {
    Task::none()
    };
  • replacement in inflorescence/src/main.rs at line 1186
    [17.10116][15.11112:11154](),[15.11112][15.11112:11154]()
    (selection, Task::none())
    [17.10116]
    [15.11154]
    (selection, task)
  • edit in inflorescence/src/file.rs at line 13
    [12.1946]
    [20.598]
    use std::borrow::Cow;
  • edit in inflorescence/src/file.rs at line 29
    [12.2283][27.66831:66878]()
    // TODO watch bad, it has to queue things!
  • edit in inflorescence/src/file.rs at line 42
    [12.2611]
    [12.2611]
    /// File cannot be loaded because it's either been moved or deleted
    SrcFileDoesntExist {
    id: Id,
    },
  • edit in inflorescence/src/file.rs at line 167
    [12.5970]
    [12.5970]
    Msg::SrcFileDoesntExist { id } => {
    let id_hash = id_hash(&id);
    return Some(Loaded {
    file_id: id_hash,
    unchanged_sections: HashSet::new(),
    });
    }
  • edit in inflorescence/src/file.rs at line 217
    [12.6711]
    [12.6711]
    }
    }
    pub fn src_file_doesnt_exist(
    state: &mut State,
    id: Id,
    changed_file: &repo::ChangedFile,
    ) -> Task<Msg> {
    let id_hash = id_hash(&id);
    if !state.diffs_cache.inner.contains(&id_hash) {
    let content = diff::FileContent::Decoded(Cow::from(""));
    let file = diff::init_file(content, Some(changed_file));
    diffs_cache_put(&mut state.diffs_cache, id_hash, Diff::Loaded(file));
    return Task::done(Msg::SrcFileDoesntExist { id });
  • edit in inflorescence/src/file.rs at line 232
    [12.6717]
    [12.6717]
    Task::none()
  • replacement in inflorescence/src/diff.rs at line 45
    [9.4699][9.4699:4801]()
    repo::ChangedFileDiff::Move => {
    without.push(DiffWithoutContents::Move);
    [9.4699]
    [9.4801]
    repo::ChangedFileDiff::Move{ old_path } => {
    without.push(DiffWithoutContents::Move { old_path: old_path.clone() });
    with.push(DiffWithContents::Move);
  • replacement in inflorescence/src/diff.rs at line 49
    [9.4816][9.4816:4910]()
    repo::ChangedFileDiff::Del => {
    with.push(DiffWithContents::Del);
    [9.4816]
    [9.4910]
    repo::ChangedFileDiff::Del { contents } => {
    let contents = match contents {
    Some(repo::Contents::Decoded(lines) | repo::Contents::ShortBase64(lines)) => Some(lines.clone()),
    Some(repo::Contents::UnknownEncoding(_)) | None => None
    };
    without.push(DiffWithoutContents::Del);
    with.push(DiffWithContents::Del{ contents });
  • replacement in inflorescence/src/diff.rs at line 138
    [9.8673][11.1724:1983]()
    /// Returns true if any of the changed file's diffs has contents
    pub fn any_diff_has_contents(changed_file: &repo::ChangedFile) -> bool {
    let (with_contents, _without_contents) =
    from_repo_changed_file(changed_file);
    !with_contents.is_empty()
    [9.8673]
    [19.9073]
    /// Returns true if from the diffs imply that the file should exist.
    pub fn should_file_exist(changed_file: &repo::ChangedFile) -> bool {
    for change in changed_file {
    match change {
    repo::ChangedFileDiff::Del { .. }
    | repo::ChangedFileDiff::AddRoot
    | repo::ChangedFileDiff::DelRoot => return false,
    repo::ChangedFileDiff::Move { .. }
    | repo::ChangedFileDiff::Undel
    | repo::ChangedFileDiff::Add
    | repo::ChangedFileDiff::SolveNameConflict
    | repo::ChangedFileDiff::UnsolveNameConflict
    | repo::ChangedFileDiff::Edit { .. }
    | repo::ChangedFileDiff::Replacement { .. }
    | repo::ChangedFileDiff::SolveOrderConflict
    | repo::ChangedFileDiff::UnsolveOrderConflict
    | repo::ChangedFileDiff::ResurrectZombines => {}
    }
    }
    true
  • replacement in inflorescence/src/diff.rs at line 207
    [7.7052][8.20873:20912]()
    DiffWithContents::Del => {
    [7.7052]
    [7.7102]
    DiffWithContents::Move => {
  • replacement in inflorescence/src/diff.rs at line 209
    [7.7152][7.7152:7232]()
    let deleted: Vec<_> = file_lines.map(str::to_string).collect();
    [7.7152]
    [7.7232]
    let lines: Vec<_> = file_lines.map(str::to_string).collect();
    let max_line_num = lines.len();
    sections.push(Section::Unchanged(lines));
    return Combined {
    sections,
    max_line_num,
    };
    }
    DiffWithContents::Del { contents } => {
    let deleted = contents
    .as_deref()
    .map(contents_to_lines)
    .unwrap_or_default();
  • replacement in iced_expl_widget/src/nav_scrollable.rs at line 651
    [28.22418][28.22418:22479]()
    Element::from(if ix == selected_section_ix {
    [28.22418]
    [28.22479]
    Element::from(if Some(ix) == selected_section_ix {
  • replacement in iced_expl_widget/src/nav_scrollable.rs at line 710
    [28.23285][28.23285:23333]()
    pub fn select_fst_section(&self) -> usize {
    [28.23285]
    [24.48282]
    pub fn select_fst_section(&self) -> Option<usize> {
  • replacement in iced_expl_widget/src/nav_scrollable.rs at line 713
    [28.23378][28.23378:23444]()
    if let Some(ix) = inner.selected_section {
    ix
    [28.23378]
    [28.23444]
    if inner.skip_sections.len() == inner.section_heights.len() {
    None
    } else if let Some(ix) = inner.selected_section {
    Some(ix)
  • edit in iced_expl_widget/src/nav_scrollable.rs at line 718
    [28.23461][28.23461:23641]()
    debug_assert!(
    inner.skip_sections.len() < inner.section_heights.len(),
    "There has to be at least one non-skip section"
    );
  • replacement in iced_expl_widget/src/nav_scrollable.rs at line 723
    [28.23872][28.23872:23901]()
    selected_section
    [28.23872]
    [24.48511]
    Some(selected_section)