A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml18
-rw-r--r--src/bar.rs9
-rw-r--r--src/lsp.rs547
-rw-r--r--src/main.rs330
-rw-r--r--src/sig.rs60
-rw-r--r--src/text.rs20
6 files changed, 618 insertions, 366 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8f23f7b..1909cfd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,7 @@ 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" }
+rust-analyzer = { version = "*", path = "../rust-analyzer/crates/rust-analyzer" }
test-log = "0.2.18"
lsp-types = { path = "../helix/helix-lsp-types", package = "helix-lsp-types" }
env_logger = "0.11.8"
@@ -50,13 +51,24 @@ itertools = "0.14.0"
pin-project = "1.1.10"
replace_with = "0.1.8"
nucleo = "0.5.0"
+tokio-util = { version = "0.7.17", features = ["rt"] }
+scopeguard = "1.2.0"
-[build-dependencies]
-cc = "*"
+[profile.dev.package.rust-analyzer]
+opt-level = 3
+debug-assertions = false
+
+[profile.dev.package.fimg]
+opt-level = 3
+debug-assertions = false
+
+[profile.dev.package.nucleo]
+opt-level = 3
+debug-assertions = false
[profile.release]
debug = 2
-# overflow-checks = true
+overflow-checks = true
# debug-assertions = true
[profile.dev]
diff --git a/src/bar.rs b/src/bar.rs
index 80a66c7..3993553 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -60,8 +60,13 @@ impl Bar {
.progress
.iter(&x.progress.guard())
.find_map(|x| match x.1 {
- Some(WorkDoneProgress::Report(x)) =>
- x.message.clone(),
+ Some((WorkDoneProgress::Report(x), b)) =>
+ format!(
+ "{}: {}",
+ b.title,
+ x.message.clone().unwrap_or_default()
+ )
+ .into(),
_ => None,
})
{
diff --git a/src/lsp.rs b/src/lsp.rs
index 53d3515..dcf8533 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -1,10 +1,11 @@
use std::collections::HashMap;
+use std::mem::forget;
use std::path::Path;
use std::sync::Arc;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering::Relaxed;
use std::task::Poll;
-use std::thread::{JoinHandle, spawn};
+use std::thread::spawn;
use std::time::Instant;
use Default::default;
@@ -15,7 +16,7 @@ use crossbeam::channel::{
};
use log::{debug, error};
use lsp_server::{
- Message, Notification as N, Request as Rq, Response as Re,
+ ErrorCode, Message, Notification as N, Request as LRq, Response as Re,
};
use lsp_types::notification::*;
use lsp_types::request::*;
@@ -23,6 +24,7 @@ use lsp_types::*;
use parking_lot::Mutex;
use serde_json::json;
use tokio::sync::oneshot;
+use tokio_util::task::AbortOnDropHandle;
use winit::window::Window;
pub struct Client {
pub runtime: tokio::runtime::Runtime,
@@ -32,8 +34,10 @@ pub struct Client {
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 progress: &'static papaya::HashMap<
+ ProgressToken,
+ Option<(WorkDoneProgress, WorkDoneProgressBegin)>,
+ >,
pub not_rx: Receiver<N>,
// pub req_rx: Receiver<Rq>,
pub semantic_tokens: (
@@ -58,21 +62,23 @@ impl Client {
params: serde_json::to_value(y).unwrap(),
}))
}
-
+ pub fn cancel(&self, rid: i32) {
+ _ = self.notify::<Cancel>(&CancelParams { id: rid.into() });
+ }
#[must_use]
- pub fn request<X: Request>(
- &self,
+ pub fn request<'me, X: Request>(
+ &'me self,
y: &X::Params,
) -> Result<
(
impl Future<Output = Result<X::Result, oneshot::error::RecvError>>
- + use<X>,
+ + use<'me, X>,
i32,
),
SendError<Message>,
> {
let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
- self.tx.send(Message::Request(Rq {
+ self.tx.send(Message::Request(LRq {
id: id.into(),
method: X::METHOD.into(),
params: serde_json::to_value(y).unwrap(),
@@ -83,8 +89,13 @@ impl Client {
self.send_to.send((id, tx)).expect("oughtnt really fail");
}
Ok((
- async {
+ async move {
+ let g = scopeguard::guard((), |()| {
+ self.cancel(id);
+ });
+
rx.await.map(|x| {
+ forget(g);
serde_json::from_value::<X::Result>(
x.result.unwrap_or_default(),
)
@@ -139,8 +150,8 @@ impl Client {
self.request::<ResolveCompletionItem>(&x).map(|x| x.0)
}
- pub fn request_complete(
- &self,
+ pub fn request_complete<'me>(
+ &'me self,
f: &Path,
(x, y): (usize, usize),
c: CompletionContext,
@@ -149,7 +160,7 @@ impl Client {
Option<CompletionResponse>,
oneshot::error::RecvError,
>,
- > + use<> {
+ > + use<'me> {
let (rx, _) = self
.request::<Completion>(&CompletionParams {
text_document_position: TextDocumentPositionParams {
@@ -168,12 +179,36 @@ impl Client {
rx
}
+ pub fn request_sig_help<'me>(
+ &'me self,
+ f: &Path,
+ (x, y): (usize, usize),
+ ) -> impl Future<
+ Output = Result<Option<SignatureHelp>, oneshot::error::RecvError>,
+ > + use<'me> {
+ self.request::<SignatureHelpRequest>(&SignatureHelpParams {
+ context: None,
+ text_document_position_params: TextDocumentPositionParams {
+ text_document: {
+ TextDocumentIdentifier {
+ uri: Url::from_file_path(f).unwrap(),
+ }
+ },
+ position: Position { line: y as _, character: x as _ },
+ },
+ work_done_progress_params: default(),
+ })
+ .unwrap()
+ .0
+ }
+
pub fn rq_semantic_tokens(
- &self,
+ &'static self,
f: &Path,
w: Option<Arc<Window>>,
) -> anyhow::Result<()> {
debug!("requested semantic tokens");
+
let Some(b"rs") = f.extension().map(|x| x.as_encoded_bytes())
else {
return Ok(());
@@ -181,8 +216,6 @@ impl Client {
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();
}
}
@@ -215,18 +248,9 @@ impl Client {
}
}
pub fn run(
- (tx, rx, iot): (
- Sender<Message>,
- Receiver<Message>,
- lsp_server::IoThreads,
- ),
+ (tx, rx): (Sender<Message>, Receiver<Message>),
workspace: WorkspaceFolder,
-) -> (
- Client,
- lsp_server::IoThreads,
- JoinHandle<()>,
- oneshot::Sender<Arc<Window>>,
-) {
+) -> (Client, std::thread::JoinHandle<()>, oneshot::Sender<Arc<Window>>) {
let now = Instant::now();
let (req_tx, req_rx) = unbounded();
let (not_tx, not_rx) = unbounded();
@@ -251,189 +275,195 @@ pub fn run(
send_to: req_tx,
not_rx,
};
- _ = c.request::<Initialize>(&InitializeParams {
- process_id: Some(std::process::id()),
+ _ = 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 {
- hover: Some(HoverClientCapabilities {
- dynamic_registration: None,
- content_format: Some(vec![
- MarkupKind::PlainText,
- MarkupKind::Markdown,
- ]),
+ capabilities: ClientCapabilities {
+ window: Some(WindowClientCapabilities {
+ work_done_progress: Some(true),
+ ..default()
}),
- completion: Some(CompletionClientCapabilities {
- dynamic_registration: Some(false),
- completion_item: Some(CompletionItemCapability {
- snippet_support: Some(true),
- commit_characters_support: Some(true),
- documentation_format: Some(vec![
- MarkupKind::Markdown,
- MarkupKind::PlainText,
- ]),
- deprecated_support: None,
- preselect_support: None,
- tag_support: Some(TagSupport {
- value_set: vec![CompletionItemTag::DEPRECATED],
+
+ 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), }),
+ signature_help: Some(SignatureHelpClientCapabilities {
+ dynamic_registration: None, signature_information: Some(SignatureInformationSettings {
+ documentation_format: Some(vec![
+ MarkupKind::Markdown,
+ MarkupKind::PlainText,
+ ]),
+ parameter_information: Some(ParameterInformationSettings {
+ label_offset_support: Some(true) }),
+ active_parameter_support: Some(true),
+ }), context_support: Some(false) }),
+ completion: Some(CompletionClientCapabilities {
+ dynamic_registration: Some(false),
+ completion_item: Some(CompletionItemCapability {
+ snippet_support: Some(true),
+ commit_characters_support: Some(true),
+ documentation_format: Some(vec![
+ MarkupKind::Markdown,
+ MarkupKind::PlainText,
+ ]),
+ deprecated_support: None,
+ preselect_support: None,
+ tag_support: Some(TagSupport {
+ value_set: vec![CompletionItemTag::DEPRECATED],
+ }),
+
+ resolve_support: Some(CompletionItemCapabilityResolveSupport {
+ properties: vec![
+ "additionalTextEdits".into(),
+ "documentation".into(),
+ ],
+ }),
+ insert_replace_support: Some(false),
+ insert_text_mode_support: Some(InsertTextModeSupport {
+ value_set: vec![InsertTextMode::AS_IS],
+ }),
+ label_details_support: Some(true),
+ ..default()
}),
-
- resolve_support: Some(CompletionItemCapabilityResolveSupport { properties: vec!["additionalTextEdits".into(), "documentation".into()] }),
- insert_replace_support: Some(false),
- insert_text_mode_support:Some(InsertTextModeSupport{
- value_set: vec![InsertTextMode::AS_IS]
+ completion_item_kind: Some(CompletionItemKindCapability {
+ value_set: Some(vec![
+ CompletionItemKind::TEXT,
+ CompletionItemKind::METHOD,
+ CompletionItemKind::FUNCTION,
+ CompletionItemKind::CONSTRUCTOR,
+ CompletionItemKind::FIELD,
+ CompletionItemKind::VARIABLE,
+ CompletionItemKind::CLASS,
+ CompletionItemKind::INTERFACE,
+ CompletionItemKind::MODULE,
+ CompletionItemKind::PROPERTY,
+ CompletionItemKind::UNIT,
+ CompletionItemKind::VALUE,
+ CompletionItemKind::ENUM,
+ CompletionItemKind::KEYWORD,
+ CompletionItemKind::SNIPPET,
+ CompletionItemKind::COLOR,
+ CompletionItemKind::FILE,
+ CompletionItemKind::REFERENCE,
+ CompletionItemKind::FOLDER,
+ CompletionItemKind::ENUM_MEMBER,
+ CompletionItemKind::CONSTANT,
+ CompletionItemKind::STRUCT,
+ CompletionItemKind::EVENT,
+ CompletionItemKind::OPERATOR,
+ CompletionItemKind::TYPE_PARAMETER,
+ ]),
+ // value_set: Some(vec![CompletionItemKind::]),
}),
- label_details_support: Some(true),
- ..default()
+ context_support: None,
+ insert_text_mode: Some(InsertTextMode::AS_IS),
+ completion_list: Some(CompletionListCapability { item_defaults: None }),
}),
- completion_item_kind: Some(
- CompletionItemKindCapability {
- value_set: Some(
-vec![CompletionItemKind::TEXT,
-CompletionItemKind::METHOD,
-CompletionItemKind::FUNCTION,
-CompletionItemKind::CONSTRUCTOR,
-CompletionItemKind::FIELD,
-CompletionItemKind::VARIABLE,
-CompletionItemKind::CLASS,
-CompletionItemKind::INTERFACE,
-CompletionItemKind::MODULE,
-CompletionItemKind::PROPERTY,
-CompletionItemKind::UNIT,
-CompletionItemKind::VALUE,
-CompletionItemKind::ENUM,
-CompletionItemKind::KEYWORD,
-CompletionItemKind::SNIPPET,
-CompletionItemKind::COLOR,
-CompletionItemKind::FILE,
-CompletionItemKind::REFERENCE,
-CompletionItemKind::FOLDER,
-CompletionItemKind::ENUM_MEMBER,
-CompletionItemKind::CONSTANT,
-CompletionItemKind::STRUCT,
-CompletionItemKind::EVENT,
-CompletionItemKind::OPERATOR,
-CompletionItemKind::TYPE_PARAMETER]
- ),
- // value_set: Some(vec![CompletionItemKind::]),
+ semantic_tokens: Some(SemanticTokensClientCapabilities {
+ dynamic_registration: Some(false),
+ requests: SemanticTokensClientCapabilitiesRequests {
+ range: Some(true),
+ full: Some(lsp_types::SemanticTokensFullOptions::Bool(true)),
},
- ),
- context_support: None,
- insert_text_mode: Some(InsertTextMode::AS_IS),
- completion_list: Some(CompletionListCapability {
- item_defaults: None,
+ 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()
}),
- }),
- 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 {
- markdown: Some(MarkdownClientCapabilities {
- version: Some("1.0.0".into()),
- parser: "markdown".into(),
- allowed_tags: Some(vec![]),
+ general: Some(GeneralClientCapabilities {
+ markdown: Some(MarkdownClientCapabilities {
+ version: Some("1.0.0".into()),
+ parser: "markdown".into(),
+ allowed_tags: Some(vec![]),
+ }),
+ position_encodings: Some(vec![PositionEncodingKind::UTF8]),
+ ..default()
}),
- position_encodings: Some(vec![PositionEncodingKind::UTF8]),
+ experimental: Some(json! {{
+ "serverStatusNotification": true,
+ "hoverActions": true,
+ }}),
..default()
+ },
+ client_info: Some(ClientInfo {
+ name: "gracilaria".into(),
+ version: Some(env!("CARGO_PKG_VERSION").into()),
}),
- experimental: Some(json! {{
- "serverStatusNotification": true,
- "hoverActions": true,
+ initialization_options: Some(json! {{
+ "cargo": {
+ "buildScripts": { "enable": true }
+ },
+ "procMacro": {
+ "enable": true,
+ "attributes": { "enable": true }
+ },
+ "inlayHints": {
+ "closureReturnTypeHints": { "enable": "with_block" },
+ "closingBraceHints": { "minLines": 5 },
+ "closureStyle": "rust_analyzer",
+ "genericParameterHints": {
+ "type": { "enable": true } },
+ "rangeExclusiveHints": { "enable": true },
+ "closureCaptureHints": { "enable": true },
+ "expressionAdjustmentHints": {
+ "hideOutsideUnsafe": true,
+ "enable": "never",
+ "mode": "prefer_prefix"
+ }
+ },
+ "semanticHighlighting": {
+ "punctuation": {
+ "separate": {
+ "macroBang": true
+ },
+ "specialization": { "enable": true },
+ "enable": true
+ }
+ },
+ "showUnlinkedFileNotification": false,
+ "completion": {
+ "fullFunctionSignatures": { "enable": true, },
+ "autoIter": { "enable": false, },
+ "autoImport": { "enable": true, },
+ "termSearch": { "enable": true, },
+ "autoself": { "enable": true, },
+ "privateEditable": { "enable": true },
+ },
}}),
- ..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,
- "attributes": { "enable": true }
- },
- "inlayHints": {
- "closureReturnTypeHints": { "enable": "with_block" },
- "closingBraceHints": { "minLines": 5 },
- "closureStyle": "rust_analyzer",
- "genericParameterHints": {
- "type": { "enable": true } },
- "rangeExclusiveHints": { "enable": true },
- "closureCaptureHints": { "enable": true },
- "expressionAdjustmentHints": {
- "hideOutsideUnsafe": true,
- "enable": "never",
- "mode": "prefer_prefix"
- }
- },
- "semanticHighlighting": {
- "punctuation": {
- "separate": {
- "macroBang": true
- },
- "specialization": { "enable": true },
- "enable": true
- }
- },
- "showUnlinkedFileNotification": false,
- "completion": {
- "fullFunctionSignatures": { "enable": true, },
- "autoIter": { "enable": false, },
- "autoImport": { "enable": true, },
- "termSearch": { "enable": true, },
- "autoself": { "enable": true, },
- "privateEditable": { "enable": true },
- },
- }}),
- trace: None,
- workspace_folders: Some(vec![workspace]),
+ trace: None,
+ workspace_folders: Some(vec![workspace]),
- ..default()
- })
- .unwrap();
+ ..default()
+ })
+ .unwrap();
let x = serde_json::from_value::<InitializeResult>(
rx.recv().unwrap().response().unwrap().result.unwrap(),
)
@@ -466,7 +496,7 @@ CompletionItemKind::TYPE_PARAMETER]
Err(RecvError) => return,
},
recv(rx) -> x => match x {
- Ok(Message::Request(rq @ Rq { method: "window/workDoneProgress/create", .. })) => {
+ Ok(Message::Request(rq @ LRq { method: "window/workDoneProgress/create", .. })) => {
match rq.load::<WorkDoneProgressCreate>() {
Ok((_, x)) => {
let g = progress.guard();
@@ -484,14 +514,20 @@ CompletionItemKind::TYPE_PARAMETER]
}
}
Ok(Message::Response(x)) => {
- if let Some((s, took)) = map.remove(&x.id.i32()) {
- debug!("request {} took {:?}", x.id, took.elapsed());
+ if let Some(e) =& x.error {
+ if e.code == ErrorCode::RequestCanceled as i32 {}
+ 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());
match s.send(x) {
Ok(()) => {}
Err(e) => {
error!(
"unable to respond to {e:?}",
-
);
}
}
@@ -501,7 +537,14 @@ CompletionItemKind::TYPE_PARAMETER]
}
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());
+ match x.clone() {
+ WorkDoneProgress::Begin(y) => {
+ progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard());
+ },
+ WorkDoneProgress::Report(_) | WorkDoneProgress::End(_) => {
+ progress.update(token, move |v| Some((x.clone(), v.clone().expect("evil lsp").1)), &progress.guard());
+ }
+ }
w.request_redraw();
}
Ok(Message::Notification(notification)) => {
@@ -515,7 +558,7 @@ CompletionItemKind::TYPE_PARAMETER]
}
}
});
- (c, iot, h, window_tx)
+ (c, h, window_tx)
}
// trait RecvEepy<T>: Sized {
@@ -614,3 +657,103 @@ impl<T, U, F: FnMut(T) -> U, Fu: Future<Output = T>> Map_<T, U, F> for Fu {
Map(self, f)
}
}
+use tokio::task;
+#[derive(Debug)]
+pub enum OnceOff<T> {
+ Waiting(task::JoinHandle<Result<T, oneshot::error::RecvError>>),
+ Waited(T),
+}
+impl<T> OnceOff<T> {
+ pub fn poll(
+ &mut self,
+ r: &tokio::runtime::Runtime,
+ ) -> anyhow::Result<()> {
+ match self {
+ OnceOff::Waiting(join_handle) if join_handle.is_finished() => {
+ *self = Self::Waited(r.block_on(join_handle)??);
+ }
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug)]
+pub struct Rq<T, R: Request, D = ()> {
+ pub result: Option<T>,
+ pub request: Option<(
+ AbortOnDropHandle<Result<R::Result, oneshot::error::RecvError>>,
+ D,
+ )>,
+}
+impl<T, R: Request, D> Default for Rq<T, R, D> {
+ fn default() -> Self {
+ Self { result: None, request: None }
+ }
+}
+impl<T, R: Request> Rq<T, R, ()> {
+ pub fn new(
+ f: task::JoinHandle<Result<R::Result, oneshot::error::RecvError>>,
+ ) -> Self {
+ Self {
+ request: Some((AbortOnDropHandle::new(f), ())),
+ result: None,
+ }
+ }
+ pub fn request(
+ &mut self,
+ f: task::JoinHandle<Result<R::Result, oneshot::error::RecvError>>,
+ ) {
+ self.request = Some((AbortOnDropHandle::new(f), ()));
+ }
+}
+impl<T, R: Request, D> Rq<T, R, D> {
+ pub fn running(&self) -> bool {
+ matches!(
+ self,
+ Self { result: Some(_), .. } | Self { request: Some(_), .. },
+ )
+ }
+ pub fn poll(
+ &mut self,
+ f: impl FnOnce(
+ Result<R::Result, oneshot::error::RecvError>,
+ D,
+ ) -> Option<T>,
+ runtime: &tokio::runtime::Runtime,
+ ) {
+ if self.request.as_mut().is_some_and(|(x, _)| x.is_finished())
+ && let Some((task, d)) = self.request.take()
+ {
+ let x = match runtime.block_on(task) {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!(
+ "unexpected join error from request poll: {e}"
+ );
+ return;
+ }
+ };
+ self.result = f(x, d);
+ }
+ }
+}
+
+pub trait RedrawAfter {
+ fn redraw_after<T, F: Future<Output = T>>(
+ &self,
+ f: F,
+ ) -> impl Future<Output = T> + use<Self, T, F>;
+}
+impl RedrawAfter for Arc<Window> {
+ fn redraw_after<T, F: Future<Output = T>>(
+ &self,
+ f: F,
+ ) -> impl Future<Output = T> + use<T, F> {
+ let w: Arc<Window> = self.clone();
+ f.map(move |x| {
+ w.request_redraw();
+ x
+ })
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index a368b4c..dcb619a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -27,13 +27,10 @@
)]
#![allow(incomplete_features, redundant_semicolons)]
use std::borrow::Cow;
-use std::io::BufReader;
+use std::fs::read_dir;
use std::num::NonZeroU32;
use std::path::{Path, PathBuf};
-use std::pin::Pin;
-use std::process::{Command, Stdio};
-use std::sync::{Arc, LazyLock, OnceLock};
-use std::thread;
+use std::sync::{Arc, LazyLock};
use std::time::Instant;
use Default::default;
@@ -42,16 +39,20 @@ use atools::prelude::AASAdd;
use diff_match_patch_rs::PatchInput;
use diff_match_patch_rs::traits::DType;
use dsb::cell::Style;
-use dsb::{Cell, F};
+use dsb::{Cell, F, Fonts};
use fimg::{Image, OverlayAt};
-use lsp_types::request::HoverRequest;
+use lsp::{OnceOff, Rq};
+use lsp_server::Connection;
+use lsp_types::request::{HoverRequest, Request, SignatureHelpRequest};
use lsp_types::*;
use parking_lot::Mutex;
use regex::Regex;
use ropey::Rope;
use rust_fsm::StateMachine;
use swash::{FontRef, Instance};
-use tokio::task::{JoinHandle, spawn_blocking};
+use tokio::runtime::Runtime;
+use tokio::task::{JoinError, JoinHandle, spawn_blocking};
+use tokio_util::task::AbortOnDropHandle;
use url::Url;
use winit::event::{
ElementState, Event, MouseButton, MouseScrollDelta, WindowEvent,
@@ -59,15 +60,17 @@ use winit::event::{
use winit::event_loop::{ControlFlow, EventLoop};
use winit::keyboard::{Key, ModifiersState, NamedKey, SmolStr};
use winit::platform::wayland::WindowAttributesExtWayland;
-use winit::window::{Icon, Window};
+use winit::window::Icon;
use crate::bar::Bar;
use crate::hov::Hovr;
+use crate::lsp::RedrawAfter;
use crate::text::{Diff, TextArea, is_word};
mod bar;
pub mod com;
pub mod hov;
mod lsp;
+mod sig;
mod sni;
mod text;
mod winit_app;
@@ -197,18 +200,24 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
.and_then(|x| x.canonicalize().ok());
let c = workspace.as_ref().zip(origin.clone()).map(
|(workspace, origin)| {
- let mut c = Command::new("rust-analyzer")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::inherit())
- .spawn()
+ // let mut c = Command::new("rust-analyzer")
+ // .stdin(Stdio::piped())
+ // .stdout(Stdio::piped())
+ // .stderr(Stdio::inherit())
+ // .spawn()
+ // .unwrap();
+ let (a, b) = Connection::memory();
+ std::thread::Builder::new()
+ .name("Rust Analyzer".into())
+ .stack_size(1024 * 1024 * 8)
+ .spawn(|| rust_analyzer::bin::run_server(b))
.unwrap();
-
- let (c, t, t2, changed) = lsp::run(
- lsp_server::stdio::stdio_transport(
- BufReader::new(c.stdout.take().unwrap()),
- c.stdin.take().unwrap(),
- ),
+ let (c, t2, changed) = lsp::run(
+ (a.sender, a.receiver),
+ // 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
@@ -220,7 +229,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
);
c.open(&origin, std::fs::read_to_string(&origin).unwrap())
.unwrap();
- (&*Box::leak(Box::new(c)), (t, t2), changed)
+ (&*Box::leak(Box::new(c)), (t2), changed)
},
);
let (lsp, t, mut w) = match c {
@@ -229,11 +238,13 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
};
macro_rules! lsp {
() => {
- lsp.as_ref().zip(origin.as_deref())
+ lsp.zip(origin.as_deref())
};
}
let hovering = &*Box::leak(Box::new(Mutex::new(None::<hov::Hovr>)));
let mut complete = CompletionState::None;
+ let mut sig_help =
+ Rq::<SignatureHelp, SignatureHelpRequest, ()>::default();
// let mut complete = None::<(CompletionResponse, (usize, usize))>;
// let mut complete_ = None::<(
// JoinHandle<
@@ -322,23 +333,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
state.consume(Action::Changed).unwrap();
window.request_redraw();
}
- if let CompletionState::Complete(o, x)= &mut complete &&
- x.as_ref().is_some_and(|(x, _)|x.is_finished()) &&
- let Some((task, c)) = x.take() && let Some(ref l) = lsp{
- // if text.cursor() ==* c_ {
- *o = l.runtime.block_on(task).ok().and_then(Result::ok).flatten().map(|x| Complete {
-r:x,start:c,selection:0,vo:0,
- } );
- if let Some(x) = o {
- std::fs::write("complete_", serde_json::to_string_pretty(&x.r).unwrap()).unwrap();
- // println!("resolved")
- }
- // println!("{complete:#?}");
- // } else {
- // println!("abort {c_:?}");
- // x.abort();
- // }
+ if let CompletionState::Complete(rq)= &mut complete && let Some(ref l) = lsp{
+ rq.poll(|f, c| {
+ f.ok().flatten().map(|x| {Complete {r:x,start:c,selection:0,vo:0,}})
+ }, &l.runtime);
}
+ lsp.map(|c| sig_help.poll(|x, ()| x.ok().flatten(), &c.runtime));
match event {
Event::AboutToWait => {}
Event::WindowEvent {
@@ -471,36 +471,22 @@ r:x,start:c,selection:0,vo:0,
i.as_mut(),(0,0)
)
};
- if let CompletionState::Complete(Some(ref x,),_) = complete {
- let c = com::s(x, 40,&filter(&text));
- if c.len() == 0 {
- complete.consume(CompletionAction::NoResult).unwrap();
- } else {
- let ppem = 20.0;
+ let place_around_cursor = |(_x, _y): (usize, usize), fonts: &mut Fonts,mut i:Image<&mut [u8], 3> ,c: &[Cell],columns:usize, ppem_:f32,ls_:f32, ox:f32, oy: f32, toy: f32| {
let met = FONT.metrics(&[]);
let fac = ppem / met.units_per_em as f32;
- let (_x, _y) = text.cursor();
- let _x = _x + text.line_number_offset()+1;
-
- // let [(_x, _y), (_x2, _)] = text.position(text.cursor);
- // let [_x, _x2] = [_x, _x2].add(text.line_number_offset()+1);
- let _y = _y.wrapping_sub(text.vo);
- // if !(cursor_position.1 == _y && (_x..=_x2).contains(&cursor_position.0)) {
- // return;
- // }
let position = (
- ((_x) as f32 * fw).round() as usize,
- ((_y as f32 as f32) * (fh + ls * fac)).round() as usize,
+ (((_x) as f32 * fw).round() + ox) as usize,
+ (((_y) as f32 * (fh + ls * fac)).round() + oy) as usize,
);
-
-
-
- let ls = 10.0;
- let mut r = c.len()/40;
- let (w, h) = dsb::size(&fonts.regular, ppem, ls, (40, r));
- let top = position.1.checked_sub(h).unwrap_or((((_y + 1) as f32) * (fh + ls * fac)).round() as usize,);
- let (_, y) = dsb::fit(&fonts.regular, ppem, ls, (window.inner_size().width as _ /* - left */,( window.inner_size().height as usize - top ) ));
+ assert!(position.0 < 8000 && position.1 < 8000);
+ let ppem = ppem_;
+ let ls = ls_;
+ let mut r = c.len()/columns;
+ let (w, h) = dsb::size(&fonts.regular, ppem, ls, (columns, r));
+ 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 - top) ));
r = r.min(y);
let left =
@@ -508,66 +494,97 @@ r:x,start:c,selection:0,vo:0,
window.inner_size().width as usize- w as usize
} else { position.0 };
- let (w, h) = dsb::size(&fonts.regular, ppem, ls, (40, r));
- // let mut i2 = Image::build(w as _, h as _).fill(BG);
+ let (w, h) = dsb::size(&fonts.regular, ppem, ls, (columns, r));
unsafe{ dsb::render(
&c,
- (40, 0),
+ (columns, 0),
ppem,
- &mut fonts,
+ fonts,
ls,
- true,
- i.as_mut(),
+ true,
+ i.copy(),
(left as _, top as _)
)};
- // dbg!(w, h, i2.width(), i2.height(), window.inner_size(), i.width(),i.height());
- // unsafe { i.overlay_at(&i2.as_ref(), left as u32, top as u32) };
- i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, [0;3]);
- }
- }
+ (is_above, left, top, w, h)
+ };
hovering.lock().as_ref().map(|x| x.span.clone().map(|sp| {
- let met = FONT.metrics(&[]);
- let fac = ppem / met.units_per_em as f32;
let [(_x, _y), (_x2, _)] = text.position(sp);
let [_x, _x2] = [_x, _x2].add(text.line_number_offset()+1);
let _y = _y.wrapping_sub(text.vo);
if !(cursor_position.1 == _y && (_x..=_x2).contains(&cursor_position.0)) {
return;
}
- let position = (
- ((_x) as f32 * fw).round() as usize,
- ((_y as f32 as f32) * (fh + ls * fac)).round() as usize,
- );
- let ppem = 18.0;
- let ls = 10.0;
- let mut r = x.item.l().min(15);
- let (w, h) = dsb::size(&fonts.regular, ppem, ls, (x.item.c, r));
- let top = position.1.checked_sub(h).unwrap_or((((_y + 1) as f32) * (fh + ls * fac)).round() as usize,);
- let (_, y) = dsb::fit(&fonts.regular, ppem, ls, (window.inner_size().width as _ /* - left */,( window.inner_size().height as usize - top ) ));
- r = r.min(y);
+ let r = x.item.l().min(15);
let c = x.item.displayable(r);
- 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, (x.item.c, r));
- // let mut i2 = Image::build(w as _, h as _).fill(BG);
- unsafe{ dsb::render(
- &c,
- (x.item.c, 0),
- ppem,
+ let (_,left, top, w, h) = place_around_cursor(
+ (_x, _y),
&mut fonts,
- ls,
- true,
i.as_mut(),
- (left as _, top as _)
- )};
- // dbg!(w, h, i2.width(), i2.height(), window.inner_size(), i.width(),i.height());
- // unsafe { i.overlay_at(&i2.as_ref(), left as u32, top as u32) };
+ c, x.item.c,
+ 18.0, 10.0, 0., 0., 0.
+ );
i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, [0;3]);
}));
+ let com = match complete {
+ CompletionState::Complete(Rq{ result: Some(ref x,),..}) => {
+ let c = com::s(x, 40,&filter(&text));
+ if c.len() == 0 {
+ complete.consume(CompletionAction::NoResult).unwrap(); None
+ } else { Some(c) }},
+ _ => None,
+ };
+ 'out: {if let Rq{result: Some(ref x), .. } = sig_help {
+ let (sig, p) = sig::active(x);
+ let c = sig::sig((sig, p), 40);
+ let (_x, _y) = text.cursor();
+ let _x = _x + text.line_number_offset()+1;
+ let Some(_y) = _y.checked_sub(text.vo) else { break 'out };
+ let (is_above,left, top, w, mut h) = place_around_cursor((_x, _y), &mut fonts, i.as_mut(), &c, 40, ppem, ls, 0., 0., 0.);
+ i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, [0;3]);
+
+ let com = com.map(|c| {
+ let (is_above_,left, top, w_, h_) = place_around_cursor(
+ (_x, _y),
+ &mut fonts,
+ i.as_mut(),
+ &c, 40, ppem, ls, 0., -(h as f32), if is_above { 0.0 } else { h as f32 }
+ );
+ i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w_ as _,h_ as _, [0;3]);
+ 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_;
+ }
+ (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(|cells| {
+ let cells = cells.displayable(cells.l().min(15));
+ let (_,left_, top_, w_, h_) = place_around_cursor((_x, _y),
+ &mut fonts, i.as_mut(), 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 });
+ i.r#box((left_.saturating_sub(1) as _, top_.saturating_sub(1) as _), w as _,h_ as _, [0;3]);
+ });
+ }
+ } else if let Some(c) = com {
+ let ppem = 20.0;
+ let (_x, _y) = text.cursor();
+ let _x = _x + text.line_number_offset()+1;
+ let _y = _y.wrapping_sub(text.vo);
+ let (_,left, top, w, h) = place_around_cursor(
+ (_x, _y),
+ &mut fonts,
+ i.as_mut(),
+ &c, 40, ppem, ls, 0., 0., 0.
+ );
+ i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, [0;3]);
+ }
+ }
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() {
@@ -658,12 +675,12 @@ r:x,start:c,selection:0,vo:0,
// assert_eq!(hover, text.index_at(cursor_position));
let (x, y) =text.xy(hover);
let text = text.clone();
- {
+ 'out: {
let mut l = hovering.lock();
if let Some(Hovr{ span: Some(span),..}) = &*l {
let [(_x, _y), (_x2, _)] = text.position(span.clone());
let [_x, _x2] = [_x, _x2].add(text.line_number_offset()+1);
- let _y = _y - text.vo;
+ let Some(_y) = _y.checked_sub(text.vo) else { break 'out };
if cursor_position.1 == _y && (_x.._x2).contains(&cursor_position.0) {
return
} else {
@@ -844,34 +861,41 @@ RUNNING.remove(&hover,&RUNNING.guard());
}
text.scroll_to_cursor();
- if cb4 != text.cursor && let CompletionState::Complete(Some(c), t)= &mut complete
- && ((text.cursor < c.start) || (!is_word(text.at_())&& (text.at_() != '.' || text.at_() != ':')) ) {
- if let Some((x, _)) = t.take() {
- x.abort();
- }
+ if cb4 != text.cursor && let CompletionState::Complete(Rq{ result: Some(c),.. })= &mut complete
+ && ((text.cursor < c.start) || (!is_word(text.at_())&& (text.at_() != '.' || text.at_() != ':')) ) {
complete = CompletionState::None;
}
-
+ if sig_help.running() && cb4 != text.cursor && let Some((lsp, path)) = lsp!() {
+ sig_help.request(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(path, text.cursor()))));
+ }
if hist.record(&text) {
change!();
}
lsp!().map(|(lsp, o)|{
let window = window.clone();
+ match event.logical_key.as_ref() {
+ Key::Character(y)
+ if let Some(x) = &lsp.initialized
+ && let Some(x) = &x.capabilities.signature_help_provider
+ && let Some(x) = &x.trigger_characters && x.contains(&y.to_string()) => {
+ sig_help.request(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(o, text.cursor()))));
+ },
+ _ => {}
+ }
match complete.consume(CompletionAction::K(event.logical_key.as_ref())).unwrap(){
Some(CDo::Request(ctx)) => {
- let x = lsp.request_complete(o, text.cursor(), ctx);
- let h = lsp.runtime.spawn(async move {
- x.await.inspect(|_| window.request_redraw())
- });
- let CompletionState::Complete(c, x) = &mut complete else { panic!()};
+ let h = AbortOnDropHandle::new(lsp.runtime.spawn(
+ window.redraw_after(lsp.request_complete(o, text.cursor(), ctx))
+ ));
+ let CompletionState::Complete(Rq{ request : x, result: c, }) = &mut complete else { panic!()};
*x = Some((h,c.as_ref().map(|x|x.start).or(x.as_ref().map(|x|x.1)).unwrap_or(text.cursor)));
}
Some(CDo::SelectNext) => {
- let CompletionState::Complete(Some(c), _) = &mut complete else { panic!()};
+ let CompletionState::Complete(Rq{ result: Some(c), .. }) = &mut complete else { panic!()};
c.next(&filter(&text));
}
Some(CDo::SelectPrevious) => {
- let CompletionState::Complete(Some(c), _) = &mut complete else { panic!()};
+ let CompletionState::Complete(Rq{ result: Some(c), .. }) = &mut complete else { panic!()};
c.back(&filter(&text));
}
Some(CDo::Finish(x)) => {
@@ -899,11 +923,9 @@ RUNNING.remove(&hover,&RUNNING.guard());
}
if hist.record(&text) { change!();}
+ sig_help = Rq::new(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(o, text.cursor()))));
}
- Some(CDo::Abort(())) => {}
-
None => {return},
- _ => panic!(),
};
});
@@ -970,12 +992,11 @@ RUNNING.remove(&hover,&RUNNING.guard());
text.insert(&clipp::paste());
hist.push_if_changed(&text);
}
- Some(Do::OpenFile(x)) => {
- origin = Some(PathBuf::from(&x));
+ Some(Do::OpenFile(x)) => { let _: anyhow::Result<()> = try {
+ origin = Some(PathBuf::from(&x).canonicalize()?);
text = TextArea::default();
- text.insert(
- &std::fs::read_to_string(x).unwrap(),
- );
+ let new = std::fs::read_to_string(x)?;
+ text.insert(&new);
text.cursor = 0;
hist = Hist {
history: vec![],
@@ -984,6 +1005,16 @@ RUNNING.remove(&hover,&RUNNING.guard());
last_edit: Instant::now(),
changed: false,
};
+ complete = CompletionState::None;
+ mtime = modify!();
+
+ lsp!().map(|(x, origin)| {
+ x.semantic_tokens.0.store(Arc::new(vec![].into()));
+ x.open(&origin,new).unwrap();
+ x.rq_semantic_tokens(origin, Some(window.clone())).unwrap();
+ });
+ bar.last_action = "open".to_string();
+ };
}
Some(
Do::MoveCursor | Do::ExtendSelectionToMouse | Do::Hover,
@@ -1186,6 +1217,7 @@ RequestBoolean(t) => {
K(_) => RequestBoolean(t),
C(_) => _,
Changed => _,
+ M(_) => _,
},
Search((x, y, m)) => {
M(MouseButton => MouseButton::Left) => Default [MoveCursor],
@@ -1260,37 +1292,27 @@ rust_fsm::state_machine! {
pub(crate) CompletionState => CompletionAction<'i> => CDo
None => Click => None,
None => K(Key<&'i str> => Key::Character(k @ ("." | ":"))) => Complete(
- (Option<Complete>, Option<(JoinHandle<
- Result<
- Option<CompletionResponse>,
- tokio::sync::oneshot::error::RecvError,
- >,
- >, usize)>) => (None,None)
+ Rq<Complete, lsp_types::request::Completion, usize> => default()
) [Request(CompletionContext => CompletionContext {trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, trigger_character:Some(k.to_string()) })],
- None => K(Key::Named(NamedKey::Space) if ctrl()) => Complete((None, None)) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })],
- None => K(Key::Character(x) if x.chars().next().is_some_and(is_word)) => Complete((None,None)) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })],
+ None => K(Key::Named(NamedKey::Space) if ctrl()) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })],
+ None => K(Key::Character(x) if x.chars().all(char::is_alphabetic)) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })],
None => K(_) => _,
// when
- Complete((Some(_x),_y)) => K(Key::Named(NamedKey::Tab) if shift()) => _ [SelectPrevious],
- Complete((Some(_x),_y)) => K(Key::Named(NamedKey::Tab)) => _ [SelectNext],
- Complete((Some(_x),_y)) => K(Key::Named(NamedKey::ArrowDown)) => _ [SelectNext],
- Complete((Some(_x),_y)) => K(Key::Named(NamedKey::ArrowUp)) => _ [SelectPrevious],
+ Complete(Rq{ result: Some(_x),request: _y }) => K(Key::Named(NamedKey::Tab) if shift()) => _ [SelectPrevious],
+ Complete(Rq { result: Some(_x),request: _y }) => K(Key::Named(NamedKey::Tab)) => _ [SelectNext],
+ Complete(Rq { result: Some(_x),request: _y }) => K(Key::Named(NamedKey::ArrowDown)) => _ [SelectNext],
+ Complete(Rq { result: Some(_x),request: _y }) => K(Key::Named(NamedKey::ArrowUp)) => _ [SelectPrevious],
// exit cases
- Complete((_x, None)) => Click => None,
- Complete((_x, None)) => NoResult => None,
- Complete((_x, Some((y, _))))=> K(Key::Named(Escape)) => None [Abort(((),) => y.abort())],
- Complete((_x, None)) => K(Key::Named(Escape)) => None,
- Complete((_x, Some((y, _)))) => Click => None [Abort(((),) => y.abort())],
- Complete((_x, Some((y, _)))) => K(Key::Character(x) if !x.chars().all(is_word)) => None [Abort(y.abort())],
- Complete((_x, None)) => K(Key::Character(x) if !x.chars().all(is_word)) => None,
+ Complete(_) => Click => None,
+ Complete(_) => NoResult => None,
+ Complete(_)=> K(Key::Named(Escape)) => None,
+ Complete(_) => K(Key::Character(x) if !x.chars().all(is_word)) => None,
- Complete((Some(x), task)) => K(Key::Named(NamedKey::Enter)) => None [Finish(Complete => {
- task.map(|(task, _)| task.abort()); x
- })],
+ Complete(Rq { result: Some(x), .. }) => K(Key::Named(NamedKey::Enter)) => None [Finish(Complete => x)],
- Complete((_x, _y)) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, trigger_character:None })],
+ Complete(_x) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, trigger_character:None })],
}
use com::Complete;
impl Default for CompletionState {
@@ -1308,6 +1330,10 @@ fn filter(text: &TextArea) -> String {
.collect::<String>()
}
}
-fn frunctinator(parameter1:usize, parameter2:u8, paramter4:u16) -> usize {
+fn frunctinator(
+ parameter1: usize,
+ parameter2: u8,
+ paramter4: u16,
+) -> usize {
0
-} \ No newline at end of file
+}
diff --git a/src/sig.rs b/src/sig.rs
new file mode 100644
index 0000000..427e37e
--- /dev/null
+++ b/src/sig.rs
@@ -0,0 +1,60 @@
+use std::iter::{repeat, repeat_n};
+
+use dsb::Cell;
+use dsb::cell::Style;
+use lsp_types::{
+ MarkupContent, MarkupKind, ParameterInformation, SignatureHelp,
+ SignatureInformation,
+};
+
+use crate::FG;
+use crate::text::{CellBuffer, color, color_};
+pub fn active(
+ sig: &SignatureHelp,
+) -> (&SignatureInformation, Option<&ParameterInformation>) {
+ let y = &sig.signatures[sig.active_signature.unwrap_or(0) as usize];
+ (
+ y,
+ sig.active_parameter
+ .zip(y.parameters.as_ref())
+ .and_then(|(i, x)| x.get(i as usize)),
+ )
+}
+
+pub fn sig(
+ (y, p): (&SignatureInformation, Option<&ParameterInformation>),
+ c: usize,
+) -> Vec<Cell> {
+ let bg = color_("#1c212b");
+ let ds: Style = Style { bg: bg, color: FG, flags: 0 };
+ let d: Cell = Cell { letter: None, style: ds };
+ let sig = y.label.chars().zip(0..).map(|(x, i)| {
+ let mut a = ds.basic(x);
+ if p.is_some_and(|x| match x.label {
+ lsp_types::ParameterLabel::Simple(_) => false,
+ lsp_types::ParameterLabel::LabelOffsets([a, b]) =>
+ (a..b).contains(&i),
+ }) {
+ a.style |= (Style::BOLD, color_("#ffcc66"));
+ }
+ a
+ });
+ let mut v = sig.collect::<Vec<_>>();
+ v.extend(repeat_n(d, c - (v.len() % c)));
+ v
+}
+pub fn doc(sig: &SignatureInformation, c: usize) -> Option<CellBuffer> {
+ sig.documentation
+ .as_ref()
+ .map(|x| match x {
+ lsp_types::Documentation::String(x) => x,
+ lsp_types::Documentation::MarkupContent(MarkupContent {
+ kind: _,
+ value,
+ }) => value,
+ })
+ .and_then(|x| {
+ crate::hov::p(&x).map(|node| crate::hov::markdown2(c, &node))
+ })
+ .map(|cells| CellBuffer { c, vo: 0, cells: cells.into() })
+}
diff --git a/src/text.rs b/src/text.rs
index 543ccdf..f16a2ab 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,7 +1,5 @@
-use std::cmp::{Ordering, min};
+use std::cmp::min;
use std::fmt::{Debug, Display};
-use std::iter::empty;
-use std::marker::Tuple;
use std::ops::{Deref, Index, IndexMut, Not as _, Range, RangeBounds};
use std::path::Path;
use std::pin::pin;
@@ -342,8 +340,16 @@ impl TextArea {
crate::sni::Snippet::parse(&x.new_text, begin)
.ok_or(anyhow!("failed to parse snippet"))?;
self.rope.try_insert(begin, &tex)?;
- self.cursor = sni.next().unwrap().r().end;
- self.tabstops = Some(sni);
+ self.cursor = match sni.next() {
+ Some(x) => {
+ self.tabstops = Some(sni);
+ x.r().end
+ }
+ None => {
+ self.tabstops = None;
+ end
+ }
+ };
Ok(())
}
pub fn cursor(&self) -> (usize, usize) {
@@ -805,9 +811,9 @@ impl TextArea {
};
if ln as usize * c + x1 < self.vo * c {
- // continue;
+ continue;
} else if ln as usize * c + x1 > self.vo * c + r * c {
- // break;
+ break;
}
let Some(tty) = leg.token_types.get(t.token_type as usize)
else {