use std::cell::Cell;
use std::cmp::Ordering;
use std::fmt::Debug;
use std::ptr::NonNull;
use crate::helix::doc_formatter::FormattedGrapheme;
use crate::helix::syntax::Highlight;
use crate::helix::{Position, Tendril};
#[derive(Debug, Clone)]
pub struct InlineAnnotation {
pub text: Tendril,
pub char_idx: usize,
}
impl InlineAnnotation {
pub fn new(char_idx: usize, text: impl Into<Tendril>) -> Self {
Self {
char_idx,
text: text.into(),
}
}
}
#[derive(Debug, Clone)]
pub struct Overlay {
pub char_idx: usize,
pub grapheme: Tendril,
}
impl Overlay {
pub fn new(char_idx: usize, grapheme: impl Into<Tendril>) -> Self {
Self {
char_idx,
grapheme: grapheme.into(),
}
}
}
pub trait LineAnnotation {
fn reset_pos(&mut self, _char_idx: usize) -> usize {
usize::MAX
}
fn skip_concealed_anchors(&mut self, conceal_end_char_idx: usize) -> usize {
self.reset_pos(conceal_end_char_idx)
}
fn process_anchor(&mut self, _grapheme: &FormattedGrapheme) -> usize {
usize::MAX
}
fn insert_virtual_lines(
&mut self,
line_end_char_idx: usize,
line_end_visual_pos: Position,
doc_line: usize,
) -> Position;
}
#[derive(Debug)]
struct Layer<'a, A, M> {
annotations: &'a [A],
current_index: Cell<usize>,
metadata: M,
}
impl<A, M: Clone> Clone for Layer<'_, A, M> {
fn clone(&self) -> Self {
Layer {
annotations: self.annotations,
current_index: self.current_index.clone(),
metadata: self.metadata.clone(),
}
}
}
impl<A, M> Layer<'_, A, M> {
pub fn reset_pos(&self, char_idx: usize, get_char_idx: impl Fn(&A) -> usize) {
let new_index = self
.annotations
.partition_point(|annot| get_char_idx(annot) < char_idx);
self.current_index.set(new_index);
}
pub fn consume(&self, char_idx: usize, get_char_idx: impl Fn(&A) -> usize) -> Option<&A> {
let annot = self.annotations.get(self.current_index.get())?;
debug_assert!(get_char_idx(annot) >= char_idx);
if get_char_idx(annot) == char_idx {
self.current_index.set(self.current_index.get() + 1);
Some(annot)
} else {
None
}
}
}
impl<'a, A, M> From<(&'a [A], M)> for Layer<'a, A, M> {
fn from((annotations, metadata): (&'a [A], M)) -> Layer<'a, A, M> {
Layer {
annotations,
current_index: Cell::new(0),
metadata,
}
}
}
fn reset_pos<A, M>(layers: &[Layer<A, M>], pos: usize, get_pos: impl Fn(&A) -> usize) {
for layer in layers {
layer.reset_pos(pos, &get_pos)
}
}
struct RawBox<T: ?Sized>(NonNull<T>);
impl<T: ?Sized> RawBox<T> {
#[allow(clippy::mut_from_ref)]
unsafe fn get(&self) -> &mut T {
&mut *self.0.as_ptr()
}
}
impl<T: ?Sized> From<Box<T>> for RawBox<T> {
fn from(box_: Box<T>) -> Self {
unsafe { Self(NonNull::new_unchecked(Box::into_raw(box_))) }
}
}
impl<T: ?Sized> Drop for RawBox<T> {
fn drop(&mut self) {
unsafe { drop(Box::from_raw(self.0.as_ptr())) }
}
}
#[derive(Default)]
pub struct TextAnnotations<'a> {
inline_annotations: Vec<Layer<'a, InlineAnnotation, Option<Highlight>>>,
overlays: Vec<Layer<'a, Overlay, Option<Highlight>>>,
line_annotations: Vec<(Cell<usize>, RawBox<dyn LineAnnotation + 'a>)>,
}
impl Debug for TextAnnotations<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TextAnnotations")
.field("inline_annotations", &self.inline_annotations)
.field("overlays", &self.overlays)
.finish_non_exhaustive()
}
}
impl<'a> TextAnnotations<'a> {
pub(crate) fn is_empty(&self) -> bool {
self.inline_annotations.is_empty()
&& self.overlays.is_empty()
&& self.line_annotations.is_empty()
}
pub fn reset_pos(&self, char_idx: usize) {
reset_pos(&self.inline_annotations, char_idx, |annot| annot.char_idx);
reset_pos(&self.overlays, char_idx, |annot| annot.char_idx);
for (next_anchor, layer) in &self.line_annotations {
next_anchor.set(unsafe { layer.get().reset_pos(char_idx) });
}
}
pub fn add_inline_annotations(
&mut self,
layer: &'a [InlineAnnotation],
highlight: Option<Highlight>,
) -> &mut Self {
if !layer.is_empty() {
self.inline_annotations.push((layer, highlight).into());
}
self
}
pub fn add_overlay(&mut self, layer: &'a [Overlay], highlight: Option<Highlight>) -> &mut Self {
if !layer.is_empty() {
self.overlays.push((layer, highlight).into());
}
self
}
pub fn add_line_annotation(&mut self, layer: Box<dyn LineAnnotation + 'a>) -> &mut Self {
self.line_annotations
.push((Cell::new(usize::MAX), layer.into()));
self
}
pub fn clear_line_annotations(&mut self) {
self.line_annotations.clear();
}
pub(crate) fn next_inline_annotation_at(
&self,
char_idx: usize,
) -> Option<(&InlineAnnotation, Option<Highlight>)> {
self.inline_annotations.iter().find_map(|layer| {
let annotation = layer.consume(char_idx, |annot| annot.char_idx)?;
Some((annotation, layer.metadata))
})
}
pub(crate) fn overlay_at(&self, char_idx: usize) -> Option<(&Overlay, Option<Highlight>)> {
let mut overlay = None;
for layer in &self.overlays {
while let Some(new_overlay) = layer.consume(char_idx, |annot| annot.char_idx) {
overlay = Some((new_overlay, layer.metadata));
}
}
overlay
}
pub(crate) fn process_virtual_text_anchors(&self, grapheme: &FormattedGrapheme) {
for (next_anchor, layer) in &self.line_annotations {
loop {
match next_anchor.get().cmp(&grapheme.char_idx) {
Ordering::Less => next_anchor
.set(unsafe { layer.get().skip_concealed_anchors(grapheme.char_idx) }),
Ordering::Equal => {
next_anchor.set(unsafe { layer.get().process_anchor(grapheme) })
}
Ordering::Greater => break,
};
}
}
}
pub(crate) fn virtual_lines_at(
&self,
char_idx: usize,
line_end_visual_pos: Position,
doc_line: usize,
) -> usize {
let mut virt_off = Position::new(0, 0);
for (_, layer) in &self.line_annotations {
virt_off += unsafe {
layer
.get()
.insert_virtual_lines(char_idx, line_end_visual_pos + virt_off, doc_line)
};
}
virt_off.row
}
}