@@ -46,6 +46,7 @@ use std::{
ops::Range,
sync::Arc,
};
+use theme::DiffStyle;
struct SelectionLayout {
head: DisplayPoint,
@@ -525,98 +526,156 @@ impl EditorElement {
layout: &mut LayoutState,
cx: &mut PaintContext,
) {
- let line_height = layout.position_map.line_height;
- let scroll_position = layout.position_map.snapshot.scroll_position();
- let scroll_top = scroll_position.y() * line_height;
- for (ix, line) in layout.line_number_layouts.iter().enumerate() {
- if let Some(line) = line {
- let line_origin = bounds.origin()
- + vec2f(
- bounds.width() - line.width() - layout.gutter_padding,
- ix as f32 * layout.position_map.line_height
- - (scroll_top % layout.position_map.line_height),
- );
- line.paint(
- line_origin,
- visible_bounds,
- layout.position_map.line_height,
- cx,
- );
- }
+ struct GutterLayout {
+ line_height: f32,
+ // scroll_position: Vector2F,
+ scroll_top: f32,
+ bounds: RectF,
}
- let (
- inserted_color,
- modified_color,
- deleted_color,
- width_multiplier,
- corner_radius,
- removed_width_mult,
- ) = {
- let editor = &cx.global::<Settings>().theme.editor;
- (
- editor.diff_background_inserted,
- editor.diff_background_modified,
- editor.diff_background_deleted,
- editor.diff_indicator_width_multiplier,
- editor.diff_indicator_corner_radius,
- editor.removed_diff_width_multiplier,
- )
- };
+ struct DiffLayout<'a> {
+ buffer_line: usize,
+ last_diff: Option<(&'a DiffHunk<u32>, usize)>,
+ }
- for hunk in &layout.diff_hunks {
- let color = match hunk.status() {
- DiffHunkStatus::Added => inserted_color,
- DiffHunkStatus::Modified => modified_color,
+ fn diff_quad(
+ status: DiffHunkStatus,
+ layout_range: Range<usize>,
+ gutter_layout: &GutterLayout,
+ diff_style: &DiffStyle,
+ ) -> Quad {
+ let color = match status {
+ DiffHunkStatus::Added => diff_style.inserted,
+ DiffHunkStatus::Modified => diff_style.modified,
//TODO: This rendering is entirely a horrible hack
DiffHunkStatus::Removed => {
- let row = hunk.buffer_range.start;
+ let row = layout_range.start;
- let offset = line_height / 2.;
- let start_y = row as f32 * line_height + offset - scroll_top;
- let end_y = start_y + line_height;
+ let offset = gutter_layout.line_height / 2.;
+ let start_y =
+ row as f32 * gutter_layout.line_height + offset - gutter_layout.scroll_top;
+ let end_y = start_y + gutter_layout.line_height;
- let width = removed_width_mult * line_height;
- let highlight_origin = bounds.origin() + vec2f(-width, start_y);
+ let width = diff_style.removed_width_em * gutter_layout.line_height;
+ let highlight_origin = gutter_layout.bounds.origin() + vec2f(-width, start_y);
let highlight_size = vec2f(width * 2., end_y - start_y);
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
- cx.scene.push_quad(Quad {
+ return Quad {
bounds: highlight_bounds,
- background: Some(deleted_color),
+ background: Some(diff_style.deleted),
border: Border::new(0., Color::transparent_black()),
- corner_radius: 1. * line_height,
- });
-
- continue;
+ corner_radius: 1. * gutter_layout.line_height,
+ };
}
};
- let start_row = hunk.buffer_range.start;
- let end_row = hunk.buffer_range.end;
+ let start_row = layout_range.start;
+ let end_row = layout_range.end;
- let start_y = start_row as f32 * line_height - scroll_top;
- let end_y = end_row as f32 * line_height - scroll_top;
+ let start_y = start_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top;
+ let end_y = end_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top;
- let width = width_multiplier * line_height;
- let highlight_origin = bounds.origin() + vec2f(-width, start_y);
+ let width = diff_style.width_em * gutter_layout.line_height;
+ let highlight_origin = gutter_layout.bounds.origin() + vec2f(-width, start_y);
let highlight_size = vec2f(width * 2., end_y - start_y);
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
- cx.scene.push_quad(Quad {
+ Quad {
bounds: highlight_bounds,
background: Some(color),
border: Border::new(0., Color::transparent_black()),
- corner_radius: corner_radius * line_height,
- });
+ corner_radius: diff_style.corner_radius * gutter_layout.line_height,
+ }
+ }
+
+ let gutter_layout = {
+ let scroll_position = layout.position_map.snapshot.scroll_position();
+ let line_height = layout.position_map.line_height;
+ GutterLayout {
+ scroll_top: scroll_position.y() * line_height,
+ // scroll_position,
+ line_height,
+ bounds,
+ }
+ };
+
+ let mut diff_layout = DiffLayout {
+ buffer_line: 0,
+ last_diff: None,
+ };
+
+ let diff_style = &cx.global::<Settings>().theme.editor.diff.clone();
+ // dbg!("***************");
+ // dbg!(&layout.diff_hunks);
+ // dbg!("***************");
+
+ // line is `None` when there's a line wrap
+ for (ix, line) in layout.line_number_layouts.iter().enumerate() {
+ // dbg!(ix);
+ if let Some(line) = line {
+ let line_origin = bounds.origin()
+ + vec2f(
+ bounds.width() - line.width() - layout.gutter_padding,
+ ix as f32 * gutter_layout.line_height
+ - (gutter_layout.scroll_top % gutter_layout.line_height),
+ );
+
+ line.paint(line_origin, visible_bounds, gutter_layout.line_height, cx);
+
+ //This line starts a buffer line, so let's do the diff calculation
+ let new_hunk = get_hunk(diff_layout.buffer_line, &layout.diff_hunks);
+
+ // This + the unwraps are annoying, but at least it's legible
+ let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) {
+ (None, None) => (false, false),
+ (None, Some(_)) => (false, true),
+ (Some(_), None) => (true, false),
+ (Some((old_hunk, _)), Some(new_hunk)) if new_hunk == old_hunk => (false, false),
+ (Some(_), Some(_)) => (true, true),
+ };
+
+ // dbg!(diff_layout.buffer_line, is_starting);
+
+ if is_ending {
+ let (last_hunk, start_line) = diff_layout.last_diff.take().unwrap();
+ // dbg!("ending");
+ // dbg!(start_line..ix);
+ cx.scene.push_quad(diff_quad(
+ last_hunk.status(),
+ start_line..ix,
+ &gutter_layout,
+ diff_style,
+ ));
+ }
+
+ if is_starting {
+ let new_hunk = new_hunk.unwrap();
+
+ diff_layout.last_diff = Some((new_hunk, ix));
+ };
+
+ diff_layout.buffer_line += 1;
+ }
+ }
+
+ // If we ran out with a diff hunk still being prepped, paint it now
+ if let Some((last_hunk, start_line)) = diff_layout.last_diff {
+ let end_line = layout.line_number_layouts.len();
+ cx.scene.push_quad(diff_quad(
+ last_hunk.status(),
+ start_line..end_line,
+ &gutter_layout,
+ diff_style,
+ ))
}
if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
let mut x = bounds.width() - layout.gutter_padding;
- let mut y = *row as f32 * line_height - scroll_top;
+ let mut y = *row as f32 * gutter_layout.line_height - gutter_layout.scroll_top;
x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
- y += (line_height - indicator.size().y()) / 2.;
+ y += (gutter_layout.line_height - indicator.size().y()) / 2.;
indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx);
}
}
@@ -1321,6 +1380,28 @@ impl EditorElement {
}
}
+/// Get the hunk that contains buffer_line, starting from start_idx
+/// Returns none if there is none found, and
+fn get_hunk(buffer_line: usize, hunks: &[DiffHunk<u32>]) -> Option<&DiffHunk<u32>> {
+ for i in 0..hunks.len() {
+ // Safety: Index out of bounds is handled by the check above
+ let hunk = hunks.get(i).unwrap();
+ if hunk.buffer_range.contains(&(buffer_line as u32)) {
+ return Some(hunk);
+ } else if hunk.status() == DiffHunkStatus::Removed
+ && buffer_line == hunk.buffer_range.start as usize
+ {
+ return Some(hunk);
+ } else if hunk.buffer_range.start > buffer_line as u32 {
+ // If we've passed the buffer_line, just stop
+ return None;
+ }
+ }
+
+ // We reached the end of the array without finding a hunk, just return none.
+ return None;
+}
+
impl Element for EditorElement {
type LayoutState = LayoutState;
type PaintState = ();
@@ -1,10 +1,11 @@
use anyhow::{anyhow, Result};
use fsevent::EventStream;
use futures::{future::BoxFuture, Stream, StreamExt};
-use git::repository::{FakeGitRepositoryState, GitRepository, LibGitRepository};
+use git::repository::{GitRepository, LibGitRepository};
use language::LineEnding;
use parking_lot::Mutex as SyncMutex;
use smol::io::{AsyncReadExt, AsyncWriteExt};
+use std::sync::Arc;
use std::{
io,
os::unix::fs::MetadataExt,
@@ -12,16 +13,17 @@ use std::{
pin::Pin,
time::{Duration, SystemTime},
};
-use util::ResultExt;
-
use text::Rope;
+use util::ResultExt;
#[cfg(any(test, feature = "test-support"))]
use collections::{btree_map, BTreeMap};
#[cfg(any(test, feature = "test-support"))]
use futures::lock::Mutex;
#[cfg(any(test, feature = "test-support"))]
-use std::sync::{Arc, Weak};
+use git::repository::FakeGitRepositoryState;
+#[cfg(any(test, feature = "test-support"))]
+use std::sync::Weak;
#[async_trait::async_trait]
pub trait Fs: Send + Sync {
@@ -488,12 +488,7 @@ pub struct Editor {
pub rename_fade: f32,
pub document_highlight_read_background: Color,
pub document_highlight_write_background: Color,
- pub diff_background_deleted: Color,
- pub diff_background_inserted: Color,
- pub diff_background_modified: Color,
- pub removed_diff_width_multiplier: f32,
- pub diff_indicator_width_multiplier: f32,
- pub diff_indicator_corner_radius: f32,
+ pub diff: DiffStyle,
pub line_number: Color,
pub line_number_active: Color,
pub guest_selections: Vec<SelectionStyle>,
@@ -577,6 +572,16 @@ pub struct CodeActions {
pub vertical_scale: f32,
}
+#[derive(Clone, Deserialize, Default)]
+pub struct DiffStyle {
+ pub inserted: Color,
+ pub modified: Color,
+ pub deleted: Color,
+ pub removed_width_em: f32,
+ pub width_em: f32,
+ pub corner_radius: f32,
+}
+
#[derive(Debug, Default, Clone, Copy)]
pub struct Interactive<T> {
pub default: T,
@@ -60,12 +60,14 @@ export default function editor(theme: Theme) {
indicator: iconColor(theme, "secondary"),
verticalScale: 0.618
},
- diffBackgroundDeleted: theme.iconColor.error,
- diffBackgroundInserted: theme.iconColor.ok,
- diffBackgroundModified: theme.iconColor.warning,
- removedDiffWidthMultiplier: 0.275,
- diffIndicatorWidthMultiplier: 0.16,
- diffIndicatorCornerRadius: 0.05,
+ diff: {
+ deleted: theme.iconColor.error,
+ inserted: theme.iconColor.ok,
+ modified: theme.iconColor.warning,
+ removedWidthEm: 0.275,
+ widthEm: 0.16,
+ cornerRadius: 0.05,
+ },
documentHighlightReadBackground: theme.editor.highlight.occurrence,
documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence,
errorColor: theme.textColor.error,