Skip to content

Commit

Permalink
Add initial text annotations support
Browse files Browse the repository at this point in the history
Currently supports putting annotations at EOL and overlaying.
  • Loading branch information
sudormrfbin committed Nov 11, 2021
1 parent 5654909 commit 89385bf
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 7 deletions.
56 changes: 50 additions & 6 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use helix_core::{
LineEnding, Position, Range, Selection,
};
use helix_view::{
decorations::{TextAnnotation, TextAnnotationKind},
document::Mode,
editor::LineNumber,
graphics::{CursorKind, Modifier, Rect, Style},
Expand Down Expand Up @@ -280,6 +281,11 @@ impl EditorView {
highlights: H,
) {
let text = doc.text().slice(..);
let last_line = std::cmp::min(
// Saturating subs to make it inclusive zero indexing.
(offset.row + viewport.height as usize).saturating_sub(1),
doc.text().len_lines().saturating_sub(1),
);

let mut spans = Vec::new();
let mut visual_x = 0u16;
Expand All @@ -289,6 +295,33 @@ impl EditorView {

let text_style = theme.get("ui.text");

let out_of_bounds = |visual_x: u16| {
visual_x < offset.col as u16 || visual_x >= viewport.width + offset.col as u16
};

let render_annotation =
|annot: &TextAnnotation, line: u16, pos: u16, surface: &mut Surface| {
let mut visual_x = pos;
for grapheme in annot.text.graphemes(true) {
if out_of_bounds(visual_x) {
break;
}
surface.set_string(
viewport.x + visual_x - offset.col as u16,
viewport.y + line,
grapheme,
annot.style,
);
visual_x = visual_x.saturating_add(grapheme.width() as u16);
}
};

let text_annotations = doc
.text_annotations()
.iter()
.filter(|t| (offset.row..last_line).contains(&t.line))
.collect::<Vec<_>>();

'outer: for event in highlights {
match event {
HighlightEvent::HighlightStart(span) => {
Expand All @@ -311,18 +344,23 @@ impl EditorView {
});

for grapheme in RopeGraphemes::new(text) {
let out_of_bounds = visual_x < offset.col as u16
|| visual_x >= viewport.width + offset.col as u16;

if LineEnding::from_rope_slice(&grapheme).is_some() {
if !out_of_bounds {
if !out_of_bounds(visual_x) {
// we still want to render an empty cell with the style
surface.set_string(
viewport.x + visual_x - offset.col as u16,
viewport.y + line,
" ",
style,
);
visual_x += 1;
}

if let Some(annot) = text_annotations
.iter()
.find(|t| t.kind.is_eol() && t.line == offset.row + line as usize)
{
render_annotation(annot, line, visual_x, surface);
}

visual_x = 0;
Expand All @@ -345,7 +383,7 @@ impl EditorView {
(grapheme.as_ref(), width)
};

if !out_of_bounds {
if !out_of_bounds(visual_x) {
// if we're offscreen just keep going until we hit a new line
surface.set_string(
viewport.x + visual_x - offset.col as u16,
Expand All @@ -361,6 +399,13 @@ impl EditorView {
}
}
}

for annot in &text_annotations {
if let TextAnnotationKind::Overlay(visual_x) = annot.kind {
let line = (annot.line - offset.row) as u16;
render_annotation(annot, line, visual_x as u16, surface);
}
}
}

/// Render brace match, etc (meant for the focused view only)
Expand Down Expand Up @@ -390,7 +435,6 @@ impl EditorView {
.add_modifier(Modifier::REVERSED)
.add_modifier(Modifier::DIM)
});

surface
.get_mut(viewport.x + pos.col as u16, viewport.y + pos.row as u16)
.set_style(style);
Expand Down
29 changes: 29 additions & 0 deletions helix-view/src/decorations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::graphics::Style;

#[derive(Clone, Copy, PartialEq)]
pub enum TextAnnotationKind {
/// Add to end of line
Eol,
/// Replace actual text or arbitary cells with annotations.
/// Specifies an offset from the 0th column.
Overlay(usize),
}

impl TextAnnotationKind {
pub fn is_eol(&self) -> bool {
*self == Self::Eol
}

pub fn is_overlay(&self) -> bool {
matches!(*self, Self::Overlay(_))
}
}

pub struct TextAnnotation {
/// String used to namespace and identify similar annotations
pub scope: String,
pub text: String,
pub style: Style,
pub line: usize,
pub kind: TextAnnotationKind,
}
20 changes: 19 additions & 1 deletion helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use helix_core::{
};
use helix_lsp::util::LspFormatting;

use crate::{DocumentId, Theme, ViewId};
use crate::{decorations::TextAnnotation, DocumentId, Theme, ViewId};

/// 8kB of buffer space for encoding and decoding `Rope`s.
const BUF_SIZE: usize = 8192;
Expand Down Expand Up @@ -104,6 +104,7 @@ pub struct Document {
version: i32, // should be usize?

diagnostics: Vec<Diagnostic>,
text_annotations: Vec<TextAnnotation>,
language_server: Option<Arc<helix_lsp::Client>>,
}

Expand Down Expand Up @@ -337,6 +338,7 @@ impl Document {
language: None,
changes,
old_state,
text_annotations: vec![],
diagnostics: Vec::new(),
version: 0,
history: Cell::new(History::default()),
Expand Down Expand Up @@ -896,6 +898,22 @@ impl Document {
.map(helix_core::path::get_relative_path)
}

pub fn text_annotations(&self) -> &[TextAnnotation] {
&self.text_annotations
}

pub fn extend_text_annotations(&mut self, annots: Vec<TextAnnotation>) {
self.text_annotations.extend(annots)
}

/// Remove annotations that return true for the given predicate
pub fn remove_text_annotations<F>(&mut self, predicate: F)
where
F: Fn(&TextAnnotation) -> bool,
{
self.text_annotations.retain(|t| !predicate(t))
}

// pub fn slice<R>(&self, range: R) -> RopeSlice where R: RangeBounds {
// self.state.doc.slice
// }
Expand Down
1 change: 1 addition & 0 deletions helix-view/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pub mod macros;

pub mod clipboard;
pub mod decorations;
pub mod document;
pub mod editor;
pub mod graphics;
Expand Down

0 comments on commit 89385bf

Please sign in to comment.