mirror of
https://github.com/helix-editor/helix.git
synced 2024-09-11 18:58:00 +03:00
feat(debug): highlight current line (#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 #2869, and as such will be handled in the future, once it lands. Closes: #5952 Signed-off-by: Filip Dutescu <filip.dutescu@gmail.com>
This commit is contained in:
parent
bbcdcd04a5
commit
d59b80514e
@ -278,8 +278,11 @@ #### Interface
|
||||
| `ui.cursor.primary.normal` | |
|
||||
| `ui.cursor.primary.insert` | |
|
||||
| `ui.cursor.primary.select` | |
|
||||
| `ui.debug.breakpoint` | Breakpoint indicator, found in the gutter |
|
||||
| `ui.debug.active` | Indicator for the line at which debugging execution is paused at, found in the gutter |
|
||||
| `ui.gutter` | Gutter |
|
||||
| `ui.gutter.selected` | Gutter for the line the cursor is on |
|
||||
| `ui.highlight.frameline` | Line at which debugging execution is paused at |
|
||||
| `ui.linenr` | Line numbers |
|
||||
| `ui.linenr.selected` | Line number for the line the cursor is on |
|
||||
| `ui.statusline` | Statusline |
|
||||
|
@ -512,4 +512,10 @@ pub fn set_exception_breakpoints(
|
||||
|
||||
self.call::<requests::SetExceptionBreakpoints>(args)
|
||||
}
|
||||
|
||||
pub fn current_stack_frame(&self) -> Option<&StackFrame> {
|
||||
self.stack_frames
|
||||
.get(&self.thread_id?)?
|
||||
.get(self.active_frame?)
|
||||
}
|
||||
}
|
||||
|
@ -93,40 +93,6 @@ pub fn render_view(
|
||||
let mut line_decorations: Vec<Box<dyn LineDecoration>> = Vec::new();
|
||||
let mut translated_positions: Vec<TranslatedPosition> = Vec::new();
|
||||
|
||||
// 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 {
|
||||
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 style = theme.get("ui.highlight");
|
||||
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
|
||||
if pos.doc_line != line {
|
||||
return;
|
||||
}
|
||||
renderer
|
||||
.surface
|
||||
.set_style(Rect::new(area.x, pos.visual_line, area.width, 1), style);
|
||||
};
|
||||
|
||||
line_decorations.push(Box::new(line_decoration));
|
||||
}
|
||||
}
|
||||
|
||||
if is_focused && config.cursorline {
|
||||
line_decorations.push(Self::cursorline_decorator(doc, view, theme))
|
||||
}
|
||||
@ -135,6 +101,23 @@ pub fn render_view(
|
||||
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations);
|
||||
}
|
||||
|
||||
// Set DAP highlights, if needed.
|
||||
if let Some(frame) = editor.current_stack_frame() {
|
||||
let dap_line = frame.line.saturating_sub(1) as usize;
|
||||
let style = theme.get("ui.highlight.frameline");
|
||||
let line_decoration = move |renderer: &mut TextRenderer, pos: LinePos| {
|
||||
if pos.doc_line != dap_line {
|
||||
return;
|
||||
}
|
||||
renderer.surface.set_style(
|
||||
Rect::new(inner.x, inner.y + pos.visual_line, inner.width, 1),
|
||||
style,
|
||||
);
|
||||
};
|
||||
|
||||
line_decorations.push(Box::new(line_decoration));
|
||||
}
|
||||
|
||||
let mut highlights =
|
||||
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
|
||||
let overlay_highlights = Self::overlay_syntax_highlights(
|
||||
@ -422,6 +405,7 @@ pub fn doc_selection_highlights(
|
||||
let primary_selection_scope = theme
|
||||
.find_scope_index_exact("ui.selection.primary")
|
||||
.unwrap_or(selection_scope);
|
||||
|
||||
let base_cursor_scope = theme
|
||||
.find_scope_index_exact("ui.cursor")
|
||||
.unwrap_or(selection_scope);
|
||||
|
@ -10,6 +10,7 @@
|
||||
view::ViewPosition,
|
||||
Align, Document, DocumentId, View, ViewId,
|
||||
};
|
||||
use dap::StackFrame;
|
||||
use helix_vcs::DiffProviderRegistry;
|
||||
|
||||
use futures_util::stream::select_all::SelectAll;
|
||||
@ -1652,6 +1653,12 @@ pub fn enter_normal_mode(&mut self) {
|
||||
doc.restore_cursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_stack_frame(&self) -> Option<&StackFrame> {
|
||||
self.debugger
|
||||
.as_ref()
|
||||
.and_then(|debugger| debugger.current_stack_frame())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_restore_indent(doc: &mut Document, view: &mut View) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
editor::GutterType,
|
||||
graphics::{Color, Style, UnderlineStyle},
|
||||
graphics::{Style, UnderlineStyle},
|
||||
Document, Editor, Theme, View,
|
||||
};
|
||||
|
||||
@ -245,9 +245,9 @@ pub fn breakpoints<'doc>(
|
||||
theme: &Theme,
|
||||
_is_focused: bool,
|
||||
) -> GutterFn<'doc> {
|
||||
let warning = theme.get("warning");
|
||||
let error = theme.get("error");
|
||||
let info = theme.get("info");
|
||||
let breakpoint_style = theme.get("ui.debug.breakpoint");
|
||||
|
||||
let breakpoints = doc.path().and_then(|path| editor.breakpoints.get(path));
|
||||
|
||||
@ -265,30 +265,52 @@ pub fn breakpoints<'doc>(
|
||||
.iter()
|
||||
.find(|breakpoint| breakpoint.line == line)?;
|
||||
|
||||
let mut style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
|
||||
let style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
|
||||
error.underline_style(UnderlineStyle::Line)
|
||||
} else if breakpoint.condition.is_some() {
|
||||
error
|
||||
} else if breakpoint.log_message.is_some() {
|
||||
info
|
||||
} else {
|
||||
warning
|
||||
breakpoint_style
|
||||
};
|
||||
|
||||
if !breakpoint.verified {
|
||||
// Faded colors
|
||||
style = if let Some(Color::Rgb(r, g, b)) = style.fg {
|
||||
style.fg(Color::Rgb(
|
||||
((r as f32) * 0.4).floor() as u8,
|
||||
((g as f32) * 0.4).floor() as u8,
|
||||
((b as f32) * 0.4).floor() as u8,
|
||||
))
|
||||
} else {
|
||||
style.fg(Color::Gray)
|
||||
}
|
||||
};
|
||||
let sym = if breakpoint.verified { "●" } else { "◯" };
|
||||
write!(out, "{}", sym).unwrap();
|
||||
Some(style)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let sym = if breakpoint.verified { "▲" } else { "⊚" };
|
||||
fn execution_pause_indicator<'doc>(
|
||||
editor: &'doc Editor,
|
||||
doc: &'doc Document,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
) -> GutterFn<'doc> {
|
||||
let style = theme.get("ui.debug.active");
|
||||
let current_stack_frame = editor.current_stack_frame();
|
||||
let frame_line = current_stack_frame.map(|frame| frame.line - 1);
|
||||
let frame_source_path = current_stack_frame.map(|frame| {
|
||||
frame
|
||||
.source
|
||||
.as_ref()
|
||||
.and_then(|source| source.path.as_ref())
|
||||
});
|
||||
let should_display_for_current_doc =
|
||||
doc.path().is_some() && frame_source_path.unwrap_or(None) == doc.path();
|
||||
|
||||
Box::new(
|
||||
move |line: usize, _selected: bool, first_visual_line: bool, out: &mut String| {
|
||||
if !first_visual_line
|
||||
|| !is_focused
|
||||
|| line != frame_line?
|
||||
|| !should_display_for_current_doc
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let sym = "▶";
|
||||
write!(out, "{}", sym).unwrap();
|
||||
Some(style)
|
||||
},
|
||||
@ -304,9 +326,11 @@ pub fn diagnostics_or_breakpoints<'doc>(
|
||||
) -> GutterFn<'doc> {
|
||||
let mut diagnostics = diagnostic(editor, doc, view, theme, is_focused);
|
||||
let mut breakpoints = breakpoints(editor, doc, view, theme, is_focused);
|
||||
let mut execution_pause_indicator = execution_pause_indicator(editor, doc, theme, is_focused);
|
||||
|
||||
Box::new(move |line, selected, first_visual_line: bool, out| {
|
||||
breakpoints(line, selected, first_visual_line, out)
|
||||
execution_pause_indicator(line, selected, first_visual_line, out)
|
||||
.or_else(|| breakpoints(line, selected, first_visual_line, out))
|
||||
.or_else(|| diagnostics(line, selected, first_visual_line, out))
|
||||
})
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
"ui.virtual.ruler" = { bg = "acme_bar_bg" }
|
||||
"ui.cursor.match" = {bg="acme_bar_bg"}
|
||||
"ui.cursor" = {bg="cursor", fg="white"}
|
||||
"ui.debug" = {fg="orange"}
|
||||
"ui.highlight.frameline" = {bg="#da8581"}
|
||||
"string" = "red"
|
||||
"comment" = "green"
|
||||
"ui.help" = {fg="black", bg="acme_bg"}
|
||||
|
@ -26,6 +26,8 @@
|
||||
"ui.cursor.primary" = { fg = "my_white", modifiers = ["reversed"] }
|
||||
"ui.cursorline.primary" = { bg = "my_black" }
|
||||
"ui.cursorline.secondary" = { bg = "my_black" }
|
||||
"ui.highlight.frameline" = { bg = "#8b6904" }
|
||||
"ui.debug" = { fg = "my_yellow1", bg = "my_gray0" }
|
||||
"ui.text" = "my_white"
|
||||
"operator" = "my_white"
|
||||
"ui.text.focus" = "my_white"
|
||||
|
@ -61,6 +61,8 @@
|
||||
"diagnostic.error"= { underline = { color = "red", style="curl"} }
|
||||
"ui.bufferline" = { fg = "gray", bg = "background" }
|
||||
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
|
||||
"ui.debug" = { fg = "orange", bg = "background" }
|
||||
"ui.highlight.frameline" = { bg = "#0067a3" }
|
||||
|
||||
"special" = "orange"
|
||||
|
||||
|
@ -61,6 +61,8 @@
|
||||
"diagnostic.error"= { underline = { color = "red", style = "curl" } }
|
||||
"ui.bufferline" = { fg = "gray", bg = "background" }
|
||||
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
|
||||
"ui.debug" = { fg = "orange", bg = "background" }
|
||||
"ui.highlight.frameline" = { bg = "#cfe0f2" }
|
||||
|
||||
"special" = "orange"
|
||||
|
||||
|
@ -61,6 +61,8 @@
|
||||
"diagnostic.error"= { underline = { color = "red", style = "curl" } }
|
||||
"ui.bufferline" = { fg = "gray", bg = "background" }
|
||||
"ui.bufferline.active" = { fg = "foreground", bg = "dark_gray" }
|
||||
"ui.debug" = { fg = "orange", bg = "background" }
|
||||
"ui.highlight.frameline" = { bg = "#0067a3" }
|
||||
|
||||
"special" = "orange"
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
||||
"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] }
|
||||
"ui.cursorline.primary" = { bg = "background_dark" }
|
||||
"ui.help" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.debug" = { fg = "red" }
|
||||
"ui.highlight.frameline" = { fg = "black", bg = "red" }
|
||||
"ui.linenr" = { fg = "comment" }
|
||||
"ui.linenr.selected" = { fg = "foreground" }
|
||||
"ui.menu" = { fg = "foreground", bg = "background_dark" }
|
||||
|
@ -25,6 +25,8 @@
|
||||
"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] }
|
||||
"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] }
|
||||
"ui.help" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.debug" = { fg = "red" }
|
||||
"ui.highlight.frameline" = { fg = "black", bg = "red" }
|
||||
"ui.linenr" = { fg = "comment" }
|
||||
"ui.linenr.selected" = { fg = "foreground" }
|
||||
"ui.menu" = { fg = "foreground", bg = "background_dark" }
|
||||
|
@ -64,6 +64,7 @@
|
||||
"ui.cursorline.primary" = { bg = "light-black" }
|
||||
|
||||
"ui.highlight" = { bg = "gray" }
|
||||
"ui.highlight.frameline" = { bg = "#97202a" }
|
||||
|
||||
"ui.linenr" = { fg = "linenr" }
|
||||
"ui.linenr.selected" = { fg = "white" }
|
||||
@ -84,6 +85,8 @@
|
||||
"ui.menu.selected" = { fg = "black", bg = "blue" }
|
||||
"ui.menu.scroll" = { fg = "white", bg = "light-gray" }
|
||||
|
||||
"ui.debug" = { fg = "red" }
|
||||
|
||||
[palette]
|
||||
|
||||
yellow = "#E5C07B"
|
||||
|
@ -78,6 +78,8 @@
|
||||
"ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] }
|
||||
|
||||
"ui.help" = { fg = "white", bg = "gray" }
|
||||
"ui.debug" = { fg = "red" }
|
||||
"ui.highlight.frameline" = { bg = "#97202a" }
|
||||
"ui.popup" = { bg = "gray" }
|
||||
"ui.window" = { fg = "gray" }
|
||||
"ui.menu" = { fg = "white", bg = "gray" }
|
||||
|
@ -69,7 +69,9 @@ label = "honey"
|
||||
"ui.cursor" = { modifiers = ["reversed"] }
|
||||
"ui.cursorline.primary" = { bg = "bossanova" }
|
||||
"ui.highlight" = { bg = "bossanova" }
|
||||
|
||||
"ui.highlight.frameline" = { bg = "#634450" }
|
||||
"ui.debug" = { fg = "#634450" }
|
||||
"ui.debug.breakpoint" = { fg = "apricot" }
|
||||
"ui.menu" = { fg = "lavender", bg = "revolver" }
|
||||
"ui.menu.selected" = { fg = "revolver", bg = "white" }
|
||||
"ui.menu.scroll" = { fg = "lavender", bg = "comet" }
|
||||
|
Loading…
Reference in New Issue
Block a user