From 37c8df5f5c667ddef8ce6bb0870b31ba9137e2d7 Mon Sep 17 00:00:00 2001 From: Sora Date: Sun, 26 Feb 2023 23:24:45 +0000 Subject: [PATCH] Ported basic sticky-context to new text api Up Use unwrap_or_default instead of manually creating new vec --- helix-core/src/syntax.rs | 3 + helix-term/src/ui/editor.rs | 157 +++++++++++++++++++++++++++++++++--- helix-view/src/editor.rs | 4 + 3 files changed, 151 insertions(+), 13 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 1b6c1b1da..81c2b8599 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -126,6 +126,9 @@ pub struct LanguageConfiguration { pub auto_pairs: Option, pub rulers: Option>, // if set, override editor's rulers + + /// List of tree-sitter nodes that should be displayed in the sticky context. + pub sticky_context_nodes: Option>, } #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 59f371bda..cbe027cea 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -22,7 +22,7 @@ use helix_core::{ }; use helix_view::{ document::{Mode, SCRATCH_BUFFER_NAME}, - editor::{CompleteAction, CursorShapeConfig}, + editor::{CompleteAction, CursorShapeConfig, LineNumber}, graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, @@ -32,7 +32,7 @@ use std::{num::NonZeroUsize, path::PathBuf, rc::Rc}; use tui::buffer::Buffer as Surface; -use super::statusline; +use super::{document::render_text, statusline}; use super::{document::LineDecoration, lsp::SignatureHelp}; pub struct EditorView { @@ -175,16 +175,6 @@ impl EditorView { Box::new(highlights) }; - Self::render_gutter( - editor, - doc, - view, - view.area, - theme, - is_focused, - &mut line_decorations, - ); - if is_focused { let cursor = doc .selection(view.id) @@ -197,6 +187,8 @@ impl EditorView { translated_positions.push((cursor, Box::new(update_cursor_cache))); } + Self::render_gutter(editor, doc, view, theme, is_focused, &mut line_decorations); + render_document( surface, inner, @@ -208,6 +200,21 @@ impl EditorView { &mut line_decorations, &mut translated_positions, ); + + if config.sticky_context { + let _line_nr = Self::render_sticky_context( + editor, + doc, + view, + surface, + &text_annotations, + &mut line_decorations, + &mut translated_positions, + theme, + ) + .unwrap_or_default(); + } + Self::render_rulers(editor, doc, view, inner, surface, theme); // if we're not at the edge of the screen, draw a right border @@ -579,7 +586,6 @@ impl EditorView { editor: &'d Editor, doc: &'d Document, view: &View, - viewport: Rect, theme: &Theme, is_focused: bool, line_decorations: &mut Vec>, @@ -592,6 +598,7 @@ impl EditorView { .collect(); let mut offset = 0; + let viewport = view.area; let gutter_style = theme.get("ui.gutter"); let gutter_selected_style = theme.get("ui.gutter.selected"); @@ -696,6 +703,130 @@ impl EditorView { ); } + /// Render the sticky context + pub fn render_sticky_context( + editor: &Editor, + doc: &Document, + view: &View, + surface: &mut Surface, + doc_annotations: &TextAnnotations, + line_decoration: &mut [Box], + translated_positions: &mut [TranslatedPosition], + theme: &Theme, + ) -> Option> { + let syntax = doc.syntax()?; + let tree = syntax.tree(); + let text = doc.text().slice(..); + let viewport = view.inner_area(doc); + let top_byte = text.char_to_byte(view.offset.anchor); + + let context_nodes = doc + .language_config() + .and_then(|lc| lc.sticky_context_nodes.as_ref()); + + let mut parent = tree + .root_node() + .descendant_for_byte_range(top_byte, top_byte) + .and_then(|n| n.parent()); + + // context is list of numbers of lines that should be rendered in the LSP context + let mut context: Vec = Vec::new(); + + while let Some(node) = parent { + // if the node is smaller than half the viewport height, skip + if (node.end_position().row - node.start_position().row) < viewport.height as usize / 2 + { + parent = node.parent(); + continue; + } + + let line = text.byte_to_line(node.start_byte()); + + // if parent of previous node is still on the same line, use the parent node + // or if the parent of previous node overlaps with the current node + if let Some(&prev_line) = context.last() { + if prev_line == line { + context.pop(); + } + } + + if context_nodes.map_or(true, |nodes| nodes.iter().any(|n| n == node.kind())) { + context.push(line); + } + + parent = node.parent(); + } + + // we render from top most (last in the list) + context.reverse(); + + // allow a maximum of half the viewport height + // to be occupied by the sticky context + if context.len() > viewport.height as usize / 2 { + context = context + .into_iter() + .take(viewport.height as usize / 2) + .collect(); + } + + // TODO: this probably needs it's own style, although it seems to work well even with cursorline + let context_style = theme.get("ui.cursorline.primary"); + + let mut context_area = viewport; + context_area.height = 1; + + let mut line_numbers = Vec::new(); + + for line_num in &context { + let line_num_anchor = text.line_to_char(*line_num); + + surface.clear_with(context_area, context_style); + + let highlights = Self::doc_syntax_highlights(doc, line_num_anchor, 1, theme); + + let mut renderer = TextRenderer::new( + surface, + doc, + theme, + view.offset.horizontal_offset, + context_area, + ); + + let mut new_offset = view.offset; + new_offset.anchor = line_num_anchor; + + render_text( + &mut renderer, + text, + new_offset, + &doc.text_format(context_area.width, Some(theme)), + doc_annotations, + highlights, + theme, + line_decoration, + translated_positions, + ); + + context_area.y += 1; + let line_number = match editor.config().line_number { + LineNumber::Absolute => *line_num, + LineNumber::Relative => { + if editor.mode() == Mode::Insert { + *line_num + } else { + let res = top_byte - *line_num; + match res { + n if n < 2 => 1, + _ => res - 1, + } + } + } + }; + line_numbers.push(line_number); + } + Some(line_numbers) + } + /// Apply the highlighting on the lines where a cursor is active pub fn cursorline_decorator( doc: &Document, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 50da3ddea..ecf4020fa 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -274,6 +274,9 @@ pub struct Config { /// Whether to color modes with different colors. Defaults to `false`. pub color_modes: bool, pub soft_wrap: SoftWrap, + + /// Display context of current cursor line if it is outside the view. + pub sticky_context: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -772,6 +775,7 @@ impl Default for Config { indent_guides: IndentGuidesConfig::default(), color_modes: false, soft_wrap: SoftWrap::default(), + sticky_context: false, } } }