Skip to content

Commit

Permalink
chore(debug): address comments
Browse files Browse the repository at this point in the history
Add two new scopes, `ui.debug.cursor` and `ui.debug.selection`, to cover
the cursor and selection highlighting during debugging, since it might
look of using the default ones. Refactor logic to ensure only ranges
found inside the line at which execution is paused at are overwritten.

Signed-off-by: Filip Dutescu <[email protected]>
  • Loading branch information
filipdutescu committed Feb 19, 2023
1 parent 929e22f commit 9950765
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 124 deletions.
113 changes: 58 additions & 55 deletions book/src/themes.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions helix-dap/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,4 +477,14 @@ impl Client {

self.call::<requests::SetExceptionBreakpoints>(args)
}

pub fn current_stack_frame(&self) -> Option<&StackFrame> {
if let (Some(frame), Some(thread_id)) = (self.active_frame, self.thread_id) {
self.stack_frames
.get(&thread_id)
.and_then(|bt| bt.get(frame))
} else {
None
}
}
}
210 changes: 167 additions & 43 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,47 +91,45 @@ impl EditorView {
let mut line_decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
let mut translated_positions: Vec<TranslatedPosition> = Vec::new();

if is_focused && config.cursorline {
line_decorations.push(Self::cursorline_decorator(doc, view, theme))
}

if is_focused && config.cursorcolumn {
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
}

// DAP: Highlight current stack frame position
let stack_frame = editor.debugger.as_ref().and_then(|debugger| {
if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id) {
debugger
.stack_frames
.get(&thread_id)
.and_then(|bt| bt.get(frame))
} else {
None
}
});
if let Some(frame) = stack_frame {
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;
let style = theme.get("ui.highlight.currentline");
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, pos.visual_line, inner.width, 1), style);

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
}
}

if stack_frame.is_none() && is_focused && config.cursorline {
line_decorations.push(Self::cursorline_decorator(doc, view, theme))
}

if stack_frame.is_none() && is_focused && config.cursorcolumn {
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
}
} else {
None
};

let mut highlights =
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
Expand All @@ -155,6 +153,23 @@ 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, dap_range): (Box<dyn Iterator<Item = HighlightEvent>>, Option<Range>) =
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, Some(Range::new(dap_line_start, dap_line_end)))
} else {
(highlights, None)
};
let highlights = syntax::merge(
highlights,
Self::doc_selection_highlights(
Expand All @@ -163,6 +178,7 @@ impl EditorView {
view,
theme,
&config.cursor_shape,
&dap_range,
),
);
let focused_view_elements = Self::highlight_focused_view_elements(view, doc, theme);
Expand Down Expand Up @@ -406,6 +422,7 @@ impl EditorView {
view: &View,
theme: &Theme,
cursor_shape_config: &CursorShapeConfig,
dap_range: &Option<Range>,
) -> Vec<(usize, std::ops::Range<usize>)> {
let text = doc.text().slice(..);
let selection = doc.selection(view.id);
Expand All @@ -426,20 +443,18 @@ impl EditorView {
let base_primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary")
.unwrap_or(base_cursor_scope);
let dap_selection_scope = theme
.find_scope_index_exact("ui.debug.selection")
.unwrap_or(selection_scope);
let dap_cursor_scope = theme
.find_scope_index_exact("ui.debug.cursor")
.unwrap_or(base_cursor_scope);

let cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.select"),
Mode::Normal => theme.find_scope_index_exact("ui.cursor.normal"),
}
.unwrap_or(base_cursor_scope);
let cursor_scope = mode.cursor_scope(theme).unwrap_or(base_cursor_scope);

let primary_cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.primary.insert"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.primary.select"),
Mode::Normal => theme.find_scope_index_exact("ui.cursor.primary.normal"),
}
.unwrap_or(base_primary_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() {
Expand Down Expand Up @@ -474,16 +489,39 @@ impl EditorView {
} else {
cursor_start
};
spans.push((selection_scope, range.anchor..selection_end));

let sel_range = Range::new(range.anchor, selection_end);
if Self::is_range_inside_dap_range(dap_range, &sel_range) {
let dap_range = dap_range.unwrap();
Self::set_dap_selection_highlights(
&mut spans,
selection_scope,
dap_selection_scope,
&dap_range,
&sel_range,
range.anchor,
selection_end,
);
} else {
spans.push((selection_scope, range.anchor..selection_end));
}
let cursor_range = Range::new(cursor_start, range.head);
if !selection_is_primary || cursor_is_block {
spans.push((cursor_scope, cursor_start..range.head));
if Self::is_range_inside_dap_range(dap_range, &cursor_range) {
let dap_range = dap_range.unwrap();
Self::set_dap_cursor_highlights(
&mut spans,
dap_cursor_scope,
&dap_range,
&cursor_range,
);
} else {
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
Expand All @@ -493,13 +531,99 @@ impl EditorView {
} else {
cursor_end
};
spans.push((selection_scope, selection_start..range.anchor));

let cursor_range = Range::new(range.head, cursor_end);
if !selection_is_primary || cursor_is_block {
if Self::is_range_inside_dap_range(dap_range, &cursor_range) {
let dap_range = dap_range.unwrap();
Self::set_dap_cursor_highlights(
&mut spans,
dap_cursor_scope,
&dap_range,
&cursor_range,
);
} else {
spans.push((cursor_scope, range.head..cursor_end));
}
}
let sel_range = Range::new(selection_start, range.anchor);
if Self::is_range_inside_dap_range(dap_range, &sel_range) {
let dap_range = dap_range.unwrap();
Self::set_dap_selection_highlights(
&mut spans,
selection_scope,
dap_selection_scope,
&dap_range,
&sel_range,
selection_start,
range.anchor,
);
} else {
spans.push((selection_scope, selection_start..range.anchor));
}
}
}

spans
}

#[inline]
pub fn is_range_inside_dap_range(dap_range: &Option<Range>, range: &Range) -> bool {
let dap_range = match dap_range {
Some(dap_range) => dap_range,
None => {
return false;
}
};

(range.anchor > dap_range.anchor || range.head > dap_range.anchor)
&& (range.anchor < dap_range.head || range.head < dap_range.head)
}

fn set_dap_selection_highlights(
spans: &mut Vec<(usize, std::ops::Range<usize>)>,
selection_scope: usize,
dap_selection_scope: usize,
dap_range: &Range,
sel_range: &Range,
original_start: usize,
original_end: usize,
) {
let dap_sel_start = if dap_range.anchor > sel_range.anchor {
dap_range.anchor
} else {
sel_range.anchor
};
let dap_sel_end = if dap_range.head < sel_range.head {
dap_range.head
} else {
sel_range.head
};

spans.push((selection_scope, original_start..dap_sel_start));
spans.push((dap_selection_scope, dap_sel_start..dap_sel_end));
spans.push((selection_scope, dap_sel_end..original_end));
}

fn set_dap_cursor_highlights(
spans: &mut Vec<(usize, std::ops::Range<usize>)>,
dap_cursor_scope: usize,
dap_range: &Range,
cursor_range: &Range,
) {
let dap_cursor_start = if dap_range.anchor > cursor_range.anchor {
dap_range.anchor
} else {
cursor_range.anchor
};
let dap_cursor_end = if dap_range.head < cursor_range.head {
dap_range.head
} else {
cursor_range.head
};
spans.push((dap_cursor_scope, dap_cursor_start..dap_cursor_end));
}

/// Render brace match, etc (meant for the focused view only)
pub fn highlight_focused_view_elements(
view: &View,
Expand Down
18 changes: 18 additions & 0 deletions helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ pub enum Mode {
Insert = 2,
}

impl Mode {
pub fn cursor_scope(&self, theme: &Theme) -> Option<usize> {
match self {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.select"),
Mode::Normal => theme.find_scope_index_exact("ui.cursor.normal"),
}
}

pub fn primary_cursor_scope(&self, theme: &Theme) -> Option<usize> {
match self {
Mode::Insert => theme.find_scope_index_exact("ui.primary.cursor.insert"),
Mode::Select => theme.find_scope_index_exact("ui.primary.cursor.select"),
Mode::Normal => theme.find_scope_index_exact("ui.primary.cursor.normal"),
}
}
}

impl Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down
12 changes: 12 additions & 0 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
view::ViewPosition,
Align, Document, DocumentId, View, ViewId,
};
use dap::StackFrame;
use helix_vcs::DiffProviderRegistry;

use futures_util::stream::select_all::SelectAll;
Expand Down Expand Up @@ -1650,6 +1651,17 @@ impl Editor {
doc.restore_cursor = false;
}
}

pub fn current_stack_frame(&self) -> Option<&StackFrame> {
let debugger = match self.debugger.as_ref() {
Some(debugger) => debugger,
None => {
return None;
}
};

debugger.current_stack_frame()
}
}

fn try_restore_indent(doc: &mut Document, view: &mut View) {
Expand Down
Loading

0 comments on commit 9950765

Please sign in to comment.