A simple CPU rendered GUI IDE experience.
somewhat diagnostic
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/com.rs | 2 | ||||
| -rw-r--r-- | src/hov.rs | 2 | ||||
| -rw-r--r-- | src/lsp.rs | 248 | ||||
| -rw-r--r-- | src/main.rs | 74 | ||||
| -rw-r--r-- | src/text.rs | 85 |
6 files changed, 330 insertions, 84 deletions
@@ -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 @@ -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 }; @@ -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; @@ -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>; |