Skip to content

Commit

Permalink
Implement view dimming during jump mode
Browse files Browse the repository at this point in the history
  • Loading branch information
semin-park committed Jan 16, 2023
1 parent 64a31d9 commit a55ee52
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 56 deletions.
13 changes: 6 additions & 7 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use insert::*;
use movement::Movement;

use self::jump::{
apply_dimming, clear_dimming, find_all_char_occurrences, find_all_identifiers_in_view,
apply_dimming, cleanup, find_all_char_occurrences, find_all_identifiers_in_view,
show_key_annotations_with_callback, sort_jump_targets, JumpSequencer, TrieNode, JUMP_KEYS,
};
use crate::{
Expand Down Expand Up @@ -5335,10 +5335,10 @@ fn jump_with_char_input(cx: &mut Context, extend_selection: bool) {
cx.on_next_key(move |cx, event| {
let key = match event.char() {
Some(key) => key,
_ => return clear_dimming(cx),
_ => return cleanup(cx),
};
if !key.is_ascii() {
return clear_dimming(cx);
return cleanup(cx);
}
let jump_targets = find_all_char_occurrences(cx, key as u8);
jump_with_targets(cx, jump_targets, extend_selection);
Expand All @@ -5347,7 +5347,7 @@ fn jump_with_char_input(cx: &mut Context, extend_selection: bool) {

fn jump_with_targets(ctx: &mut Context, mut jump_targets: Vec<Range>, extend_selection: bool) {
if jump_targets.is_empty() {
return clear_dimming(ctx);
return cleanup(ctx);
}
// Jump targets are sorted based on their distance to the current cursor.
jump_targets = sort_jump_targets(ctx, jump_targets);
Expand All @@ -5356,7 +5356,7 @@ fn jump_with_targets(ctx: &mut Context, mut jump_targets: Vec<Range>, extend_sel
}
if jump_targets.len() == 1 {
jump_to(ctx, jump_targets[0], extend_selection);
return clear_dimming(ctx);
return cleanup(ctx);
}
let root = TrieNode::build(JUMP_KEYS, jump_targets);
show_key_annotations_with_callback(ctx, root.generate(), move |ctx, event| {
Expand Down Expand Up @@ -5397,7 +5397,6 @@ fn handle_key(
key: u8,
extend_selection: bool,
) -> bool {
clear_dimming(ctx);
match sequencer.choose(key) {
Some(subnode) => {
sequencer = *subnode;
Expand Down Expand Up @@ -5438,7 +5437,7 @@ fn handle_key_event(
None => true,
};
if finished {
clear_dimming(ctx);
cleanup(ctx);
}
}

Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/commands/jump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub(crate) mod locations;
pub(crate) mod score;
pub(crate) mod sequencer;

pub use annotate::{apply_dimming, clear_dimming, show_key_annotations_with_callback, JUMP_KEYS};
pub use annotate::{apply_dimming, cleanup, show_key_annotations_with_callback, JUMP_KEYS};
pub use locations::{find_all_char_occurrences, find_all_identifiers_in_view};
pub use score::sort_jump_targets;
pub use sequencer::{JumpAnnotation, JumpSequence, JumpSequencer, TrieNode};
21 changes: 17 additions & 4 deletions helix-term/src/commands/jump/annotate.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
use super::JumpAnnotation;
use crate::commands::Context;
use helix_core::text_annotations::Overlay;
use helix_view::input::KeyEvent;
use helix_view::{input::KeyEvent, View};
use std::rc::Rc;

pub const JUMP_KEYS: &[u8] = b"etovxqpdygfblzhckisuran";

pub fn apply_dimming(_ctx: &mut Context) {
// TODO: Implement dimming
#[inline]
pub fn apply_dimming(ctx: &mut Context) {
let (view, doc) = current!(ctx.editor);
if doc.config.load().dim_during_jump {
view.dimmed = true;
}
view.in_visual_jump_mode = true;
}

#[inline]
fn clear_dimming(view: &mut View) {
view.dimmed = false;
view.in_visual_jump_mode = false;
}

pub fn clear_dimming(ctx: &mut Context) {
#[inline]
pub fn cleanup(ctx: &mut Context) {
let mut view = view_mut!(ctx.editor);
clear_dimming(view);
view.visual_jump_labels[0] = Rc::new([]);
view.visual_jump_labels[1] = Rc::new([]);
view.visual_jump_labels[2] = Rc::new([]);
Expand Down
135 changes: 91 additions & 44 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use helix_core::{
ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary,
},
movement::Direction,
syntax::{self, HighlightEvent},
syntax::{self, Highlight, HighlightEvent},
text_annotations::TextAnnotations,
unicode::width::UnicodeWidthStr,
visual_offset_from_block, Position, Range, Selection, Transaction,
visual_offset_from_block, Position, Range, RopeSlice, Selection, Transaction,
};
use helix_view::{
apply_transaction,
Expand Down Expand Up @@ -134,28 +134,44 @@ impl EditorView {
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
}

let mut highlights =
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
let overlay_highlights = Self::overlay_syntax_highlights(
doc,
view.offset.anchor,
inner.height,
&text_annotations,
);
if !overlay_highlights.is_empty() {
highlights = Box::new(syntax::merge(highlights, overlay_highlights));
}
let gather_base_hl = || {
if view.dimmed {
Self::dimmed_view(doc, view.offset.anchor, inner.height, theme)
} else {
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme)
}
};

for diagnostic in Self::doc_diagnostics_highlights(doc, theme) {
// Most of the `diagnostic` Vecs are empty most of the time. Skipping
// a merge for any empty Vec saves a significant amount of work.
if diagnostic.is_empty() {
continue;
let gather_overlay_hl = |highlights: Box<dyn Iterator<Item = HighlightEvent>>| {
let overlay_hl = Self::overlay_syntax_highlights(
doc,
view.offset.anchor,
inner.height,
&text_annotations,
);
if overlay_hl.is_empty() {
highlights
} else {
Box::new(syntax::merge(highlights, overlay_hl))
}
highlights = Box::new(syntax::merge(highlights, diagnostic));
}
};

let gather_diagnostic_hl = |mut highlights: Box<dyn Iterator<Item = HighlightEvent>>| {
for diagnostic in Self::doc_diagnostics_highlights(doc, theme) {
// Most of the `diagnostic` Vecs are empty most of the time. Skipping
// a merge for any empty Vec saves a significant amount of work.
if diagnostic.is_empty() {
continue;
}
highlights = Box::new(syntax::merge(highlights, diagnostic));
}
highlights
};

let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
let gather_selection_hl = |highlights: Box<dyn Iterator<Item = HighlightEvent>>| {
if !is_focused {
return highlights;
}
let highlights = syntax::merge(
highlights,
Self::doc_selection_highlights(
Expand All @@ -172,8 +188,19 @@ impl EditorView {
} else {
Box::new(syntax::merge(highlights, focused_view_elements))
}
};

let highlights = if view.in_visual_jump_mode {
let mut hl = gather_base_hl();
hl = gather_overlay_hl(hl);
hl = gather_selection_hl(hl);
hl
} else {
Box::new(highlights)
let mut hl = gather_base_hl();
hl = gather_overlay_hl(hl);
hl = gather_diagnostic_hl(hl);
hl = gather_selection_hl(hl);
hl
};

Self::render_gutter(
Expand Down Expand Up @@ -264,6 +291,18 @@ impl EditorView {
.for_each(|area| surface.set_style(area, ruler_theme))
}

#[inline]
fn viewport_byte_range(text: RopeSlice, anchor: usize, height: u16) -> std::ops::Range<usize> {
let row = text.char_to_line(anchor.min(text.len_chars()));
// Saturating subs to make it inclusive zero indexing.
let last_line = text.len_lines().saturating_sub(1);
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
let start = text.line_to_byte(row.min(last_line));
let end = text.line_to_byte(last_visible_line + 1);

start..end
}

pub fn overlay_syntax_highlights(
doc: &Document,
anchor: usize,
Expand All @@ -272,15 +311,7 @@ impl EditorView {
) -> Vec<(usize, std::ops::Range<usize>)> {
let text = doc.text().slice(..);

let range = {
let row = text.char_to_line(anchor);
let last_line = doc.text().len_lines().saturating_sub(1);
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
let start = text.line_to_char(row.min(last_line));
let end = text.line_to_char(last_visible_line + 1);

start..end
};
let range = Self::viewport_byte_range(text, anchor, height);
text_annotations.collect_overlay_highlights(range)
}

Expand All @@ -294,19 +325,7 @@ impl EditorView {
_theme: &Theme,
) -> Box<dyn Iterator<Item = HighlightEvent> + 'doc> {
let text = doc.text().slice(..);
let row = text.char_to_line(anchor.min(text.len_chars()));

let range = {
// Calculate viewport byte ranges:
// Saturating subs to make it inclusive zero indexing.
let last_line = text.len_lines().saturating_sub(1);
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
let start = text.line_to_byte(row.min(last_line));
let end = text.line_to_byte(last_visible_line + 1);

start..end
};

let range = Self::viewport_byte_range(text, anchor, height);
match doc.syntax() {
Some(syntax) => {
let iter = syntax
Expand Down Expand Up @@ -338,6 +357,34 @@ impl EditorView {
}
}

pub fn dimmed_view(
doc: &Document,
anchor: usize,
height: u16,
theme: &Theme,
) -> Box<dyn Iterator<Item = HighlightEvent>> {
// Using comment style for dimming the view
let highlight = theme.find_scope_index("comment");
if highlight.is_none() {
return Box::new(::std::iter::empty());
}
let highlight = highlight.unwrap();

let text = doc.text().slice(..);
let range = Self::viewport_byte_range(text, anchor, height);
Box::new(
[
HighlightEvent::HighlightStart(Highlight(highlight)),
HighlightEvent::Source {
start: text.byte_to_char(range.start),
end: text.byte_to_char(range.end),
},
HighlightEvent::HighlightEnd,
]
.into_iter(),
)
}

/// Get highlight spans for document diagnostics
pub fn doc_diagnostics_highlights(
doc: &Document,
Expand Down
2 changes: 2 additions & 0 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ pub struct Config {
/// Whether to color modes with different colors. Defaults to `false`.
pub color_modes: bool,
pub soft_wrap: SoftWrap,
pub dim_during_jump: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -674,6 +675,7 @@ impl Default for Config {
indent_guides: IndentGuidesConfig::default(),
color_modes: false,
soft_wrap: SoftWrap::default(),
dim_during_jump: true,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions helix-view/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub struct View {
pub offset: ViewPosition,
pub area: Rect,
pub doc: DocumentId,
// If true, greys out the view.
pub dimmed: bool,
pub jumps: JumpList,
// documents accessed from this view from the oldest one to last viewed one
pub docs_access_history: Vec<DocumentId>,
Expand All @@ -133,6 +135,7 @@ pub struct View {
// array contains the remaining characters. The purpose of this is such that the leading
// character can be painted differently from the remaining characters.
pub visual_jump_labels: [Rc<[Overlay]>; 3],
pub in_visual_jump_mode: bool,
}

impl fmt::Debug for View {
Expand All @@ -155,6 +158,7 @@ impl View {
horizontal_offset: 0,
vertical_offset: 0,
},
dimmed: false,
area: Rect::default(), // will get calculated upon inserting into tree
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
docs_access_history: Vec::new(),
Expand All @@ -163,6 +167,7 @@ impl View {
gutters: gutter_types,
doc_revisions: HashMap::new(),
visual_jump_labels: [Rc::new([]), Rc::new([]), Rc::new([])],
in_visual_jump_mode: false,
}
}

Expand Down

0 comments on commit a55ee52

Please sign in to comment.