aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r--helix-term/src/ui/editor.rs190
1 files changed, 188 insertions, 2 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 72b8adc1..6428870e 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -15,6 +15,7 @@ use helix_core::{
unicode::width::UnicodeWidthStr,
LineEnding, Position, Range, Selection,
};
+use helix_dap::{SourceBreakpoint, StackFrame};
use helix_view::{
document::Mode,
editor::LineNumber,
@@ -29,6 +30,8 @@ use std::borrow::Cow;
use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
use tui::buffer::Buffer as Surface;
+use super::{Prompt, PromptEvent};
+
pub struct EditorView {
keymaps: Keymaps,
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
@@ -71,6 +74,7 @@ impl EditorView {
is_focused: bool,
loader: &syntax::Loader,
config: &helix_view::editor::Config,
+ debugger: &Option<helix_dap::Client>,
) {
let inner = view.inner_area();
let area = view.area;
@@ -87,7 +91,9 @@ impl EditorView {
};
Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
- Self::render_gutter(doc, view, view.area, surface, theme, is_focused, config);
+ Self::render_gutter(
+ doc, view, view.area, surface, theme, is_focused, config, debugger,
+ );
if is_focused {
Self::render_focused_view_elements(view, doc, inner, theme, surface);
@@ -106,7 +112,7 @@ impl EditorView {
}
}
- self.render_diagnostics(doc, view, inner, surface, theme);
+ self.render_diagnostics(doc, view, inner, surface, theme, debugger);
let statusline_area = view
.area
@@ -409,6 +415,7 @@ impl EditorView {
theme: &Theme,
is_focused: bool,
config: &helix_view::editor::Config,
+ debugger: &Option<helix_dap::Client>,
) {
let text = doc.text().slice(..);
let last_line = view.last_line(doc);
@@ -438,6 +445,15 @@ impl EditorView {
.map(|range| range.cursor_line(text))
.collect();
+ let mut breakpoints: Option<Vec<SourceBreakpoint>> = None;
+ let mut stack_pointer: Option<StackFrame> = None;
+ if let Some(debugger) = debugger {
+ if let Some(path) = doc.path() {
+ breakpoints = debugger.breakpoints.get(path).cloned();
+ stack_pointer = debugger.stack_pointer.clone()
+ }
+ }
+
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
use helix_core::diagnostic::Severity;
if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
@@ -457,6 +473,36 @@ impl EditorView {
let selected = cursors.contains(&line);
+ if let Some(bps) = breakpoints.as_ref() {
+ if let Some(breakpoint) = bps.iter().find(|breakpoint| breakpoint.line - 1 == line)
+ {
+ if breakpoint.condition.is_some() {
+ surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, error);
+ } else if breakpoint.log_message.is_some() {
+ surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, info);
+ } else {
+ surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, warning);
+ }
+ }
+ }
+
+ if let Some(sp) = stack_pointer.as_ref() {
+ if let Some(src) = sp.source.as_ref() {
+ if doc
+ .path()
+ .map(|path| src.path == Some(path.clone()))
+ .unwrap_or(false)
+ && sp.line - 1 == line
+ {
+ surface.set_style(
+ Rect::new(viewport.x, viewport.y + i as u16, 6, 1),
+ helix_view::graphics::Style::default()
+ .bg(helix_view::graphics::Color::LightYellow),
+ );
+ }
+ }
+ }
+
let text = if line == last_line && !draw_last {
" ~".into()
} else {
@@ -493,6 +539,7 @@ impl EditorView {
viewport: Rect,
surface: &mut Surface,
theme: &Theme,
+ debugger: &Option<helix_dap::Client>,
) {
use helix_core::diagnostic::Severity;
use tui::{
@@ -530,6 +577,31 @@ impl EditorView {
lines.extend(text.lines);
}
+ if let Some(debugger) = debugger {
+ if let Some(path) = doc.path() {
+ if let Some(breakpoints) = debugger.breakpoints.get(path) {
+ let line = doc.text().char_to_line(cursor);
+ if let Some(breakpoint) = breakpoints
+ .iter()
+ .find(|breakpoint| breakpoint.line - 1 == line)
+ {
+ if let Some(condition) = &breakpoint.condition {
+ lines.extend(
+ Text::styled(condition, info.add_modifier(Modifier::UNDERLINED))
+ .lines,
+ );
+ }
+ if let Some(log_message) = &breakpoint.log_message {
+ lines.extend(
+ Text::styled(log_message, info.add_modifier(Modifier::UNDERLINED))
+ .lines,
+ );
+ }
+ }
+ }
+ }
+ }
+
let paragraph = Paragraph::new(lines).alignment(Alignment::Right);
let width = 80.min(viewport.width);
let height = 15.min(viewport.height);
@@ -647,6 +719,70 @@ impl EditorView {
event: KeyEvent,
) -> Option<KeymapResult> {
self.autoinfo = None;
+
+ if let Some(picker) = cxt.editor.debug_config_picker.clone() {
+ match event {
+ KeyEvent {
+ code: KeyCode::Esc, ..
+ } => {}
+ KeyEvent {
+ code: KeyCode::Char(char),
+ ..
+ } => {
+ let name = match picker.iter().find(|t| t.starts_with(char)) {
+ Some(n) => n.clone(),
+ None => return None,
+ };
+ let prompt = Prompt::new(
+ "arg:".to_owned(),
+ None,
+ |_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
+ move |cx: &mut crate::compositor::Context,
+ input: &str,
+ event: PromptEvent| {
+ if event != PromptEvent::Validate {
+ return;
+ }
+ commands::dap_start_impl(
+ cx.editor,
+ Some(&name),
+ None,
+ Some(vec![input]),
+ );
+ },
+ );
+ cxt.push_layer(Box::new(prompt));
+ }
+ _ => return None,
+ }
+ cxt.editor.debug_config_picker = None;
+ return None;
+ }
+
+ if cxt.editor.variables.is_some() {
+ match event {
+ KeyEvent {
+ code: KeyCode::Char('h'),
+ ..
+ } => {
+ cxt.editor.variables_page = cxt.editor.variables_page.saturating_sub(1);
+ }
+ KeyEvent {
+ code: KeyCode::Char('l'),
+ ..
+ } => {
+ cxt.editor.variables_page = cxt.editor.variables_page.saturating_add(1);
+ }
+ KeyEvent {
+ code: KeyCode::Esc, ..
+ } => {
+ cxt.editor.variables = None;
+ }
+ _ => {}
+ }
+ return None;
+ }
+
match self.keymaps.get_mut(&mode).unwrap().get(event) {
KeymapResult::Matched(command) => command.execute(cxt),
KeymapResult::Pending(node) => self.autoinfo = Some(node.into()),
@@ -1016,9 +1152,59 @@ impl Component for EditorView {
is_focused,
loader,
&cx.editor.config,
+ &cx.editor.debugger,
);
}
+ if let Some(ref vars) = cx.editor.variables {
+ let mut text = String::new();
+ let mut height = 0;
+ let mut max_len = 20;
+
+ let per_page = 15;
+ let num_vars = vars.len();
+ let start = (per_page * cx.editor.variables_page).min(num_vars);
+ let end = (start + per_page).min(num_vars);
+ for line in vars[start..end].to_vec() {
+ max_len = max_len.max(line.len() as u16);
+ height += 1;
+ text.push_str(&line);
+ }
+
+ if vars.len() > per_page {
+ text += "\nMove h, l";
+ height += 1;
+ }
+
+ let mut info = Info {
+ height: 20.min(height + 2),
+ width: 70.min(max_len),
+ title: format!("{} variables", num_vars),
+ text: text + "\nExit Esc",
+ };
+ info.render(area, surface, cx);
+ }
+
+ if let Some(ref configs) = cx.editor.debug_config_picker {
+ let mut text = String::new();
+ let mut height = 0;
+ let mut max_len = 20;
+
+ for line in configs {
+ max_len = max_len.max(line.len() as u16 + 2);
+ height += 1;
+ text.push_str(&format!("{} {}\n", line.chars().next().unwrap(), line));
+ }
+
+ let mut info = Info {
+ height: 20.min(height + 1),
+ width: 70.min(max_len),
+ title: "Debug targets".to_owned(),
+ text: text + "Exit Esc",
+ };
+ info.render(area, surface, cx);
+ }
+
if let Some(ref mut info) = self.autoinfo {
info.render(area, surface, cx);
}