Skip to content

Commit 8929f6a

Browse files
committed
implement consistent behaviour for highlight overlays
1 parent e8f0886 commit 8929f6a

File tree

7 files changed

+486
-254
lines changed

7 files changed

+486
-254
lines changed

helix-core/src/syntax.rs

+36-137
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use crate::{
22
auto_pairs::AutoPairs,
33
chars::char_is_line_ending,
44
diagnostic::Severity,
5+
graphemes::ensure_grapheme_boundary_next_byte,
56
regex::Regex,
67
transaction::{ChangeSet, Operation},
78
Rope, RopeSlice, Tendril,
89
};
10+
pub use overlay::{monotonic_overlay, overlapping_overlay, Span};
911

1012
use arc_swap::{ArcSwap, Guard};
1113
use slotmap::{DefaultKey as LayerId, HopSlotMap};
@@ -25,6 +27,8 @@ use serde::{Deserialize, Serialize};
2527

2628
use helix_loader::grammar::{get_language, load_runtime_file};
2729

30+
mod overlay;
31+
2832
fn deserialize_regex<'de, D>(deserializer: D) -> Result<Option<Regex>, D::Error>
2933
where
3034
D: serde::Deserializer<'de>,
@@ -885,7 +889,7 @@ impl Syntax {
885889
source: RopeSlice<'a>,
886890
range: Option<std::ops::Range<usize>>,
887891
cancellation_flag: Option<&'a AtomicUsize>,
888-
) -> impl Iterator<Item = Result<HighlightEvent, Error>> + 'a {
892+
) -> HighlightIter<'a> {
889893
let mut layers = self
890894
.layers
891895
.iter()
@@ -1142,7 +1146,7 @@ pub enum Error {
11421146
}
11431147

11441148
/// Represents a single step in rendering a syntax-highlighted document.
1145-
#[derive(Copy, Clone, Debug)]
1149+
#[derive(Copy, Clone, Debug, PartialEq)]
11461150
pub enum HighlightEvent {
11471151
Source { start: usize, end: usize },
11481152
HighlightStart(Highlight),
@@ -1184,7 +1188,36 @@ struct LocalScope<'a> {
11841188
}
11851189

11861190
#[derive(Debug)]
1187-
struct HighlightIter<'a> {
1191+
pub struct CharacterHighlightIter<'a>(HighlightIter<'a>);
1192+
1193+
impl Iterator for CharacterHighlightIter<'_> {
1194+
type Item = HighlightEvent;
1195+
1196+
fn next(&mut self) -> Option<Self::Item> {
1197+
let res = match self.0.next()?.unwrap() {
1198+
// TODO: use byte slices directly
1199+
// convert byte offsets to char offset
1200+
HighlightEvent::Source { start, end } => {
1201+
let text = self.0.source;
1202+
let start = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, start));
1203+
let end = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, end));
1204+
HighlightEvent::Source { start, end }
1205+
}
1206+
event => event,
1207+
};
1208+
1209+
Some(res)
1210+
}
1211+
}
1212+
1213+
impl<'a> HighlightIter<'a> {
1214+
pub fn to_chars(self) -> CharacterHighlightIter<'a> {
1215+
CharacterHighlightIter(self)
1216+
}
1217+
}
1218+
1219+
#[derive(Debug)]
1220+
pub struct HighlightIter<'a> {
11881221
source: RopeSlice<'a>,
11891222
byte_offset: usize,
11901223
cancellation_flag: Option<&'a AtomicUsize>,
@@ -1859,140 +1892,6 @@ fn injection_for_match<'a>(
18591892
(language_name, content_node, included_children)
18601893
}
18611894

1862-
pub struct Merge<I> {
1863-
iter: I,
1864-
spans: Box<dyn Iterator<Item = (usize, std::ops::Range<usize>)>>,
1865-
1866-
next_event: Option<HighlightEvent>,
1867-
next_span: Option<(usize, std::ops::Range<usize>)>,
1868-
1869-
queue: Vec<HighlightEvent>,
1870-
}
1871-
1872-
/// Merge a list of spans into the highlight event stream.
1873-
pub fn merge<I: Iterator<Item = HighlightEvent>>(
1874-
iter: I,
1875-
spans: Vec<(usize, std::ops::Range<usize>)>,
1876-
) -> Merge<I> {
1877-
let spans = Box::new(spans.into_iter());
1878-
let mut merge = Merge {
1879-
iter,
1880-
spans,
1881-
next_event: None,
1882-
next_span: None,
1883-
queue: Vec::new(),
1884-
};
1885-
merge.next_event = merge.iter.next();
1886-
merge.next_span = merge.spans.next();
1887-
merge
1888-
}
1889-
1890-
impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
1891-
type Item = HighlightEvent;
1892-
fn next(&mut self) -> Option<Self::Item> {
1893-
use HighlightEvent::*;
1894-
if let Some(event) = self.queue.pop() {
1895-
return Some(event);
1896-
}
1897-
1898-
loop {
1899-
match (self.next_event, &self.next_span) {
1900-
// this happens when range is partially or fully offscreen
1901-
(Some(Source { start, .. }), Some((span, range))) if start > range.start => {
1902-
if start > range.end {
1903-
self.next_span = self.spans.next();
1904-
} else {
1905-
self.next_span = Some((*span, start..range.end));
1906-
};
1907-
}
1908-
_ => break,
1909-
}
1910-
}
1911-
1912-
match (self.next_event, &self.next_span) {
1913-
(Some(HighlightStart(i)), _) => {
1914-
self.next_event = self.iter.next();
1915-
Some(HighlightStart(i))
1916-
}
1917-
(Some(HighlightEnd), _) => {
1918-
self.next_event = self.iter.next();
1919-
Some(HighlightEnd)
1920-
}
1921-
(Some(Source { start, end }), Some((_, range))) if start < range.start => {
1922-
let intersect = range.start.min(end);
1923-
let event = Source {
1924-
start,
1925-
end: intersect,
1926-
};
1927-
1928-
if end == intersect {
1929-
// the event is complete
1930-
self.next_event = self.iter.next();
1931-
} else {
1932-
// subslice the event
1933-
self.next_event = Some(Source {
1934-
start: intersect,
1935-
end,
1936-
});
1937-
};
1938-
1939-
Some(event)
1940-
}
1941-
(Some(Source { start, end }), Some((span, range))) if start == range.start => {
1942-
let intersect = range.end.min(end);
1943-
let event = HighlightStart(Highlight(*span));
1944-
1945-
// enqueue in reverse order
1946-
self.queue.push(HighlightEnd);
1947-
self.queue.push(Source {
1948-
start,
1949-
end: intersect,
1950-
});
1951-
1952-
if end == intersect {
1953-
// the event is complete
1954-
self.next_event = self.iter.next();
1955-
} else {
1956-
// subslice the event
1957-
self.next_event = Some(Source {
1958-
start: intersect,
1959-
end,
1960-
});
1961-
};
1962-
1963-
if intersect == range.end {
1964-
self.next_span = self.spans.next();
1965-
} else {
1966-
self.next_span = Some((*span, intersect..range.end));
1967-
}
1968-
1969-
Some(event)
1970-
}
1971-
(Some(event), None) => {
1972-
self.next_event = self.iter.next();
1973-
Some(event)
1974-
}
1975-
// Can happen if cursor at EOF and/or diagnostic reaches past the end.
1976-
// We need to actually emit events for the cursor-at-EOF situation,
1977-
// even though the range is past the end of the text. This needs to be
1978-
// handled appropriately by the drawing code by not assuming that
1979-
// all `Source` events point to valid indices in the rope.
1980-
(None, Some((span, range))) => {
1981-
let event = HighlightStart(Highlight(*span));
1982-
self.queue.push(HighlightEnd);
1983-
self.queue.push(Source {
1984-
start: range.start,
1985-
end: range.end,
1986-
});
1987-
self.next_span = self.spans.next();
1988-
Some(event)
1989-
}
1990-
(None, None) => None,
1991-
e => unreachable!("{:?}", e),
1992-
}
1993-
}
1994-
}
1995-
19961895
#[cfg(test)]
19971896
mod test {
19981897
use super::*;

0 commit comments

Comments
 (0)