A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml21
-rw-r--r--ra-support560
-rw-r--r--src/bar.rs29
-rw-r--r--src/lsp.rs501
-rw-r--r--src/main.rs119
-rw-r--r--src/text.rs285
6 files changed, 1437 insertions, 78 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2ed4b48..23fc1dd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,25 @@ regex = "1.11.3"
tree-house = { version = "0.3.0", features = ["fixtures"] }
helix-loader = { path = "../helix/helix-loader/" }
helix-core = { path = "../helix/helix-core/" }
+serde_json = "1.0.145"
+serde = "1.0.228"
+serde_derive = "1.0.228"
+log = "0.4.28"
+crossbeam = { version = "0.8.4", features = ["nightly", "crossbeam-channel"] }
+lsp-server = { version = "0.7.9", path = "../rust-analyzer/lib/lsp-server" }
+test-log = "0.2.18"
+lsp-types = { path = "../helix/helix-lsp-types", package = "helix-lsp-types" }
+env_logger = "0.11.8"
+oneshot = { version = "0.1.11", default-features = false, features = [
+ "async",
+ "std",
+] }
+url = "2.5.7"
+anyhow = "1.0.100"
+arc-swap = "1.7.1"
+tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
+regex-cursor = "0.1.5"
+papaya = "0.2.3"
[build-dependencies]
cc = "*"
@@ -37,4 +56,4 @@ cc = "*"
[profile.release]
debug = 2
# overflow-checks = true
-debug-assertions = true
+# debug-assertions = true
diff --git a/ra-support b/ra-support
new file mode 100644
index 0000000..db00ec7
--- /dev/null
+++ b/ra-support
@@ -0,0 +1,560 @@
+InitializeResult {
+ capabilities: ServerCapabilities {
+ position_encoding: Some(
+ PositionEncodingKind(
+ "utf-8",
+ ),
+ ),
+ text_document_sync: Some(
+ Options(
+ TextDocumentSyncOptions {
+ open_close: Some(
+ true,
+ ),
+ change: Some(
+ Incremental,
+ ),
+ will_save: None,
+ will_save_wait_until: None,
+ save: Some(
+ SaveOptions(
+ SaveOptions {
+ include_text: None,
+ },
+ ),
+ ),
+ },
+ ),
+ ),
+ selection_range_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ hover_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ completion_provider: Some(
+ CompletionOptions {
+ resolve_provider: Some(
+ false,
+ ),
+ trigger_characters: Some(
+ [
+ ":",
+ ".",
+ "'",
+ "(",
+ ],
+ ),
+ all_commit_characters: None,
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ completion_item: Some(
+ CompletionOptionsCompletionItem {
+ label_details_support: Some(
+ false,
+ ),
+ },
+ ),
+ },
+ ),
+ signature_help_provider: Some(
+ SignatureHelpOptions {
+ trigger_characters: Some(
+ [
+ "(",
+ ",",
+ "<",
+ ],
+ ),
+ retrigger_characters: None,
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ },
+ ),
+ definition_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ type_definition_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ implementation_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ references_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ document_highlight_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ document_symbol_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ workspace_symbol_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ code_action_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ code_lens_provider: Some(
+ CodeLensOptions {
+ resolve_provider: Some(
+ true,
+ ),
+ },
+ ),
+ document_formatting_provider: Some(
+ Left(
+ true,
+ ),
+ ),
+ document_range_formatting_provider: Some(
+ Left(
+ false,
+ ),
+ ),
+ document_on_type_formatting_provider: Some(
+ DocumentOnTypeFormattingOptions {
+ first_trigger_character: ".",
+ more_trigger_character: Some(
+ [
+ "=",
+ "<",
+ ">",
+ "{",
+ "(",
+ "|",
+ "+",
+ ],
+ ),
+ },
+ ),
+ rename_provider: Some(
+ Right(
+ RenameOptions {
+ prepare_provider: Some(
+ true,
+ ),
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ },
+ ),
+ ),
+ document_link_provider: None,
+ color_provider: None,
+ folding_range_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ declaration_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ execute_command_provider: None,
+ workspace: Some(
+ WorkspaceServerCapabilities {
+ workspace_folders: Some(
+ WorkspaceFoldersServerCapabilities {
+ supported: Some(
+ true,
+ ),
+ change_notifications: Some(
+ Left(
+ true,
+ ),
+ ),
+ },
+ ),
+ file_operations: Some(
+ WorkspaceFileOperationsServerCapabilities {
+ did_create: None,
+ will_create: None,
+ did_rename: None,
+ will_rename: Some(
+ FileOperationRegistrationOptions {
+ filters: [
+ FileOperationFilter {
+ scheme: Some(
+ "file",
+ ),
+ pattern: FileOperationPattern {
+ glob: "**/*.rs",
+ matches: Some(
+ File,
+ ),
+ options: None,
+ },
+ },
+ FileOperationFilter {
+ scheme: Some(
+ "file",
+ ),
+ pattern: FileOperationPattern {
+ glob: "**",
+ matches: Some(
+ Folder,
+ ),
+ options: None,
+ },
+ },
+ ],
+ },
+ ),
+ did_delete: None,
+ will_delete: None,
+ },
+ ),
+ },
+ ),
+ call_hierarchy_provider: Some(
+ Simple(
+ true,
+ ),
+ ),
+ semantic_tokens_provider: Some(
+ SemanticTokensOptions(
+ SemanticTokensOptions {
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ legend: SemanticTokensLegend {
+ token_types: [
+ SemanticTokenType(
+ "comment",
+ ),
+ SemanticTokenType(
+ "decorator",
+ ),
+ SemanticTokenType(
+ "enumMember",
+ ),
+ SemanticTokenType(
+ "enum",
+ ),
+ SemanticTokenType(
+ "function",
+ ),
+ SemanticTokenType(
+ "interface",
+ ),
+ SemanticTokenType(
+ "keyword",
+ ),
+ SemanticTokenType(
+ "macro",
+ ),
+ SemanticTokenType(
+ "method",
+ ),
+ SemanticTokenType(
+ "namespace",
+ ),
+ SemanticTokenType(
+ "number",
+ ),
+ SemanticTokenType(
+ "operator",
+ ),
+ SemanticTokenType(
+ "parameter",
+ ),
+ SemanticTokenType(
+ "property",
+ ),
+ SemanticTokenType(
+ "string",
+ ),
+ SemanticTokenType(
+ "struct",
+ ),
+ SemanticTokenType(
+ "typeParameter",
+ ),
+ SemanticTokenType(
+ "variable",
+ ),
+ SemanticTokenType(
+ "type",
+ ),
+ SemanticTokenType(
+ "angle",
+ ),
+ SemanticTokenType(
+ "arithmetic",
+ ),
+ SemanticTokenType(
+ "attributeBracket",
+ ),
+ SemanticTokenType(
+ "attribute",
+ ),
+ SemanticTokenType(
+ "bitwise",
+ ),
+ SemanticTokenType(
+ "boolean",
+ ),
+ SemanticTokenType(
+ "brace",
+ ),
+ SemanticTokenType(
+ "bracket",
+ ),
+ SemanticTokenType(
+ "builtinAttribute",
+ ),
+ SemanticTokenType(
+ "builtinType",
+ ),
+ SemanticTokenType(
+ "character",
+ ),
+ SemanticTokenType(
+ "colon",
+ ),
+ SemanticTokenType(
+ "comma",
+ ),
+ SemanticTokenType(
+ "comparison",
+ ),
+ SemanticTokenType(
+ "constParameter",
+ ),
+ SemanticTokenType(
+ "const",
+ ),
+ SemanticTokenType(
+ "deriveHelper",
+ ),
+ SemanticTokenType(
+ "derive",
+ ),
+ SemanticTokenType(
+ "dot",
+ ),
+ SemanticTokenType(
+ "escapeSequence",
+ ),
+ SemanticTokenType(
+ "formatSpecifier",
+ ),
+ SemanticTokenType(
+ "generic",
+ ),
+ SemanticTokenType(
+ "invalidEscapeSequence",
+ ),
+ SemanticTokenType(
+ "label",
+ ),
+ SemanticTokenType(
+ "lifetime",
+ ),
+ SemanticTokenType(
+ "logical",
+ ),
+ SemanticTokenType(
+ "macroBang",
+ ),
+ SemanticTokenType(
+ "parenthesis",
+ ),
+ SemanticTokenType(
+ "procMacro",
+ ),
+ SemanticTokenType(
+ "punctuation",
+ ),
+ SemanticTokenType(
+ "selfKeyword",
+ ),
+ SemanticTokenType(
+ "selfTypeKeyword",
+ ),
+ SemanticTokenType(
+ "semicolon",
+ ),
+ SemanticTokenType(
+ "static",
+ ),
+ SemanticTokenType(
+ "toolModule",
+ ),
+ SemanticTokenType(
+ "typeAlias",
+ ),
+ SemanticTokenType(
+ "union",
+ ),
+ SemanticTokenType(
+ "unresolvedReference",
+ ),
+ ],
+ token_modifiers: [
+ SemanticTokenModifier(
+ "async",
+ ),
+ SemanticTokenModifier(
+ "documentation",
+ ),
+ SemanticTokenModifier(
+ "declaration",
+ ),
+ SemanticTokenModifier(
+ "static",
+ ),
+ SemanticTokenModifier(
+ "defaultLibrary",
+ ),
+ SemanticTokenModifier(
+ "associated",
+ ),
+ SemanticTokenModifier(
+ "attribute",
+ ),
+ SemanticTokenModifier(
+ "callable",
+ ),
+ SemanticTokenModifier(
+ "constant",
+ ),
+ SemanticTokenModifier(
+ "consuming",
+ ),
+ SemanticTokenModifier(
+ "controlFlow",
+ ),
+ SemanticTokenModifier(
+ "crateRoot",
+ ),
+ SemanticTokenModifier(
+ "injected",
+ ),
+ SemanticTokenModifier(
+ "intraDocLink",
+ ),
+ SemanticTokenModifier(
+ "library",
+ ),
+ SemanticTokenModifier(
+ "macro",
+ ),
+ SemanticTokenModifier(
+ "mutable",
+ ),
+ SemanticTokenModifier(
+ "procMacro",
+ ),
+ SemanticTokenModifier(
+ "public",
+ ),
+ SemanticTokenModifier(
+ "reference",
+ ),
+ SemanticTokenModifier(
+ "trait",
+ ),
+ SemanticTokenModifier(
+ "unsafe",
+ ),
+ ],
+ },
+ range: Some(
+ true,
+ ),
+ full: Some(
+ Delta {
+ delta: Some(
+ true,
+ ),
+ },
+ ),
+ },
+ ),
+ ),
+ moniker_provider: None,
+ linked_editing_range_provider: None,
+ inline_value_provider: None,
+ inlay_hint_provider: Some(
+ Right(
+ Options(
+ InlayHintOptions {
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ resolve_provider: Some(
+ false,
+ ),
+ },
+ ),
+ ),
+ ),
+ diagnostic_provider: Some(
+ Options(
+ DiagnosticOptions {
+ identifier: Some(
+ "rust-analyzer",
+ ),
+ inter_file_dependencies: true,
+ workspace_diagnostics: false,
+ work_done_progress_options: WorkDoneProgressOptions {
+ work_done_progress: None,
+ },
+ },
+ ),
+ ),
+ experimental: Some(
+ Object {
+ "childModules": Bool(true),
+ "externalDocs": Bool(true),
+ "hoverRange": Bool(true),
+ "joinLines": Bool(true),
+ "matchingBrace": Bool(true),
+ "moveItem": Bool(true),
+ "onEnter": Bool(true),
+ "openCargoToml": Bool(true),
+ "parentModule": Bool(true),
+ "runnables": Object {
+ "kinds": Array [
+ String("cargo"),
+ ],
+ },
+ "ssr": Bool(true),
+ "workspaceSymbolScopeKindFiltering": Bool(true),
+ },
+ ),
+ },
+ server_info: Some(
+ ServerInfo {
+ name: "rust-analyzer",
+ version: Some(
+ "1.92.0-nightly (f6aa851 2025-10-07)",
+ ),
+ },
+ ),
+} \ No newline at end of file
diff --git a/src/bar.rs b/src/bar.rs
index e453651..80a66c7 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -2,8 +2,10 @@ use std::iter::{once, repeat};
use dsb::Cell;
use dsb::cell::Style;
+use lsp_types::WorkDoneProgress;
use winit::keyboard::{Key, ModifiersState, NamedKey};
+use crate::lsp::Client;
use crate::text::TextArea;
pub struct Bar {
@@ -20,6 +22,7 @@ impl Bar {
fname: &str,
state: &super::State,
t: &TextArea,
+ lsp: Option<&Client>,
) {
let row = &mut into[oy * w..oy * w + w];
row.fill(Cell {
@@ -51,10 +54,28 @@ impl Bar {
.iter_mut()
.zip(fname.chars())
.for_each(|(x, y)| x.letter = Some(y));
- row.iter_mut()
- .rev()
- .zip(self.last_action.chars().rev())
- .for_each(|(x, y)| x.letter = Some(y));
+ // lsp.map(|x| dbg!(x.progress));
+ if let Some(x) = lsp
+ && let Some(m) = x
+ .progress
+ .iter(&x.progress.guard())
+ .find_map(|x| match x.1 {
+ Some(WorkDoneProgress::Report(x)) =>
+ x.message.clone(),
+ _ => None,
+ })
+ {
+ dbg!(&m);
+ row.iter_mut()
+ .rev()
+ .zip(m.chars().rev())
+ .for_each(|(x, y)| x.letter = Some(y));
+ } else {
+ row.iter_mut()
+ .rev()
+ .zip(self.last_action.chars().rev())
+ .for_each(|(x, y)| x.letter = Some(y));
+ }
}
State::Procure(x, r) => {
r.prompt()
diff --git a/src/lsp.rs b/src/lsp.rs
new file mode 100644
index 0000000..3db71e7
--- /dev/null
+++ b/src/lsp.rs
@@ -0,0 +1,501 @@
+use std::collections::HashMap;
+use std::io::BufReader;
+use std::path::Path;
+use std::process::{Command, Stdio};
+use std::sync::atomic::AtomicI32;
+use std::sync::atomic::Ordering::Relaxed;
+use std::thread::{JoinHandle, sleep, spawn};
+use std::time::{Duration, Instant};
+
+use Default::default;
+use anyhow::Error;
+use arc_swap::ArcSwap;
+use crossbeam::channel::{
+ Receiver, RecvError, SendError, Sender, unbounded,
+};
+use log::{debug, error};
+use lsp_server::{
+ Message, Notification as N, Request as Rq, Response as Re,
+};
+use lsp_types::notification::{
+ Cancel, DidOpenTextDocument, Notification, Progress, SetTrace,
+};
+use lsp_types::request::{
+ Initialize, Request, SemanticTokensFullRequest, WorkDoneProgressCreate,
+};
+use lsp_types::*;
+use parking_lot::Mutex;
+use serde_json::json;
+
+pub struct Client {
+ pub runtime: tokio::runtime::Runtime,
+
+ pub tx: Sender<Message>,
+ pub id: AtomicI32,
+ pub initialized: Option<InitializeResult>,
+ // pub pending: HashMap<i32, oneshot::Sender<Re>>,
+ pub send_to: Sender<(i32, oneshot::Sender<Re>)>,
+ pub progress:
+ &'static papaya::HashMap<ProgressToken, Option<WorkDoneProgress>>,
+ pub not_rx: Receiver<N>,
+ pub req_rx: Receiver<Rq>,
+ pub semantic_tokens: (
+ &'static ArcSwap<Box<[SemanticToken]>>,
+ Mutex<Option<(tokio::task::JoinHandle<Result<(), Error>>, i32)>>,
+ ),
+}
+
+impl Drop for Client {
+ fn drop(&mut self) {
+ panic!("please dont")
+ }
+}
+
+impl Client {
+ pub fn notify<X: Notification>(
+ &self,
+ y: &X::Params,
+ ) -> Result<(), SendError<Message>> {
+ self.tx.send(Message::Notification(N {
+ method: X::METHOD.into(),
+ params: serde_json::to_value(y).unwrap(),
+ }))
+ }
+
+ #[must_use]
+ pub fn request<X: Request>(
+ &self,
+ y: &X::Params,
+ ) -> Result<(oneshot::Receiver<Re>, i32), SendError<Message>> {
+ let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
+ self.tx.send(Message::Request(Rq {
+ id: id.into(),
+ method: X::METHOD.into(),
+ params: serde_json::to_value(y).unwrap(),
+ }))?;
+ let (tx, rx) = oneshot::channel();
+ if self.initialized.is_some() {
+ debug!("sent request {id}'s handler");
+ self.send_to.send((id, tx)).expect("oughtnt really fail");
+ }
+ Ok((rx, id))
+ }
+ pub fn open(
+ &self,
+ f: &Path,
+ text: String,
+ ) -> Result<(), SendError<Message>> {
+ self.notify::<DidOpenTextDocument>(&DidOpenTextDocumentParams {
+ text_document: TextDocumentItem {
+ uri: url::Url::from_file_path(f).unwrap(),
+ language_id: "rust".into(),
+ version: 0,
+ text,
+ },
+ })
+ }
+ pub fn edit(
+ &self,
+ f: &Path,
+ text: String,
+ ) -> Result<(), SendError<Message>> {
+ static V: AtomicI32 = AtomicI32::new(0);
+ self.notify::<lsp_types::notification::DidChangeTextDocument>(
+ &DidChangeTextDocumentParams {
+ text_document: VersionedTextDocumentIdentifier {
+ uri: url::Url::from_file_path(f).unwrap(),
+ version: V.fetch_add(1, Relaxed),
+ },
+ content_changes: vec![TextDocumentContentChangeEvent {
+ range: None,
+ range_length: None,
+ text,
+ }],
+ },
+ )
+ }
+ pub fn rq_semantic_tokens(&self, f: &Path) -> anyhow::Result<()> {
+ debug!("requested semantic tokens");
+ let mut p = self.semantic_tokens.1.lock();
+ if let Some((h, task)) = &*p {
+ if !h.is_finished() {
+ debug!("cancelled previous semantic tokens request");
+ self.notify::<Cancel>(&CancelParams { id: task.into() })?;
+ h.abort();
+ }
+ }
+ let (rx, id) = self.request::<SemanticTokensFullRequest>(
+ &SemanticTokensParams {
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ text_document: TextDocumentIdentifier::new(
+ url::Url::from_file_path(f).unwrap(),
+ ),
+ },
+ )?;
+ let d = self.semantic_tokens.0;
+ let x = self.runtime.spawn(async move {
+ let x = rx.await?;
+ debug!("received semantic tokens");
+
+ let y = x.load::<SemanticTokensResult>()?;
+ match y {
+ SemanticTokensResult::Partial(_) =>
+ panic!("i told the lsp i dont support this"),
+ SemanticTokensResult::Tokens(x) =>
+ d.store(x.data.into_boxed_slice().into()),
+ };
+ anyhow::Ok(())
+ });
+ *p = Some((x, id));
+
+ Ok(())
+ }
+}
+pub fn run(
+ (tx, rx, iot): (
+ Sender<Message>,
+ Receiver<Message>,
+ lsp_server::IoThreads,
+ ),
+ workspace: WorkspaceFolder,
+) -> (Client, lsp_server::IoThreads, JoinHandle<()>) {
+ let now = Instant::now();
+ let (req_tx, req_rx) = unbounded();
+ let (not_tx, not_rx) = unbounded();
+ let (_req_tx, _req_rx) = unbounded();
+ let mut c = Client {
+ tx,
+ req_rx: _req_rx,
+ progress: Box::leak(Box::new(papaya::HashMap::new())),
+ runtime: tokio::runtime::Builder::new_multi_thread()
+ .thread_name("lsp runtime")
+ .build()
+ .unwrap(),
+ id: AtomicI32::new(0),
+ initialized: None,
+ semantic_tokens: (
+ Box::leak(Box::new(ArcSwap::new(
+ vec![].into_boxed_slice().into(),
+ ))),
+ Mutex::new(None),
+ ),
+ send_to: req_tx,
+ not_rx,
+ };
+ c.request::<Initialize>(&InitializeParams {
+ process_id: Some(std::process::id()),
+
+ capabilities: ClientCapabilities {
+ window: Some(WindowClientCapabilities {
+ work_done_progress: Some(true),
+ ..default()
+ }),
+ text_document: Some(TextDocumentClientCapabilities {
+ semantic_tokens: Some(SemanticTokensClientCapabilities {
+ dynamic_registration: Some(false),
+ requests: SemanticTokensClientCapabilitiesRequests {
+ range: Some(true),
+ full: Some(
+ lsp_types::SemanticTokensFullOptions::Bool(
+ true,
+ ),
+ ),
+ },
+ token_modifiers: [
+ "associated",
+ "attribute",
+ "callable",
+ "constant",
+ "consuming",
+ "controlFlow",
+ "crateRoot",
+ "injected",
+ "intraDocLink",
+ "library",
+ "macro",
+ "mutable",
+ "procMacro",
+ "public",
+ "reference",
+ "trait",
+ "unsafe",
+ ]
+ .map(|x| x.into())
+ .to_vec(),
+ overlapping_token_support: Some(true),
+ multiline_token_support: Some(true),
+ server_cancel_support: Some(false),
+ augments_syntax_tokens: Some(false),
+ ..default()
+ }),
+ ..default()
+ }),
+ general: Some(GeneralClientCapabilities {
+ position_encodings: Some(vec![PositionEncodingKind::UTF8]),
+ ..default()
+ }),
+ ..default()
+ },
+ client_info: Some(ClientInfo {
+ name: "gracilaria".into(),
+ version: Some(env!("CARGO_PKG_VERSION").into()),
+ }),
+ initialization_options: Some(json! {{
+ "cargo": {
+ "buildScripts": {
+ "enable": true,
+ }
+ },
+ "procMacro": {
+ "enable": true,
+ },
+ "procMacro.attributes.enable": true,
+ "inlayHints.closureReturnTypeHints.enable": "with_block",
+ "inlayHints.closingBraceHints.minLines": 5,
+ "inlayHints.closureStyle": "rust_analyzer",
+ "showUnlinkedFileNotification": false,
+ "inlayHints.genericParameterHints.type.enable": true,
+ "inlayHints.rangeExclusiveHints.enable": true,
+ "inlayHints.closureCaptureHints.enable": true,
+ "inlayHints.expressionAdjustmentHints.hideOutsideUnsafe": true,
+ "inlayHints.expressionAdjustmentHints.enable": "never",
+ "inlayHints.expressionAdjustmentHints.mode": "prefer_prefix",
+ "semanticHighlighting.punctuation.separate.macro.bang": true,
+ "semanticHighlighting.punctuation.enable": true,
+ "semanticHighlighting.punctuation.specialization.enable": true,
+ }}),
+ trace: None,
+ workspace_folders: Some(vec![workspace]),
+
+ ..default()
+ })
+ .unwrap();
+ let x = serde_json::from_value::<InitializeResult>(
+ rx.recv().unwrap().response().unwrap().result.unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ x.capabilities.position_encoding,
+ Some(PositionEncodingKind::UTF8)
+ );
+ c.initialized = Some(x);
+ c.notify::<lsp_types::notification::Initialized>(
+ &InitializedParams {},
+ )
+ .unwrap();
+ c.notify::<SetTrace>(&SetTraceParams {
+ value: lsp_types::TraceValue::Verbose,
+ })
+ .unwrap();
+ let progress = c.progress;
+ log::info!("lsp took {:?} to initialize", now.elapsed());
+ let h = spawn(move || {
+ let mut map = HashMap::new();
+ loop {
+ crossbeam::select! {
+ recv(req_rx) -> x => match x {
+ Ok((x, y)) => {
+ debug!("received request {x}");
+ assert!(map.insert(x, y).is_none());
+ }
+ Err(RecvError) => return,
+ },
+ recv(rx) -> x => match x {
+ Ok(Message::Request(rq @ Rq { method: "window/workDoneProgress/create", .. })) => {
+ match rq.load::<WorkDoneProgressCreate>() {
+ Ok((_, x)) => {
+ let g = progress.guard();
+ progress.insert(x.token, None, &g);
+ },
+ Err(lsp_server::ExtractError::MethodMismatch(..)) => {},
+ e => {
+ error!("{e:?}");
+ }
+ };
+ }
+ Ok(Message::Request(x)) => {
+ if let Err(e) = _req_tx.send(x) {
+ error!("couldnt receive request {e:?}");
+ }
+ }
+ Ok(Message::Response(x)) => {
+ if let Some(s) = map.remove(&x.id.i32()) {
+ match s.send(x) {
+ Ok(()) => {}
+ Err(e) => {
+ error!(
+ "unable to respond to {:?}",
+ e.into_inner()
+ );
+ }
+ }
+ } else {
+ error!("request {x:?} was dropped.")
+ }
+ }
+ Ok(Message::Notification(x @ N { method: "$/progress", .. })) => {
+ let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap();
+ progress.update(token, move |_| Some(x.clone()), &progress.guard());
+ }
+ Ok(Message::Notification(notification)) => {
+ debug!("rx {notification:?}");
+ not_tx
+ .send(notification)
+ .expect("why library drop this??? no drop!!");
+ }
+ Err(RecvError) => return,
+ }
+ }
+ }
+ });
+ (c, iot, h)
+}
+
+pub fn x() {
+ let mut c = Command::new("/home/os/.cargo/bin/rust-analyzer")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::inherit())
+ .spawn()
+ .unwrap();
+
+ log::info!("helol");
+
+ let (c, rx, iot) = run(
+ lsp_server::stdio::stdio_transport(
+ BufReader::new(c.stdout.take().unwrap()),
+ c.stdin.take().unwrap(),
+ ),
+ WorkspaceFolder {
+ uri: "file:///home/os/gracilaria".parse().unwrap(),
+ name: "gracilaria".into(),
+ },
+ );
+ let n = c.not_rx.clone();
+ let r = c.req_rx.clone();
+ let p = c.progress;
+
+ // c.request::<SemanticTokensFullRequest>(&SemanticTokensParams {
+ // work_done_progress_params: default(),
+ // partial_result_params: default(),
+ // text_document: TextDocumentIdentifier::new(
+ // url::Url::from_file_path(Path::new(
+ // "/home/os/gracilaria/src/text.rs",
+ // ))
+ // .unwrap(),
+ // ),
+ // })
+ // .unwrap();
+ sleep(Duration::from_secs(40));
+ c.open(
+ Path::new("/home/os/gracilaria/src/user.rs"),
+ "fn main() {}".into(),
+ )
+ .unwrap();
+ c.rq_semantic_tokens(Path::new("/home/os/gracilaria/src/user.rs"))
+ .unwrap();
+
+ dbg!(rx.writer.join().unwrap()).unwrap();
+
+ spawn(|| {
+ for elem in r {
+ match &*elem.method {
+ x if x == WorkDoneProgressCreate::METHOD => {
+ elem.load::<WorkDoneProgressCreate>().unwrap();
+ }
+ _ => {}
+ }
+ }
+ });
+ loop {}
+ drop(c);
+
+ // let wait = c
+ // .request::<SemanticTokensFullRequest>(&SemanticTokensParams {
+ // work_done_progress_params: default(),
+ // partial_result_params: default(),
+ // text_document: TextDocumentIdentifier {
+ // uri: "file:///home/os/gracilaria/src/main.rs"
+ // .parse()
+ // .unwrap(),
+ // },
+ // })
+ // .unwrap();
+ // spawn(|| {
+ // let x = wait.recv_eepy().unwrap();
+ // println!(
+ // "found! {:#?}",
+ // x.extract::<SemanticTokensResult>().unwrap()
+ // );
+ // });
+}
+
+trait RecvEepy<T>: Sized {
+ fn recv_eepy(self) -> Result<T, RecvError> {
+ self.recv_sleepy(100)
+ }
+ fn recv_sleepy(self, x: u64) -> Result<T, RecvError>;
+}
+
+impl<T> RecvEepy<T> for oneshot::Receiver<T> {
+ fn recv_sleepy(self, x: u64) -> Result<T, RecvError> {
+ loop {
+ return match self.recv_timeout(Duration::from_millis(x)) {
+ Err(oneshot::RecvTimeoutError::Timeout) => continue,
+ Ok(x) => Ok(x),
+ Err(oneshot::RecvTimeoutError::Disconnected) =>
+ Err(crossbeam::channel::RecvError),
+ };
+ }
+ }
+}
+
+trait Void<T> {
+ fn void(self) -> Result<T, ()>;
+}
+impl<T, E> Void<T> for Result<T, E> {
+ fn void(self) -> Result<T, ()> {
+ self.map_err(|_| ())
+ }
+}
+
+#[test]
+fn x22() {
+ let (tx, rx) = std::sync::mpmc::channel::<u8>();
+
+ let rx2 = rx.clone();
+ spawn(move || {
+ loop {
+ println!("t1 {}", rx.recv().unwrap());
+ }
+ });
+ spawn(move || {
+ loop {
+ println!("t2 {}", rx2.recv().unwrap());
+ }
+ });
+ spawn(move || {
+ for n in 0..20 {
+ tx.send(n).unwrap();
+ }
+ });
+ loop {}
+}
+
+#[test]
+fn x33() {
+ let y = serde_json::to_string(&SemanticTokensParams {
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ text_document: TextDocumentIdentifier::new(
+ url::Url::from_file_path(Path::new(
+ "/home/os/gracilaria/src/text.rs",
+ ))
+ .unwrap(),
+ ),
+ })
+ .unwrap();
+ println!("{y}");
+ let y = serde_json::from_str::<SemanticTokensParams>(&y).unwrap();
+}
diff --git a/src/main.rs b/src/main.rs
index 7fa9356..349dd30 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(
+ mpmc_channel,
const_cmp,
const_default,
import_trait_associated_functions,
@@ -13,8 +14,10 @@
)]
#![allow(incomplete_features, redundant_semicolons)]
use std::convert::identity;
+use std::io::BufReader;
use std::num::NonZeroU32;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Stdio};
use std::sync::LazyLock;
use std::time::Instant;
@@ -24,10 +27,16 @@ use diff_match_patch_rs::PatchInput;
use dsb::cell::Style;
use dsb::{Cell, F};
use fimg::Image;
+use lsp_types::{
+ SemanticTokensOptions, SemanticTokensServerCapabilities,
+ ServerCapabilities, TextDocumentIdentifier,
+ TextDocumentPositionParams, WorkspaceFolder,
+};
use regex::Regex;
use ropey::Rope;
use rust_fsm::StateMachineImpl;
use swash::{FontRef, Instance};
+use url::Url;
use winit::event::{
ElementState, Event, MouseButton, MouseScrollDelta, WindowEvent,
};
@@ -37,10 +46,12 @@ use winit::keyboard::{Key, ModifiersState, NamedKey, SmolStr};
use crate::bar::Bar;
use crate::text::{Diff, TextArea};
mod bar;
+mod lsp;
mod text;
mod winit_app;
fn main() {
- // text::man();
+ env_logger::init();
+ // lsp::x();
entry(EventLoop::new().unwrap())
}
#[derive(Debug)]
@@ -108,10 +119,13 @@ impl Hist {
self.push_if_changed(x);
}
}
- pub fn record(&mut self, _: &TextArea) {
+ pub fn record(&mut self, new: &TextArea) -> bool {
// self.test_push(x);
- self.last_edit = Instant::now();
- self.changed = true;
+ if new.rope != self.last.rope {
+ self.last_edit = Instant::now();
+ self.changed = true;
+ }
+ new.rope != self.last.rope
}
}
@@ -126,8 +140,10 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
let ls = 20.0;
let mut text = TextArea::default();
- let mut origin =
- std::env::args().nth(1).and_then(|x| PathBuf::try_from(x).ok());
+ let mut origin = std::env::args()
+ .nth(1)
+ .and_then(|x| PathBuf::try_from(x).ok())
+ .and_then(|x| x.canonicalize().ok());
let mut fonts = dsb::Fonts::new(
F::FontRef(*FONT, &[(2003265652, 550.0)]),
F::instance(*FONT, *BFONT),
@@ -145,6 +161,48 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
text.insert(&std::fs::read_to_string(x).unwrap());
text.cursor = 0;
});
+ fn rooter(x: &Path) -> Option<PathBuf> {
+ for f in std::fs::read_dir(&x).unwrap().filter_map(Result::ok) {
+ if f.file_name() == "Cargo.toml" {
+ return Some(f.path().with_file_name("").to_path_buf());
+ }
+ }
+ x.parent().and_then(rooter)
+ }
+ let workspace = origin
+ .as_ref()
+ .and_then(|x| rooter(&x.parent().unwrap()))
+ .and_then(|x| x.canonicalize().ok());
+ let c = workspace.zip(origin.clone()).map(|(workspace, origin)| {
+ let mut c = Command::new("rust-analyzer")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::inherit())
+ .spawn()
+ .unwrap();
+
+ let (c, t, t2) = lsp::run(
+ lsp_server::stdio::stdio_transport(
+ BufReader::new(c.stdout.take().unwrap()),
+ c.stdin.take().unwrap(),
+ ),
+ WorkspaceFolder {
+ uri: Url::from_file_path(&workspace).unwrap(),
+ name: workspace
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .into_owned(),
+ },
+ );
+ c.open(&origin, std::fs::read_to_string(&origin).unwrap())
+ .unwrap();
+ ((c, origin), (t, t2))
+ });
+ let (lsp, t) = c.unzip();
+
+ // let mut hl_result = None;
+
let mut hist = Hist {
history: vec![],
redo_history: vec![],
@@ -159,6 +217,19 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
.map(|x| x.metadata().unwrap().modified().unwrap())
};
}
+ macro_rules! change {
+ () => {
+ lsp.as_ref()
+ .map(|(x, origin)| {
+ x.edit(&origin, text.rope.to_string()).unwrap();
+ x.rq_semantic_tokens(origin).unwrap();
+ })
+ .unwrap();
+ };
+ }
+ lsp.as_ref()
+ .map(|(x, origin)| x.rq_semantic_tokens(origin).unwrap())
+ .unwrap();
let mut mtime = modify!();
macro_rules! save {
() => {{
@@ -301,6 +372,16 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
}
},
origin.as_deref(),
+ lsp.as_ref().and_then(|(x, _)| { match &x.initialized {
+ Some(lsp_types::InitializeResult {
+ capabilities: ServerCapabilities {
+ semantic_tokens_provider:
+ Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{
+ legend,..
+ })),..
+ },..
+ }) => Some(legend), _ => None, }}.map(|leg|(x.semantic_tokens.0.load(), leg))
+ ),
);
bar.write_to(
@@ -314,6 +395,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
.unwrap_or("new buffer"),
&state,
&text,
+ lsp.as_ref().map(|x| &x.0)
);
println!("cell=");
@@ -376,16 +458,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
)
.as_chunks_unchecked_mut::<4>()
};
- fimg::overlay::copy_rgb_bgr_(
- i.flatten(),
- 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();
}
@@ -438,7 +511,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
if button == MouseButton::Left {
unsafe { CLICKING = true };
}
- match state.consume(Action::M(button)).unwrap() {
+ match dbg!(state.consume(Action::M(button)).unwrap() ){
Some(Do::MoveCursor) => {
text.cursor = text.index_at(cursor_position);
text.setc();
@@ -460,6 +533,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
None => {}
_ => unreachable!(),
}
+ window.request_redraw();
}
Event::WindowEvent {
event:
@@ -486,6 +560,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
let rows = rows.floor() as usize;
text.vo = text.vo.saturating_sub(rows);
}
+ window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::ModifiersChanged(modifiers),
@@ -542,17 +617,21 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
hist.test_push(&text);
handle2(event.logical_key, &mut text);
text.scroll_to_cursor();
- hist.record(&text);
+ if hist.record(&text) {
+ change!();
+ }
}
Some(Do::Undo) => {
hist.test_push(&text);
hist.undo(&mut text);
bar.last_action = "undid".to_string();
+ change!();
}
Some(Do::Redo) => {
hist.test_push(&text);
hist.redo(&mut text);
bar.last_action = "redid".to_string();
+ change!();
}
Some(Do::Quit) => elwt.exit(),
Some(Do::StartSelection) => {
@@ -604,7 +683,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
hist.push_if_changed(&text);
}
Some(Do::OpenFile(x)) => {
- origin = Some(PathBuf::try_from(&x).unwrap());
+ origin = Some(PathBuf::from(&x));
text = TextArea::default();
text.insert(
&std::fs::read_to_string(x).unwrap(),
diff --git a/src/text.rs b/src/text.rs
index 08c1cf2..1a66d11 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,8 +1,8 @@
use std::cmp::min;
use std::fmt::{Debug, Display};
-use std::ops::{Not as _, Range};
+use std::ops::{Deref, Not as _, Range};
use std::path::Path;
-use std::sync::LazyLock;
+use std::sync::{Arc, LazyLock};
use atools::prelude::*;
use diff_match_patch_rs::{DiffMatchPatch, Patches};
@@ -11,33 +11,125 @@ use dsb::cell::Style;
use helix_core::Syntax;
use helix_core::syntax::{HighlightEvent, Loader};
use implicit_fn::implicit_fn;
+use log::error;
+use lsp_types::{
+ SemanticToken, SemanticTokensLegend, SemanticTokensServerCapabilities,
+};
use ropey::{Rope, RopeSlice};
+use tree_house::fixtures;
use winit::keyboard::{NamedKey, SmolStr};
+
+use crate::MODIFIERS;
+use crate::text::semantic::{MCOLORS, MODIFIED, MSTYLE};
macro_rules! theme {
- ($($x:literal $color:literal),+ $(,)?) => {
+ ($n:literal $($x:literal $color:literal $($style:expr)?),+ $(,)?) => {
#[rustfmt::skip]
- const NAMES: [&str; 15] = [$($x),+];
+ pub const NAMES: [&str; $n] = [$($x),+];
#[rustfmt::skip]
- 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.tail()));
+ pub const STYLES: [u8; NAMES.len()] = [$(
+ ($($style, )? 0, ).0
+ ),+];
+ };
}
-theme! {
+theme! { 15
"attribute" b"#ffd173",
- "comment" b"#5c6773",
+ "comment" b"#5c6773" Style::ITALIC,
"constant" b"#DFBFFF",
- "function" b"#FFD173",
+ "function" b"#FFD173" Style::ITALIC,
"variable.builtin" b"#FFAD66",
- "keyword" b"#FFAD66",
+ "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
"number" b"#dfbfff",
"operator" b"#F29E74",
"punctuation" b"#cccac2",
"string" b"#D5FF80",
- "tag" b"#DFBFFF",
- "type" b"#73D0FF",
+ "tag" b"#5CCFE6" Style::ITALIC | Style::BOLD,
+ "type" b"#73D0FF" Style::ITALIC | Style::BOLD,
"variable" b"#cccac2",
"variable.parameter" b"#DFBFFF",
"namespace" b"#73d0ff",
}
+
+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 MSTYLE: [u8; MODIFIED.len()] = [$(($($style, )? 0, ).0 ,)+];
+ };
+ }
+ use super::color;
+ theme! { 19
+ "comment" b"#5c6773" Style::ITALIC,
+ // "decorator" b"#cccac2",
+ // "enumMember" b"#cccac2",
+ "function" b"#FFD173" Style::ITALIC,
+ "interface" b"#5CCFE6",
+ "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+ "macro" b"#fbc351" Style::BOLD,
+ "method" b"#FFD173" Style::ITALIC,
+ // "namespace" b"#cccac2",
+ "number" b"#dfbfff",
+ "operator" b"#F29E74",
+ // "property" b"#cccac2",
+ "string" b"#D5FF80",
+ // "struct" b"#cccac2",
+ // "typeParameter" b"#cccac2",
+ "enum" b"#73b9ff" Style::ITALIC | Style::BOLD,
+ "builtinType" b"#73d0ff" Style::ITALIC,
+ // "type" b"#73d0ff" Style::ITALIC | Style::BOLD,
+ "typeAlias" b"#5ce6d8" Style::ITALIC | Style::BOLD,
+ "struct" b"#73d0ff" Style::ITALIC | Style::BOLD,
+ // "variable" b"#cccac2",
+ // "angle" b"#cccac2",
+ // "arithmetic" b"#cccac2",
+ // "attributeBracket" b"#cccac2",
+ "parameter" b"#DFBFFF",
+ "namespace" b"#73d0ff",
+ // "attributeBracket" b"#cccac2",
+ // "attribute" b"#cccac2",
+ // "bitwise" b"#cccac2",
+ // "boolean" b"#cccac2",
+ // "brace" b"#cccac2",
+ // "bracket" b"#cccac2",
+ // "builtinAttribute" b"#cccac2",
+ // "character" b"#cccac2",
+ // "colon" b"#cccac2",
+ // "comma" b"#cccac2",
+ // "comparison" b"#cccac2",
+ // "constParameter" b"#cccac2",
+ "const" b"#DFBFFF",
+ // "deriveHelper" b"#cccac2",
+ // "derive" b"#cccac2",
+ // "dot" b"#cccac2",
+ // "escapeSequence" b"#cccac2",
+ // "formatSpecifier" b"#cccac2",
+ // "generic" b"#cccac2",
+ // "invalidEscapeSequence" b"#cccac2",
+ // "label" b"#cccac2",
+ // "lifetime" b"#cccac2",
+ // "logical" b"#cccac2",
+ "macroBang" b"#f28f74",
+ // "parenthesis" b"#cccac2",
+ // "procMacro" b"#cccac2",
+ // "punctuation" b"#cccac2",
+ "selfKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+ "selfTypeKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+ // "semicolon" b"#cccac2",
+ // "static" b"#cccac2",
+ // "toolModule" b"#cccac2",
+ // "union" b"#cccac2",
+ // "unresolvedReference" b"#cccac2",
+ }
+ modified! { 2
+ "function" . "library" b"#F28779",
+ "variable" . "mutable" b"#e6dab6",
+ }
+}
const fn of(x: &'static str) -> usize {
let mut i = 0;
while i < NAMES.len() {
@@ -49,12 +141,6 @@ const fn of(x: &'static str) -> usize {
panic!()
}
-const STYLES: [u8; NAMES.len()] = amap::amap_d! {
- const { of("type") } | const { of("keyword") } | const { of("tag") } => Style::ITALIC | Style::BOLD,
- const { of("comment") } | const { of("function") }=> Style::ITALIC,
-
-};
-
const fn color(x: &[u8; 6]) -> [u8; 3] {
car::map!(
car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(),
@@ -115,6 +201,7 @@ pub struct TextArea {
}
impl Debug for TextArea {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ String::new();
f.debug_struct("TextArea")
.field("rope", &self.rope)
.field("cursor", &self.cursor)
@@ -168,7 +255,8 @@ impl TextArea {
.unwrap_or_default())
.min(x.saturating_sub(self.line_number_offset() + 1))
})
- .unwrap_or(self.rope.len_chars())
+ .unwrap_or(usize::MAX)
+ .min(self.rope.len_chars())
}
pub fn insert_(&mut self, c: SmolStr) {
@@ -413,36 +501,7 @@ impl TextArea {
}
}
- pub fn slice<'c>(
- &self,
- (c, r): (usize, usize),
- cell: &'c mut [Cell],
- range: Range<usize>,
- ) -> &'c mut [Cell] {
- let [(x1, y1), (x2, y2)] = dbg!(self.position(range));
- &mut cell[y1 * c + x1..y2 * c + x2]
- }
-
- #[implicit_fn]
- pub fn write_to(
- &mut self,
- color: [u8; 3],
- bg: [u8; 3],
- (into, (w, _)): (&mut [Cell], (usize, usize)),
- (ox, oy): (usize, usize),
- selection: Option<Range<usize>>,
- apply: impl FnOnce((usize, usize), &mut Self, &mut [Cell]),
- path: Option<&Path>,
- ) {
- let (c, r) = (self.c, self.r);
- let mut cells = vec![
- Cell {
- style: Style { color, bg, flags: 0 },
- letter: None,
- };
- (self.l().max(r) + r - 1) * c
- ];
-
+ pub fn tree_sit<'c>(&self, path: Option<&Path>, cell: &'c mut [Cell]) {
let language = path
.and_then(|x| LOADER.language_for_filename(x))
.unwrap_or_else(|| LOADER.language_for_name("rust").unwrap());
@@ -471,8 +530,9 @@ impl TextArea {
s as u32..e as u32,
);
let mut at = 0;
- let mut highlight_stack = Vec::with_capacity(8);
+ let (c, r) = (self.c, self.r);
+ let mut highlight_stack = Vec::with_capacity(8);
loop {
let (e, new_highlights) = h.advance();
if e == HighlightEvent::Refresh {
@@ -498,16 +558,50 @@ impl TextArea {
c,
);
- cells.get_mut(y1 * c + x1..y2 * c + x2).map(|x| {
+ cell.get_mut(y1 * c + x1..y2 * c + x2).map(|x| {
x.iter_mut().for_each(|x| {
- x.style.flags = STYLES[h.idx()];
+ x.style.flags |= STYLES[h.idx()];
x.style.color = COLORS[h.idx()];
})
});
}
at = end;
}
- drop(h);
+ }
+
+ pub fn slice<'c>(
+ &self,
+ (c, r): (usize, usize),
+ cell: &'c mut [Cell],
+ range: Range<usize>,
+ ) -> &'c mut [Cell] {
+ let [(x1, y1), (x2, y2)] = self.position(range);
+ &mut cell[y1 * c + x1..y2 * c + x2]
+ }
+
+ #[implicit_fn]
+ pub fn write_to<'lsp>(
+ &mut self,
+ color: [u8; 3],
+ bg: [u8; 3],
+ (into, (w, _)): (&mut [Cell], (usize, usize)),
+ (ox, oy): (usize, usize),
+ selection: Option<Range<usize>>,
+ apply: impl FnOnce((usize, usize), &mut Self, &mut [Cell]),
+ path: Option<&Path>,
+ tokens: Option<(
+ arc_swap::Guard<Arc<Box<[SemanticToken]>>>,
+ &SemanticTokensLegend,
+ )>,
+ ) {
+ let (c, r) = (self.c, self.r);
+ let mut cells = vec![
+ Cell {
+ style: Style { color, bg, flags: 0 },
+ letter: None,
+ };
+ (self.l().max(r) + r - 1) * c
+ ];
for (l, y) in self.rope.lines().zip(0..) {
for (e, x) in l.chars().take(c).zip(0..) {
@@ -516,6 +610,89 @@ impl TextArea {
}
}
}
+
+ if let Some((t, leg)) = tokens
+ && t.len() > 0
+ {
+ let mut ln = 0;
+ let mut ch = 0;
+ for t in &**t {
+ ln += t.delta_line;
+ ch = match t.delta_line {
+ 1.. => t.delta_start,
+ 0 => ch + t.delta_start,
+ };
+ let x: Result<(usize, usize), ropey::Error> = try {
+ let x1 = self.rope.try_byte_to_char(
+ self.rope.try_line_to_byte(ln as _)? + ch as usize,
+ )? - self.rope.try_line_to_char(ln as _)?;
+ let x2 = self.rope.try_byte_to_char(
+ self.rope.try_line_to_byte(ln as _)?
+ + ch as usize
+ + t.length as usize,
+ )? - self.rope.try_line_to_char(ln as _)?;
+ (x1, x2)
+ };
+ let Ok((x1, x2)) = x else {
+ continue;
+ };
+
+ if ln as usize * c + x1 < self.vo * c {
+ continue;
+ } else if ln as usize * c + x1 > self.vo * c + r * c {
+ break;
+ }
+ let slice = cells
+ .get_mut(ln as usize * c + x1..ln as usize * c + x2)
+ .expect("good slice");
+ let Some(tty) = leg.token_types.get(t.token_type as usize)
+ else {
+ error!(
+ "issue while loading semantic token {t:?}; \
+ couldnt find in legend"
+ );
+ continue;
+ };
+ if let Some(f) =
+ semantic::NAMES.iter().position(|&x| x == tty.as_str())
+ {
+ slice.iter_mut().for_each(|x| {
+ x.style.color = semantic::COLORS[f];
+ x.style.flags |= semantic::STYLES[f];
+ });
+ }
+ // println!(
+ // "{tty:?}: {}",
+ // slice
+ // .iter()
+ // .flat_map(|x| x.letter)
+ // .collect::<String>()
+ // );
+ let mut modi = t.token_modifiers_bitset;
+ while modi != 0 {
+ let bit = modi.trailing_zeros();
+
+ leg.token_modifiers
+ .get(bit as usize)
+ .and_then(|modi| {
+ MODIFIED.iter().position(|&(x, y)| {
+ (x == tty.as_str()) & (y == modi.as_str())
+ })
+ })
+ .map(|i| {
+ slice.iter_mut().for_each(|x| {
+ x.style.color = MCOLORS[i];
+ x.style.flags |= MSTYLE[i];
+ });
+ });
+
+ modi &= !(1 << bit);
+ }
+ }
+ } else {
+ self.tree_sit(path, &mut cells);
+ }
+
selection.map(|x| self.position(x)).map(|[(x1, y1), (x2, y2)]| {
(y1 * c + x1..y2 * c + x2).for_each(|x| {
cells
@@ -673,6 +850,8 @@ impl TextArea {
panic!()
};
assert!(r.start < r.end);
+ dbg!(to, &r);
+
self.cursor = to;
self.setc();
r