Skip to content

Commit

Permalink
chore(debug): refactor line highlight, keep working on cursor and sel
Browse files Browse the repository at this point in the history
Signed-off-by: Filip Dutescu <[email protected]>
  • Loading branch information
filipdutescu committed Feb 17, 2023
1 parent b26b98e commit a9d342f
Showing 1 changed file with 73 additions and 156 deletions.
229 changes: 73 additions & 156 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,38 @@ impl EditorView {
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
}

// DAP: Highlight current stack frame position
let dap_line = if let Some(frame) = editor.current_stack_frame() {
if doc.path().is_some()
&& frame
.source
.as_ref()
.and_then(|source| source.path.as_ref())
== doc.path()
{
let line = frame.line - 1; // convert to 0-indexing
let dap_current_style = theme.get("ui.debug.current");
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
if pos.doc_line != line {
return;
}

renderer.surface.set_style(
Rect::new(inner.x, inner.y + pos.visual_line, inner.width, 1),
dap_current_style,
);
};

// This adds the line background.
line_decorations.push(Box::new(line_decoration));
Some(line)
} else {
None
}
} else {
None
};

let mut highlights =
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
let overlay_highlights = Self::overlay_syntax_highlights(
Expand All @@ -121,6 +153,22 @@ impl EditorView {
}

let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
// This ensures foreground, cursor and selections are highlighted properly for DAP.
let highlights = if let Some(dap_line) = dap_line {
let dap_current_index = theme.find_scope_index("ui.debug.current").expect(
"Could not find scope `ui.debug.current` or any suitable fallback in theme!",
);
let text = doc.text();
let dap_line_start = text.line_to_char(dap_line);
let dap_line_end = text.line_to_char(dap_line + 1);
let highlights = Box::new(syntax::merge(
highlights,
vec![(dap_current_index, dap_line_start..dap_line_end)],
));
highlights
} else {
highlights
};
let highlights = syntax::merge(
highlights,
Self::doc_selection_highlights(
Expand Down Expand Up @@ -153,52 +201,38 @@ impl EditorView {
== doc.path()
{
let line = frame.line - 1; // convert to 0-indexing
let style = theme.get("ui.debug.current");
let dap_current_index = theme.find_scope_index("ui.debug.current").expect(
"Could not find scope `ui.debug.current` or any suitable fallback in theme!",
);
let dap_current_style = theme.get("ui.debug.current");
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
if pos.doc_line != line {
return;
}

renderer.surface.set_style(
Rect::new(inner.x, inner.y + pos.visual_line, inner.width, 1),
style,
dap_current_style,
);
};

// This adds the line background.
line_decorations.push(Box::new(line_decoration));
// This ensures foreground, cursor and selections are highlighted properly.

let mut dap_range = Range::new(0, 0);
for (i, l) in doc.text().lines().enumerate() {
if i == line {
dap_range.head += l.len_chars();
break;
}
dap_range.anchor += l.len_chars();
dap_range.head += l.len_chars();
}
let current_scope = theme.find_scope_index("ui.debug.current").expect(
"No fallback defined for the `ui.debug.current` scope in the theme!",
);
let highlights = Box::new(syntax::merge(
highlights,
vec![(current_scope, dap_range.anchor..dap_range.head)],
));
let dap_highlights = Self::doc_dap_highlights(
editor.mode(),
doc,
view,
theme,
&config.cursor_shape,
dap_range,
);
Box::new(syntax::merge(highlights, dap_highlights))
// let dap_highlights = Self::doc_dap_highlights(
// editor.mode(),
// doc,
// view,
// theme,
// &config.cursor_shape,
// Range::new(dap_line_start, dap_line_end),
// );
// Box::new(syntax::merge(highlights, dap_highlights))
highlights
} else {
Box::new(highlights)
highlights
}
} else {
Box::new(highlights)
highlights
};

Self::render_gutter(
Expand Down Expand Up @@ -452,101 +486,11 @@ impl EditorView {
let base_primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary")
.unwrap_or(base_cursor_scope);

let cursor_scope = mode.cursor_scope(theme).unwrap_or(base_cursor_scope);

let primary_cursor_scope = mode
.primary_cursor_scope(theme)
.unwrap_or(base_primary_cursor_scope);

let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
for (i, range) in selection.iter().enumerate() {
let selection_is_primary = i == primary_idx;
let (cursor_scope, selection_scope) = if selection_is_primary {
(primary_cursor_scope, primary_selection_scope)
} else {
(cursor_scope, selection_scope)
};

// Special-case: cursor at end of the rope.
if range.head == range.anchor && range.head == text.len_chars() {
if !selection_is_primary || cursor_is_block {
// Bar and underline cursors are drawn by the terminal
// BUG: If the editor area loses focus while having a bar or
// underline cursor (eg. when a regex prompt has focus) then
// the primary cursor will be invisible. This doesn't happen
// with block cursors since we manually draw *all* cursors.
spans.push((cursor_scope, range.head..range.head + 1));
}
continue;
}

let range = range.min_width_1(text);
if range.head > range.anchor {
// Standard case.
let cursor_start = prev_grapheme_boundary(text, range.head);
// non block cursors look like they exclude the cursor
let selection_end =
if selection_is_primary && !cursor_is_block && mode != Mode::Insert {
range.head
} else {
cursor_start
};
spans.push((selection_scope, range.anchor..selection_end));
if !selection_is_primary || cursor_is_block {
spans.push((cursor_scope, cursor_start..range.head));
}
} else {
// Reverse case.
let cursor_end = next_grapheme_boundary(text, range.head);
if !selection_is_primary || cursor_is_block {
spans.push((cursor_scope, range.head..cursor_end));
}
// non block cursors look like they exclude the cursor
let selection_start = if selection_is_primary
&& !cursor_is_block
&& !(mode == Mode::Insert && cursor_end == range.anchor)
{
range.head
} else {
cursor_end
};
spans.push((selection_scope, selection_start..range.anchor));
}
}

spans
}

/// Highlight current debug line text (concretely, override all other highlights), and selection and colour cursor if any.
pub fn doc_dap_highlights(
mode: Mode,
doc: &Document,
view: &View,
theme: &Theme,
cursor_shape_config: &CursorShapeConfig,
dap_current_range: Range,
) -> Vec<(usize, std::ops::Range<usize>)> {
let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();

let text = doc.text().slice(..);
let selection = doc.selection(view.id);
let primary_idx = selection.primary_index();

let cursorkind = cursor_shape_config.from_mode(mode);
let cursor_is_block = cursorkind == CursorKind::Block;

let selection_scope = theme
.find_scope_index_exact("ui.selection")
.expect("could not find `ui.selection` scope in the theme!");
let dap_selection_scope = theme
.find_scope_index_exact("ui.debug.selection")
.unwrap_or(selection_scope);
let base_cursor_scope = theme
.find_scope_index_exact("ui.cursor")
.unwrap_or(selection_scope);
let base_primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary")
let dap_cursor_scope = theme
.find_scope_index_exact("ui.debug.cursor")
.unwrap_or(base_cursor_scope);

let cursor_scope = mode.cursor_scope(theme).unwrap_or(base_cursor_scope);
Expand All @@ -555,16 +499,13 @@ impl EditorView {
.primary_cursor_scope(theme)
.unwrap_or(base_primary_cursor_scope);

let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
for (i, range) in selection.iter().enumerate() {
if Self::is_selection_outside_dap_line(&dap_current_range, &range) {
continue;
}

let selection_is_primary = i == primary_idx;
let (cursor_scope, selection_scope) = if selection_is_primary {
(primary_cursor_scope, dap_selection_scope)
(primary_cursor_scope, primary_selection_scope)
} else {
(cursor_scope, dap_selection_scope)
(cursor_scope, selection_scope)
};

// Special-case: cursor at end of the rope.
Expand All @@ -583,18 +524,6 @@ impl EditorView {
let range = range.min_width_1(text);
if range.head > range.anchor {
// Standard case.
let dap_anchor = if range.anchor < dap_current_range.anchor {
dap_current_range.anchor
} else {
range.anchor
};
let dap_head = if range.head > dap_current_range.head {
dap_current_range.head
} else {
range.head
};
let range = Range::new(dap_anchor, dap_head);

let cursor_start = prev_grapheme_boundary(text, range.head);
// non block cursors look like they exclude the cursor
let selection_end =
Expand All @@ -609,18 +538,6 @@ impl EditorView {
}
} else {
// Reverse case.
let dap_anchor = if range.anchor < dap_current_range.head {
dap_current_range.head
} else {
range.anchor
};
let dap_head = if range.head > dap_current_range.anchor {
dap_current_range.anchor
} else {
range.head
};
let range = Range::new(dap_anchor, dap_head);

let cursor_end = next_grapheme_boundary(text, range.head);
if !selection_is_primary || cursor_is_block {
spans.push((cursor_scope, range.head..cursor_end));
Expand All @@ -641,9 +558,9 @@ impl EditorView {
spans
}

pub fn is_selection_outside_dap_line(dap_range: &Range, selection_range: &Range) -> bool {
(selection_range.anchor < dap_range.anchor && selection_range.head < dap_range.anchor)
|| (selection_range.anchor > dap_range.head && selection_range.head > dap_range.head)
pub fn is_range_outside_another(range1: &Range, range2: &Range) -> bool {
(range2.anchor < range1.anchor && range2.head < range1.anchor)
|| (range2.anchor > range1.head && range2.head > range1.head)
}

/// Render brace match, etc (meant for the focused view only)
Expand Down

0 comments on commit a9d342f

Please sign in to comment.