Highlight active lines

This commit is contained in:
Antonio Scandurra
2021-07-30 09:50:28 -07:00
committed by Max Brunsfeld
parent 3abed88c76
commit db9cfb0d2b
4 changed files with 77 additions and 27 deletions
+3 -2
View File
@@ -15,8 +15,9 @@ modal_match_text = 0xcccccc
modal_match_text_highlight = 0x18a3ff modal_match_text_highlight = 0x18a3ff
[editor] [editor]
background = 0x1c1d1e background = 0x131415
gutter_background = 0x1c1d1e gutter_background = 0x131415
active_line_background = 0x1c1d1e
line_number = 0x5a5a5b line_number = 0x5a5a5b
line_number_active = 0xffffff line_number_active = 0xffffff
default_text = 0xd4d4d4 default_text = 0xd4d4d4
+3 -3
View File
@@ -27,7 +27,7 @@ use smallvec::SmallVec;
use smol::Timer; use smol::Timer;
use std::{ use std::{
cmp::{self, Ordering}, cmp::{self, Ordering},
collections::HashSet, collections::BTreeMap,
fmt::Write, fmt::Write,
iter::FromIterator, iter::FromIterator,
mem, mem,
@@ -2357,7 +2357,7 @@ impl Snapshot {
pub fn layout_line_numbers( pub fn layout_line_numbers(
&self, &self,
rows: Range<u32>, rows: Range<u32>,
active_rows: &HashSet<u32>, active_rows: &BTreeMap<u32, bool>,
font_cache: &FontCache, font_cache: &FontCache,
layout_cache: &TextLayoutCache, layout_cache: &TextLayoutCache,
theme: &Theme, theme: &Theme,
@@ -2373,7 +2373,7 @@ impl Snapshot {
.enumerate() .enumerate()
{ {
let display_row = rows.start + ix as u32; let display_row = rows.start + ix as u32;
let color = if active_rows.contains(&display_row) { let color = if active_rows.contains_key(&display_row) {
theme.editor.line_number_active.0 theme.editor.line_number_active.0
} else { } else {
theme.editor.line_number.0 theme.editor.line_number.0
+70 -22
View File
@@ -14,10 +14,10 @@ use gpui::{
}; };
use json::json; use json::json;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cmp::Ordering, collections::HashSet, ops::Range};
use std::{ use std::{
cmp::{self}, cmp::{self, Ordering},
collections::HashMap, collections::{BTreeMap, HashMap},
ops::Range,
}; };
pub struct EditorElement { pub struct EditorElement {
@@ -182,19 +182,64 @@ impl EditorElement {
true true
} }
fn paint_gutter(&mut self, rect: RectF, layout: &LayoutState, cx: &mut PaintContext) { fn paint_background(
let settings = self.view(cx.app).settings.borrow(); &self,
let theme = &settings.theme; gutter_bounds: RectF,
text_bounds: RectF,
layout: &LayoutState,
cx: &mut PaintContext,
) {
let bounds = gutter_bounds.union_rect(text_bounds);
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height; let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
let editor = self.view(cx.app);
cx.scene.push_layer(Some(rect)); let settings = editor.settings.borrow();
let theme = &settings.theme;
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
bounds: rect, bounds: gutter_bounds,
background: Some(theme.editor.gutter_background.0), background: Some(theme.editor.gutter_background.0),
border: Border::new(0., ColorU::transparent_black()), border: Border::new(0., ColorU::transparent_black()),
corner_radius: 0., corner_radius: 0.,
}); });
cx.scene.push_quad(Quad {
bounds: text_bounds,
background: Some(theme.editor.background.0),
border: Border::new(0., ColorU::transparent_black()),
corner_radius: 0.,
});
if !editor.single_line {
let mut active_rows = layout.active_rows.iter().peekable();
while let Some((start_row, contains_non_empty_selection)) = active_rows.next() {
let mut end_row = *start_row;
while active_rows.peek().map_or(false, |r| {
*r.0 == end_row + 1 && r.1 == contains_non_empty_selection
}) {
active_rows.next().unwrap();
end_row += 1;
}
if !contains_non_empty_selection {
let origin = vec2f(
bounds.origin_x(),
bounds.origin_y() + (layout.line_height * *start_row as f32) - scroll_top,
);
let size = vec2f(
bounds.width(),
layout.line_height * (end_row - start_row + 1) as f32,
);
cx.scene.push_quad(Quad {
bounds: RectF::new(origin, size),
background: Some(theme.editor.active_line_background.0),
border: Border::default(),
corner_radius: 0.,
});
}
}
}
}
fn paint_gutter(&mut self, rect: RectF, layout: &LayoutState, cx: &mut PaintContext) {
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
for (ix, line) in layout.line_number_layouts.iter().enumerate() { for (ix, line) in layout.line_number_layouts.iter().enumerate() {
if let Some(line) = line { if let Some(line) = line {
let line_origin = rect.origin() let line_origin = rect.origin()
@@ -209,8 +254,6 @@ impl EditorElement {
); );
} }
} }
cx.scene.pop_layer();
} }
fn paint_text(&mut self, bounds: RectF, layout: &LayoutState, cx: &mut PaintContext) { fn paint_text(&mut self, bounds: RectF, layout: &LayoutState, cx: &mut PaintContext) {
@@ -225,12 +268,6 @@ impl EditorElement {
let scroll_left = scroll_position.x() * max_glyph_width; let scroll_left = scroll_position.x() * max_glyph_width;
cx.scene.push_layer(Some(bounds)); cx.scene.push_layer(Some(bounds));
cx.scene.push_quad(Quad {
bounds,
background: Some(theme.background.0),
border: Border::new(0., ColorU::transparent_black()),
corner_radius: 0.,
});
// Draw selections // Draw selections
let corner_radius = 2.5; let corner_radius = 2.5;
@@ -393,7 +430,7 @@ impl Element for EditorElement {
let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen
let mut selections = HashMap::new(); let mut selections = HashMap::new();
let mut active_rows = HashSet::new(); let mut active_rows = BTreeMap::new();
self.update_view(cx.app, |view, cx| { self.update_view(cx.app, |view, cx| {
let replica_id = view.replica_id(cx); let replica_id = view.replica_id(cx);
for selection_set_id in view.active_selection_sets(cx).collect::<Vec<_>>() { for selection_set_id in view.active_selection_sets(cx).collect::<Vec<_>>() {
@@ -405,6 +442,7 @@ impl Element for EditorElement {
) { ) {
set.push(selection.clone()); set.push(selection.clone());
if selection_set_id.replica_id == replica_id { if selection_set_id.replica_id == replica_id {
let is_empty = selection.start == selection.end;
let mut selection_start; let mut selection_start;
let mut selection_end; let mut selection_end;
if selection.start < selection.end { if selection.start < selection.end {
@@ -416,10 +454,13 @@ impl Element for EditorElement {
}; };
selection_start = snapshot.prev_row_boundary(selection_start).0; selection_start = snapshot.prev_row_boundary(selection_start).0;
selection_end = snapshot.next_row_boundary(selection_end).0; selection_end = snapshot.next_row_boundary(selection_end).0;
active_rows.extend( for row in cmp::max(selection_start.row(), start_row)
cmp::max(selection_start.row(), start_row) ..=cmp::min(selection_end.row(), end_row)
..=cmp::min(selection_end.row(), end_row), {
); let contains_non_empty_selection =
active_rows.entry(row).or_insert(!is_empty);
*contains_non_empty_selection |= !is_empty;
}
} }
} }
@@ -478,6 +519,7 @@ impl Element for EditorElement {
overscroll, overscroll,
text_offset, text_offset,
snapshot, snapshot,
active_rows,
line_layouts, line_layouts,
line_number_layouts, line_number_layouts,
line_height, line_height,
@@ -520,17 +562,22 @@ impl Element for EditorElement {
cx: &mut PaintContext, cx: &mut PaintContext,
) -> Self::PaintState { ) -> Self::PaintState {
if let Some(layout) = layout { if let Some(layout) = layout {
cx.scene.push_layer(Some(bounds));
let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size); let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
let text_bounds = RectF::new( let text_bounds = RectF::new(
bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
layout.text_size, layout.text_size,
); );
self.paint_background(gutter_bounds, text_bounds, layout, cx);
if layout.gutter_size.x() > 0. { if layout.gutter_size.x() > 0. {
self.paint_gutter(gutter_bounds, layout, cx); self.paint_gutter(gutter_bounds, layout, cx);
} }
self.paint_text(text_bounds, layout, cx); self.paint_text(text_bounds, layout, cx);
cx.scene.pop_layer();
Some(PaintState { Some(PaintState {
bounds, bounds,
text_bounds, text_bounds,
@@ -590,6 +637,7 @@ pub struct LayoutState {
gutter_padding: f32, gutter_padding: f32,
text_size: Vector2F, text_size: Vector2F,
snapshot: Snapshot, snapshot: Snapshot,
active_rows: BTreeMap<u32, bool>,
line_layouts: Vec<text_layout::Line>, line_layouts: Vec<text_layout::Line>,
line_number_layouts: Vec<Option<text_layout::Line>>, line_number_layouts: Vec<Option<text_layout::Line>>,
line_height: f32, line_height: f32,
+1
View File
@@ -57,6 +57,7 @@ pub struct UiTheme {
pub struct EditorTheme { pub struct EditorTheme {
pub background: Color, pub background: Color,
pub gutter_background: Color, pub gutter_background: Color,
pub active_line_background: Color,
pub line_number: Color, pub line_number: Color,
pub line_number_active: Color, pub line_number_active: Color,
pub default_text: Color, pub default_text: Color,