diff --cc src/delta.rs index 9a65aaa,ab08d84..0000000 --- a/src/delta.rs +++ b/src/delta.rs @@@ -21,7 -21,7 +21,13 @@@ pub enum State HunkZero(DiffType), // In hunk; unchanged line (prefix) HunkMinus(DiffType, Option), // In hunk; removed line (diff_type, raw_line) HunkPlus(DiffType, Option), // In hunk; added line (diff_type, raw_line) ++<<<<<<< HEAD + MergeConflict(MergeParents, merge_conflict::MergeConflictCommit), ++||||||| parent of b2b28c8... Display merge conflict branches ++ MergeConflict(merge_conflict::Source), ++======= + MergeConflict(merge_conflict::MergeConflictCommit), ++>>>>>>> b2b28c8... Display merge conflict branches SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short Blame(String, Option), // In a line of `git blame` output (commit, repeat_blame_line). diff --cc src/handlers/hunk.rs index 26cb288,7df74ae..0000000 --- a/src/handlers/hunk.rs +++ b/src/handlers/hunk.rs @@@ -141,28 -141,19 +141,45 @@@ fn new_line_state(new_line: &str, prev_ | HunkZero(Unified) | HunkPlus(Unified, _) | HunkHeader(Unified, _, _) => Unified, ++<<<<<<< HEAD + HunkHeader(Combined(Number(n), InMergeConflict::No), _, _) => { + Combined(Number(*n), InMergeConflict::No) + } + // The prefixes are specific to the previous line, but the number of merge parents remains + // equal to the prefix length. + HunkHeader(Combined(Prefix(prefix), InMergeConflict::No), _, _) => { + Combined(Number(prefix.len()), InMergeConflict::No) + } + HunkMinus(Combined(Prefix(prefix), in_merge_conflict), _) + | HunkZero(Combined(Prefix(prefix), in_merge_conflict)) + | HunkPlus(Combined(Prefix(prefix), in_merge_conflict), _) => { + Combined(Number(prefix.len()), in_merge_conflict.clone()) + } + _ => delta_unreachable(&format!( + "Unexpected state in new_line_state: {:?}", + prev_state + )), ++||||||| parent of b2b28c8... Display merge conflict branches ++ HunkHeader(Combined(Number(n)), _, _) => Combined(Number(*n)), ++ HunkMinus(Combined(Prefix(prefix)), _) ++ | HunkZero(Combined(Prefix(prefix))) ++ | HunkPlus(Combined(Prefix(prefix)), _) => Combined(Number(prefix.len())), ++ _ => delta_unreachable(&format!("diff_type: unexpected state: {:?}", prev_state)), ++======= + HunkHeader(Combined(Number(n)), _, _) => Combined(Number(*n)), + // The prefixes are specific to the previous line, but the number of merge parents remains + // equal to the prefix length. + HunkHeader(Combined(Prefix(prefix)), _, _) + | HunkMinus(Combined(Prefix(prefix)), _) + | HunkZero(Combined(Prefix(prefix))) + | HunkPlus(Combined(Prefix(prefix)), _) => Combined(Number(prefix.len())), + _ => delta_unreachable(&format!("diff_type: unexpected state: {:?}", prev_state)), ++>>>>>>> b2b28c8... Display merge conflict branches }; - let (prefix_char, prefix) = match diff_type { - Unified => (new_line.chars().next(), None), - Combined(Number(n_parents)) => { + let (prefix_char, prefix, in_merge_conflict) = match diff_type { + Unified => (new_line.chars().next(), None, None), + Combined(Number(n_parents), in_merge_conflict) => { let prefix = &new_line[..min(n_parents, new_line.len())]; let prefix_char = match prefix.chars().find(|c| c == &'-' || c == &'+') { Some(c) => Some(c), diff --cc src/handlers/merge_conflict.rs index a956f2e,3a7e7b9..0000000 --- a/src/handlers/merge_conflict.rs +++ b/src/handlers/merge_conflict.rs @@@ -1,9 -1,10 +1,18 @@@ -use std::cmp::min; use std::ops::{Index, IndexMut}; ++<<<<<<< HEAD use super::draw; use crate::cli; use crate::config::{self, delta_unreachable}; +use crate::delta::{DiffType, InMergeConflict, MergeParents, State, StateMachine}; ++||||||| parent of b2b28c8... Display merge conflict branches + use crate::delta::{DiffType, MergeParents, State, StateMachine}; ++======= ++use super::draw; ++use crate::cli; ++use crate::config::{self, delta_unreachable}; ++use crate::delta::{DiffType, MergeParents, State, StateMachine}; ++>>>>>>> b2b28c8... Display merge conflict branches use crate::minusplus::MinusPlus; use crate::paint; use crate::style::DecorationStyle; @@@ -28,7 -29,8 +37,15 @@@ pub type MergeConflictCommitNames = Mer impl<'a> StateMachine<'a> { pub fn handle_merge_conflict_line(&mut self) -> std::io::Result { use DiffType::*; ++<<<<<<< HEAD + use MergeConflictCommit::*; ++||||||| parent of b2b28c8... Display merge conflict branches ++ use MergeParents::*; ++ use Source::*; ++======= + use MergeConflictCommit::*; + use MergeParents::*; ++>>>>>>> b2b28c8... Display merge conflict branches use State::*; let mut handled_line = false; @@@ -36,36 -38,28 +53,113 @@@ return Ok(handled_line); } ++<<<<<<< HEAD + match self.state.clone() { + HunkHeader(Combined(merge_parents, InMergeConflict::No), _, _) + | HunkMinus(Combined(merge_parents, InMergeConflict::No), _) + | HunkZero(Combined(merge_parents, InMergeConflict::No)) + | HunkPlus(Combined(merge_parents, InMergeConflict::No), _) => { + handled_line = self.enter_merge_conflict(&merge_parents) + } + MergeConflict(merge_parents, Ours) => { + handled_line = self.enter_ancestral(&merge_parents) + || self.enter_theirs(&merge_parents) + || self.exit_merge_conflict(&merge_parents)? + || self.store_line( + Ours, + HunkPlus(Combined(merge_parents, InMergeConflict::Yes), None), + ); ++||||||| parent of b2b28c8... Display merge conflict branches ++ // TODO: don't allocate on heap at this point ++ let prefix = self.line[..min(self.line.len(), 2)].to_string(); ++ let diff_type = Combined(Prefix(prefix)); ++ ++ match self.state { ++ // The only transition into a merge conflict is HunkZero => MergeConflict(Ours) ++ // TODO: shouldn't this be HunkZero(Some(_))? ++ HunkZero(_) => { ++ if self.line.starts_with("++<<<<<<<") { ++ self.state = MergeConflict(Ours); ++ handled_line = true ++ } + } ++ MergeConflict(Ours) => { ++ if self.line.starts_with("++|||||||") { ++ self.state = MergeConflict(Ancestral); ++ } else if self.line.starts_with("++=======") { ++ self.state = MergeConflict(Theirs); ++ } else if self.line.starts_with("++>>>>>>>") { ++ self.paint_buffered_merge_conflict_lines(diff_type)?; ++ } else { ++ let line = self.painter.prepare(&self.line, diff_type.n_parents()); ++ self.painter.merge_conflict_lines[Ours].push((line, HunkPlus(diff_type, None))); ++ } ++ handled_line = true ++======= + // TODO: don't allocate on heap at this point + let prefix = self.line[..min(self.line.len(), 2)].to_string(); + let diff_type = Combined(Prefix(prefix)); + + match self.state { + // The only transition into a merge conflict is HunkZero => MergeConflict(Ours) + // TODO: shouldn't this be HunkZero(Some(_))? + HunkZero(_) => handled_line = self.enter_merge_conflict(), + MergeConflict(Ours) => { + handled_line = self.enter_ancestral() + || self.enter_theirs() + || self.exit_merge_conflict(diff_type.clone())? + || self.store_line(Ours, HunkPlus(diff_type, None)); ++>>>>>>> b2b28c8... Display merge conflict branches + } ++<<<<<<< HEAD + MergeConflict(merge_parents, Ancestral) => { + handled_line = self.enter_theirs(&merge_parents) + || self.exit_merge_conflict(&merge_parents)? + || self.store_line( + Ancestral, + HunkMinus(Combined(merge_parents, InMergeConflict::Yes), None), + ); ++||||||| parent of b2b28c8... Display merge conflict branches ++ MergeConflict(Ancestral) => { ++ if self.line.starts_with("++=======") { ++ self.state = MergeConflict(Theirs); ++ } else if self.line.starts_with("++>>>>>>>") { ++ self.paint_buffered_merge_conflict_lines(diff_type)?; ++ } else { ++ let line = self.painter.prepare(&self.line, diff_type.n_parents()); ++ self.painter.merge_conflict_lines[Ancestral] ++ .push((line, HunkMinus(diff_type, None))); ++ } ++ handled_line = true ++======= + MergeConflict(Ancestral) => { + handled_line = self.enter_theirs() + || self.exit_merge_conflict(diff_type.clone())? + || self.store_line(Ancestral, HunkMinus(diff_type, None)); ++>>>>>>> b2b28c8... Display merge conflict branches } ++<<<<<<< HEAD + MergeConflict(merge_parents, Theirs) => { + handled_line = self.exit_merge_conflict(&merge_parents)? + || self.store_line( + Theirs, + HunkPlus(Combined(merge_parents, InMergeConflict::Yes), None), + ); ++||||||| parent of b2b28c8... Display merge conflict branches ++ MergeConflict(Theirs) => { ++ if self.line.starts_with("++>>>>>>>") { ++ self.paint_buffered_merge_conflict_lines(diff_type)?; ++ } else { ++ let line = self.painter.prepare(&self.line, diff_type.n_parents()); ++ self.painter.merge_conflict_lines[Theirs] ++ .push((line, HunkPlus(diff_type, None))); ++ } ++ handled_line = true ++======= + MergeConflict(Theirs) => { + handled_line = self.exit_merge_conflict(diff_type.clone())? + || self.store_line(Theirs, HunkPlus(diff_type, None)); ++>>>>>>> b2b28c8... Display merge conflict branches } _ => {} } @@@ -73,65 -67,60 +167,124 @@@ Ok(handled_line) } ++<<<<<<< HEAD + fn enter_merge_conflict(&mut self, merge_parents: &MergeParents) -> bool { + use State::*; + if let Some(commit) = parse_merge_marker(&self.line, "++<<<<<<<") { + self.state = MergeConflict(merge_parents.clone(), Ours); + self.painter.merge_conflict_commit_names[Ours] = Some(commit.to_string()); + true + } else { + false + } + } + + fn enter_ancestral(&mut self, merge_parents: &MergeParents) -> bool { + use State::*; + if let Some(commit) = parse_merge_marker(&self.line, "++|||||||") { + self.state = MergeConflict(merge_parents.clone(), Ancestral); + self.painter.merge_conflict_commit_names[Ancestral] = Some(commit.to_string()); + true + } else { + false + } + } + + fn enter_theirs(&mut self, merge_parents: &MergeParents) -> bool { + use State::*; + if self.line.starts_with("++=======") { + self.state = MergeConflict(merge_parents.clone(), Theirs); + true + } else { + false + } + } + + fn exit_merge_conflict(&mut self, merge_parents: &MergeParents) -> std::io::Result { + if let Some(commit) = parse_merge_marker(&self.line, "++>>>>>>>") { + self.painter.merge_conflict_commit_names[Theirs] = Some(commit.to_string()); + self.paint_buffered_merge_conflict_lines(merge_parents)?; + Ok(true) + } else { + Ok(false) + } + } + + fn store_line(&mut self, commit: MergeConflictCommit, state: State) -> bool { + use State::*; + if let HunkMinus(diff_type, _) | HunkZero(diff_type) | HunkPlus(diff_type, _) = &state { + let line = self.painter.prepare(&self.line, diff_type.n_parents()); + self.painter.merge_conflict_lines[commit].push((line, state)); + true + } else { + delta_unreachable(&format!("Invalid state: {:?}", state)) + } + } + + fn paint_buffered_merge_conflict_lines( + &mut self, + merge_parents: &MergeParents, + ) -> std::io::Result<()> { + use DiffType::*; + use State::*; ++||||||| parent of b2b28c8... Display merge conflict branches ++ fn paint_buffered_merge_conflict_lines(&mut self, diff_type: DiffType) -> std::io::Result<()> { ++======= + fn enter_merge_conflict(&mut self) -> bool { + use State::*; + if let Some(commit) = parse_merge_marker(&self.line, "++<<<<<<<") { + self.state = MergeConflict(Ours); + self.painter.merge_conflict_commit_names[Ours] = Some(commit.to_string()); + true + } else { + false + } + } + + fn enter_ancestral(&mut self) -> bool { + use State::*; + if let Some(commit) = parse_merge_marker(&self.line, "++|||||||") { + self.state = MergeConflict(Ancestral); + self.painter.merge_conflict_commit_names[Ancestral] = Some(commit.to_string()); + true + } else { + false + } + } + + fn enter_theirs(&mut self) -> bool { + use State::*; + if self.line.starts_with("++=======") { + self.state = MergeConflict(Theirs); + true + } else { + false + } + } + + fn exit_merge_conflict(&mut self, diff_type: DiffType) -> std::io::Result { + if let Some(commit) = parse_merge_marker(&self.line, "++>>>>>>>") { + self.painter.merge_conflict_commit_names[Theirs] = Some(commit.to_string()); + self.paint_buffered_merge_conflict_lines(diff_type)?; + Ok(true) + } else { + Ok(false) + } + } + + fn store_line(&mut self, commit: MergeConflictCommit, state: State) -> bool { + use State::*; + if let HunkMinus(diff_type, _) | HunkZero(diff_type) | HunkPlus(diff_type, _) = &state { + let line = self.painter.prepare(&self.line, diff_type.n_parents()); + self.painter.merge_conflict_lines[commit].push((line, state)); + true + } else { + delta_unreachable(&format!("Invalid state: {:?}", state)) + } + } + + fn paint_buffered_merge_conflict_lines(&mut self, diff_type: DiffType) -> std::io::Result<()> { ++>>>>>>> b2b28c8... Display merge conflict branches self.painter.emit()?; write_merge_conflict_bar("▼", &mut self.painter, self.config)?; @@@ -163,6 -152,6 +316,7 @@@ } } ++<<<<<<< HEAD fn write_subhunk_header( derived_commit_type: &MergeConflictCommit, decoration_style: &str, @@@ -197,63 -186,63 +351,170 @@@ Ok(()) } + #[allow(unused)] + fn write_merge_conflict_line( + painter: &mut paint::Painter, + config: &config::Config, + ) -> std::io::Result<()> { + let (mut draw_fn, _pad, decoration_ansi_term_style) = draw::get_draw_function( + DecorationStyle::from_str("bold ol", config.true_color, config.git_config.as_ref()), + ); + draw_fn( + painter.writer, + "", + "", + &config.decorations_width, + config.hunk_header_style, + decoration_ansi_term_style, + )?; + Ok(()) + } ++||||||| parent of b2b28c8... Display merge conflict branches ++pub use Source::*; ++======= ++fn write_subhunk_header( ++ derived_commit_type: &MergeConflictCommit, ++ decoration_style: &str, ++ painter: &mut paint::Painter, ++ config: &config::Config, ++) -> std::io::Result<()> { ++ let (mut draw_fn, pad, decoration_ansi_term_style) = ++ draw::get_draw_function(DecorationStyle::from_str( ++ decoration_style, ++ config.true_color, ++ config.git_config.as_ref(), ++ )); ++ let derived_commit_name = &painter.merge_conflict_commit_names[derived_commit_type]; ++ let text = if let Some(_ancestral_commit) = &painter.merge_conflict_commit_names[Ancestral] { ++ format!( ++ "ancestor {} {}{}", ++ config.right_arrow, ++ derived_commit_name.as_deref().unwrap_or("?"), ++ if pad { " " } else { "" } ++ ) ++ } else { ++ derived_commit_name.as_deref().unwrap_or("?").to_string() ++ }; ++ draw_fn( ++ painter.writer, ++ &text, ++ &text, ++ &config.decorations_width, ++ config.hunk_header_style, ++ decoration_ansi_term_style, ++ )?; ++ Ok(()) ++} ++>>>>>>> b2b28c8... Display merge conflict branches + ++<<<<<<< HEAD + fn write_merge_conflict_bar( + s: &str, + painter: &mut paint::Painter, + config: &config::Config, + ) -> std::io::Result<()> { + if let cli::Width::Fixed(width) = config.decorations_width { + writeln!(painter.writer, "{}", s.repeat(width))?; + } + Ok(()) + } + + fn parse_merge_marker<'a>(line: &'a str, marker: &str) -> Option<&'a str> { + match line.strip_prefix(marker) { + Some(suffix) => { + let suffix = suffix.trim(); + if !suffix.is_empty() { + Some(suffix) + } else { + None + } + } + None => None, + } + } + + pub use MergeConflictCommit::*; + + impl Index for MergeConflictCommits { + type Output = T; + fn index(&self, commit: MergeConflictCommit) -> &Self::Output { + match commit { ++||||||| parent of b2b28c8... Display merge conflict branches ++impl Index for MergeConflictLines { ++ type Output = Vec<(String, State)>; ++ fn index(&self, source: Source) -> &Self::Output { ++ match source { ++======= +#[allow(unused)] +fn write_merge_conflict_line( + painter: &mut paint::Painter, + config: &config::Config, +) -> std::io::Result<()> { + let (mut draw_fn, _pad, decoration_ansi_term_style) = draw::get_draw_function( + DecorationStyle::from_str("bold ol", config.true_color, config.git_config.as_ref()), + ); + draw_fn( + painter.writer, + "", + "", + &config.decorations_width, + config.hunk_header_style, + decoration_ansi_term_style, + )?; + Ok(()) +} + +fn write_merge_conflict_bar( + s: &str, + painter: &mut paint::Painter, + config: &config::Config, +) -> std::io::Result<()> { + if let cli::Width::Fixed(width) = config.decorations_width { + writeln!(painter.writer, "{}", s.repeat(width))?; + } + Ok(()) +} + +fn parse_merge_marker<'a>(line: &'a str, marker: &str) -> Option<&'a str> { + match line.strip_prefix(marker) { + Some(suffix) => { + let suffix = suffix.trim(); + if !suffix.is_empty() { + Some(suffix) + } else { + None + } + } + None => None, + } +} + +pub use MergeConflictCommit::*; + +impl Index for MergeConflictCommits { + type Output = T; + fn index(&self, commit: MergeConflictCommit) -> &Self::Output { + match commit { + Ours => &self.ours, + Ancestral => &self.ancestral, + Theirs => &self.theirs, + } + } +} + ++impl Index<&MergeConflictCommit> for MergeConflictCommits { ++ type Output = T; ++ fn index(&self, commit: &MergeConflictCommit) -> &Self::Output { ++ match commit { ++>>>>>>> b2b28c8... Display merge conflict branches + Ours => &self.ours, + Ancestral => &self.ancestral, + Theirs => &self.theirs, + } + } + } + ++<<<<<<< HEAD impl Index<&MergeConflictCommit> for MergeConflictCommits { type Output = T; fn index(&self, commit: &MergeConflictCommit) -> &Self::Output { @@@ -268,6 -257,6 +529,15 @@@ impl IndexMut for MergeConflictCommits { fn index_mut(&mut self, commit: MergeConflictCommit) -> &mut Self::Output { match commit { ++||||||| parent of b2b28c8... Display merge conflict branches ++impl IndexMut for MergeConflictLines { ++ fn index_mut(&mut self, source: Source) -> &mut Self::Output { ++ match source { ++======= ++impl IndexMut for MergeConflictCommits { ++ fn index_mut(&mut self, commit: MergeConflictCommit) -> &mut Self::Output { ++ match commit { ++>>>>>>> b2b28c8... Display merge conflict branches Ours => &mut self.ours, Ancestral => &mut self.ancestral, Theirs => &mut self.theirs,