ZVI4AWERNOTDJ3765HJXRBZT57XPNKVONQ6TGOGNPOL2VN42KMJQC WT3GA27PQ2AOAIGK65O3Q4DMX4AZDVNULBLRL6GF4QW6QCASUEAAC UB2ITZJSDADVINSQEZ3HA6PVGA7OA6JYFG5GMSO7Y7LOXJC4FI7AC EC3TVL4X6VZZVLOKUN63LC73ADPHBHMZO7QMDXGX2ZPURVI4B4XQC KT5UYXGKEEXUHURNOYFVIG7WQ3Y3SJZMM2TP4OSW6NXSXQ5XXRHAC ELG3UDT6OJFEYSJR7HZEC65IUWBMGPPPCXEW3CDW5T74R6KC5LIAC S2NVIFXRFER4SRA37WCT5XTXHDHAL5WIGGKY4A4XOTPLTKTZSRGQC W7IUT3ZVMFH77IGKLAL7WX7IVVTGTY3FKEJ3WHMP3KI37B6NENLQC YBJRDOTCX3ZRDB5EVXJBR55FX3CADCSIGMYWNYVC2PD5W3GXR3DQC KM5PSZ4A2FJOPHJA6RC7LHZAUXLQDZDQC2DVSE5YUORLFIPZO74QC 2VUX5BTDKHX3TJ677NW34H5WLSWH35C3PU46C7MXCN5O7PAZVXNQC D7A7MSIHJS3IAOLEPK52M4CZLDPLO7JB3Y62XACT2AM6UUCPQ6BAC 4WO3ZJM2RNYZCBPS7FGYAEBELYD57OSS7LEUYCWGZBCAY272SNQQC BJXUYQ2YQMVULJITT5FEA6NERJVLWFKEAWSBYZVIB7KAT27KOWBAC W4LFX7IHQ7SDX67ATSGWDB5IN6472ZJDBKY2XZ54SBJEYD5GAT5QC PTFDJ567XGGF26TE7KVQT7WPZIWV737DBO24VFIPEWPVAVKEKADQC AMPZ2BXK4IGUZO3OPBRSJ6Z4GI5K4PRFMLUGTR6AP4FKKRWQG7LQC NOB64XMRXRLXGYS4JMGTFQMH5KMD7DHSTQ4YYBZ73E4PICW7QBQAC AXSXZQDGLPSLBYY3WEI5CCJFWFY33HPRVW6QQFL46OKWZH4G4YSAC V55EAIWQXWER2HWKZHPJBV7DDJMSPSPWSO3FSSAYODJHVDBHUN6QC NRCUG4R2NIM2ANIETSUZ7WZDXFOOCMJ73ROP5MDYJA4RUT4PYA4QC Y5ATDI2HRWTTYJAVUR7SVWQVB4ZKKDZF3UVE4JJQFZ7RX7H7VPJQC YBLPPHZN7OSTXDQP6EKWYWK4FR4JRTLAUN5KDBBHF66C5TW74KXAC UMO6U2ZTJ6LTWJXAKQMEJO2MCMFSJZJZ6UZ2SPY2FPU4OPVKPIMAC B4RMW5AEGAJX5CFC4RFPI6Y3NBSDM7GZKNBPPTTICRZSDZSYNXHQC MJDGPSHGF62FTVWZBE7MFNJTUQD42OBVJEOSVPBT553UFJLTEMXQC SWWE2R6MVBX5CNM6X3WLXZTSRTU53PBJL7WJSFVF77XBPXDX4COAC 6YZAVBWU6E5FYOI5JGEIPXGZLIKAW6LS2AOFIQWEE5DMOPPCD5PQC let (src_file_load_tx, src_file_load_rx) = watch::channel("".to_string());let untracked_file_load_rx = WatchStream::from_changes(src_file_load_rx);
let (src_file_load_tx, src_file_load_rx) = watch::channel((FileId {path: "".to_string(),file_kind: FileKind::Untracked,},0,));let src_file_load_rx = WatchStream::from_changes(src_file_load_rx);
let untracked_file_load_task = Task::run(untracked_file_load_rx.map(move |file_path| (repo_path_clone.clone(), file_path)).then(|(repo_path, file_path)| async {load_src_file(repo_path, file_path).await
let src_file_load_task = Task::run(src_file_load_rx.map(move |(id, cache_counter): (FileId, usize)| {(repo_path_clone.clone(), id, cache_counter)}).then(|(repo_path, id, cache_counter)| async move {load_src_file(repo_path, id, cache_counter).await
src_files_cache: SrcFilesCache,src_file_load_tx: watch::Sender<String>,/// Keyed by file name matching `repo::ChangedFiles`changed_files_contents: ChangedFilesContents,
src_file_load_tx: watch::Sender<(FileId, usize)>,diffs_cache: DiffsCache,
impl WeightScale<String, SrcFile> for SrcFileCacheWeight {fn weight(&self, key: &String, value: &SrcFile) -> usize {let key_weight = key.len();let value_weight = match value {SrcFile::Loading => 0,SrcFile::Loaded(file_editor_content) => match file_editor_content {FileEditorContent::Decoded(content) => content.text().len(),FileEditorContent::ShortBase64(string) => string.len(),FileEditorContent::UnknownEncoding => 0,},};key_weight + value_weight}
#[derive(Debug)]enum FileDiff {Loading,Loaded(contents_diff::File),
#[derive(Debug)]enum FileEditorContent {Decoded(text_editor::Content),/// Short byte sequence of unknown encoding encoded with base64 for/// display. Must be shorter than [`MAX_LEN_BASE64_DISPLAY`]ShortBase64(String),UnknownEncoding,
#[derive(Debug, Clone, Hash, PartialEq, Eq)]struct FileId {path: String,file_kind: FileKind,
#[derive(Debug)]enum ChangedFileDiffWithContents {Add,Edit {line: usize,deleted: bool,contents: FileEditorContent,},Replacement {line: usize,/// Deleted linechange_contents: FileEditorContent,/// Added linesreplacement_contents: FileEditorContent,},Del,Undel,
impl WeightScale<FileId, FileDiff> for DiffsCacheWeight {fn weight(&self, key: &FileId, value: &FileDiff) -> usize {let key_weight = key.path.len();let val_weight = match value {FileDiff::Loading => 0,FileDiff::Loaded(file) => match file {contents_diff::File::Decoded(contents_diff::DecodedFile {combined:contents_diff::Combined {sections,max_line_num: _,},diffs_without_contents,}) => {mem::size_of_val(sections)+ mem::size_of::<usize>()+ mem::size_of_val(diffs_without_contents)}contents_diff::File::Undecodable(contents_diff::UndecodableFile {diffs_with_contents,diffs_without_contents,},) => {mem::size_of_val(diffs_with_contents)+ mem::size_of_val(diffs_without_contents)}},};key_weight + val_weight}
#[derive(Debug)]enum ChangedFileDiffWithoutContents {Move,SolveNameConflict,UnsolveNameConflict,SolveOrderConflict,UnsolveOrderConflict,ResurrectZombines,AddRoot,DelRoot,
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]enum FileKind {Untracked,Changed,
let untracked_file_selection = |repo: &repo::State,ix: usize,src_files_cache: &mut SrcFilesCache,src_file_load_tx: &watch::Sender<String,>|-> cursor::Selection {let path = repo.untracked_files.iter().nth(ix).unwrap().clone();
let untracked_file_selection =|repo: &repo::State,ix: usize,diffs_cache: &mut DiffsCache,src_file_load_tx: &watch::Sender<(FileId, usize)>|-> cursor::Selection {let path = repo.untracked_files.iter().nth(ix).unwrap().clone();
state.cursor.selection = match state.cursor.selection.as_ref() {Some(cursor::Selection::UntrackedFile { ix, path: _ }) => {let new_selection =if repo.untracked_files.len().saturating_sub(1)== *ix{if repo.changed_files.is_empty() {let ix = 0;
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,)}} else {let ix = ix + 1;
};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() {let ix = 0;changed_file_selection(repo,ix,&mut state.diffs_cache,&state.src_file_load_tx,)} else {let ix = 0;untracked_file_selection(repo,ix,&mut state.diffs_cache,&state.src_file_load_tx,)}
let ix = ix + 1;untracked_file_selection(repo,ix,&mut state.src_files_cache,&state.src_file_load_tx,)};Some(new_selection)}Some(cursor::Selection::ChangedFile { ix, path: _ }) => {let new_selection = if repo.changed_files.len().saturating_sub(1)== *ix{if repo.untracked_files.is_empty() {
changed_file_selection(&state.changed_files_contents,ix,&mut state.src_files_cache,&state.src_file_load_tx,)} else {let ix = 0;untracked_file_selection(
Some(untracked_file_selection(
ix,&mut state.src_files_cache,&state.src_file_load_tx,)}} else {let ix = ix + 1;changed_file_selection(&state.changed_files_contents,ix,&mut state.src_files_cache,&state.src_file_load_tx,)};Some(new_selection)}None => {if repo.untracked_files.is_empty() {if repo.changed_files.is_empty() {None} else {let ix = 0;Some(changed_file_selection(&state.changed_files_contents,
cursor::Selection::ChangedFile { path, .. } => {let diffs = state.changed_files_contents.get(path).unwrap();if any_diff_has_contents(diffs) {load_src_file_if_not_cached(&mut state.src_files_cache,&state.src_file_load_tx,path,);
cursor::Select::ChangedFile { ix, path } => {if let Some(diffs) = state.repo.as_ref().and_then(|repo| repo.changed_files.get(&path)){if any_diff_has_contents(diffs) {load_src_file_if_not_cached(&mut state.diffs_cache,&state.src_file_load_tx,FileId {path: path.clone(),file_kind: FileKind::Changed,},);}}cursor::Selection::ChangedFile {ix,path,diffs: contents_diff::State::default(),
let content = match encoding {Some(encoding) => {let decoded = encoding.decode(&data);FileEditorContent::Decoded(text_editor::Content::with_text(&decoded,))}None => {if data.len() <= MAX_LEN_BASE64_DISPLAY {let encoded =format!("b{}", data_encoding::BASE64.encode(&data));FileEditorContent::ShortBase64(encoded)} else {FileEditorContent::UnknownEncoding
if state.diffs_cache.counter == cache_counter {let file_content = match encoding {Some(encoding) => {let decoded = encoding.decode(&data);contents_diff::FileContent::Decoded(decoded)
}};match state.src_files_cache.get(&path) {Some(SrcFile::Loading | SrcFile::Loaded(_)) => {src_files_cache_put(&mut state.src_files_cache,path,SrcFile::Loaded(content),);}None => {}}Task::none()}Message::UntrackedFileContentsAction { path, action } => {// Read-onlyif !action.is_edit() {if let Some(SrcFile::Loaded(FileEditorContent::Decoded(mut content),..,)) = state.src_files_cache.pop(&path){content.perform(action);src_files_cache_put(&mut state.src_files_cache,path,SrcFile::Loaded(FileEditorContent::Decoded(content)),);
None => contents_diff::FileContent::UnknownEncoding,};match id.file_kind {FileKind::Untracked => {let file_diff: contents_diff::File =contents_diff::init_file(file_content, None);diffs_cache_put(&mut state.diffs_cache,id,FileDiff::Loaded(file_diff),);}FileKind::Changed => {if let Some(repo) = state.repo.as_ref() {let changed_file = repo.changed_files.get(&id.path);let file_diff: contents_diff::File =contents_diff::init_file(file_content,changed_file,);diffs_cache_put(&mut state.diffs_cache,id,FileDiff::Loaded(file_diff),);}}
Message::ChangedFileContentsAction { file, ix, action } => {// Read-onlyif !action.is_edit() {if let Some(contents) =state.changed_files_contents.get_mut(&file){todo!()// if let Some(content) = contents.get_mut(ix) {// match content {// Some(FileEditorContent::Decoded(content)) => {// content.perform(action);// }// _ => panic!("Unexpected content: {content:?}"),// }// }
Message::FileDiffsContentsAction { id, action } => {if let Some(selection) = state.cursor.selection.as_mut() {match (selection, id.file_kind) {(cursor::Selection::UntrackedFile { ix: _, path, diffs },FileKind::Untracked,) if path == &id.path => {contents_diff::update(diffs, action);}(cursor::Selection::ChangedFile { ix: _, path, diffs },FileKind::Changed,) if path == &id.path => {contents_diff::update(diffs, action);}_ => {// Selection has changed}
cursor::Selection::ChangedFile { ix: _, path } => {let diffs =state.changed_files_contents.get(path).unwrap();if any_diff_has_contents(diffs) {load_src_file_if_not_cached(&mut state.src_files_cache,&state.src_file_load_tx,path,);
cursor::Selection::ChangedFile {ix: _,path,diffs: _,} => {if let Some(diffs) = repo.changed_files.get(path) {if any_diff_has_contents(diffs) {load_src_file_if_not_cached(&mut state.diffs_cache,&state.src_file_load_tx,FileId {path: path.clone(),file_kind: FileKind::Changed,},);}
}fn changed_files_contents(changed_files: &repo::ChangedFiles,) -> ChangedFilesContents {changed_files.iter().map(|(path, diffs)| {let contents = changed_file_contents(diffs);(path.clone(), contents)}).collect()
fn changed_file_contents(diffs: &repo::ChangedFile) -> ChangedFileContents {let (with_contents, without_contents) = diffs.iter().fold((vec![], vec![]),|(mut acc_with, mut acc_without), diff| {match diff {repo::ChangedFileDiff::Move => {acc_without.push(ChangedFileDiffWithoutContents::Move)}repo::ChangedFileDiff::Del => {acc_with.push(ChangedFileDiffWithContents::Del)}repo::ChangedFileDiff::Undel => {acc_with.push(ChangedFileDiffWithContents::Undel)}repo::ChangedFileDiff::Add => {acc_with.push(ChangedFileDiffWithContents::Add)}repo::ChangedFileDiff::SolveNameConflict => acc_without.push(ChangedFileDiffWithoutContents::SolveNameConflict),repo::ChangedFileDiff::UnsolveNameConflict => acc_without.push(ChangedFileDiffWithoutContents::UnsolveNameConflict),repo::ChangedFileDiff::Edit {line,deleted,contents,} => acc_with.push(ChangedFileDiffWithContents::Edit {line: *line,deleted: *deleted,contents: contents_to_file_editor_content(contents),}),repo::ChangedFileDiff::Replacement {line,change_contents,replacement_contents,} => acc_with.push(ChangedFileDiffWithContents::Replacement {line: *line,change_contents: contents_to_file_editor_content(change_contents,),replacement_contents: contents_to_file_editor_content(replacement_contents,),}),repo::ChangedFileDiff::SolveOrderConflict => acc_without.push(ChangedFileDiffWithoutContents::SolveOrderConflict),repo::ChangedFileDiff::UnsolveOrderConflict => acc_without.push(ChangedFileDiffWithoutContents::UnsolveOrderConflict),repo::ChangedFileDiff::ResurrectZombines => acc_without.push(ChangedFileDiffWithoutContents::ResurrectZombines),repo::ChangedFileDiff::AddRoot => {acc_without.push(ChangedFileDiffWithoutContents::AddRoot)}repo::ChangedFileDiff::DelRoot => {acc_without.push(ChangedFileDiffWithoutContents::DelRoot)}};(acc_with, acc_without)},);ChangedFileContents {with_contents,without_contents,}}fn contents_to_file_editor_content(contents: &repo::Contents,) -> FileEditorContent {match contents {repo::Contents::Decoded(content) => {FileEditorContent::Decoded(text_editor::Content::with_text(content))}repo::Contents::ShortBase64(short) => {FileEditorContent::ShortBase64(short.clone())}repo::Contents::UnknownEncoding(_vec) => {FileEditorContent::UnknownEncoding}}}
if !cache.contains(path) {src_files_cache_put(cache, path.to_string(), SrcFile::Loading);load_tx.send(path.to_string()).unwrap();
if !cache.inner.contains(&id) {diffs_cache_put(cache, id.clone(), FileDiff::Loading);load_tx.send((id, cache.counter)).unwrap();
fn src_files_cache_put(cache: &mut SrcFilesCache, key: String, value: SrcFile) {if let Err((key, value)) = cache.put_with_weight(key, value) {let kv_weight = SrcFileCacheWeight.weight(&key, &value);info!("Source file cache is too small to hold {key}. Resizing cache to to {kv_weight} fit it.");cache.resize(NonZero::new(kv_weight).unwrap());let res = cache.put_with_weight(key, value);
fn diffs_cache_put(cache: &mut DiffsCache, key: FileId, value: FileDiff) {if let Err((key, value)) = cache.inner.put_with_weight(key, value) {let kv_weight = DiffsCacheWeight.weight(&key, &value);info!("Source file cache is too small to hold {}. Resizing cache to to {kv_weight} fit it.", key.path);cache.inner.resize(NonZero::new(kv_weight).unwrap());let res = cache.inner.put_with_weight(key, value);
fn any_diff_has_contents(changed_file: &ChangedFileContents) -> bool {!changed_file.with_contents.is_empty()
fn any_diff_has_contents(changed_file: &repo::ChangedFile) -> bool {let (with_contents, _without_contents) =contents_diff::diffs_from_repo_diffs(changed_file);!with_contents.is_empty()
Some(cursor::Selection::UntrackedFile { path, .. }) => {debug_assert!(repo.untracked_files.contains(path));let diff = match &state.src_files_cache.peek(path) {Some(SrcFile::Loaded(content)) => match content {FileEditorContent::Decoded(content) => {let path_buf = PathBuf::from(path);let file_ext = path_buf.extension().and_then(ffi::OsStr::to_str);let editor = text_editor(content).wrapping(text::Wrapping::WordOrGlyph).on_action(|action|Message::UntrackedFileContentsAction{path: path.clone(), action});if let Some(file_ext) = file_ext {el(editor.highlight(file_ext, highlighter::Theme::SolarizedDark))} else {el(editor)}},FileEditorContent::ShortBase64(content) => {el(text(content))}FileEditorContent::UnknownEncoding => {el("Unknown encoding")}},Some(SrcFile::Loading) => el(text("Loading...")),None => panic!("Unexpectedly, the src file {path} is not even being loaded"),
Some(cursor::Selection::UntrackedFile {ix: _,path,diffs: selection_state,}) => {let id = FileId {path: path.clone(),file_kind: FileKind::Untracked,};let diffs = match state.diffs_cache.inner.peek(&id) {Some(FileDiff::Loaded(file)) => {contents_diff::view(selection_state, file).map(move |msg| Message::FileDiffsContentsAction {id: id.clone(),action: msg,},)}None | Some(FileDiff::Loading) => el(text("loading...")),
Some(cursor::Selection::ChangedFile { path, ix: _ }) => {let diffs = state.changed_files_contents.get(path).unwrap();let _src_file_contents: Option<text_editor::Content> =if any_diff_has_contents(diffs) {// TODO: load file contents from cacheNone} else {None};match state.src_files_cache.peek(path) {Some(SrcFile::Loaded(content)) => {let file_content = match content {FileEditorContent::Decoded(content) => {content.text()}FileEditorContent::ShortBase64(_) => todo!(),FileEditorContent::UnknownEncoding => todo!(),};let with_contents = diffs.with_contents.iter().map(|change| match dbg!(change) {ChangedFileDiffWithContents::Add => {contents_diff::ChangedFileDiffWithContents::Add}ChangedFileDiffWithContents::Edit {line,deleted,contents,} => contents_diff::ChangedFileDiffWithContents::Edit {line: *line,deleted: *deleted,contents: match contents {FileEditorContent::Decoded(content) => contents_diff::ChangeContents::Decoded(content.text()) ,FileEditorContent::ShortBase64(_) => todo!(),FileEditorContent::UnknownEncoding => todo!(),
Some(cursor::Selection::ChangedFile {path,ix: _,diffs: selection_state,}) => {let id = FileId {path: path.clone(),file_kind: FileKind::Changed,};let diffs = match state.diffs_cache.inner.peek(&id) {Some(FileDiff::Loaded(file)) => {contents_diff::view(selection_state, file).map(move |msg| Message::FileDiffsContentsAction {id: id.clone(),action: msg,
},ChangedFileDiffWithContents::Replacement {line,change_contents,replacement_contents,} => contents_diff::ChangedFileDiffWithContents::Replacement {line:*line ,change_contents: match change_contents {FileEditorContent::Decoded(content) => contents_diff::ChangeContents::Decoded(content.text()) ,FileEditorContent::ShortBase64(_) => todo!(),FileEditorContent::UnknownEncoding => todo!(),},replacement_contents: match replacement_contents {FileEditorContent::Decoded(content) => contents_diff::ChangeContents::Decoded(content.text()) ,FileEditorContent::ShortBase64(_) => todo!(),FileEditorContent::UnknownEncoding => todo!(),}},ChangedFileDiffWithContents::Del => contents_diff::ChangedFileDiffWithContents::Del,ChangedFileDiffWithContents::Undel => contents_diff::ChangedFileDiffWithContents::Undel,}).collect::<Vec<_>>();let state =contents_diff::init(&file_content, &with_contents);let diffs =contents_diff::view(state).map(|msg| todo!());el(column([view_diff_header(format!("{path} diff:")),el(scrollable(diffs)),]))
)
pub enum ChangedFileDiffWithContents {Add,Edit {line: usize,deleted: bool,contents: ChangeContents,},Replacement {line: usize,/// Deleted linechange_contents: ChangeContents,/// Added linesreplacement_contents: ChangeContents,},Del,Undel,
pub enum File {Decoded(DecodedFile),Undecodable(UndecodableFile),}#[derive(Debug)]pub struct DecodedFile {pub combined: Combined,pub diffs_without_contents: Vec<DiffWithoutContents>,
pub enum ChangeContents {Decoded(String),/// Short byte sequence of unknown encoding encoded with base64 for/// display. Must be shorter than [`MAX_LEN_BASE64_DISPLAY`]ShortBase64(String),UnknownEncoding,
pub struct UndecodableFile {pub diffs_with_contents: Vec<DiffWithContents>,pub diffs_without_contents: Vec<DiffWithoutContents>,
pub enum Section {Unchanged(Lines),/// `deleted` and `added` are together because for/// `ChangedFileDiffWithContents::Replacement` they begin on the same line/// numberChanged {deleted: Lines,added: Lines,},
pub enum FileContent<'a> {Decoded(Cow<'a, str>),UnknownEncoding,}pub fn init_file(file_content: FileContent<'_>,changed_file: Option<&repo::ChangedFile>,) -> File {let (diffs_with_contents, diffs_without_contents) = changed_file.map(diffs_from_repo_diffs).unwrap_or((vec![], vec![]));match file_content {FileContent::Decoded(file_content) => {let combined =combine_decoded_contents(&file_content, diffs_with_contents);File::Decoded(DecodedFile {combined,diffs_without_contents,})}FileContent::UnknownEncoding => File::Undecodable(UndecodableFile {diffs_with_contents,diffs_without_contents,}),}
pub fn init(file_contents: &str,changes: &[ChangedFileDiffWithContents],) -> State {let changes_len = changes.len();let mut file_lines = file_contents.split('\n');let mut sections = Vec::with_capacity(changes.len());
pub fn combine_decoded_contents(file_content: &str,diffs_with_contents: Vec<DiffWithContents>,) -> Combined {let changes_len = diffs_with_contents.len();let mut file_lines = file_content.split('\n');let mut sections = Vec::with_capacity(diffs_with_contents.len());
fn contents_to_lines(contents: &ChangeContents) -> Lines {match contents {ChangeContents::Decoded(string) => {string.split('\n').map(str::to_string).collect()}ChangeContents::ShortBase64(_) => todo!(),ChangeContents::UnknownEncoding => todo!(),}
pub fn diffs_from_repo_diffs(changed_file: &repo::ChangedFile,) -> (Vec<DiffWithContents>, Vec<DiffWithoutContents>) {changed_file.iter().fold((vec![], vec![]), |(mut with, mut without), diff| {match diff {repo::ChangedFileDiff::Move => {without.push(DiffWithoutContents::Move);},repo::ChangedFileDiff::Del => {with.push(DiffWithContents::Del);},repo::ChangedFileDiff::Undel => {with.push(DiffWithContents::Undel);},repo::ChangedFileDiff::Add => {with.push(DiffWithContents::Add);},repo::ChangedFileDiff::SolveNameConflict => {without.push(DiffWithoutContents::Move);},repo::ChangedFileDiff::UnsolveNameConflict => {without.push(DiffWithoutContents::Move);},repo::ChangedFileDiff::Edit { line, deleted, contents } => match contents{repo::Contents::Decoded(lines) => {with.push(DiffWithContents::Edit {line: *line,deleted: *deleted,contents: lines.clone(),});},repo::Contents::ShortBase64(short) => {without.push(DiffWithoutContents::Edit {line: *line,deleted: *deleted,contents: UndecodableContents::ShortBase64(short.clone()),});},repo::Contents::UnknownEncoding(_bytes) => {without.push(DiffWithoutContents::Edit {line: *line,deleted: *deleted,contents: UndecodableContents::UnknownEncoding,});},},repo::ChangedFileDiff::Replacement { line, change_contents, replacement_contents } => match (change_contents, replacement_contents) {(repo::Contents::Decoded(change), repo::Contents::Decoded(replacement)) => {with.push(DiffWithContents::Replacement {line: *line,change_contents: change.clone(),replacement_contents: replacement.clone(),});},(repo::Contents::ShortBase64(change), repo::Contents::ShortBase64(replacement)) => {without.push(DiffWithoutContents::Replacement {line: *line,change_contents: UndecodableContents::ShortBase64(change.clone()),replacement_contents: UndecodableContents::ShortBase64(replacement.clone()),});},(repo::Contents::UnknownEncoding(_change), repo::Contents::UnknownEncoding(_replacement)) => {without.push(DiffWithoutContents::Replacement {line: *line,change_contents: UndecodableContents::UnknownEncoding,replacement_contents: UndecodableContents::UnknownEncoding,});},_ => {unimplemented!("The change and replacement have different encoding!");}},repo::ChangedFileDiff::SolveOrderConflict => {without.push(DiffWithoutContents::SolveOrderConflict);},repo::ChangedFileDiff::UnsolveOrderConflict => {without.push(DiffWithoutContents::UnsolveOrderConflict);},repo::ChangedFileDiff::ResurrectZombines => {without.push(DiffWithoutContents::ResurrectZombines);},repo::ChangedFileDiff::AddRoot => {without.push(DiffWithoutContents::AddRoot);},repo::ChangedFileDiff::DelRoot => {without.push(DiffWithoutContents::DelRoot);},};(with, without)})}#[derive(Debug)]pub enum DiffWithContents {Add,Edit {line: usize,deleted: bool,contents: String,},Replacement {line: usize,/// Deleted linechange_contents: String,/// Added linesreplacement_contents: String,},Del,Undel,}#[allow(dead_code)] // TODO rm once `view_undecodable` is implemented#[derive(Debug)]pub enum DiffWithoutContents {// _________________________________________________________________________// Cases that never have contents:Move,SolveNameConflict,UnsolveNameConflict,SolveOrderConflict,UnsolveOrderConflict,ResurrectZombines,AddRoot,DelRoot,// _________________________________________________________________________// Cases that normally have contents, but in these cases the contents are// not decodable:Edit {line: usize,deleted: bool,contents: UndecodableContents,},Replacement {line: usize,/// Deleted linechange_contents: UndecodableContents,/// Added linesreplacement_contents: UndecodableContents,},}#[allow(dead_code)] // TODO rm once `view_undecodable` is implemented#[derive(Debug)]pub enum UndecodableContents {/// Short byte sequence of unknown encoding encoded with base64 for/// display. Must be shorter than [`crate::repo::MAX_LEN_BASE64_DISPLAY`]ShortBase64(String),UnknownEncoding,
#[allow(dead_code)] // TODO rm once `view_decoded` is fully implemented#[derive(Debug, Default)]pub struct State {pub selected_sections: Vec<usize>,pub expanded_unchanged_sections: Vec<usize>,pub collapsed_changed_sections: Vec<usize>,}#[derive(Debug)]pub enum Section {Unchanged(Lines),/// `deleted` and `added` are together because for/// `ChangedFileDiffWithContents::Replacement` they begin on the same line/// numberChanged {deleted: Lines,added: Lines,},}fn contents_to_lines(contents: &str) -> Lines {contents.split('\n').map(str::to_string).collect()}
pub fn update(state: &mut State, msg: Msg) {}
pub fn update(_state: &mut State, _msg: Msg) {}pub fn view<'a>(state: &'a State, file: &'a File) -> Element<'a, Msg> {match file {File::Decoded(decoded_file) => view_decoded(state, decoded_file),File::Undecodable(undecodable_file) => {view_undecodable(state, undecodable_file)}}}