Highlight merge conflicts and provide for resolving them (#28065)

TODO:

- [x] Make it work in the project diff:
  - [x] Support non-singleton buffers
  - [x] Adjust excerpt boundaries to show full conflicts
- [x] Write tests for conflict-related events and state management
- [x] Prevent hunk buttons from appearing inside conflicts
- [x] Make sure it works over SSH, collab
- [x] Allow separate theming of markers

Bonus:

- [ ] Count of conflicts in toolbar
- [ ] Keyboard-driven navigation and resolution
- [ ] ~~Inlay hints to contextualize "ours"/"theirs"~~

Release Notes:

- Implemented initial support for resolving merge conflicts.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Cole Miller 2025-04-23 12:38:46 -04:00 committed by GitHub
parent ef54b58346
commit 724c935196
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1626 additions and 184 deletions

View file

@ -143,6 +143,11 @@ impl ThemeColors {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: orange().light().step_12(),
version_control_ignored: gray().light().step_12(),
version_control_conflict_ours_background: green().light().step_10().alpha(0.5),
version_control_conflict_theirs_background: blue().light().step_10().alpha(0.5),
version_control_conflict_ours_marker_background: green().light().step_10().alpha(0.7),
version_control_conflict_theirs_marker_background: blue().light().step_10().alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
}
}
@ -258,6 +263,11 @@ impl ThemeColors {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: orange().dark().step_12(),
version_control_ignored: gray().dark().step_12(),
version_control_conflict_ours_background: green().dark().step_10().alpha(0.5),
version_control_conflict_theirs_background: blue().dark().step_10().alpha(0.5),
version_control_conflict_ours_marker_background: green().dark().step_10().alpha(0.7),
version_control_conflict_theirs_marker_background: blue().dark().step_10().alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
}
}
}

View file

@ -201,6 +201,23 @@ pub(crate) fn zed_default_dark() -> Theme {
version_control_renamed: MODIFIED_COLOR,
version_control_conflict: crate::orange().light().step_12(),
version_control_ignored: crate::gray().light().step_12(),
version_control_conflict_ours_background: crate::green()
.light()
.step_12()
.alpha(0.5),
version_control_conflict_theirs_background: crate::blue()
.light()
.step_12()
.alpha(0.5),
version_control_conflict_ours_marker_background: crate::green()
.light()
.step_12()
.alpha(0.7),
version_control_conflict_theirs_marker_background: crate::blue()
.light()
.step_12()
.alpha(0.7),
version_control_conflict_divider_background: Hsla::default(),
},
status: StatusColors {
conflict: yellow,

View file

@ -586,6 +586,26 @@ pub struct ThemeColorsContent {
/// Ignored version control color.
#[serde(rename = "version_control.ignored")]
pub version_control_ignored: Option<String>,
/// Background color for row highlights of "ours" regions in merge conflicts.
#[serde(rename = "version_control.conflict.ours_background")]
pub version_control_conflict_ours_background: Option<String>,
/// Background color for row highlights of "theirs" regions in merge conflicts.
#[serde(rename = "version_control.conflict.theirs_background")]
pub version_control_conflict_theirs_background: Option<String>,
/// Background color for row highlights of "ours" conflict markers in merge conflicts.
#[serde(rename = "version_control.conflict.ours_marker_background")]
pub version_control_conflict_ours_marker_background: Option<String>,
/// Background color for row highlights of "theirs" conflict markers in merge conflicts.
#[serde(rename = "version_control.conflict.theirs_marker_background")]
pub version_control_conflict_theirs_marker_background: Option<String>,
/// Background color for row highlights of the "ours"/"theirs" divider in merge conflicts.
#[serde(rename = "version_control.conflict.divider_background")]
pub version_control_conflict_divider_background: Option<String>,
}
impl ThemeColorsContent {
@ -1037,6 +1057,26 @@ impl ThemeColorsContent {
.and_then(|color| try_parse_color(color).ok())
// Fall back to `conflict`, for backwards compatibility.
.or(status_colors.ignored),
version_control_conflict_ours_background: self
.version_control_conflict_ours_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_theirs_background: self
.version_control_conflict_theirs_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_ours_marker_background: self
.version_control_conflict_ours_marker_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_theirs_marker_background: self
.version_control_conflict_theirs_marker_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
version_control_conflict_divider_background: self
.version_control_conflict_divider_background
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
}
}
}

View file

@ -261,6 +261,14 @@ pub struct ThemeColors {
pub version_control_conflict: Hsla,
/// Represents an ignored entry in version control systems.
pub version_control_ignored: Hsla,
/// Represents the "ours" region of a merge conflict.
pub version_control_conflict_ours_background: Hsla,
/// Represents the "theirs" region of a merge conflict.
pub version_control_conflict_theirs_background: Hsla,
pub version_control_conflict_ours_marker_background: Hsla,
pub version_control_conflict_theirs_marker_background: Hsla,
pub version_control_conflict_divider_background: Hsla,
}
#[derive(EnumIter, Debug, Clone, Copy, AsRefStr)]