Skip to content

Commit 686f766

Browse files
filipdutescuTriton171
authored andcommitted
feat(debug): highlight current line (helix-editor#5957)
Add new theme highlight keys, for setting the colour of the breakpoint character and the current line at which execution has been paused at. The two new keys are `ui.highlight.frameline` and `ui.debug.breakpoint`. Highlight according to those keys, both the line at which debugging is paused at and the breakpoint indicator. Add an indicator for the current line at which execution is paused at, themed by the `ui.debug.active` theme scope. Update various themes to showcase how the new functionality works. Better icons are dependent on helix-editor#2869, and as such will be handled in the future, once it lands. Closes: helix-editor#5952 Signed-off-by: Filip Dutescu <[email protected]>
1 parent 873c1f3 commit 686f766

File tree

15 files changed

+98
-53
lines changed

15 files changed

+98
-53
lines changed

book/src/themes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,11 @@ These scopes are used for theming the editor interface:
278278
| `ui.cursor.primary.normal` | |
279279
| `ui.cursor.primary.insert` | |
280280
| `ui.cursor.primary.select` | |
281+
| `ui.debug.breakpoint` | Breakpoint indicator, found in the gutter |
282+
| `ui.debug.active` | Indicator for the line at which debugging execution is paused at, found in the gutter |
281283
| `ui.gutter` | Gutter |
282284
| `ui.gutter.selected` | Gutter for the line the cursor is on |
285+
| `ui.highlight.frameline` | Line at which debugging execution is paused at |
283286
| `ui.linenr` | Line numbers |
284287
| `ui.linenr.selected` | Line number for the line the cursor is on |
285288
| `ui.statusline` | Statusline |

helix-dap/src/client.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,4 +512,10 @@ impl Client {
512512

513513
self.call::<requests::SetExceptionBreakpoints>(args)
514514
}
515+
516+
pub fn current_stack_frame(&self) -> Option<&StackFrame> {
517+
self.stack_frames
518+
.get(&self.thread_id?)?
519+
.get(self.active_frame?)
520+
}
515521
}

helix-term/src/ui/editor.rs

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -93,40 +93,6 @@ impl EditorView {
9393
let mut line_decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
9494
let mut translated_positions: Vec<TranslatedPosition> = Vec::new();
9595

96-
// DAP: Highlight current stack frame position
97-
let stack_frame = editor.debugger.as_ref().and_then(|debugger| {
98-
if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id) {
99-
debugger
100-
.stack_frames
101-
.get(&thread_id)
102-
.and_then(|bt| bt.get(frame))
103-
} else {
104-
None
105-
}
106-
});
107-
if let Some(frame) = stack_frame {
108-
if doc.path().is_some()
109-
&& frame
110-
.source
111-
.as_ref()
112-
.and_then(|source| source.path.as_ref())
113-
== doc.path()
114-
{
115-
let line = frame.line - 1; // convert to 0-indexing
116-
let style = theme.get("ui.highlight");
117-
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
118-
if pos.doc_line != line {
119-
return;
120-
}
121-
renderer
122-
.surface
123-
.set_style(Rect::new(area.x, pos.visual_line, area.width, 1), style);
124-
};
125-
126-
line_decorations.push(Box::new(line_decoration));
127-
}
128-
}
129-
13096
if is_focused && config.cursorline {
13197
line_decorations.push(Self::cursorline_decorator(doc, view, theme))
13298
}
@@ -135,6 +101,23 @@ impl EditorView {
135101
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
136102
}
137103

104+
// Set DAP highlights, if needed.
105+
if let Some(frame) = editor.current_stack_frame() {
106+
let dap_line = frame.line.saturating_sub(1) as usize;
107+
let style = theme.get("ui.highlight.frameline");
108+
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
109+
if pos.doc_line != dap_line {
110+
return;
111+
}
112+
renderer.surface.set_style(
113+
Rect::new(inner.x, inner.y + pos.visual_line, inner.width, 1),
114+
style,
115+
);
116+
};
117+
118+
line_decorations.push(Box::new(line_decoration));
119+
}
120+
138121
let mut highlights =
139122
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
140123
let overlay_highlights = Self::overlay_syntax_highlights(
@@ -422,6 +405,7 @@ impl EditorView {
422405
let primary_selection_scope = theme
423406
.find_scope_index_exact("ui.selection.primary")
424407
.unwrap_or(selection_scope);
408+
425409
let base_cursor_scope = theme
426410
.find_scope_index_exact("ui.cursor")
427411
.unwrap_or(selection_scope);

helix-view/src/editor.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
view::ViewPosition,
1111
Align, Document, DocumentId, View, ViewId,
1212
};
13+
use dap::StackFrame;
1314
use helix_vcs::DiffProviderRegistry;
1415

1516
use futures_util::stream::select_all::SelectAll;
@@ -1652,6 +1653,12 @@ impl Editor {
16521653
doc.restore_cursor = false;
16531654
}
16541655
}
1656+
1657+
pub fn current_stack_frame(&self) -> Option<&StackFrame> {
1658+
self.debugger
1659+
.as_ref()
1660+
.and_then(|debugger| debugger.current_stack_frame())
1661+
}
16551662
}
16561663

16571664
fn try_restore_indent(doc: &mut Document, view: &mut View) {

helix-view/src/gutter.rs

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::Write;
22

33
use crate::{
44
editor::GutterType,
5-
graphics::{Color, Style, UnderlineStyle},
5+
graphics::{Style, UnderlineStyle},
66
Document, Editor, Theme, View,
77
};
88

@@ -245,9 +245,9 @@ pub fn breakpoints<'doc>(
245245
theme: &Theme,
246246
_is_focused: bool,
247247
) -> GutterFn<'doc> {
248-
let warning = theme.get("warning");
249248
let error = theme.get("error");
250249
let info = theme.get("info");
250+
let breakpoint_style = theme.get("ui.debug.breakpoint");
251251

252252
let breakpoints = doc.path().and_then(|path| editor.breakpoints.get(path));
253253

@@ -265,30 +265,52 @@ pub fn breakpoints<'doc>(
265265
.iter()
266266
.find(|breakpoint| breakpoint.line == line)?;
267267

268-
let mut style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
268+
let style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
269269
error.underline_style(UnderlineStyle::Line)
270270
} else if breakpoint.condition.is_some() {
271271
error
272272
} else if breakpoint.log_message.is_some() {
273273
info
274274
} else {
275-
warning
275+
breakpoint_style
276276
};
277277

278-
if !breakpoint.verified {
279-
// Faded colors
280-
style = if let Some(Color::Rgb(r, g, b)) = style.fg {
281-
style.fg(Color::Rgb(
282-
((r as f32) * 0.4).floor() as u8,
283-
((g as f32) * 0.4).floor() as u8,
284-
((b as f32) * 0.4).floor() as u8,
285-
))
286-
} else {
287-
style.fg(Color::Gray)
288-
}
289-
};
278+
let sym = if breakpoint.verified { "●" } else { "◯" };
279+
write!(out, "{}", sym).unwrap();
280+
Some(style)
281+
},
282+
)
283+
}
284+
285+
fn execution_pause_indicator<'doc>(
286+
editor: &'doc Editor,
287+
doc: &'doc Document,
288+
theme: &Theme,
289+
is_focused: bool,
290+
) -> GutterFn<'doc> {
291+
let style = theme.get("ui.debug.active");
292+
let current_stack_frame = editor.current_stack_frame();
293+
let frame_line = current_stack_frame.map(|frame| frame.line - 1);
294+
let frame_source_path = current_stack_frame.map(|frame| {
295+
frame
296+
.source
297+
.as_ref()
298+
.and_then(|source| source.path.as_ref())
299+
});
300+
let should_display_for_current_doc =
301+
doc.path().is_some() && frame_source_path.unwrap_or(None) == doc.path();
302+
303+
Box::new(
304+
move |line: usize, _selected: bool, first_visual_line: bool, out: &mut String| {
305+
if !first_visual_line
306+
|| !is_focused
307+
|| line != frame_line?
308+
|| !should_display_for_current_doc
309+
{
310+
return None;
311+
}
290312

291-
let sym = if breakpoint.verified { "▲" } else { "⊚" };
313+
let sym = "▶";
292314
write!(out, "{}", sym).unwrap();
293315
Some(style)
294316
},
@@ -304,9 +326,11 @@ pub fn diagnostics_or_breakpoints<'doc>(
304326
) -> GutterFn<'doc> {
305327
let mut diagnostics = diagnostic(editor, doc, view, theme, is_focused);
306328
let mut breakpoints = breakpoints(editor, doc, view, theme, is_focused);
329+
let mut execution_pause_indicator = execution_pause_indicator(editor, doc, theme, is_focused);
307330

308331
Box::new(move |line, selected, first_visual_line: bool, out| {
309-
breakpoints(line, selected, first_visual_line, out)
332+
execution_pause_indicator(line, selected, first_visual_line, out)
333+
.or_else(|| breakpoints(line, selected, first_visual_line, out))
310334
.or_else(|| diagnostics(line, selected, first_visual_line, out))
311335
})
312336
}

runtime/themes/acme.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"ui.virtual.ruler" = { bg = "acme_bar_bg" }
1313
"ui.cursor.match" = {bg="acme_bar_bg"}
1414
"ui.cursor" = {bg="cursor", fg="white"}
15+
"ui.debug" = {fg="orange"}
16+
"ui.highlight.frameline" = {bg="#da8581"}
1517
"string" = "red"
1618
"comment" = "green"
1719
"ui.help" = {fg="black", bg="acme_bg"}

runtime/themes/autumn.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"ui.cursor.primary" = { fg = "my_white", modifiers = ["reversed"] }
2727
"ui.cursorline.primary" = { bg = "my_black" }
2828
"ui.cursorline.secondary" = { bg = "my_black" }
29+
"ui.highlight.frameline" = { bg = "#8b6904" }
30+
"ui.debug" = { fg = "my_yellow1", bg = "my_gray0" }
2931
"ui.text" = "my_white"
3032
"operator" = "my_white"
3133
"ui.text.focus" = "my_white"

runtime/themes/ayu_dark.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
"diagnostic.error"= { underline = { color = "red", style="curl"} }
6262
"ui.bufferline" = { fg = "gray", bg = "background" }
6363
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
64+
"ui.debug" = { fg = "orange", bg = "background" }
65+
"ui.highlight.frameline" = { bg = "#0067a3" }
6466

6567
"special" = "orange"
6668

runtime/themes/ayu_light.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
"diagnostic.error"= { underline = { color = "red", style = "curl" } }
6262
"ui.bufferline" = { fg = "gray", bg = "background" }
6363
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
64+
"ui.debug" = { fg = "orange", bg = "background" }
65+
"ui.highlight.frameline" = { bg = "#cfe0f2" }
6466

6567
"special" = "orange"
6668

runtime/themes/ayu_mirage.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
"diagnostic.error"= { underline = { color = "red", style = "curl" } }
6262
"ui.bufferline" = { fg = "gray", bg = "background" }
6363
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
64+
"ui.debug" = { fg = "orange", bg = "background" }
65+
"ui.highlight.frameline" = { bg = "#0067a3" }
6466

6567
"special" = "orange"
6668

runtime/themes/dracula.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] }
2626
"ui.cursorline.primary" = { bg = "background_dark" }
2727
"ui.help" = { fg = "foreground", bg = "background_dark" }
28+
"ui.debug" = { fg = "red" }
29+
"ui.highlight.frameline" = { fg = "black", bg = "red" }
2830
"ui.linenr" = { fg = "comment" }
2931
"ui.linenr.selected" = { fg = "foreground" }
3032
"ui.menu" = { fg = "foreground", bg = "background_dark" }

runtime/themes/dracula_at_night.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] }
2626
"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] }
2727
"ui.help" = { fg = "foreground", bg = "background_dark" }
28+
"ui.debug" = { fg = "red" }
29+
"ui.highlight.frameline" = { fg = "black", bg = "red" }
2830
"ui.linenr" = { fg = "comment" }
2931
"ui.linenr.selected" = { fg = "foreground" }
3032
"ui.menu" = { fg = "foreground", bg = "background_dark" }

runtime/themes/onedark.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"ui.cursorline.primary" = { bg = "light-black" }
6565

6666
"ui.highlight" = { bg = "gray" }
67+
"ui.highlight.frameline" = { bg = "#97202a" }
6768

6869
"ui.linenr" = { fg = "linenr" }
6970
"ui.linenr.selected" = { fg = "white" }
@@ -84,6 +85,8 @@
8485
"ui.menu.selected" = { fg = "black", bg = "blue" }
8586
"ui.menu.scroll" = { fg = "white", bg = "light-gray" }
8687

88+
"ui.debug" = { fg = "red" }
89+
8790
[palette]
8891

8992
yellow = "#E5C07B"

runtime/themes/onedarker.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878
"ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] }
7979

8080
"ui.help" = { fg = "white", bg = "gray" }
81+
"ui.debug" = { fg = "red" }
82+
"ui.highlight.frameline" = { bg = "#97202a" }
8183
"ui.popup" = { bg = "gray" }
8284
"ui.window" = { fg = "gray" }
8385
"ui.menu" = { fg = "white", bg = "gray" }

theme.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ label = "honey"
6969
"ui.cursor" = { modifiers = ["reversed"] }
7070
"ui.cursorline.primary" = { bg = "bossanova" }
7171
"ui.highlight" = { bg = "bossanova" }
72-
72+
"ui.highlight.frameline" = { bg = "#634450" }
73+
"ui.debug" = { fg = "#634450" }
74+
"ui.debug.breakpoint" = { fg = "apricot" }
7375
"ui.menu" = { fg = "lavender", bg = "revolver" }
7476
"ui.menu.selected" = { fg = "revolver", bg = "white" }
7577
"ui.menu.scroll" = { fg = "lavender", bg = "comet" }

0 commit comments

Comments
 (0)