A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | src/edi.rs | 21 | ||||
| -rw-r--r-- | src/lsp.rs | 6 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/rnd.rs | 100 | ||||
| -rw-r--r-- | src/text.rs | 151 | ||||
| -rw-r--r-- | src/text/inlay.rs | 2 |
6 files changed, 246 insertions, 35 deletions
@@ -113,6 +113,13 @@ pub struct Requests { RequestError<lsp_request!("textDocument/definition")>, >, #[serde(skip)] + pub document_symbols: Rq< + Option<Vec<DocumentSymbol>>, + Option<DocumentSymbolResponse>, + (), + RequestError<lsp_request!("textDocument/documentSymbol")>, + >, + #[serde(skip)] pub git_diff: Rq<imara_diff::Diff, imara_diff::Diff, (), ()>, } #[derive( @@ -195,7 +202,12 @@ macro_rules! change { } .ok_or(()) }); + let origin = origin.to_owned(); $self.requests.git_diff.request(t); + if $self.requests.document_symbols.result != Some(None) { + let h = x.runtime.spawn(async move { x.document_symbols(&origin).await }); + $self.requests.document_symbols.request(h); + } }); }; } @@ -493,6 +505,15 @@ impl Editor { ); self.requests.hovering.poll(|x, _| x.ok().flatten(), &r); self.requests.git_diff.poll(|x, _| x.ok(), &r); + self.requests.document_symbols.poll( + |x, _| { + x.ok().flatten().map(|x| match x { + DocumentSymbolResponse::Flat(_) => None, + DocumentSymbolResponse::Nested(x) => Some(x), + }) + }, + &r, + ); } #[implicit_fn] pub fn cursor_moved( @@ -32,7 +32,7 @@ use tokio_util::task::AbortOnDropHandle; use winit::window::Window; use crate::text::cursor::ceach; -use crate::text::{SortTedits, TextArea}; +use crate::text::{RopeExt, SortTedits, TextArea}; #[derive(Debug)] pub struct Client { pub runtime: tokio::runtime::Runtime, @@ -479,6 +479,10 @@ impl Client { t.cursor.first_mut().position = t.l_position(x).unwrap(); } } + + pub fn legend(&self) -> Option<&SemanticTokensLegend> { + match &self.initialized{Some(lsp_types::InitializeResult {capabilities: ServerCapabilities {semantic_tokens_provider:Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{legend,..})),..}, ..})=> {Some(legend)},_ => None,} + } pub fn inlay( &'static self, f: &Path, diff --git a/src/main.rs b/src/main.rs index 65be56b..fe94363 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,6 @@ coroutine_trait, cell_get_cloned, import_trait_associated_functions, - if_let_guard, deref_patterns, generic_const_exprs, const_trait_impl, @@ -1,4 +1,4 @@ -use std::iter::once; +use std::iter::{chain, once, repeat_n}; use std::os::fd::AsFd; use std::sync::{Arc, LazyLock}; use std::time::Instant; @@ -19,7 +19,7 @@ use winit::window::Window; use crate::edi::st::State; use crate::edi::{Editor, lsp_m}; use crate::lsp::Rq; -use crate::text::{CoerceOption, RopeExt, col}; +use crate::text::{CoerceOption, RopeExt, col, color_}; use crate::{ BG, BORDER, CompletionAction, CompletionState, FG, FONT, com, filter, lsp, sig, @@ -143,7 +143,6 @@ pub fn render( diag.iter().flat_map(|diag| { let sev = diag.severity.unwrap_or(DiagnosticSeverity::ERROR); let sev_ = match sev { - DiagnosticSeverity::ERROR => EType::Error, DiagnosticSeverity::WARNING => EType::Warning, DiagnosticSeverity::HINT => EType::Hint, @@ -357,7 +356,7 @@ pub fn render( ); let is_above = position.1.checked_sub(h).is_some(); let top = position.1.checked_sub(h).unwrap_or( - ((((_y + 1) as f32) * (fh + ls * fac)).round() + toy) + ((((_y/*+ 1*/) as f32) * (fh + ls * fac)).round() + toy) as usize, ); let (_, y) = dsb::fit( @@ -395,6 +394,98 @@ pub fn render( }; Ok((is_above, left, top, w, h)) }; + // dbg!(&ed.requests.document_symbols); + + if let Some(Some(x)) = &ed.requests.document_symbols.result + && let Some((_, y, z)) = text.sticky_context( + &x, + text.line_to_char(text.vo.saturating_sub(1)), + ) + && let Ok(l) = text.try_line_to_char(text.vo + 4) + && y.contains(&l) + // && text.cursor.iter().any(|x| y.contains(&*x)) + { + let mut cells = vec![]; + for e in z { + let p = text.l_position(e.selection_range.start).unwrap(); + if text.char_to_line(text.l_position(e.range.end).unwrap()) + == text.vo + { + continue; + } + + let r = if let Some(r) = e.sticky_range { + let r = text.l_range(r).unwrap(); + text.char_to_line(r.start)..=text.char_to_line(r.end) + } else { + let x = text.char_to_line(p); + x..=x + }; + + let d = Cell { + style: Style { + fg: crate::FG, + bg: col!("#191d27"), + ..Default::default() + }, + letter: None, + }; + + // let mut cells = + // chain([Cell::default()], x.to_string().chars().map(Cell::basic)).chain([Cell::default()]).collect(); + + for (x, rel, mut cell) in text + .colored_lines(r, lsp_m!(ed).and_then(|x| x.legend())) + { + if rel == 0 { + let rem = cells.len() % c; + if rem != 0 { + cells.extend(repeat_n(d, c - rem)); + } + cells.extend( + chain( + [d], + x.to_string().chars().map(|x| { + Style::new( + [67, 76, 87], + col!("#191d27"), + ) + .basic(x) + }), + ) + .chain([d]), + ); + } + cell.style.bg = col!("#191d27"); + cells.push(cell); + } + + let rem = cells.len() % c; + if rem != 0 { + cells.extend(repeat_n(d, c - rem)); + } + } + + if let Ok((.., w, h)) = place_around( + (0, 0), + i.copy(), + &cells, + c, + 14.0, + -200., + 0., + 0., + 0., + ) { + i.filled_box( + (w as u32, 0), + i.width() - w as u32, + h as _, + color_("#191d27"), + ); + } + // dbg !(x); + } let mut pass = true; if let Some((lsp, p)) = lsp_m!(ed + p) && let Some(diag) = lsp.diagnostics.get( @@ -627,6 +718,7 @@ pub fn render( } _ => None, }; + 'out: { if let Rq { result: Some((ref x, vo, ref mut max)), .. } = ed.requests.sig_help 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 { diff --git a/src/text/inlay.rs b/src/text/inlay.rs index cacc9f9..37b98cf 100644 --- a/src/text/inlay.rs +++ b/src/text/inlay.rs @@ -2,7 +2,7 @@ use Default::default; use lsp_types::{InlayHint, InlayHintLabel, Location}; use serde_derive::{Deserialize, Serialize}; -use crate::text::TextArea; +use crate::text::{RopeExt, TextArea}; pub type Inlay = Marking<Box<[(char, Option<Location>)]>>; #[derive(Clone, Debug, Default, Serialize, Deserialize)] |