A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/rnd.rs')
| -rw-r--r-- | src/rnd.rs | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/src/rnd.rs b/src/rnd.rs new file mode 100644 index 0000000..5f8354b --- /dev/null +++ b/src/rnd.rs @@ -0,0 +1,754 @@ +use std::iter::once; +use std::os::fd::AsFd; +use std::sync::{Arc, LazyLock}; +use std::time::Instant; + +use atools::prelude::*; +use dsb::{Cell, Fonts}; +use dsb::cell::Style; +use fimg::{Image, OverlayAt}; +use fimg::pixels::Blend; +use lsp_types::*; +use rust_fsm::StateMachine; +use softbuffer::Surface; +use swash::{FontRef, Instance}; +use url::Url; +use winit::dpi::{PhysicalPosition, PhysicalSize}; +use winit::window::Window; + +use crate::edi::st::State; +use crate::edi::{Editor, lsp_m}; +use crate::lsp::Rq; +use crate::text::{CoerceOption, col}; +use crate::{BG, BORDER, CompletionAction, CompletionState, FG, com, filter, lsp, sig}; + +#[implicit_fn::implicit_fn] +pub fn render( + ed: &mut Editor, + cells: &mut [Cell], + ppem: f32, + window: &mut Arc<Window>, + fw: f32, + fh: f32, + ls: f32, + c: usize, + r: usize, + surface: Option<&mut Surface<Arc<Window>, Arc<Window>>>, + cursor_position: (usize, usize), + fonts: &mut dsb::Fonts, + mut i: Image<&mut [u8], 3>, +) { + let text = &mut ed.text; + let (cx, cy) = text.cursor_visual(); + let met = super::FONT.metrics(&[]); + let fac = ppem / met.units_per_em as f32; + window.set_ime_cursor_area( + PhysicalPosition::new( + ((cx + text.line_number_offset()) as f64 * (fw) as f64) + .round(), + ((cy.saturating_sub(text.vo)) as f64 * (fh + ls * fac) as f64) + .floor(), + ), + PhysicalSize::new(fw, fh), + ); + let Some(surface) = surface else { + eprintln!( + "RedrawRequested fired before Resumed or after Suspended" + ); + return; + }; + let size = window.inner_size(); + + if size.height != 0 && size.width != 0 { + let now = Instant::now(); + if c * r != cells.len() { + return; + } + cells.fill(Cell { + style: Style { fg: BG, secondary_color: BG, bg: BG, flags: 0 }, + letter: None, + }); + let x = match &ed.state { + State::Selection(x) => Some(x.clone()), + _ => None, + }; + text.line_numbers( + (c, r - 1), + [67, 76, 87], + BG, + (cells, (c, r)), + (1, 0), + ); + let t_ox = text.line_number_offset() + 1; + text.c = c - t_ox; + text.r = r - 1; + // let mut text = text.clone(); + // for (_, inlay) in inlay.result.as_ref().into_iter().flatten().chunk_by(|x| x.position.line).into_iter() { + // let mut off = 0; + // for inlay in inlay { + // let label = match &inlay.label { + // InlayHintLabel::String(x) => x.clone(), + // InlayHintLabel::LabelParts(v) => { + // v.iter().map(_.value.clone()).collect::<String>() + // }, + // }; + // text.rope.insert(text.l_position(inlay.position).unwrap() + off, &label); + // off += label.chars().count(); + // } + // } + + text.write_to( + (cells, (c, r)), + (t_ox, 0), + x, + |(_c, _r), text, mut x| { + if let Some(hl) = &ed.document_highlights.result { + for DocumentHighlight { range: r, .. } in hl { + // let s = match kind { + // Some(DocumentHighlightKind::READ) => Style::UNDERLINE, + // Some(DocumentHighlightKind::WRITE) => Style::UNDERLINE, + // _ => Style::UNDERCURL, + // }; + let (x1, y1) = text.map_to_visual((r.start.character as _, r.start.line as _)); + let (x2, y2) = text.map_to_visual((r.end.character as _, r.end.line as _)); + x.get_simple((x1, y1), (x2, y2)).coerce().for_each(|x| { + x.style.bg = col!("#3a4358"); + }); + } + } + if let Some(LocationLink { + origin_selection_range: Some(r), .. + }) = ed.def.result { _ = try { + let (x1, y1) = text.map_to_visual((r.start.character as _, r.start.line as _)); + let (x2, y2) = text.map_to_visual((r.end.character as _, r.end.line as _)); + x.get_simple((x1, y1), (x2, y2))?.iter_mut().for_each(|x| { + x.style.flags |= Style::UNDERLINE; + x.style.fg = col!("#FFD173"); + }); + } } + if let Some((lsp, p)) = lsp_m!(ed + p) && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) { + #[derive(Copy, Clone, Debug)] + enum EType { + Hint, Info, Error,Warning,Related(DiagnosticSeverity), + } + let mut occupied = vec![]; + 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, + _ => EType::Info, + }; + once((diag.range, &*diag.message, sev_)).chain(diag.related_information.iter().flatten().filter(|sp| sp.location.uri == uri).map(move |x| { + (x.location.range, &*x.message, EType::Related(sev)) + })) + }).for_each(|(mut r, m, sev)| { + if let EType::Related(x) = sev && x != DiagnosticSeverity::ERROR { + return; + } + let p = r.start.line; + while occupied.contains(&r.start.line) { + r.start.line+=1; + }; + occupied.push(r.start.line); + let f = |cell:&mut Cell| { + cell.style.bg.blend(match sev { + EType::Error => col!("#ff66662c"), + EType::Warning | EType::Hint | EType::Info => col!("#9469242c"), + EType::Related(DiagnosticSeverity::ERROR) => col!("#dfbfff26"), + EType::Related(_) => col!("#ffad6625"), + }); + }; + if r.start == r.end { + x.get(text.map_to_visual((r.start.character as _, p as _))).map(f); + } else { + x.get_range(text.map_to_visual((r.start.character as _, p as _)), + text.map_to_visual((r.end.character as usize, r.end.line as _))) + .for_each(f) + } + let l = r.start.line as usize; + let Some(x_) = text.visual_eol(l).map(_+2) else { + return; + }; + let m = m.lines().next().unwrap_or(m); + x.get_range( + (x_, l), + (x_ + m.chars().count(), l), + ).zip(m.chars()).for_each(|(x, ch)| { + let (bg, fg) = match sev { + EType::Warning => { col!("#ff942f1b", "#fa973a") }, + EType::Error => { col!("#ff942f1b", "#f26462") }, + EType::Related(DiagnosticSeverity::WARNING) => { col!("#dfbfff26", "#DFBFFF") } + _ => return + }; + x.style.bg.blend(bg); + x.style.fg = fg; + x.letter = Some(ch); + }) + }); + } + if let State::Search(re, j, _) = &ed.state { + re.find_iter(&text.rope.to_string()) + .enumerate() + .for_each(|(i, m)| { + for x in x.get_range( + text.map_to_visual(text.xy(text.rope.byte_to_char(m.start())).unwrap()),text.map_to_visual( text.xy(text + .rope + .byte_to_char( + m.end(), + )).unwrap())) + { + x.style.bg = if i == *j { + [105, 83, 128] + } else { + [65, 62, 83] + } + } + }); + } + }, + ed.origin.as_deref(), + ed.semantic_tokens.result.as_deref().zip( + match lsp_m!(ed) { + Some(lsp::Client { initialized: Some(lsp_types::InitializeResult { + capabilities: ServerCapabilities { + semantic_tokens_provider: + Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{ + legend,.. + })),.. + }, .. + }), .. + }) => Some(legend), + _ => None, + }), + ); + + ed.bar.write_to( + BG, + FG, + (cells, (c, r)), + r - 1, + ed.origin + .as_ref() + .map(|x| { + ed.workspace + .as_ref() + .and_then(|w| x.strip_prefix(w).ok()) + .unwrap_or(&x) + .to_str() + .unwrap() + }) + .unwrap_or("new buffer"), + &ed.state, + &text, + lsp_m!(ed), + ); + unsafe { + dsb::render( + &cells, + (c, r), + ppem, + fonts, + ls, + true, + i.copy(), + (0, 0), + ) + }; + + let mut place_around = |(_x, _y): (usize, usize), + i: Image<&mut [u8], 3>, + c: &[Cell], + columns: usize, + ppem_: f32, + ls_: f32, + ox: f32, + oy: f32, + toy: f32| { + let met = super::FONT.metrics(&[]); + let fac = ppem / met.units_per_em as f32; + let position = ( + (((_x) as f32 * fw).round() + ox) as usize, + (((_y) as f32 * (fh + ls * fac)).round() + oy) as usize, + ); + + let ppem = ppem_; + let ls = ls_; + let mut r = c.len() / columns; + assert_eq!(c.len() % columns, 0); + let (w, h) = dsb::size(&fonts.regular, ppem, ls, (columns, r)); + // std::fs::write("cells", Cell::store(c)); + + if w >= size.width as usize + || (position.1 + h >= size.height as usize + && !position.1.checked_sub(h).is_some()) + || position.1 >= size.height as usize + || position.0 >= size.width as usize + { + unsafe { + dsb::render_owned( + c, + (columns, c.len() / columns), + ppem, + fonts, + ls, + true, + ) + .save("fail.png") + }; + return Err(()); + } + assert!( + w < window.inner_size().width as _ + && h < window.inner_size().height as _ + ); + 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) + as usize, + ); + let (_, y) = dsb::fit( + &fonts.regular, + ppem, + ls, + ( + window.inner_size().width as _, /* - left */ + ((window.inner_size().height as usize) + .saturating_sub(top)), + ), + ); /* suspicious saturation */ + r = r.min(y); + + let left = if position.0 + w as usize + > window.inner_size().width as usize + { + window.inner_size().width as usize - w as usize + } else { + position.0 + }; + + let (w, h) = dsb::size(&fonts.regular, ppem, ls, (columns, r)); + unsafe { + dsb::render( + &c, + (columns, 0), + ppem, + fonts, + ls, + true, + i, + (left as _, top as _), + ) + }; + Ok((is_above, left, top, w, h)) + }; + let mut pass = true; + if let Some((lsp, p)) = lsp_m!(ed + p) + && let Some(diag) = lsp.diagnostics.get( + &Url::from_file_path(p).unwrap(), + &lsp.diagnostics.guard(), + ) + { + let dawg = diag.iter().filter(|diag| { + text.l_range(diag.range).is_some_and(|x| { + x.contains(&text.mapped_index_at(cursor_position)) + && (text.vo..text.vo + r) + .contains(&(diag.range.start.line as _)) + }) + }); + for diag in dawg { + match diag + .data + .as_ref() + .unwrap_or_default() + .get("rendered") + { + Some(x) if let Some(x) = x.as_str() => { + let mut t = pattypan::term::Terminal::new( + (95, (r.saturating_sub(5)) as _), + false, + ); + for b in x + .replace('\n', "\r\n") + .replace("⸬", ":") + .replace("/home/os", "") + .bytes() + { + t.rx( + b, + std::fs::File::open("/dev/null") + .unwrap() + .as_fd(), + ); + } + let y_lim = t + .cells + .rows() + .position(|x| x.iter().all(_.letter.is_none())) + .unwrap_or(20); + let c = t.cells.c() as usize; + let Some(x_lim) = t + .cells + .rows() + .map( + _.iter() + .rev() + .take_while(_.letter.is_none()) + .count(), + ) + .map(|x| c - x) + .max() + else { + continue; + }; + let n = t + .cells + .rows() + .take(y_lim) + .flat_map(|x| &x[..x_lim]) + .copied() + .collect::<Vec<_>>(); + let Ok((_, left, top, w, h)) = place_around( + { + let (x, y) = text.map_to_visual(( + diag.range.start.character as _, + diag.range.start.line as usize, + )); + ( + x + text.line_number_offset() + 1, + y - text.vo, + ) + }, + i.copy(), + &n, + x_lim, + 17.0, + 0., + 0., + 0., + 0., + ) else { + continue; + }; + pass = false; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + } + _ => {} + } + } + }; + ed.hovering.result.as_ref().filter(|_| pass).map(|x| { + x.span.clone().map(|[(_x, _y), (_x2, _)]| { + // let [(_x, _y), (_x2, _)] = text.position(sp); + // dbg!(x..=x2, cursor_position.0) + // if !(_x..=_x2).contains(&&(cursor_position.0 .wrapping_sub( text.line_number_offset()+1))) { + // return + // } + + let [_x, _x2] = + [_x, _x2].add(text.line_number_offset() + 1); + let Some(_y) = _y.checked_sub(text.vo) else { + return; + }; + + // if !(cursor_position.1 == _y && (_x..=_x2).contains(&cursor_position.0)) { + // return; + // } + + let r = x.item.l().min(15); + let c = x.item.displayable(r); + let Ok((_, left, top, w, h)) = place_around( + (_x, _y), + i.copy(), + c, + x.item.c, + 18.0, + 10.0, + 0., + 0., + 0., + ) else { + return; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + }) + }); + match &ed.state { + State::CodeAction(Rq { result: Some(x), .. }) => 'out: { + let m = x.maxc(); + let c = x.write(m); + let (_x, _y) = text.cursor_visual(); + let _x = _x + text.line_number_offset() + 1; + let Some(_y) = _y.checked_sub(text.vo) else { + println!("rah"); + break 'out; + }; + let Ok((is_above, left, top, w, mut h)) = place_around( + (_x, _y), + i.copy(), + &c, + m, + ppem, + ls, + 0., + 0., + 0., + ) else { + println!("ra?"); + break 'out; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + } + State::Symbols(Rq { result: Some(x), .. }) => 'out: { + let ws = ed.workspace.as_deref().unwrap(); + let c = x.cells(50, ws); + // let (_x, _y) = text.cursor_visual(); + let _x = 0; + let _y = r - 1; + let Ok((is_above, left, top, w, mut h)) = place_around( + (_x, _y), + i.copy(), + &c, + 50, + ppem, + ls, + 0., + 0., + 0., + ) else { + println!("ra?"); + break 'out; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + } + _ => {} + } + let com = match ed.complete { + CompletionState::Complete(Rq { + result: Some(ref x), .. + }) => { + let c = com::s(x, 40, &filter(&text)); + if c.len() == 0 { + ed.complete + .consume(CompletionAction::NoResult) + .unwrap(); + None + } else { + Some(c) + } + } + _ => None, + }; + 'out: { + if let Rq { result: Some((ref x, vo, ref mut max)), .. } = + ed.sig_help + { + let (sig, p) = sig::active(x); + let c = sig::sig((sig, p), 40); + let (_x, _y) = text.cursor_visual(); + let _x = _x + text.line_number_offset() + 1; + let Some(_y) = _y.checked_sub(text.vo) else { break 'out }; + let Ok((is_above, left, top, w, mut h)) = place_around( + (_x, _y), + i.copy(), + &c, + 40, + ppem, + ls, + 0., + 0., + 0., + ) else { + break 'out; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + let com = com.and_then(|c| { + let Ok((is_above_, left, top, w_, h_)) = place_around( + (_x, _y), + i.copy(), + &c, + 40, + ppem, + ls, + 0., + -(h as f32), + if is_above { 0.0 } else { h as f32 }, + ) else { + return None; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w_ as _, + h_ as _, + BORDER, + ); + if is_above { + // completion below, we need to push the docs, if any, below only below us, if the sig help is still above. + h = h_; + } else { + h += h_; + } + Some((is_above_, left, top, w_, h_)) + }); + { + let ppem = 15.0; + let ls = 10.0; + let (fw, _) = dsb::dims(&FONT, ppem); + let cols = (w as f32 / fw).floor() as usize; + sig::doc(sig, cols).map(|mut cells| { + *max = Some(cells.l()); + cells.vo = vo; + let cells = cells.displayable(cells.l().min(15)); + let Ok((_, left_, top_, _w_, h_)) = place_around( + (_x, _y), + i.copy(), + cells, + cols, + ppem, + ls, + 0., + -(h as f32), + if is_above { + com.filter(|x| !x.0) + .map(|(_is, _l, _t, _w, h)| h) + .unwrap_or_default() + as f32 + } else { + h as f32 + }, + ) else { + return; + }; + i.r#box( + ( + left_.saturating_sub(1) as _, + top_.saturating_sub(1) as _, + ), + w as _, + h_ as _, + BORDER, + ); + }); + } + } else if let Some(c) = com { + let ppem = 20.0; + let (_x, _y) = text.cursor_visual(); + let _x = _x + text.line_number_offset() + 1; + let _y = _y.wrapping_sub(text.vo); + let Ok((_, left, top, w, h)) = place_around( + (_x, _y), + i.copy(), + &c, + 40, + ppem, + ls, + 0., + 0., + 0., + ) else { + break 'out; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + } + } + let met = FONT.metrics(&[]); + let fac = ppem / met.units_per_em as f32; + // if x.view_o == Some(x.cells.row) || x.view_o.is_none() { + let (fw, fh) = dsb::dims(&FONT, ppem); + let cursor = Image::<_, 4>::build(3, (fh).ceil() as u32) + .fill([0xFF, 0xCC, 0x66, 255]); + let mut draw_at = |x: usize, y: usize, w| unsafe { + let x = (x + t_ox).saturating_sub(text.ho) % c; + + if (text.vo..text.vo + r).contains(&y) { + i.overlay_at( + w, + (x as f32 * fw).floor() as u32, + ((y - text.vo) as f32 * (fh + ls * fac)).floor() + as u32, + // 4 + ((x - 1) as f32 * sz) as u32, + // (x as f32 * (ppem * 1.25)) as u32 - 20, + ); + } + }; + let (x, y) = text.cursor_visual(); + let image = Image::<_, 4>::build(2, (fh).ceil() as u32) + .fill([82, 82, 82, 255]); + for stop in + text.tabstops.as_ref().into_iter().flat_map(|x| x.list()) + { + let Some((x, y)) = text.xy(stop.clone().r().end) else { + continue; + }; + draw_at(x, y, &image); + } + if matches!(ed.state, State::Default | State::Selection(_)) { + draw_at(x, y, &cursor); + } + window.pre_present_notify(); + let buffer = surface.buffer_mut().unwrap(); + let x = unsafe { + std::slice::from_raw_parts_mut( + buffer.as_ptr() as *mut u8, + buffer.len() * 4, + ) + .as_chunks_unchecked_mut::<4>() + }; + fimg::overlay::copy_rgb_bgr_(i.flatten(), x); + dbg!(now.elapsed()); + buffer.present().unwrap(); + } +} |