A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml3
-rw-r--r--src/com.rs2
-rw-r--r--src/hov.rs2
-rw-r--r--src/lsp.rs248
-rw-r--r--src/main.rs74
-rw-r--r--src/text.rs85
6 files changed, 330 insertions, 84 deletions
diff --git a/Cargo.toml b/Cargo.toml
index fdedde9..0f5b6d9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2024"
[dependencies]
-atools = "0.1.7"
dsb = { version = "0.1.0", path = "../dsb" }
fimg = { git = "https://github.com/bend-n/fimg" }
implicit-fn = "0.1.0"
@@ -51,6 +50,8 @@ replace_with = "0.1.8"
nucleo = "0.5.0"
tokio-util = { version = "0.7.17", features = ["rt"] }
scopeguard = "1.2.0"
+arc-swap = "1.7.1"
+atools = "0.1.10"
[profile.dev.package.rust-analyzer]
opt-level = 3
diff --git a/src/com.rs b/src/com.rs
index 6d7f13b..3af3be3 100644
--- a/src/com.rs
+++ b/src/com.rs
@@ -174,7 +174,7 @@ fn r(
indices: &[u32],
to: &mut Vec<Cell>,
) {
- let bg = if selected { color(*b"262d3b") } else { color(*b"1c212b") };
+ let bg = if selected { color(b"#262d3b") } else { color(b"#1c212b") };
let ds: Style = Style { bg: bg, color: FG, flags: 0 };
let d: Cell = Cell { letter: None, style: ds };
diff --git a/src/hov.rs b/src/hov.rs
index c70e4c9..6e467da 100644
--- a/src/hov.rs
+++ b/src/hov.rs
@@ -261,7 +261,7 @@ pub fn markdown2(c: usize, x: &Node) -> Vec<Cell> {
}
r.to
}
-pub const BG: [u8; 3] = text::color(*b"191E27");
+pub const BG: [u8; 3] = text::color(b"#191E27");
#[test]
fn t() {
use std::time::Instant;
diff --git a/src/lsp.rs b/src/lsp.rs
index 912b195..5ee1f26 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -1,6 +1,7 @@
use std::collections::HashMap;
+use std::fmt::Display;
use std::mem::forget;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering::Relaxed;
@@ -9,12 +10,14 @@ use std::thread::spawn;
use std::time::Instant;
use Default::default;
+use anyhow::bail;
use crossbeam::channel::{
Receiver, RecvError, SendError, Sender, unbounded,
};
use log::{debug, error};
use lsp_server::{
ErrorCode, Message, Notification as N, Request as LRq, Response as Re,
+ ResponseError,
};
use lsp_types::notification::*;
use lsp_types::request::*;
@@ -35,8 +38,9 @@ pub struct Client {
ProgressToken,
Option<(WorkDoneProgress, WorkDoneProgressBegin)>,
>,
+ pub diagnostics: &'static papaya::HashMap<Url, Vec<Diagnostic>>,
pub not_rx: Receiver<N>,
- // pub req_rx: Receiver<Rq>,
+ pub req_rx: Receiver<LRq>,
}
impl Drop for Client {
@@ -44,7 +48,30 @@ impl Drop for Client {
panic!("please dont")
}
}
-
+#[derive(Debug)]
+pub enum RequestError {
+ Rx,
+ Cancelled(Re, DiagnosticServerCancellationData),
+}
+impl From<oneshot::error::RecvError> for RequestError {
+ fn from(_: oneshot::error::RecvError) -> Self {
+ Self::Rx
+ }
+}
+impl std::error::Error for RequestError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+impl Display for RequestError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Rx => write!(f, "couldnt get from thingy"),
+ Self::Cancelled(x, y) =>
+ write!(f, "server cancelled us. {x:?} {y:?}"),
+ }
+ }
+}
impl Client {
pub fn notify<X: Notification>(
&self,
@@ -64,8 +91,7 @@ impl Client {
y: &X::Params,
) -> Result<
(
- impl Future<Output = Result<X::Result, oneshot::error::RecvError>>
- + use<'me, X>,
+ impl Future<Output = Result<X::Result, RequestError>> + use<'me, X>,
i32,
),
SendError<Message>,
@@ -87,13 +113,22 @@ impl Client {
self.cancel(id);
});
- rx.await.map(|x| {
- forget(g);
- serde_json::from_value::<X::Result>(
+ let mut x = rx.await?;
+ forget(g);
+ if let Some(ResponseError { code, ref mut data, .. }) =
+ x.error
+ && code == ErrorCode::ServerCancelled as i32
+ {
+ let e = serde_json::from_value(
+ data.take().unwrap_or_default(),
+ );
+ Err(RequestError::Cancelled(x, e.expect("lsp??")))
+ } else {
+ Ok(serde_json::from_value::<X::Result>(
x.result.unwrap_or_default(),
)
- .expect("lsp badg")
- })
+ .expect("lsp badg"))
+ }
},
id,
))
@@ -137,7 +172,7 @@ impl Client {
&self,
x: CompletionItem,
) -> Result<
- impl Future<Output = Result<CompletionItem, oneshot::error::RecvError>>,
+ impl Future<Output = Result<CompletionItem, RequestError>>,
SendError<Message>,
> {
self.request::<ResolveCompletionItem>(&x).map(|x| x.0)
@@ -149,19 +184,12 @@ impl Client {
(x, y): (usize, usize),
c: CompletionContext,
) -> impl Future<
- Output = Result<
- Option<CompletionResponse>,
- oneshot::error::RecvError,
- >,
+ Output = Result<Option<CompletionResponse>, RequestError>,
> + use<'me> {
let (rx, _) = self
.request::<Completion>(&CompletionParams {
text_document_position: TextDocumentPositionParams {
- text_document: {
- TextDocumentIdentifier {
- uri: Url::from_file_path(f).unwrap(),
- }
- },
+ text_document: f.tid(),
position: Position { line: y as _, character: x as _ },
},
work_done_progress_params: default(),
@@ -176,17 +204,12 @@ impl Client {
&'me self,
f: &Path,
(x, y): (usize, usize),
- ) -> impl Future<
- Output = Result<Option<SignatureHelp>, oneshot::error::RecvError>,
- > + use<'me> {
+ ) -> impl Future<Output = Result<Option<SignatureHelp>, RequestError>>
+ + use<'me> {
self.request::<SignatureHelpRequest>(&SignatureHelpParams {
context: None,
text_document_position_params: TextDocumentPositionParams {
- text_document: {
- TextDocumentIdentifier {
- uri: Url::from_file_path(f).unwrap(),
- }
- },
+ text_document: f.tid(),
position: Position { line: y as _, character: x as _ },
},
work_done_progress_params: default(),
@@ -195,6 +218,117 @@ impl Client {
.0
}
+ pub fn pull_all_diag(
+ &self,
+ f: PathBuf,
+ ) -> impl Future<Output = anyhow::Result<()>> {
+ let r = self
+ .request::<lsp_request!("workspace/diagnostic")>(&default())
+ .unwrap()
+ .0;
+ log::info!("pulling diagnostics");
+ async move {
+ let x = r.await?;
+ log::info!("{x:?}");
+ // match x {
+ // DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
+ // related_documents,
+ // full_document_diagnostic_report:FullDocumentDiagnosticReport { items,.. },
+ // })) => {
+ // let l = self.diagnostics.guard();
+ // self.diagnostics.insert(f.tid().uri, items, &l);
+ // for (uri, rel) in related_documents.into_iter().flatten() {
+ // match rel {
+ // DocumentDiagnosticReportKind::Full(FullDocumentDiagnosticReport { items, .. }) => {
+ // self.diagnostics.insert(uri, items, &l);
+ // },
+ // DocumentDiagnosticReportKind::Unchanged(_) => {},
+ // }
+ // }
+ // log::info!("pulled diagnostics");
+ // },
+ // _ => bail!("fuck that"),
+ // };
+ Ok(())
+ }
+ }
+ pub fn pull_diag(
+ &self,
+ f: PathBuf,
+ previous: Option<String>,
+ ) -> impl Future<Output = anyhow::Result<Option<String>>> {
+ let p = DocumentDiagnosticParams {
+ text_document: f.tid(),
+ identifier: try {
+ match self
+ .initialized
+ .as_ref()?
+ .capabilities
+ .diagnostic_provider
+ .as_ref()?
+ {
+ DiagnosticServerCapabilities::RegistrationOptions(
+ x,
+ ) => x.diagnostic_options.identifier.clone()?,
+ _ => None?,
+ }
+ },
+ previous_result_id: previous,
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ };
+ let (r, _) = self
+ .request::<lsp_request!("textDocument/diagnostic")>(&p)
+ .unwrap();
+ log::info!("pulling diagnostics");
+
+ async move {
+ let x = match r.await {
+ Ok(x) => x,
+ Err(RequestError::Cancelled(x, y)) if y.retrigger_request => {
+ self.request::<lsp_request!("textDocument/diagnostic")>(&p,).unwrap().0.await?
+ },
+ Err(e) => return Err(e.into()),
+ };
+ log::info!("{x:?}");
+ match x {
+ DocumentDiagnosticReportResult::Report(
+ DocumentDiagnosticReport::Full(
+ RelatedFullDocumentDiagnosticReport {
+ related_documents,
+ full_document_diagnostic_report:
+ FullDocumentDiagnosticReport {
+ items,
+ result_id,
+ },
+ },
+ ),
+ ) => {
+ let l = self.diagnostics.guard();
+ self.diagnostics.insert(f.tid().uri, items, &l);
+ for (uri, rel) in
+ related_documents.into_iter().flatten()
+ {
+ match rel {
+ DocumentDiagnosticReportKind::Full(
+ FullDocumentDiagnosticReport {
+ items, ..
+ },
+ ) => {
+ self.diagnostics.insert(uri, items, &l);
+ }
+ DocumentDiagnosticReportKind::Unchanged(_) => {
+ }
+ }
+ }
+ log::info!("pulled diagnostics");
+ Ok(result_id)
+ }
+ _ => bail!("fuck that"),
+ }
+ }
+ }
+
pub fn rq_semantic_tokens(
&'static self,
to: &mut Rq<Box<[SemanticToken]>, Box<[SemanticToken]>>,
@@ -211,9 +345,7 @@ impl Client {
&SemanticTokensParams {
work_done_progress_params: default(),
partial_result_params: default(),
- text_document: TextDocumentIdentifier::new(
- url::Url::from_file_path(f).unwrap(),
- ),
+ text_document: f.tid(),
},
)?;
let x = self.runtime.spawn(async move {
@@ -252,7 +384,9 @@ pub fn run(
.unwrap(),
id: AtomicI32::new(0),
initialized: None,
+ diagnostics: Box::leak(Box::new(papaya::HashMap::default())),
send_to: req_tx,
+ req_rx: _req_rx,
not_rx,
};
_ = c
@@ -264,13 +398,23 @@ pub fn run(
work_done_progress: Some(true),
..default()
}),
-
+ workspace: Some(WorkspaceClientCapabilities {
+ diagnostic: Some(DiagnosticWorkspaceClientCapabilities { refresh_support: Some(true) }),
+ ..default()
+ }),
+
text_document: Some(TextDocumentClientCapabilities {
hover: Some(HoverClientCapabilities {
dynamic_registration: None,
content_format: Some(vec![MarkupKind::PlainText, MarkupKind::Markdown]),
}),
- diagnostic: Some(DiagnosticClientCapabilities { dynamic_registration: None, related_document_support: Some(true), }),
+ diagnostic: Some(DiagnosticClientCapabilities { dynamic_registration: None, related_document_support: Some(true) }),
+ publish_diagnostics: Some(PublishDiagnosticsClientCapabilities {
+ related_information: Some(true),
+ code_description_support: Some(true),
+ data_support: Some(true),
+ ..default()
+ }),
signature_help: Some(SignatureHelpClientCapabilities {
dynamic_registration: None, signature_information: Some(SignatureInformationSettings {
documentation_format: Some(vec![
@@ -388,6 +532,8 @@ pub fn run(
..default()
}),
experimental: Some(json! {{
+ "colorDiagnosticOutput": true,
+ "codeActionGroup": true,
"serverStatusNotification": true,
"hoverActions": true,
}}),
@@ -419,6 +565,8 @@ pub fn run(
"mode": "prefer_prefix"
}
},
+ "checkOnSave": true,
+ "diagnostics": { "enable": true },
"semanticHighlighting": {
"punctuation": {
"separate": {
@@ -462,6 +610,7 @@ pub fn run(
})
.unwrap();
let progress = c.progress;
+ let d = c.diagnostics;
log::info!("lsp took {:?} to initialize", now.elapsed());
let h = spawn(move || {
let mut map = HashMap::new();
@@ -490,16 +639,22 @@ pub fn run(
}
Ok(Message::Request(x)) => {
if let Err(e) = _req_tx.send(x) {
- error!("couldnt receive request {e:?}");
+ let m = e.to_string();
+ error!("couldnt receive request {m}: {:?}", e.into_inner());
}
}
Ok(Message::Response(x)) => {
if let Some(e) =& x.error {
if e.code == ErrorCode::RequestCanceled as i32 {}
+ else if e.code == ErrorCode::ServerCancelled as i32 {
+ if let Some((s, _)) = map.remove(&x.id.i32()) {
+ log::info!("request {} cancelled", x.id);
+ _ = s.send(x);
+ }
+ }
else {
error!("received error from lsp for response {x:?}");
}
-
}
else if let Some((s, took)) = map.remove(&x.id.i32()) {
log::info!("request {} took {:?}", x.id, took.elapsed());
@@ -515,6 +670,16 @@ pub fn run(
error!("request {x:?} was dropped.")
}
}
+ Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => {
+ debug!("got diagnostics");
+ match rq.load::<PublishDiagnostics>() {
+ Ok(x) => {
+ d.insert(x.uri, x.diagnostics, &d.guard());
+ w.request_redraw();
+ },
+ e => error!("{e:?}"),
+ }
+ },
Ok(Message::Notification(x @ N { method: "$/progress", .. })) => {
let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap();
match x.clone() {
@@ -659,7 +824,7 @@ impl<T> OnceOff<T> {
}
#[derive(Debug)]
-pub struct Rq<T, R, D = (), E = oneshot::error::RecvError> {
+pub struct Rq<T, R, D = (), E = RequestError> {
pub result: Option<T>,
pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>,
}
@@ -727,3 +892,14 @@ impl RedrawAfter for Arc<Window> {
})
}
}
+
+pub trait PathURI {
+ fn tid(&self) -> TextDocumentIdentifier;
+}
+impl PathURI for Path {
+ fn tid(&self) -> TextDocumentIdentifier {
+ TextDocumentIdentifier {
+ uri: Url::from_file_path(self).expect("ok"),
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 759ebc8..dab2dc4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
// this looks pretty good though
#![feature(tuple_trait, unboxed_closures, fn_traits)]
#![feature(
+ str_as_str,
lazy_type_alias,
const_convert,
const_result_trait_fn,
@@ -11,7 +12,8 @@
new_range_api,
iter_collect_into,
mpmc_channel,
- const_cmp,super_let,
+ const_cmp,
+ super_let,
gen_blocks,
const_default,
coroutines,
@@ -40,9 +42,10 @@ use diff_match_patch_rs::PatchInput;
use diff_match_patch_rs::traits::DType;
use dsb::cell::Style;
use dsb::{Cell, F, Fonts};
+use fimg::pixels::Blend;
use fimg::{Image, OverlayAt};
-use lsp::Rq;
-use lsp_server::Connection;
+use lsp::{PathURI, Rq};
+use lsp_server::{Connection, Request as LRq};
use lsp_types::request::{HoverRequest, SignatureHelpRequest};
use lsp_types::*;
use regex::Regex;
@@ -63,7 +66,7 @@ use winit::window::Icon;
use crate::bar::Bar;
use crate::hov::Hovr;
use crate::lsp::{RedrawAfter, RqS};
-use crate::text::{Diff, TextArea, is_word};
+use crate::text::{Diff, TextArea, color, is_word};
mod bar;
pub mod com;
pub mod hov;
@@ -238,11 +241,14 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
lsp.zip(origin.as_deref())
};
}
- let mut hovering = Rq::<Hovr, Option<Hovr>, usize, anyhow::Error>::default();
+ let mut hovering =
+ Rq::<Hovr, Option<Hovr>, usize, anyhow::Error>::default();
let mut complete = CompletionState::None;
let mut sig_help = // vo, lines
RqS::<(SignatureHelp, usize, Option<usize>), SignatureHelpRequest, ()>::default();
let mut semantic_tokens = default();
+ let mut diag =
+ Rq::<String, Option<String>, (), anyhow::Error>::default();
// let mut complete = None::<(CompletionResponse, (usize, usize))>;
// let mut complete_ = None::<(
// JoinHandle<
@@ -270,16 +276,24 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
};
}
- lsp!().map(|(x, origin)| x.rq_semantic_tokens(&mut semantic_tokens, origin, None).unwrap());
+ lsp!().map(|(x, origin)| {
+ x.rq_semantic_tokens(&mut semantic_tokens, origin, None).unwrap()
+ });
let mut mtime: Option<std::time::SystemTime> = modify!();
macro_rules! save {
() => {{
- std::fs::write(
- origin.as_ref().unwrap(),
- &text.rope.to_string(),
- )
- .unwrap();
+ let t = text.rope.to_string();
+ std::fs::write(origin.as_ref().unwrap(), &t).unwrap();
bar.last_action = "saved".into();
+ lsp!().map(|(l, o)| {
+ l.notify::<lsp_notification!("textDocument/didSave")>(
+ &DidSaveTextDocumentParams {
+ text_document: o.tid(),
+ text: Some(t),
+ },
+ )
+ .unwrap();
+ });
mtime = modify!();
}};
}
@@ -287,8 +301,10 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
move |elwt| {
let window = winit_app::make_window(elwt, |x| {
x.with_title("gracilaria")
+ .with_decorations(false)
.with_name("com.bendn.gracilaria", "")
.with_window_icon(Some(Icon::from_rgba(include_bytes!("../dist/icon-32").to_vec(), 32, 32).unwrap()))
+
});
if let Some(x) = w.take() {
x.send(window.clone()).unwrap();
@@ -331,7 +347,18 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
state.consume(Action::Changed).unwrap();
window.request_redraw();
}
- if let Some(l) = lsp {
+ if let Some((l, o)) = lsp!() {
+ for rq in l.req_rx.try_iter() {
+ match rq {
+ LRq { method: "workspace/diagnostic/refresh", .. } => {
+ let x = l.pull_diag(o.into(), diag.result.clone());
+ diag.request(l.runtime.spawn(x));
+ },
+ rq =>
+ log::debug!("discarding request {rq:?}"),
+ }
+ }
+ diag.poll(|x, _|x.ok().flatten(), &l.runtime);
if let CompletionState::Complete(rq)= &mut complete {
rq.poll(|f, (c,_)| {
f.ok().flatten().map(|x| {Complete {r:x,start:c,selection:0,vo:0,}})
@@ -420,7 +447,24 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
(t_ox, 0),
x,
|(_c, _r), text, mut x| {
-
+ if let Some((lsp, p)) = lsp!() && let Some(diag) = lsp.diagnostics.get(&Url::from_file_path(p).unwrap(), &lsp.diagnostics.guard()) {
+ for diag in diag {
+ let f = |cell:&mut Cell| {
+ let sev = diag.severity.unwrap_or(DiagnosticSeverity::ERROR);
+ cell.style.bg.blend(match sev {
+ DiagnosticSeverity::ERROR => color(b"#ff66662c"),
+ DiagnosticSeverity::WARNING => color(b"#9469242c"),
+ _ => return
+ });
+ };
+ if diag.range.start == diag.range.end {
+ x.get((diag.range.start.character as _, diag.range.start.line as _)).map(f);
+ } else {
+ x.get_range((diag.range.start.character as _, diag.range.start.line as _), (diag.range.end.character as usize, diag.range.end.line as _))
+ .for_each(f)
+ }
+ }
+ }
if let State::Search(re, j, _) = &state {
re.find_iter(&text.rope.to_string())
.enumerate()
@@ -1186,7 +1230,7 @@ impl State {
}
}
-use std::ops::{Range};
+use std::ops::Range;
rust_fsm::state_machine! {
#[derive(Debug)]
@@ -1359,6 +1403,6 @@ fn frunctinator(
_parameter2: u8,
_paramter4: u16,
) -> usize {
- lower::saturating::math! { parameter1 };
+ lower::saturating::math! { parameter1 };
0
}
diff --git a/src/text.rs b/src/text.rs
index c57e824..7e2e2ec 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,9 +1,9 @@
use std::cmp::min;
use std::fmt::{Debug, Display};
-use std::ops::{Deref, Index, IndexMut, Not as _, Range, RangeBounds};
+use std::ops::{Deref, Not as _, Range, RangeBounds};
use std::path::Path;
use std::pin::pin;
-use std::sync::{Arc, LazyLock};
+use std::sync::LazyLock;
use std::vec::Vec;
use anyhow::anyhow;
@@ -27,7 +27,7 @@ macro_rules! theme {
#[rustfmt::skip]
pub const NAMES: [&str; [$($x),+].len()] = [$($x),+];
#[rustfmt::skip]
- pub const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| color(x.tail()));
+ pub const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| color(x));
pub const STYLES: [u8; NAMES.len()] = [$(
($($style, )? 0, ).0
),+];
@@ -53,14 +53,13 @@ theme! {
}
mod semantic {
- use atools::prelude::*;
use dsb::cell::Style;
macro_rules! modified {
($count:literal $($x:literal . $mod:literal $color:literal $($style:expr)?,)+ $(,)?) => {
pub const MODIFIED: [(&str, &str); $count] = [
$(($x, $mod),)+
];
- pub const MCOLORS: [[u8;3]; MODIFIED.len()] = car::map!([$($color),+], |x| color(x.tail()));
+ pub const MCOLORS: [[u8;3]; MODIFIED.len()] = car::map!([$($color),+], |x| color(x));
pub const MSTYLE: [u8; MODIFIED.len()] = [$(($($style, )? 0, ).0 ,)+];
};
}
@@ -148,11 +147,10 @@ const fn of(x: &'static str) -> usize {
}
pub const fn color_(x: &str) -> [u8; 3] {
- let Some([_, x @ ..]): Option<[u8; 7]> = x.as_bytes().try_into().ok()
- else {
+ let Some(x): Option<[u8; 7]> = x.as_bytes().try_into().ok() else {
panic!()
};
- color(x)
+ color(&x)
}
pub const fn set_a([a, b, c]: [u8; 3], to: f32) -> [u8; 3] {
[
@@ -161,12 +159,16 @@ pub const fn set_a([a, b, c]: [u8; 3], to: f32) -> [u8; 3] {
(((c as f32 / 255.0) * to) * 255.0) as u8,
]
}
-pub const fn color(x: [u8; 6]) -> [u8; 3] {
- car::map!(
- car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(),
- |[a, b]| a * 16 + b
- )
+pub const fn color<const N: usize>(x: &[u8; N]) -> [u8; (N - 1) / 2]
+where
+ [(); N - 1]:,
+ [(); (N - 1) % 2 + usize::MAX]:,
+{
+ let x = x.tail();
+ let parse = car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>();
+ car::map!(parse, |[a, b]| a * 16 + b)
}
+
#[derive(Clone, Debug)]
pub struct Diff {
pub changes: (Patches<u8>, Patches<u8>),
@@ -362,7 +364,7 @@ impl TextArea {
self.xy(c).0
}
pub fn y(&self, c: usize) -> usize {
- self.xy(c).1
+ self.rope.char_to_line(c)
}
pub fn xy(&self, c: usize) -> (usize, usize) {
@@ -618,6 +620,7 @@ impl TextArea {
self.setc();
self.set_ho();
}
+ #[lower::apply(saturating)]
pub fn backspace(&mut self) {
if let Some(tabstops) = &mut self.tabstops
&& let Some((_, StopP::Range(find))) =
@@ -770,12 +773,22 @@ impl TextArea {
.zip(0..)
{
if e != '\n' {
- cells[(x + self.ho, y)].letter = Some(e);
- cells[(x + self.ho, y)].style.color = crate::FG;
- cells[(x + self.ho, y)].style.bg = crate::BG;
+ cells.get((x + self.ho, y)).unwrap().letter = Some(e);
+ cells.get((x + self.ho, y)).unwrap().style.color =
+ crate::FG;
+ cells.get((x + self.ho, y)).unwrap().style.bg =
+ crate::BG;
}
}
}
+ cells
+ .get_range(
+ (self.ho, self.y(self.cursor)),
+ (self.ho + c, self.y(self.cursor)),
+ )
+ .for_each(|x| {
+ x.style.bg = color(b"#1a1f29");
+ });
// let tokens = None::<(
// arc_swap::Guard<Arc<Box<[SemanticToken]>>>,
@@ -1208,7 +1221,9 @@ pub fn hl(
// );
#[coroutine]
static move || {
- let syntax = Syntax::new(text.slice(..), lang, &LOADER).unwrap();
+ let Ok(syntax) = Syntax::new(text.slice(..), lang, &LOADER) else {
+ return;
+ };
let mut h = syntax.highlighter(text.slice(..), &LOADER, r);
let mut at = 0;
@@ -1418,6 +1433,16 @@ impl<'a> Output<'a> {
// self.output.from_point(b),
// )
}
+ // impl<'a> IndexMut<(usize, usize)> for Output<'a> {
+ // fn index_mut(&mut self, p: (usize, usize)) -> &mut Self::Output {
+ // let x = self.from_point_global(self.translate(p).unwrap());
+ // &mut self.into[x]
+ // }
+ // }
+ pub fn get(&mut self, p: (usize, usize)) -> Option<&mut Cell> {
+ let n = self.from_point_global(self.translate(p)?);
+ self.into.get_mut(n)
+ }
}
// impl<'a> Index<usize> for Output<'a> {
@@ -1435,19 +1460,19 @@ impl<'a> Output<'a> {
// }
// }
-impl<'a> Index<(usize, usize)> for Output<'a> {
- type Output = Cell;
+// impl<'a> Index<(usize, usize)> for Output<'a> {
+// type Output = Cell;
- fn index(&self, p: (usize, usize)) -> &Self::Output {
- &self.into[self.from_point_global(self.translate(p).unwrap())]
- }
-}
-impl<'a> IndexMut<(usize, usize)> for Output<'a> {
- fn index_mut(&mut self, p: (usize, usize)) -> &mut Self::Output {
- let x = self.from_point_global(self.translate(p).unwrap());
- &mut self.into[x]
- }
-}
+// fn index(&self, p: (usize, usize)) -> &Self::Output {
+// &self.into[self.from_point_global(self.translate(p).unwrap())]
+// }
+// }
+// impl<'a> IndexMut<(usize, usize)> for Output<'a> {
+// fn index_mut(&mut self, p: (usize, usize)) -> &mut Self::Output {
+// let x = self.from_point_global(self.translate(p).unwrap());
+// &mut self.into[x]
+// }
+// }
pub trait CoerceOption<T> {
fn coerce(self) -> impl Iterator<Item = T>;