Ported basic sticky-context to new text api

Up

Use unwrap_or_default instead of manually creating new vec
pull/6118/merge^2
Sora 1 year ago committed by SoraTenshi
parent 621ab0e57f
commit 37c8df5f5c

@ -126,6 +126,9 @@ pub struct LanguageConfiguration {
pub auto_pairs: Option<AutoPairs>,
pub rulers: Option<Vec<u16>>, // if set, override editor's rulers
/// List of tree-sitter nodes that should be displayed in the sticky context.
pub sticky_context_nodes: Option<Vec<String>>,
}
#[derive(Debug, PartialEq, Eq, Hash)]

@ -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<Box<(dyn LineDecoration + 'd)>>,
@ -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<dyn LineDecoration + '_>],
translated_positions: &mut [TranslatedPosition],
theme: &Theme,
) -> Option<Vec<usize>> {
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<usize> = 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,

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

Loading…
Cancel
Save