A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text.rs')
| -rw-r--r-- | src/text.rs | 151 |
1 files changed, 123 insertions, 28 deletions
diff --git a/src/text.rs b/src/text.rs index bdf9078..00a2e65 100644 --- a/src/text.rs +++ b/src/text.rs @@ -16,7 +16,8 @@ use helix_core::Syntax; use helix_core::syntax::{HighlightEvent, Loader}; use implicit_fn::implicit_fn; use lsp_types::{ - Location, Position, SemanticTokensLegend, SnippetTextEdit, TextEdit, + DocumentSymbol, Location, Position, SemanticTokensLegend, + SnippetTextEdit, TextEdit, }; use ropey::{Rope, RopeSlice}; use serde::{Deserialize, Serialize}; @@ -177,6 +178,12 @@ pub trait RopeExt { /// or eof fn eol(&self, li: usize) -> usize; + + fn l_pos_to_char(&self, p: Position) -> Option<(usize, usize)>; + fn l_position(&self, p: Position) -> Option<usize>; + fn to_l_position(&self, l: usize) -> Option<lsp_types::Position>; + fn l_range(&self, r: lsp_types::Range) -> Option<Range<usize>>; + fn to_l_range(&self, r: Range<usize>) -> Option<lsp_types::Range>; } impl RopeExt for Rope { fn position( @@ -238,6 +245,33 @@ impl RopeExt for Rope { .unwrap_or(usize::MAX) .min(self.len_chars()) } + fn l_pos_to_char(&self, p: Position) -> Option<(usize, usize)> { + self.l_position(p).and_then(|x| self.xy(x)) + } + + fn l_position(&self, p: Position) -> Option<usize> { + self.try_byte_to_char( + self.try_line_to_byte(p.line as _).ok()? + + (p.character as usize) + .min(self.get_line(p.line as _)?.len_bytes()), + ) + .ok() + } + fn to_l_position(&self, l: usize) -> Option<lsp_types::Position> { + Some(Position { + line: self.y(l)? as _, + character: self.x_bytes(l)? as _, + }) + } + fn l_range(&self, r: lsp_types::Range) -> Option<Range<usize>> { + Some(self.l_position(r.start)?..self.l_position(r.end)?) + } + fn to_l_range(&self, r: Range<usize>) -> Option<lsp_types::Range> { + Some(lsp_types::Range { + start: self.to_l_position(r.start)?, + end: self.to_l_position(r.end)?, + }) + } } impl TextArea { @@ -739,33 +773,54 @@ impl TextArea { }) } - pub fn l_pos_to_char(&self, p: Position) -> Option<(usize, usize)> { - self.l_position(p).and_then(|x| self.xy(x)) - } - - pub fn l_position(&self, p: Position) -> Option<usize> { - self.rope - .try_byte_to_char( - self.rope.try_line_to_byte(p.line as _).ok()? - + (p.character as usize) - .min(self.rope.get_line(p.line as _)?.len_bytes()), - ) - .ok() - } - pub fn to_l_position(&self, l: usize) -> Option<lsp_types::Position> { - Some(Position { - line: self.y(l)? as _, - character: self.x_bytes(l)? as _, - }) - } - pub fn l_range(&self, r: lsp_types::Range) -> Option<Range<usize>> { - Some(self.l_position(r.start)?..self.l_position(r.end)?) - } - pub fn to_l_range(&self, r: Range<usize>) -> Option<lsp_types::Range> { - Some(lsp_types::Range { - start: self.to_l_position(r.start)?, - end: self.to_l_position(r.end)?, - }) + pub gen fn colored_lines( + &self, + slice: impl Iterator<Item = usize>, + leg: Option<&SemanticTokensLegend>, + ) -> (usize, usize, Cell) { + let mut tokens = self.tokens.iter(); + let mut curr: Option<&TokenD> = tokens.next(); + // for ln in self.rope.slice(slice) {} + // let s = self.rope.char_to_line(slice.start); + for l in slice { + // let c = 0; + // let l = self.char_to_line(c); + // let relative = c - self.rope.line_to_char(l); + for (e, i) in self.source_map(l).coerce().zip(0..) { + if e.c() == '\n' { + continue; + } + let mut c = Cell::default(); + c.letter = Some(e.c()); + c.style = match e { + Mapping::Char(_, _, abspos) if let Some(leg) = leg => + if let Some(curr) = curr + && (curr.range.0..curr.range.1) + .contains(&(abspos as _)) + { + curr.style(leg) + } else { + while let Some(c) = curr + && c.range.0 < abspos as _ + { + curr = tokens.next(); + } + if let Some(curr) = curr + && (curr.range.0..curr.range.1) + .contains(&(abspos as _)) + { + curr.style(leg) + } else { + Style::new(crate::FG, crate::BG) + } + }, + Mapping::Char(..) => Style::new(crate::FG, crate::BG), + Mapping::Fake(Marking { .. }, ..) => + Style::new(const { color_("#536172") }, crate::BG), + }; + yield (l, i, c) + } + } } #[implicit_fn] @@ -984,6 +1039,46 @@ impl TextArea { } } } + pub fn sticky_context<'local, 'further>( + &'local self, + syms: &'further [DocumentSymbol], + at: usize, + ) -> Option<( + &'further DocumentSymbol, + std::ops::Range<usize>, + Vec<&'further DocumentSymbol>, + )> { + /// for the shortest range + fn search<'local, 'further>( + x: &'further DocumentSymbol, + best: &'local mut Option<( + &'further DocumentSymbol, + std::ops::Range<usize>, + Vec<&'further DocumentSymbol>, + )>, + look: usize, + r: &'_ Rope, + mut path: Vec<&'further DocumentSymbol>, + ) { + path.push(x); + if let Some(y) = r.l_range(x.range) + && y.contains(&look) + { + if best.as_ref().is_none_or(|(_, r, _)| r.len() > y.len()) + { + *best = Some((x, y, path.clone())) + } + for lem in x.children.as_ref().coerce() { + search(lem, best, look, r, path.clone()) + } + } + } + let mut best = None; + for sym in syms { + search(sym, &mut best, at, &self.rope, vec![]); + } + best + } } pub fn is_word(r: char) -> bool { |