start of to_record selection
[?]
Oct 11, 2025, 12:52 PM
LFEMJYYDO45ASMQSOJ3TNID7B5UZXDHB3NWFZJXWOAWNBS6GMDEACDependencies
- [2]
IQDCHWCPload a pijul repo - [3]
KT5UYXGKfix selection after adding file, add changed file diffs - [4]
S2NVIFXRallow to enter record msg - [5]
A5YBC77Vrecord! - [6]
AMPZ2BXKshow changed files diffs (only Edit atm) - [7]
MJDGPSHGWIP contents diff - [8]
3SYSJKYLadd app icon - [9]
23SFYK4Qbig view refactor into a new crate - [10]
MYGIBRRHwip custom theme - [11]
PKJCFSBMtheme improvements - [12]
I2AG42PAnew cols layout - [13]
DXAYDIMQupdate to latest pijul - [14]
PTWZYQFRuse nav-scrollable for repo status - [15]
OJPGHVC3entire log! - [16]
WAOGSCOJvery nice refactor, wip adding channels logs - [17]
WH57EHNMupdate tests - [18]
EJPSD5XOshared allowed actions conditions between update and view - [19]
YK3MOJJLchonky refactor, wip other channels logs & diffs - [20]
TQEZQJV4finish other channels logs selection - [21]
7WCB5YQJrefactor msgs and modules - [22]
AZ5D2LQUallow to set record description - [23]
PKLUHYE4allow to copy change hash - [24]
CULHFNIVadd error report view - [25]
U3EAZKHRallow to copy error report - [26]
IFQPVMBDerror handling for repo actions - [27]
Z752SDILadvanced shaping for file names - [28]
W4LFX7IHgroup diffs by file name - [29]
OPXFZKEBview tests setup - [30]
NOB64XMRfmt and clippy - [31]
SASAN2XCuse nav-scrollable - [32]
3XRG4BB6rewritten nav-scrollable! - [33]
NWJD6VM6mv libflowers libflorescence - [34]
7SSBM4UQview: refactor repo view - [35]
A6Z4O6RCactions menu - [36]
JZXYSIYDchannel selection! - [*]
SWWE2R6Mdisplay basic repo stuff - [*]
6YZAVBWUInitial commit
Change contents
- replacement in libflorescence/src/repo.rs at line 111
#[derive(Clone, Debug, PartialEq, Eq, strum::Display)]#[derive(Clone, Debug, PartialEq, Eq, strum::Display, Hash)] - replacement in libflorescence/src/repo.rs at line 173
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] - edit in libflorescence/src/repo.rs at line 625
// NOTE: This is relevant to change selection - replacement in inflorescence_view/src/view.rs at line 9
use inflorescence_model::{action, model, selection};use inflorescence_model::{action, model, selection, to_record}; - edit in inflorescence_view/src/view.rs at line 27
const TO_RECORD_LABEL_INCLUDE: &str = "■";const TO_RECORD_LABEL_EXCLUDE: &str = "□";const TO_RECORD_LABEL_PARTIAL: &str = "▤"; - edit in inflorescence_view/src/view.rs at line 40
ToRecord(to_record::Msg), - edit in inflorescence_view/src/view.rs at line 129
to_record, - edit in inflorescence_view/src/view.rs at line 170
let selected = match to_record::determine_file(to_record, file_path, changed_files) {to_record::PickSet::Include => TO_RECORD_LABEL_INCLUDE,to_record::PickSet::Exclude => TO_RECORD_LABEL_EXCLUDE,to_record::PickSet::Partial => TO_RECORD_LABEL_PARTIAL,}; - replacement in inflorescence_view/src/view.rs at line 178
el(button(text(file_path)let to_record_toggle =el(button(text(selected).size(21).shaping(text::Shaping::Advanced).center().wrapping(text::Wrapping::None)).width(31).height(31).padding(0).on_press_with(||Msg::ToRecord(to_record::Msg::ToggleFile{file :file_path.clone()})));let file_name_view =el(button(text(file_path) - replacement in inflorescence_view/src/view.rs at line 201
));el(row([to_record_toggle,file_name_view,])) - edit in inflorescence_view/src/view.rs at line 407
let selected = match to_record.overall {to_record::PickSet::Include => TO_RECORD_LABEL_INCLUDE,to_record::PickSet::Exclude => TO_RECORD_LABEL_EXCLUDE,to_record::PickSet::Partial => TO_RECORD_LABEL_PARTIAL,};let to_record_toggle = el(button(text(selected).size(21).shaping(text::Shaping::Advanced).center().wrapping(text::Wrapping::None),).width(31).height(31).padding(0).on_press_with(|| Msg::ToRecord(to_record::Msg::ToggleOverall))); - replacement in inflorescence_view/src/view.rs at line 428
.chain([el(text("Changed files:"))]).chain([el(row([to_record_toggle, el(text("Changed files:"))]).align_y(Alignment::Center).spacing(SPACING))]) - edit in inflorescence_view/src/view/test.rs at line 72
to_record: default(), - edit in inflorescence_view/src/view/test.rs at line 118
to_record: default(), - edit in inflorescence_view/src/view/test.rs at line 159
to_record: default(), - edit in inflorescence_view/src/view/test.rs at line 190
to_record: default(), - file addition: to_record.rs[16.29627]
//! Selection of changes to include or exclude in the next record.use libflorescence::prelude::*;use libflorescence::repo;use std::collections::HashMap;#[derive(Debug, Clone, PartialEq, Eq)]pub enum Msg {ToggleOverall,ToggleFile {file: String,},ToggleChange {file: String,change: repo::ChangedFileDiff,},}#[derive(Clone, Copy, Debug)]pub enum Pick {Include,Exclude,}#[derive(Clone, Copy, Debug, Default)]pub enum PickSet {#[default]Include,Exclude,Partial,}#[derive(Clone, Debug, Default)]pub struct State {/// State of all filespub overall: PickSet,/// Map from file name to its changes state. This is preserved even when/// `overall` selection changes.pub files: HashMap<String, PickSet>,/// Map from file name to its individual change state. This is preserved/// even when `overall` and `files` selection changespub changes: HashMap<String, PartialFile>,}#[derive(Clone, Debug, Default)]pub struct PartialFile {/// Map from file name to its state// NOTE: When it's possible to select individual lines/chars to record,// then the value would be `PickSet` and the inner-most value of// selected ranges would be `Pick`pub changes: HashMap<repo::ChangedFileDiff, Pick>,}pub fn determine_file(state: &State,file: &str,changed_files: &repo::ChangedFiles,) -> PickSet {match state.overall {PickSet::Include => PickSet::Include,PickSet::Exclude => PickSet::Exclude,PickSet::Partial => {if let Some(pick) = state.files.get(file) {*pick} else if file_has_any_partial_change(file, state, changed_files) {PickSet::Partial} else {default_pick_set(state)}}}}pub fn update(state: &mut State, msg: Msg, changed_files: &repo::ChangedFiles) {match msg {Msg::ToggleOverall => {state.overall = next_overall_pick(state, changed_files);}Msg::ToggleFile { file } => {if matches!(state.overall, PickSet::Include | PickSet::Exclude) {// Clear out the last partial statesfor changed_file in changed_files.keys() {state.files.insert(changed_file.clone(), state.overall);}state.changes.clear();}let pick = next_file_pick(&file, state, changed_files);state.files.insert(file, pick);state.overall = determine_overall_pick(state, changed_files);}Msg::ToggleChange { file, change } => {if matches!(state.overall, PickSet::Include | PickSet::Exclude) {// Clear out the last partial statesfor changed_file in changed_files.keys() {state.files.insert(changed_file.clone(), state.overall);}state.changes.clear();}let current = state.changes.get(&file).and_then(|file| file.changes.get(&change).copied()).unwrap_or_else(|| default_pick(state));let pick = next_pick(current);let partial_file =state.changes.entry(file.clone()).or_insert_with(default);partial_file.changes.insert(change, pick);let file_pick = determine_file_pick(&file, state, changed_files).unwrap_or(match pick {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude,});state.files.insert(file, file_pick);state.overall = determine_overall_pick(state, changed_files);}}dbg!(&state);}pub fn next_overall_pick(state: &State,changed_files: &repo::ChangedFiles,) -> PickSet {let maybe_next = next_pick_set(state.overall);if let PickSet::Partial = maybe_next&& !any_partial_change(state, changed_files){next_pick_set(maybe_next)} else {maybe_next}}pub fn next_file_pick(file: &str,state: &State,changed_files: &repo::ChangedFiles,) -> PickSet {let current = state.files.get(file).copied().unwrap_or_else(|| default_pick_set(state));let maybe_next = next_pick_set(current);if let PickSet::Partial = maybe_next&& !file_has_any_partial_change(file, state, changed_files){next_pick_set(maybe_next)} else {maybe_next}}fn next_pick_set(pick: PickSet) -> PickSet {match pick {PickSet::Include => PickSet::Partial,PickSet::Partial => PickSet::Exclude,PickSet::Exclude => PickSet::Include,}}fn next_pick(pick: Pick) -> Pick {match pick {Pick::Include => Pick::Exclude,Pick::Exclude => Pick::Include,}}/// Determine the default value for a file that is not explicitly set.fn default_pick_set(state: &State) -> PickSet {match default_pick(state) {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude,}}/// Determine the default value for a change that is not explicitly set.fn default_pick(state: &State) -> Pick {let State {overall,files,changes,} = state;if matches!(overall, PickSet::Partial)|| files.values().any(|pick| matches!(pick, PickSet::Partial | PickSet::Exclude))|| changes.values().any(|file| {file.changes.values().any(|pick| matches!(pick, Pick::Exclude))}){Pick::Exclude} else {Pick::Include}}fn any_partial_change(state: &State,changed_files: &repo::ChangedFiles,) -> bool {let files = changed_files.keys();if state.files.len() == files.len() {let (mut has_include, mut has_exclude) = (false, false);state.files.iter().any(|(file, pick)| {has_exclude |= matches!(pick, PickSet::Exclude);has_include |= matches!(pick, PickSet::Include);(has_include && has_exclude)|| matches!(pick, PickSet::Partial)|| file_has_any_partial_change(file, state, changed_files)})} else {state.files.iter().any(|(file, pick)| {matches!(pick, PickSet::Exclude | PickSet::Partial)|| file_has_any_partial_change(file, state, changed_files)})}}fn file_has_any_partial_change(file: &str,state: &State,changed_files: &repo::ChangedFiles,) -> bool {let changes = changed_files.get(file).unwrap();let (mut has_include, mut has_exclude) = (false, false);state.changes.get(file).map(|file| {if file.changes.len() == changes.len() {// The number of of changes in selection is the same as number// of actual changes, we need to have both exclude and include// to be partialfile.changes.values().any(|pick| {has_exclude |= matches!(pick, Pick::Exclude);has_include |= matches!(pick, Pick::Include);has_include && has_exclude})} else {// There is the same number of changes in selection is not the// same as number of actual changes, we only need to find 1// exclusion to be partialfile.changes.values().any(|pick| matches!(pick, Pick::Exclude))}}).unwrap_or_default()}fn determine_overall_pick(state: &State,changed_files: &repo::ChangedFiles,) -> PickSet {let files = changed_files.keys();let mut pick = None;if state.files.len() != files.len() {pick = Some(default_pick_set(state));}for file_pick in state.files.values() {if let Some(pick) = &mut pick {*pick = match (*pick, *file_pick) {(PickSet::Include, PickSet::Include) => PickSet::Include,(PickSet::Exclude, PickSet::Exclude) => PickSet::Exclude,(PickSet::Include, PickSet::Exclude)| (PickSet::Include, PickSet::Partial)| (PickSet::Exclude, PickSet::Include)| (PickSet::Exclude, PickSet::Partial)| (PickSet::Partial, PickSet::Include)| (PickSet::Partial, PickSet::Exclude)| (PickSet::Partial, PickSet::Partial) => PickSet::Partial,};if let PickSet::Partial = pick {break;}} else {pick = Some(*file_pick);}}pick.unwrap_or(PickSet::Include)}fn determine_file_pick(file: &str,state: &State,changed_files: &repo::ChangedFiles,) -> Option<PickSet> {let changes = changed_files.get(file).unwrap();let mut pick = None;if let Some(file) = state.changes.get(file) {if file.changes.len() != changes.len() {pick = Some(default_pick_set(state));}for change_pick in file.changes.values() {if let Some(pick) = &mut pick {*pick = match (*pick, change_pick) {(PickSet::Include, Pick::Include) => PickSet::Include,(PickSet::Exclude, Pick::Exclude) => PickSet::Exclude,(PickSet::Exclude, Pick::Include)| (PickSet::Include, Pick::Exclude)| (PickSet::Partial, Pick::Include)| (PickSet::Partial, Pick::Exclude) => PickSet::Partial,};if let PickSet::Partial = pick {break;}} else {pick = Some(match change_pick {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Include,});}}}pick} - replacement in inflorescence_model/src/model.rs at line 1
use crate::{action, diff, log, selection};use crate::{action, diff, log, selection, to_record}; - edit in inflorescence_model/src/model.rs at line 65
pub to_record: to_record::State, - replacement in inflorescence_model/src/lib.rs at line 6[16.31773]→[16.31773:31846](∅→∅),[16.31846]→[19.17857:17883](∅→∅),[19.17883]→[16.31880:31969](∅→∅),[16.31880]→[16.31880:31969](∅→∅),[16.31969]→[19.17884:17940](∅→∅),[19.17940]→[16.32022:32117](∅→∅),[16.32022]→[16.32022:32117](∅→∅),[16.32117]→[19.17941:18009](∅→∅),[19.18009]→[16.32182:32401](∅→∅),[16.32182]→[16.32182:32401](∅→∅),[16.32401]→[18.3383:3498](∅→∅),[18.3498]→[16.32401:32404](∅→∅),[16.32401]→[16.32401:32404](∅→∅),[16.32404]→[17.2636:2808](∅→∅),[17.2808]→[16.32404:33559](∅→∅),[16.32404]→[16.32404:33559](∅→∅),[16.33559]→[20.325:460](∅→∅),[20.460]→[19.18010:18083](∅→∅),[16.33559]→[19.18010:18083](∅→∅),[19.18083]→[16.33559:33625](∅→∅),[16.33559]→[16.33559:33625](∅→∅),[16.33625]→[19.18084:18128](∅→∅),[19.18128]→[16.33686:34389](∅→∅),[16.33686]→[16.33686:34389](∅→∅),[16.35107]→[16.35107:35110](∅→∅),[16.35110]→[19.18129:18154](∅→∅),[19.18154]→[16.35550:35583](∅→∅),[16.35550]→[16.35550:35583](∅→∅),[16.35583]→[19.18155:18202](∅→∅),[19.18202]→[20.461:534](∅→∅),[20.534]→[19.18202:18358](∅→∅),[19.18202]→[19.18202:18358](∅→∅),[19.18358]→[16.35803:35805](∅→∅),[16.35803]→[16.35803:35805](∅→∅)
use iced_expl_widget::nav_scrollable;use libflorescence::identity::Id;use libflorescence::repo;use iced::widget::text_editor;use std::collections::HashMap;use std::path::PathBuf;pub fn is_ready(state: &State) -> Option<&ReadyState> {if let SubState::Ready(state) = &state.sub {return Some(state);}None}pub fn is_ready_mut(state: &mut State) -> Option<&mut ReadyState> {if let SubState::Ready(state) = &mut state.sub {return Some(state);}None}#[derive(Debug)]pub struct State {pub window_size: iced::Size,pub repo_path: PathBuf,pub sub: SubState,/// Cache allowed actions. Used for action filter and for view.pub allowed_actions: Vec<action::Binding>,}#[allow(clippy::large_enum_variant,reason = "There's only ever 1 instance and the small sized cases are expected to always turn into the largest `Ready` case")]#[derive(Debug)]pub enum SubState {Loading {user_ids: Vec<Id>,repo: Option<repo::State>,},SelectingId {user_ids: Vec<Id>,user_selection_ix: usize,user_selection_nav: nav_scrollable::State,repo: Option<repo::State>,},Ready(ReadyState),}#[derive(Debug)]pub struct ReadyState {pub user_id: Id,pub repo: repo::State,pub selection: selection::State,pub navigation: Navigation,pub record_msg: Option<RecordMsg>,/// `Some` when we're writing a name of the new channel forked from currentpub forking_channel_name: Option<String>,pub logs: Logs,}#[derive(Debug)]pub enum RecordMsg {Typing(text_editor::Content),Canceled { old_msg: String },}#[derive(Debug, Default)]pub struct Navigation {/// Scrollable status view contains the overview of untracked files,/// changed files and most recent log changespub status_nav: nav_scrollable::State,/// Logs shown in the status view.pub status_logs_navs: log::Navs,/// Other channels selection's navpub other_channels_nav: nav_scrollable::State,/// Other channel log change selection's navpub other_channel_log_nav: nav_scrollable::State,/// Selected channel's name#[cfg(debug_assertions)]pub other_channel_name: Option<String>,/// Navs for a log of selected channel other than the currentpub other_channel_logs_navs: log::Navs,/// Entire log's change selection navpub entire_log_nav: nav_scrollable::State,/// Logs for the entire logpub entire_logs_navs: log::Navs,/// Diffs for untracked and changed files shown in status viewpub files_diffs: diff::FilesState,/// Diffs of status log changes, entire log changes and other channels'/// logspub log_diffs: log::FilesAndState,}#[derive(Debug, Default)]pub struct Logs {/// Populated when requested to look at the entire log of changespub entire_log: Option<Log>,/// Keys are channel namespub other_channels_logs: HashMap<String, Log>,}#[derive(Debug)]pub enum Log {Loading,Loaded { log: repo::Log },}pub fn init_channel_nav(navigation: &mut Navigation,#[cfg(debug_assertions)] name: String,) {navigation.other_channel_log_nav = nav_scrollable::State::default();navigation.other_channel_logs_navs = log::Navs::default();#[cfg(debug_assertions)]{navigation.other_channel_name = Some(name);}}[16.31773]pub mod to_record; - replacement in inflorescence_model/src/action.rs at line 2
use crate::{model, selection};use crate::{model, selection, to_record}; - edit in inflorescence_model/src/action.rs at line 36
ToRecord(to_record::Msg),/// Same purpose as `to_record::ToggleFile`, but without file name for/// action filteringToRecordToggleSelectedFile, - edit in inflorescence_model/src/action.rs at line 80
(ToRecord(left), ToRecord(right)) => left == right,(ToRecordToggleSelectedFile, ToRecordToggleSelectedFile) => true,(ToRecord(to_record::Msg::ToggleFile { file: _ }),ToRecordToggleSelectedFile,) => true,(ToRecordToggleSelectedFile,ToRecord(to_record::Msg::ToggleFile { file: _ }),) => true, - edit in inflorescence_model/src/action.rs at line 108
(ToRecord(_), _) => false,(ToRecordToggleSelectedFile, _) => false, - edit in inflorescence_model/src/action.rs at line 288
};let to_record_toggle_overall = |next: to_record::PickSet| {let label = match next {to_record::PickSet::Include => "include all files",to_record::PickSet::Exclude => "exclude all files",to_record::PickSet::Partial => "partial files",};Binding {key: "T",label,msg: Some(FilteredMsg::ToRecord(to_record::Msg::ToggleOverall)),}};let to_record_toggle_file = |next: to_record::PickSet| {let label = match next {to_record::PickSet::Include => "include selected file",to_record::PickSet::Exclude => "exclude selected file",to_record::PickSet::Partial => "partial selected file",};Binding {key: "t",label,msg: Some(FilteredMsg::ToRecordToggleSelectedFile),} - edit in inflorescence_model/src/action.rs at line 322
next_to_record_pick,next_to_record_file_pick, - edit in inflorescence_model/src/action.rs at line 352
push_if_some(next_to_record_pick, to_record_toggle_overall, ma);push_if_some(next_to_record_file_pick, to_record_toggle_file, ma); - replacement in inflorescence_model/src/action.rs at line 370
SubState::StatusLogDiff { can_record } => {SubState::StatusLogDiff {can_record,next_to_record_file_pick,} => { - edit in inflorescence_model/src/action.rs at line 385
push_if_some(next_to_record_file_pick, to_record_toggle_file, ma); - edit in inflorescence_model/src/action.rs at line 507
}}fn push_if_some<T, F>(predicate: Option<T>,to_add: F,actions: &mut Vec<Binding>,) whereF: Fn(T) -> Binding,{if let Some(value) = predicate {actions.push(to_add(value)) - edit in inflorescence_model/src/action.rs at line 535
next_to_record_pick: Option<to_record::PickSet>,next_to_record_file_pick: Option<to_record::PickSet>, - edit in inflorescence_model/src/action.rs at line 546
next_to_record_file_pick: Option<to_record::PickSet>, - edit in inflorescence_model/src/action.rs at line 624
to_record, - edit in inflorescence_model/src/action.rs at line 650
let next_to_record_pick = can_record.then(|| {to_record::next_overall_pick(to_record, changed_files)}); - edit in inflorescence_model/src/action.rs at line 660
let next_to_record_file_pick = None; - replacement in inflorescence_model/src/action.rs at line 662
SubState::StatusLogDiff { can_record }SubState::StatusLogDiff {can_record,next_to_record_file_pick,} - edit in inflorescence_model/src/action.rs at line 674
next_to_record_pick,next_to_record_file_pick, - edit in inflorescence_model/src/action.rs at line 699
let next_to_record_file_pick =Some(to_record::next_file_pick(path,to_record,changed_files,)); - replacement in inflorescence_model/src/action.rs at line 706
SubState::StatusLogDiff { can_record }SubState::StatusLogDiff {can_record,next_to_record_file_pick,} - edit in inflorescence_model/src/action.rs at line 718
next_to_record_pick,next_to_record_file_pick, - replacement in inflorescence_model/src/action.rs at line 735
SubState::StatusLogDiff { can_record }let next_to_record_file_pick = None;SubState::StatusLogDiff {can_record,next_to_record_file_pick,} - edit in inflorescence_model/src/action.rs at line 759
next_to_record_pick,next_to_record_file_pick: None, - edit in inflorescence_model/src/action.rs at line 768
next_to_record_pick,next_to_record_file_pick: None, - replacement in inflorescence/src/main.rs at line 12
use inflorescence_model::{action, model};use inflorescence_model::{action, model, to_record}; - edit in inflorescence/src/main.rs at line 152
to_record: default(), - edit in inflorescence/src/main.rs at line 318
view::Msg::ToRecord(msg) => {if let Some(ReadyState {to_record, repo, ..}) = model::is_ready_mut(&mut state.model){to_record::update(to_record, msg, &repo.changed_files);}Task::none()} - edit in inflorescence/src/main.rs at line 506
action::FilteredMsg::ToRecord(msg) => {if let Some(ReadyState {to_record, repo, ..}) = model::is_ready_mut(&mut state.model){to_record::update(to_record, msg, &repo.changed_files);}Task::none()}action::FilteredMsg::ToRecordToggleSelectedFile => {if let Some(ReadyState {to_record,repo,selection,..}) = model::is_ready_mut(&mut state.model)&& let Some(selection::Status::ChangedFile {ix: _,path,diff_selected: _,}) = selection.status.as_ref(){let msg = to_record::Msg::ToggleFile { file: path.clone() };to_record::update(to_record, msg, &repo.changed_files);}Task::none()} - edit in inflorescence/src/main.rs at line 1003
to_record: default(), - edit in inflorescence/src/main.rs at line 1073
to_record: _, - edit in inflorescence/src/main.rs at line 1445
"t" => {action(action::FilteredMsg::ToRecordToggleSelectedFile)} - edit in inflorescence/src/main.rs at line 1511
"t" if mods == Modifiers::SHIFT => {action(action::FilteredMsg::ToRecord(to_record::Msg::ToggleOverall,))} - edit in Cargo.lock at line 2456
][[package]]name = "imara-diff"version = "0.1.8"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2"dependencies = ["hashbrown", - edit in Cargo.lock at line 2879
"imara-diff",