A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml1
-rw-r--r--LigaComicMono.ttfbin0 -> 64044 bytes
-rw-r--r--fail.pngbin0 -> 15945 bytes
-rw-r--r--flake.lock82
-rw-r--r--flake.nix83
-rw-r--r--src/act.rs10
-rw-r--r--src/bar.rs12
-rw-r--r--src/com.rs (renamed from src/complete.rs)47
-rw-r--r--src/commands.rs395
-rw-r--r--src/edi.rs229
-rw-r--r--src/edi/st.rs47
-rw-r--r--src/hov.rs2
-rw-r--r--src/lsp.rs92
-rw-r--r--src/main.rs15
-rw-r--r--src/menu.rs10
-rw-r--r--src/menu/generic.rs129
-rw-r--r--src/rnd.rs49
-rw-r--r--src/sym.rs166
-rw-r--r--src/text.rs65
-rw-r--r--test525
20 files changed, 1177 insertions, 782 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b59988a..daa7273 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -67,6 +67,7 @@ imara-diff = "0.2.0"
vecto = "0.1.1"
rangemap = { version = "1.7.1", features = ["const_fn", "nightly", "serde1"] }
itern = "0.1.1"
+png = "0.18.0"
[profile.dev.package]
rust-analyzer.opt-level = 3
diff --git a/LigaComicMono.ttf b/LigaComicMono.ttf
new file mode 100644
index 0000000..a28bc72
--- /dev/null
+++ b/LigaComicMono.ttf
Binary files differ
diff --git a/fail.png b/fail.png
new file mode 100644
index 0000000..37d2dca
--- /dev/null
+++ b/fail.png
Binary files differ
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..4c0ec34
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,82 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1769740369,
+ "narHash": "sha256-xKPyJoMoXfXpDM5DFDZDsi9PHArf2k5BJjvReYXoFpM=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "6308c3b21396534d8aaeac46179c14c439a89b8a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs",
+ "rust-overlay": "rust-overlay"
+ }
+ },
+ "rust-overlay": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1769915446,
+ "narHash": "sha256-f1F/umtX3ZD7fF9DHSloVHc0mnAT0ry0YK2jI/6E0aI=",
+ "owner": "oxalica",
+ "repo": "rust-overlay",
+ "rev": "bc00300f010275e46feb3c3974df6587ff7b7808",
+ "type": "github"
+ },
+ "original": {
+ "owner": "oxalica",
+ "repo": "rust-overlay",
+ "type": "github"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..dc427ab
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,83 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
+ flake-utils.url = "github:numtide/flake-utils";
+ rust-overlay = {
+ url = "github:oxalica/rust-overlay";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ outputs = inputs @ { self, nixpkgs, flake-utils, rust-overlay, ... }:
+ flake-utils.lib.eachDefaultSystem (
+ system: let
+ overlays = [ (import rust-overlay) ];
+ pkgs = import nixpkgs { inherit system overlays; };
+ in {
+ devShells.default = with pkgs; mkShell rec {
+ buildInputs = [
+ (rust-bin.nightly.latest.minimal.override {
+ extensions = [ "clippy" "rust-analyzer" "rust-docs" "rust-src" "miri" ];
+ })
+ # We use nightly rustfmt features.
+ (rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt))
+
+ # Vulkan dependencies
+ shaderc
+ spirv-tools
+ vulkan-loader
+ vulkan-tools
+ vulkan-tools-lunarg
+ vulkan-validation-layers
+ vulkan-extension-layer
+
+ # winit dependencies
+ libxkbcommon
+ wayland
+ xorg.libX11
+ xorg.libXcursor
+ xorg.libXi
+ xorg.libXrandr
+ SDL2
+
+ clang
+ libclang
+ glibc.dev
+
+ valgrind
+ gdb
+ ];
+
+ LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
+ SHADERC_LIB_DIR = lib.makeLibraryPath [ shaderc ];
+ VK_LAYER_PATH = "${vulkan-validation-layers}/share/vulkan/explicit_layer.d";
+ };
+ devShells.CI = with pkgs; mkShell rec {
+ buildInputs = [
+ (rust-bin.stable.latest.minimal.override {
+ extensions = [ "clippy" ];
+ # Windows CI unfortunately needs to cross-compile from within WSL because Nix doesn't
+ # work on Windows.
+ targets = [ "x86_64-pc-windows-msvc" ];
+ })
+ # We use nightly rustfmt features.
+ (rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt))
+
+ # Vulkan dependencies
+ shaderc
+
+ # winit dependencies
+ libxkbcommon
+ wayland
+ xorg.libX11
+ xorg.libXcursor
+ xorg.libXi
+ xorg.libXrandr
+ ];
+
+ LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
+ SHADERC_LIB_DIR = lib.makeLibraryPath [ shaderc ];
+ };
+ }
+ );
+}
diff --git a/src/act.rs b/src/act.rs
index d35cd3a..3912c65 100644
--- a/src/act.rs
+++ b/src/act.rs
@@ -48,6 +48,16 @@ impl CodeActions {
}
pub fn down(&mut self) {
+ // let mut adj = |y: &mut usize, max| {
+ // *y += 1;
+ // if *y == max {
+ // self.vo = 0;
+ // *y = 0;
+ // }
+ // if *y >= self.vo + 13 {
+ // self.vo += 1;
+ // }
+ // };
match &mut self.inner {
N::Many(x, Entry::Outside(y), _) => {
let n = x.len();
diff --git a/src/bar.rs b/src/bar.rs
index 75d9e5b..d9400a6 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -98,14 +98,16 @@ impl Bar {
});
}
State::Command(x) => {
- chain(['>'], x.tedit.rope.chars()).zip(row).for_each(
- |(x, y)| {
+ ":".chars()
+ .zip(repeat(Style::BOLD | Style::ITALIC))
+ .chain(s(&x.tedit.rope.to_string()))
+ .zip(row)
+ .for_each(|((x, z), y)| {
*y = Cell {
letter: Some(x),
- style: Style { flags: 0, ..y.style },
+ style: Style { flags: z, ..y.style },
}
- },
- );
+ });
}
State::Symbols(Rq {
result: Some(Symbols { tedit, .. }),
diff --git a/src/complete.rs b/src/com.rs
index fb326b0..e157787 100644
--- a/src/complete.rs
+++ b/src/com.rs
@@ -8,7 +8,7 @@ use lsp_types::*;
use serde::{Deserialize, Serialize};
use crate::FG;
-use crate::menu::{Key, back, charc, filter, next, score};
+use crate::menu::{Key, back, filter, next, score};
use crate::text::{col, color_, set_a};
#[derive(Serialize, Deserialize)]
@@ -75,12 +75,57 @@ pub fn s(completion: &Complete, c: usize, f: &str) -> Vec<Cell> {
.skip(completion.vo)
.take(N);
+ // let Some((s, x)) = i.next() else {
+ // return vec![];
+ // };
+
+ // let mut q = Dq::<_, 13>::new((s, x));
+ // for (s, x) in i {
+ // if q.first().0 <= s {
+ // q.push_front((s, x));
+ // }
+ // }
+
+ // fuzzy_aho_corasick::FuzzyAhoCorasickBuilder::new()
+ // .fuzzy(
+ // FuzzyLimits::new()
+ // .insertions(20)
+ // .deletions(2)
+ // .edits(4)
+ // .substitutions(5)
+ // .swaps(3),
+ // .penalties(FuzzyPenalties {
+ // )
+ // insertion: 0.0,
+ // deletion: 1.0,
+ // substitution: 0.5,
+ // swap: 0.5,
+ // })
+ // .build(
+ // y.iter().map(|x| x.filter_text.as_deref().unwrap_or(&x.label)),
+ // )
+ // .search(filter, 0.25)
+ // .into_iter()
+ // .map(|x| &y[x.pattern_index])
+ // // .take(13);
+ // // for x in y
+ // // .iter()
+ // // .filter(|x| {
+ // // x.filter_text
+ // // .as_deref()
+ // // .unwrap_or(&x.label)
+ // // .starts_with(filter)
+ // // })
+ // .take(13)
i.for_each(|((_, x, indices), i)| {
r(x, c, i == completion.selection, &indices, &mut out)
});
out
}
+fn charc(c: &str) -> usize {
+ c.chars().count()
+}
#[implicit_fn::implicit_fn]
fn r(
x: &CompletionItem,
diff --git a/src/commands.rs b/src/commands.rs
index 9e01796..05135cb 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,281 +1,164 @@
use std::iter::repeat;
use std::path::Path;
-use std::process::Stdio;
-use std::sync::Arc;
use Default::default;
-use Into::into;
-use anyhow::{anyhow, bail};
use dsb::Cell;
use dsb::cell::Style;
-use lsp_types::*;
-use rust_analyzer::lsp::ext::*;
use crate::FG;
-use crate::edi::{Editor, lsp_m};
-use crate::lsp::{Anonymize, PathURI};
-use crate::menu::charc;
-use crate::menu::generic::{GenericMenu, MenuData};
-use crate::text::{RopeExt, SortTedits, col, color_};
+use crate::menu::{back, filter, next, score};
+use crate::text::{TextArea, col, color_};
-macro_rules! commands {
- ($(#[doc = $d: literal] $t:tt $identifier: ident: $c:literal),+ $(,)?) => {
- #[derive(Copy, Clone, PartialEq, Eq)]
- pub enum Cmd {
- $(#[doc = $d] $identifier),+
- }
- impl Cmd {
- pub const ALL: [Cmd; { [$($c),+].len() }] = [$(Self::$identifier,)+];
- pub fn name(self) -> &'static str {
- match self {
- $(Self::$identifier => $c,)+
- }
- }
- pub fn desc(self) -> &'static str {
- match self {
- $(Self::$identifier => $d,)+
- }
- }
- pub fn needs_lsp(self) -> bool {
- match self {
- $(Self::$identifier => stringify!($t) == "@",)+
- }
- }
- }
- };
+const COMMANDS: [(&str, &str); 3] = [
+ ("w", "Write / Save"),
+ ("q", "Quit"),
+ ("exit-vim-mode", "Go back to default editting mode"),
+];
+
+#[derive(Debug, Default)]
+pub struct Commands {
+ pub tedit: TextArea,
+ pub selection: usize,
+ pub vo: usize,
}
-commands!(
- /// move item at cursor down
- @ RAMoveID: "move-item-down",
- /// move item at cursor up
- @ RAMoveIU: "move-item-up",
- /// restart rust analyzer
- @ RARestart: "ra-restart",
- /// go to parent module
- @ RAParent: "parent",
- /// join lines under cursors.
- @ RAJoinLines: "join-lines",
- /// gets list of runnables
- @ RARunnables: "runnables",
- /// Open docs for type at cursor
- @ RADocs: "open-docs",
- /// Rebuilds rust-analyzer proc macros
- @ RARebuildProcMacros: "rebuild-proc-macros",
- /// Cancels current running rust-analyzer check process
- @ RACancelFlycheck: "cancel-flycheck",
- /// Opens Cargo.toml file for this workspace
- @ RAOpenCargoToml: "open-cargo-toml",
- /// Runs the test at the cursor
- @ RARunTest: "run-test",
-);
-pub enum Cmds {}
-impl MenuData for Cmds {
- const HEIGHT: usize = 30;
- type Data = ();
- type Element<'a> = Cmd;
- fn gn((): &()) -> impl Iterator<Item = Cmd> {
- Cmd::ALL.into_iter()
+const N: usize = 30;
+impl Commands {
+ fn f(&self) -> String {
+ self.tedit.rope.to_string()
+ }
+ pub fn next(&mut self) {
+ let n = filter_c(self, &self.f()).count();
+ // coz its bottom up
+ back::<N>(n, &mut self.selection, &mut self.vo);
+ }
+
+ pub fn sel(&self) -> &str {
+ let f = self.f();
+ score_c(filter_c(self, &f), &f)[self.selection].1
+ }
+
+ pub fn back(&mut self) {
+ let n = filter_c(self, &self.f()).count();
+ next::<N>(n, &mut self.selection, &mut self.vo);
}
+ pub fn cells(&self, c: usize, ws: &Path) -> Vec<Cell> {
+ let f = self.f();
+ let mut out = vec![];
+ let v = score_c(filter_c(self, &f), &f);
+ let vlen = v.len();
+ let i = v.into_iter().zip(0..vlen).skip(self.vo).take(N).rev();
- fn r(
- _: &Self::Data,
- x: Cmd,
- _: &Path,
- c: usize,
- selected: bool,
- indices: &[u32],
- to: &mut Vec<Cell>,
- ) {
- let bg = if selected { col!("#262d3b") } else { col!("#1c212b") };
+ // let Some((s, x)) = i.next() else {
+ // return vec![];
+ // };
- let ds: Style = Style::new(FG, bg);
- let d: Cell = Cell { letter: None, style: ds };
- let mut b = vec![d; c];
- let (bgt, col, ty) = (col!("#FFFFFF"), col!("#ACACAC"), "");
- b.iter_mut().zip(ty.chars()).for_each(|(x, c)| {
- *x = (Style::new(col, bgt) | Style::BOLD).basic(c)
+ // let mut q = Dq::<_, 13>::new((s, x));
+ // for (s, x) in i {
+ // if q.first().0 <= s {
+ // q.push_front((s, x));
+ // }
+ // }
+
+ // fuzzy_aho_corasick::FuzzyAhoCorasickBuilder::new()
+ // .fuzzy(
+ // FuzzyLimits::new()
+ // .insertions(20)
+ // .deletions(2)
+ // .edits(4)
+ // .substitutions(5)
+ // .swaps(3),
+ // .penalties(FuzzyPenalties {
+ // )
+ // insertion: 0.0,
+ // deletion: 1.0,
+ // substitution: 0.5,
+ // swap: 0.5,
+ // })
+ // .build(
+ // y.iter().map(|x| x.filter_text.as_deref().unwrap_or(&x.label)),
+ // )
+ // .search(filter, 0.25)
+ // .into_iter()
+ // .map(|x| &y[x.pattern_index])
+ // // .take(13);
+ // // for x in y
+ // // .iter()
+ // // .filter(|x| {
+ // // x.filter_text
+ // // .as_deref()
+ // // .unwrap_or(&x.label)
+ // // .starts_with(filter)
+ // // })
+ // .take(13)
+ i.for_each(|((_, x, indices), i)| {
+ r(x, ws, c, i == self.selection, &indices, &mut out)
});
- let i = &mut b[..];
- let qualifier = x.desc().chars();
- let _left = i.len() as i32
- - (charc(&x.name()) as i32 + qualifier.clone().count() as i32)
- - 3;
- i.iter_mut()
- .zip(
- x.name()
- .chars()
- .chain([' '])
- .map(|x| ds.basic(x))
- .zip(0..)
- .chain(
- qualifier
- .map(|x| {
- Style {
- bg,
- fg: color_("#858685"),
- ..default()
- }
- .basic(x)
- })
- .zip(repeat(u32::MAX)),
- ),
- )
- .for_each(|(a, (b, i))| {
- *a = b;
- if indices.contains(&i) {
- a.style |= (Style::BOLD, color_("#ffcc66"));
- }
- });
- to.extend(b);
+ out
}
}
-pub type Commands = GenericMenu<Cmds>;
-impl<'a> crate::menu::Key<'a> for Cmd {
+fn score_c<'a>(
+ x: impl Iterator<Item = &'a str>,
+ filter: &'_ str,
+) -> Vec<(u32, &'a str, Vec<u32>)> {
+ score(x, filter)
+}
+
+fn filter_c<'a>(
+ _me: &'a Commands,
+ f: &'_ str,
+) -> impl Iterator<Item = &'a str> {
+ filter(COMMANDS.into_iter().map(|(x, _)| x), f)
+}
+impl<'a> crate::menu::Key<'a> for &'a str {
fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> {
- self.name()
+ *self
}
}
+fn charc(c: &str) -> usize {
+ c.chars().count()
+}
+#[implicit_fn::implicit_fn]
+fn r(
+ x: &str,
+ _workspace: &Path,
+ c: usize,
+ selected: bool,
+ indices: &[u32],
+ to: &mut Vec<Cell>,
+) {
+ let bg = if selected { col!("#262d3b") } else { col!("#1c212b") };
-impl Editor {
- pub fn handle_command(
- &mut self,
- z: Cmd,
- w: Arc<winit::window::Window>,
- ) -> anyhow::Result<()> {
- if !z.needs_lsp() {
- return Ok(());
- }
- let Some((l, o)) = lsp_m!(self + p) else {
- bail!("no lsp");
- };
-
- match z {
- Cmd::RAMoveIU | Cmd::RAMoveID => {
- let r = self
- .text
- .to_l_position(*self.text.cursor.first())
- .unwrap();
- let mut x = l.request_immediate::<rust_analyzer::lsp::ext::MoveItem>(&MoveItemParams {
- direction: if let Cmd::RAMoveIU = z { MoveItemDirection::Up } else { MoveItemDirection::Down },
- text_document: o.tid(),
- range: Range { start : r, end : r},
- })?;
+ let ds: Style = Style::new(FG, bg);
+ let d: Cell = Cell { letter: None, style: ds };
+ let mut b = vec![d; c];
+ let (bgt, col, ty) = (col!("#FFFFFF"), col!("#ACACAC"), "");
+ b.iter_mut().zip(ty.chars()).for_each(|(x, c)| {
+ *x = (Style::new(col, bgt) | Style::BOLD).basic(c)
+ });
+ let i = &mut b[2..];
+ let qualifier = COMMANDS.iter().find(|y| y.0 == x).unwrap().1.chars();
+ let _left = i.len() as i32
+ - (charc(&x) as i32 + qualifier.clone().count() as i32)
+ - 3;
- x.sort_tedits();
- for t in x {
- self.text.apply_snippet_tedit(&t)?;
- }
- }
- Cmd::RARestart => {
- _ = l.request::<ReloadWorkspace>(&())?.0;
- }
- Cmd::RAParent => {
- let Some(GotoDefinitionResponse::Link([ref x])) =
- l.request_immediate::<ParentModule>(
- &TextDocumentPositionParams {
- text_document: o.tid(),
- position: self
- .text
- .to_l_position(*self.text.cursor.first())
- .unwrap(),
- },
- )?
- else {
- self.bar.last_action = "no such parent".into();
- return Ok(());
- };
- self.open_loclink(x, w);
- }
- Cmd::RAJoinLines => {
- let teds =
- l.request_immediate::<JoinLines>(&JoinLinesParams {
- ranges: self
- .text
- .cursor
- .iter()
- .filter_map(|x| {
- self.text.to_l_range(
- x.sel.map(into).unwrap_or(*x..*x),
- )
- })
- .collect(),
- text_document: o.tid(),
- })?;
- self.text
- .apply_tedits(&mut { teds })
- .map_err(|_| anyhow!("couldnt apply edits"))?;
- }
- Cmd::RADocs => {
- let u = l.request_immediate::<ExternalDocs>(
- &TextDocumentPositionParams {
- position: self
- .text
- .to_l_position(*self.text.cursor.first())
- .unwrap(),
- text_document: o.tid(),
- },
- )?;
- use rust_analyzer::lsp::ext::ExternalDocsResponse::*;
- std::process::Command::new("firefox-nightly")
- .arg(
- match &u {
- WithLocal(ExternalDocsPair {
- web: Some(x),
- ..
- }) if let Some("doc.rust-lang.org") =
- x.domain()
- && let Some(x) =
- x.path().strip_prefix("/nightly/")
- && option_env!("USER") == Some("os") =>
- format!("http://127.0.0.1:3242/{x}"), // i have a lighttpd server running
- WithLocal(ExternalDocsPair {
- local: Some(url),
- ..
- }) if let Ok(p) = url.to_file_path()
- && p.exists() =>
- url.to_string(),
- WithLocal(ExternalDocsPair {
- web: Some(url),
- ..
- })
- | Simple(Some(url)) => url.to_string(),
- _ => return Ok(()),
- }
- .to_string(),
- )
- .stdout(Stdio::null())
- .spawn()
- .unwrap();
+ i.iter_mut()
+ .zip(
+ x.chars().chain([' ']).map(|x| ds.basic(x)).zip(0..).chain(
+ qualifier
+ .map(|x| {
+ Style { bg, fg: color_("#858685"), ..default() }
+ .basic(x)
+ })
+ .zip(repeat(u32::MAX)),
+ ),
+ )
+ .for_each(|(a, (b, i))| {
+ *a = b;
+ if indices.contains(&i) {
+ a.style |= (Style::BOLD, color_("#ffcc66"));
}
- Cmd::RARebuildProcMacros => {
- _ = l.request::<RebuildProcMacros>(&())?;
- }
- Cmd::RACancelFlycheck => l.notify::<CancelFlycheck>(&())?,
- Cmd::RAOpenCargoToml => {
- let Some(GotoDefinitionResponse::Scalar(x)) =
- &l.request_immediate::<OpenCargoToml>(
- &OpenCargoTomlParams { text_document: o.tid() },
- )?
- else {
- bail!("wtf?");
- };
- self.open_loc(x, w);
- }
- Cmd::RARunnables => {
- let x = l
- .runtime
- .block_on(l.runnables(
- o,
- self.text.to_l_position(*self.text.cursor.first()),
- )?)
- .anonymize()?;
- // self.state = State::Runnables;
- }
- _ => unimplemented!(),
- }
-
- Ok(())
- }
+ });
+ to.extend(b);
}
diff --git a/src/edi.rs b/src/edi.rs
index d840540..31ac71a 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -1,6 +1,5 @@
use std::borrow::Cow;
use std::collections::HashMap;
-use std::fs::OpenOptions;
use std::mem::take;
use std::ops::ControlFlow;
use std::path::{Path, PathBuf};
@@ -8,14 +7,12 @@ use std::sync::Arc;
use std::time::SystemTime;
use Default::default;
-use anyhow::anyhow;
use implicit_fn::implicit_fn;
use lsp_server::{Connection, Request as LRq, ResponseError};
use lsp_types::request::*;
use lsp_types::*;
use regex::Regex;
use ropey::Rope;
-use rust_analyzer::lsp::ext::OnTypeFormatting;
use rust_fsm::StateMachine;
use serde_derive::{Deserialize, Serialize};
use tokio::sync::oneshot::Sender;
@@ -29,7 +26,7 @@ pub mod st;
use st::*;
use crate::bar::Bar;
-use crate::complete::Complete;
+use crate::com::Complete;
use crate::hov::{self, Hovr};
use crate::lsp::{
self, Anonymize, Client, Map_, PathURI, RedrawAfter, RequestError, Rq,
@@ -38,7 +35,9 @@ use crate::meta::META;
use crate::sym::{Symbols, SymbolsList, SymbolsType};
use crate::text::cursor::{Ronge, ceach};
use crate::text::hist::{ClickHistory, Hist};
-use crate::text::{self, Mapping, RopeExt, SortTedits, TextArea};
+use crate::text::{
+ self, CoerceOption, Mapping, RopeExt, SortTedits, TextArea,
+};
use crate::{
BoolRequest, CDo, CompletionAction, CompletionState, act, alt, ctrl,
filter, hash, shift, sig, sym, trm,
@@ -178,11 +177,6 @@ macro_rules! change {
($self:ident, $w:expr) => {
change!(@$self, Some($w))
};
- (just $self:ident) => {
- lsp!($self + p).map(|(x, origin)| {
- x.edit(&origin, $self.text.rope.to_string()).unwrap();
- })
- };
(@$self:ident, $w:expr) => {
lsp!($self + p).map(|(x, origin)| {
x.edit(&origin, $self.text.rope.to_string()).unwrap();
@@ -217,7 +211,6 @@ macro_rules! change {
});
};
}
-
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" {
@@ -267,6 +260,7 @@ impl Editor {
.map(|x| x.path().to_owned())
.collect::<Vec<_>>()
});
+ assert!(me.tree.is_some());
let l = me.workspace.as_ref().map(|workspace| {
let dh = std::panic::take_hook();
let main = std::thread::current_id();
@@ -388,10 +382,8 @@ impl Editor {
self.bar.last_action = "saved".into();
lsp!(self + p).map(|(l, o)| {
let v = l.runtime.block_on(l.format(o)).unwrap();
- if let Some(v) = v {
- if let Err(()) =
- self.text.apply_tedits_adjusting(&mut { v })
- {
+ for v in v.coerce() {
+ if let Err(()) = self.text.apply_adjusting(&v) {
eprintln!("unhappy fmt")
}
}
@@ -453,7 +445,7 @@ impl Editor {
|x, (_, p)| {
let Some(p) = p else { unreachable!() };
x.ok().flatten().map(|r| sym::Symbols {
- data: (r, p.data.1, p.data.2),
+ r,
selection: 0,
vo: 0,
..p
@@ -728,8 +720,6 @@ impl Editor {
vo: 0,
cells: cells.into(),
},
- range: x.range,
- // range: x.range.and_then(|x| text.l_range(x)),
}
.into(),
))
@@ -809,8 +799,23 @@ impl Editor {
self.hist.lc = text.cursor.clone();
}
Some(Do::GoToDefinition) => {
- if let Some(x) = self.requests.def.result.clone() {
- self.open_loclink(&x, w.clone());
+ if let Some(LocationLink {
+ ref target_uri,
+ target_range,
+ ..
+ }) = self.requests.def.result
+ {
+ self.open(
+ &target_uri.to_file_path().unwrap(),
+ w.clone(),
+ )
+ .unwrap();
+
+ self.text.cursor.just(
+ self.text.l_position(target_range.start).unwrap(),
+ &self.text.rope,
+ );
+ self.text.scroll_to_cursor();
}
}
Some(Do::InsertCursorAtMouse) => {
@@ -826,7 +831,6 @@ impl Editor {
_ => unreachable!(),
}
}
-
pub fn nav_back(&mut self) {
self.chist.back().map(|x| {
self.text.cursor.just(
@@ -941,9 +945,9 @@ impl Editor {
else {
unreachable!()
};
- x.data.2 = sym::SymbolsType::Document;
+ x.ty = sym::SymbolsType::Document;
let p = p.to_owned();
- take(&mut x.data.0);
+ take(&mut x.r);
*request = Some((
DropH::new(lsp.runtime.spawn(
window.redraw_after(async move {
@@ -956,12 +960,6 @@ impl Editor {
(),
));
},
- Some(Do::ProcessCommand(x)) => {
- let z = x.sel();
- if let Err(e) = self.handle_command(z, window.clone()) {
- self.bar.last_action = format!("{e}");
- }
- }
Some(Do::SymbolsHandleKey) => {
if let Some(lsp) = lsp!(self) {
let State::Symbols(Rq { result: Some(x), request }) =
@@ -978,7 +976,7 @@ impl Editor {
.is_some()
|| ptedit != x.tedit.rope
{
- if x.data.2 == SymbolsType::Workspace {
+ if x.ty == SymbolsType::Workspace {
*request = Some((
DropH::new(
lsp.runtime.spawn(
@@ -1007,13 +1005,6 @@ impl Editor {
unreachable!()
};
x.next();
- match x.sel().at {
- sym::GoTo::R(x) => {
- let x = self.text.l_range(x).unwrap();
- self.text.vo = self.text.char_to_line(x.start);
- }
- _ => {}
- }
}
Some(Do::SymbolsSelectPrev) => {
let State::Symbols(Rq { result: Some(x), .. }) =
@@ -1022,13 +1013,18 @@ impl Editor {
unreachable!()
};
x.back();
- match x.sel().at {
- sym::GoTo::R(x) => {
- let x = self.text.l_range(x).unwrap();
- self.text.vo = self.text.char_to_line(x.start);
- }
- _ => {}
- }
+ }
+ Some(Do::CommandNext) => {
+ let State::Command(x) = &mut self.state else {
+ unreachable!()
+ };
+ x.next();
+ }
+ Some(Do::CommandPrev) => {
+ let State::Command(x) = &mut self.state else {
+ unreachable!()
+ };
+ x.back();
}
Some(Do::SymbolsSelect) => {
let State::Symbols(Rq { result: Some(x), .. }) =
@@ -1044,7 +1040,7 @@ impl Editor {
let f = x
.uri
.to_file_path()
- .map_err(|()| anyhow!("dammit"))?
+ .map_err(|()| anyhow::anyhow!("dammit"))?
.canonicalize()?;
self.state = State::Default;
self.requests.complete = CompletionState::None;
@@ -1058,7 +1054,7 @@ impl Editor {
let p = self
.text
.l_position(r.start)
- .ok_or(anyhow!("rah"))?;
+ .ok_or(anyhow::anyhow!("rah"))?;
if p != 0 {
self.text.cursor.just(p, &self.text.rope);
}
@@ -1236,58 +1232,14 @@ impl Editor {
self.requests.complete
{
} else {
- if let Some(x) = handle2(
- &event.logical_key,
- &mut self.text,
- lsp!(self + p),
- ) && let Some((l, p)) = lsp!(self + p)
- && let Some(
- InitializeResult {
- capabilities:
- ServerCapabilities {
- document_on_type_formatting_provider:
- Some(DocumentOnTypeFormattingOptions {
- first_trigger_character,
- more_trigger_character: Some(t),
- }),
- ..
- },
- ..
- },
- ..,
- ) = &l.initialized
- && (first_trigger_character == first_trigger_character
- || t.iter().any(|y| y == x))
- && self.text.cursor.inner.len() == 1
- && change!(just self).is_some()
- && let Ok(Some(mut x)) = l
- .request_immediate::<OnTypeFormatting>(
- &DocumentOnTypeFormattingParams {
- text_document_position:
- TextDocumentPositionParams {
- text_document: p.tid(),
- position: self
- .text
- .to_l_position(
- *self.text.cursor.first(),
- )
- .unwrap(),
- },
- ch: x.into(),
- options: FormattingOptions {
- tab_size: 4,
- ..default()
- },
- },
- )
- {
- x.sort_tedits();
- for x in x {
- self.text.apply_snippet_tedit(&x).unwrap();
- }
+ handle2(
+ &event.logical_key,
+ &mut self.text,
+ lsp!(self + p),
+ );
}
- };
self.text.scroll_to_cursor();
+ inlay!(self);
if cb4 != self.text.cursor.first()
&& let CompletionState::Complete(Rq {
result: Some(c),
@@ -1703,38 +1655,33 @@ impl Editor {
let position = self.text.line_to_char(y);
self.text.cursor.add(position + x, &self.text.rope);
}
+ Some(Do::ProcessCommand(text)) => match text.sel() {
+ "w" => self.save(),
+ "q" => return ControlFlow::Break(()),
+ "exit-vim-mode" => {
+ self.state = State::Default;
+ }
+ _ => {}
+ },
None => {}
}
ControlFlow::Continue(())
}
pub fn apply_wsedit(&mut self, x: WorkspaceEdit, f: &Path) {
+ let mut f_ = |edits: &mut [SnippetTextEdit]| {
+ edits.sort_tedits();
+ // let mut first = false;
+ for edit in edits {
+ self.text.apply_snippet_tedit(edit).unwrap();
+ }
+ };
let mut f2 =
- |TextDocumentEdit { mut edits, text_document, .. }| {
- edits.sort_tedits();
+ |TextDocumentEdit { edits, text_document, .. }| {
if text_document.uri != f.tid().uri {
- let f = OpenOptions::new()
- .read(true)
- .create(true)
- .write(true)
- .open(text_document.uri.path())
- .unwrap();
- let mut r = Rope::from_reader(f).unwrap();
- for edit in &edits {
- TextArea::apply_snippet_tedit_raw(edit, &mut r);
- }
- r.write_to(
- OpenOptions::new()
- .write(true)
- .truncate(true)
- .open(text_document.uri.path())
- .unwrap(),
- )
- .unwrap();
- } else {
- for edit in &edits {
- self.text.apply_snippet_tedit(edit).unwrap();
- }
+ log::error!("didnt apply to {}", text_document.uri);
+ return;
}
+ f_(&mut { edits });
};
match x {
WorkspaceEdit {
@@ -1859,23 +1806,8 @@ impl Editor {
.unwrap();
});
}
- self.set_title(w);
Ok(())
}
- pub fn set_title(&self, w: Option<Arc<Window>>) {
- if let Some(x) = w
- && let Some(t) = self.title()
- {
- x.set_title(&t);
- }
- }
- pub fn title(&self) -> Option<String> {
- [self.workspace.as_deref(), self.origin.as_deref()]
- .try_map(|x| {
- x.and_then(Path::file_name).and_then(|x| x.to_str())
- })
- .map(|[wo, or]| format!("gracilaria - {wo} - {or}"))
- }
pub fn store(&mut self) -> anyhow::Result<()> {
let ws = self.workspace.clone();
@@ -1905,33 +1837,6 @@ impl Editor {
}
Ok(())
}
- /// this is so dumb
- pub fn open_loc(
- &mut self,
- Location { uri, range }: &Location,
- w: Arc<Window>,
- ) {
- self.open(&uri.to_file_path().unwrap(), w.clone()).unwrap();
-
- self.text.cursor.just(
- self.text.l_position(range.start).unwrap(),
- &self.text.rope,
- );
- self.text.scroll_to_cursor();
- }
- pub fn open_loclink(
- &mut self,
- LocationLink { target_uri, target_range, .. }: &LocationLink,
- w: Arc<Window>,
- ) {
- self.open(&target_uri.to_file_path().unwrap(), w.clone()).unwrap();
-
- self.text.cursor.just(
- self.text.l_position(target_range.start).unwrap(),
- &self.text.rope,
- );
- self.text.scroll_to_cursor();
- }
}
use NamedKey::*;
diff --git a/src/edi/st.rs b/src/edi/st.rs
index de25b1b..3186b05 100644
--- a/src/edi/st.rs
+++ b/src/edi/st.rs
@@ -36,7 +36,44 @@ rust_fsm::state_machine! {
pub(crate) State => #[derive(Debug)] pub(crate) Action => #[derive(Debug)] pub(crate) Do
Dead => K(Key => _) => Dead,
+Normal => {
+ K(Key::Character(x) if x == "i") => Insert,
+ K(Key::Character(x) if x == ":") => Command (Commands => default()),
+ K(Key::Character(x) if x == "/") => Procure((default(), InputRequest::Search)),
+ K(Key::Character(x) if x == "p") => _ [Paste],
+ K(Key::Character(x) if x == "u") => _ [Undo],
+ K(Key::Character(x) if x == "y") => _ [Redo],
+ K(Key::Named(Space)) => SpaceMode,
+ K(_) => _,
+ M(_) => _,
+ C(_) => _,
+},
+Insert => {
+ K(Key::Named(Escape)) => Normal,
+ K(k) => _ [Edit],
+ K(_) => _,
+ M(_) => _,
+ C(_) => _,
+},
+Command(_) => K(Key::Named(Escape)) => Normal,
+Command(t) => K(Key::Named(Enter)) => Normal [ProcessCommand(Commands => t)],
+Command(t) => K(Key::Named(Tab) if shift()) => _ [CommandPrev],
+Command(t) => K(Key::Named(Tab)) => _ [CommandNext],
+Command(mut t) => K(k) => Command({ handle2(&k, &mut t.tedit, None); t }),
+Command(t) => C(_) => _,
+Command(t) => M(_) => _,
+Command(t) => K(_) => _,
+
+SpaceMode => K(Key::Named(Escape)) => Normal,
+SpaceMode => K(Key::Character(x) if x == "f") => _ [Symbols],
+SpaceMode => K(Key::Character(x) if x == "a") => _ [CodeAction],
+SpaceMode => K(Key::Character(x) if x == "c") => _ [Comment(Self::SpaceMode)],
+SpaceMode => C(_) => _,
+SpaceMode => M(_) => _,
+SpaceMode => K(_) => _,
+
Default => {
+ K(Key::Character(x) if x == "n" && ctrl()) => Normal,
K(Key::Character(x) if x == "s" && ctrl()) => Save [Save],
K(Key::Character(x) if x == "q" && ctrl()) => Dead [Quit],
K(Key::Character(x) if x == "v" && ctrl()) => _ [Paste],
@@ -50,7 +87,6 @@ Default => {
K(Key::Character(x) if x == "0" && ctrl()) => _ [MatchingBrace],
K(Key::Character(x) if x == "`" && ctrl()) => _ [SpawnTerminal],
K(Key::Character(y) if y == "/" && ctrl()) => Default [Comment(State => State::Default)],
- K(Key::Character(x) if x == "p" && ctrl()) => Command(Commands => Commands::default()),
K(Key::Named(F1)) => Procure((default(), InputRequest::RenameSymbol)),
K(Key::Named(k @ (ArrowUp | ArrowDown)) if alt()) => _ [InsertCursor(Direction => {
if k == ArrowUp {Direction::Above} else { Direction::Below }
@@ -71,19 +107,12 @@ Default => {
K(_) => _ [Edit],
M(_) => _,
},
-Command(_) => K(Key::Named(Escape)) => Default,
-Command(t) => K(Key::Named(Enter)) => Default [ProcessCommand(Commands => t)],
-Command(mut t) => K(Key::Named(Tab) if shift()) => Command({ t.back();t }),
-Command(mut t) => K(Key::Named(Tab)) => Command({ t.next(); t }),
-Command(mut t) => K(k) => Command({ handle2(&k, &mut t.tedit, None); t }),
-Command(t) => C(_) => _,
-Command(t) => K(_) => _,
Symbols(Rq { result: Some(_x), request: _rq }) => {
K(Key::Named(Tab) if shift()) => _ [SymbolsSelectNext],
K(Key::Named(ArrowDown)) => _ [SymbolsSelectNext],
K(Key::Named(ArrowUp | Tab)) => _ [SymbolsSelectPrev],
K(Key::Named(Enter)) => _ [SymbolsSelect],
- K(Key::Named(Escape)) => Default,
+ K(Key::Named(Escape)) => Normal,
},
Symbols(Rq::<Symbols, Option<SymbolsList>, (), AQErr> => _rq) => {
K(Key::Character(x) if x == "d" && ctrl()) => _ [SwitchType], // crahs cond methinks
diff --git a/src/hov.rs b/src/hov.rs
index a0f8261..9b74b51 100644
--- a/src/hov.rs
+++ b/src/hov.rs
@@ -306,8 +306,6 @@ fn t() {
pub struct Hovr {
pub(crate) span: Option<[(VisualX, usize); 2]>,
pub(crate) item: crate::text::CellBuffer,
- #[serde(skip)]
- pub(crate) range: Option<lsp_types::Range>,
}
pub type VisualX = usize;
diff --git a/src/lsp.rs b/src/lsp.rs
index 88ab350..df96a9b 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -24,7 +24,6 @@ use lsp_server::{
use lsp_types::notification::*;
use lsp_types::request::*;
use lsp_types::*;
-use rust_analyzer::lsp::ext::*;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::sync::oneshot;
@@ -64,7 +63,6 @@ pub enum RequestError<X> {
Rx(PhantomData<X>),
Failure(Re, #[serde(skip)] Option<Backtrace>),
Cancelled(Re, DiagnosticServerCancellationData),
- Send(Message),
}
pub type AQErr = RequestError<LSPError>;
impl Request for LSPError {
@@ -80,7 +78,6 @@ pub trait Anonymize<T> {
impl<T, E> Anonymize<T> for Result<T, RequestError<E>> {
fn anonymize(self) -> Result<T, RequestError<LSPError>> {
self.map_err(|e| match e {
- RequestError::Send(x) => RequestError::Send(x),
RequestError::Rx(_) => RequestError::Rx(PhantomData),
RequestError::Failure(r, b) => RequestError::Failure(r, b),
RequestError::Cancelled(r, d) => RequestError::Cancelled(r, d),
@@ -99,16 +96,9 @@ impl<X: Request + std::fmt::Debug> std::error::Error for RequestError<X> {
None
}
}
-impl<X> From<SendError<Message>> for RequestError<X> {
- fn from(x: SendError<Message>) -> Self {
- Self::Send(x.into_inner())
- }
-}
impl<X: Request> Display for RequestError<X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::Send(x) =>
- write!(f, "{} failed; couldnt send {x:?}", X::METHOD),
Self::Rx(_) =>
write!(f, "{} failed; couldnt get from thingy", X::METHOD),
Self::Failure(x, bt) => write!(
@@ -134,12 +124,6 @@ impl Client {
pub fn cancel(&self, rid: i32) {
_ = self.notify::<Cancel>(&CancelParams { id: rid.into() });
}
- pub fn request_immediate<'me, X: Request>(
- &'me self,
- y: &X::Params,
- ) -> Result<X::Result, RequestError<X>> {
- self.runtime.block_on(self.request::<X>(y)?.0)
- }
#[must_use]
pub fn request<'me, X: Request>(
&'me self,
@@ -465,14 +449,10 @@ impl Client {
>,
> {
self.request::<lsp_request!("workspace/symbol")>(
- &lsp_types::WorkspaceSymbolParams {
+ &WorkspaceSymbolParams {
query: f,
- search_scope: Some(
- lsp_types::WorkspaceSymbolSearchScope::Workspace,
- ),
- search_kind: Some(
- lsp_types::WorkspaceSymbolSearchKind::AllSymbols,
- ),
+ search_scope: Some(WorkspaceSymbolSearchScope::Workspace),
+ search_kind: Some(WorkspaceSymbolSearchKind::AllSymbols),
..Default::default()
},
)
@@ -484,13 +464,15 @@ impl Client {
f: &Path,
t: &'a mut TextArea,
) {
- if let Ok([x]) = self.runtime.block_on(
- self.request::<MatchingBrace>(&MatchingBraceParams {
- text_document: f.tid(),
- positions: vec![
- t.to_l_position(*t.cursor.first()).unwrap(),
- ],
- })
+ if let Ok(Some([x])) = self.runtime.block_on(
+ self.request::<lsp_request!("experimental/matchingBrace")>(
+ &MatchingBraceParams {
+ text_document: f.tid(),
+ positions: vec![
+ t.to_l_position(*t.cursor.first()).unwrap(),
+ ],
+ },
+ )
.unwrap()
.0,
) {
@@ -555,6 +537,14 @@ impl Client {
)
.unwrap()
.0
+ .map(|x| {
+ x.map(|x| {
+ x.map(|mut x| {
+ x.sort_tedits();
+ x
+ })
+ })
+ })
}
pub fn rq_semantic_tokens(
&'static self,
@@ -603,10 +593,12 @@ impl Client {
let r = self
.runtime
.block_on(
- self.request::<OnEnter>(&TextDocumentPositionParams {
- text_document: f.tid(),
- position: t.to_l_position(*c).unwrap(),
- })
+ self.request::<lsp_request!("experimental/onEnter")>(
+ &TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: t.to_l_position(*c).unwrap(),
+ },
+ )
.unwrap()
.0,
)
@@ -622,25 +614,6 @@ impl Client {
}
});
}
- pub fn runnables(
- &self,
- t: &Path,
- c: Option<Position>,
- ) -> Result<
- impl Future<
- Output = Result<
- Vec<Runnable>,
- RequestError<Runnables>,
- >,
- >,
- SendError<Message>,
- > {
- self.request::<Runnables>(&RunnablesParams {
- text_document: t.tid(),
- position: c,
- })
- .map(|(x, _)| x)
- }
}
pub fn run(
(tx, rx): (Sender<Message>, Receiver<Message>),
@@ -695,9 +668,6 @@ pub fn run(
..default()
}),
text_document: Some(TextDocumentClientCapabilities {
- on_type_formatting: Some(DocumentOnTypeFormattingClientCapabilities {
- dynamic_registration: Some(false),
- }),
document_highlight: Some(default()),
formatting: Some(DynamicRegistrationClientCapabilities { dynamic_registration: Some(false) }),
inlay_hint: Some(InlayHintClientCapabilities { dynamic_registration: None, resolve_support: Some(InlayHintResolveClientCapabilities {
@@ -863,7 +833,6 @@ pub fn run(
"hoverActions": true,
"workspaceSymbolScopeKindFiltering": true,
"onEnter": true,
- "localDocs": true,
}}),
..default()
},
@@ -879,11 +848,6 @@ pub fn run(
"enable": true,
"attributes": { "enable": true }
},
- "hover": {
- "documentation": {
- "keywords": { "enable": false },
- },
- },
"inlayHints": {
"closureReturnTypeHints": { "enable": "with_block" },
"closingBraceHints": { "minLines": 5 },
@@ -892,7 +856,6 @@ pub fn run(
"rangeExclusiveHints": { "enable": true },
"closureCaptureHints": { "enable": true },
},
- "typing": { "triggerChars": ".=<>{(+" },
"assist": { "preferSelf": true },
"checkOnSave": true,
"diagnostics": { "enable": true },
@@ -919,9 +882,6 @@ pub fn run(
"autoself": { "enable": true, },
"privateEditable": { "enable": true },
},
- "imports": {
- "granularity": "group",
- },
}}),
trace: None,
workspace_folders: Some(vec![workspace]),
diff --git a/src/main.rs b/src/main.rs
index cdde4c4..1311b32 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,5 @@
#![feature(
btree_set_entry,
- associated_type_defaults,
- array_try_map,
tuple_trait,
unboxed_closures,
fn_traits,
@@ -45,6 +43,9 @@ mod act;
mod edi;
mod git;
mod meta;
+// mod new;
+// mod new;
+mod commands;
mod rnd;
mod sym;
mod trm;
@@ -80,8 +81,7 @@ use crate::edi::st::*;
use crate::lsp::RqS;
use crate::text::{TextArea, col, is_word};
mod bar;
-mod commands;
-mod complete;
+mod com;
pub mod hov;
mod lsp;
pub mod menu;
@@ -139,7 +139,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
None => None,
};
let (fw, fh) = dsb::dims(&fonts.bold, ls);
- let title = ed.title();
+
let app = winit_app::WinitAppBuilder::with_init(
move |elwt| {
let window = winit_app::make_window(elwt, |x| {
@@ -159,8 +159,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
if let Some(x) = w.take() {
x.send(window.clone()).unwrap();
}
- let w_ = window.clone();
- title.as_deref().map(move |x| w_.set_title(x));
+
window.set_ime_allowed(true);
window.set_ime_purpose(winit::window::ImePurpose::Terminal);
let context =
@@ -480,7 +479,7 @@ rust_fsm::state_machine! {
Complete(_x) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, trigger_character:None })],
}
-use complete::Complete;
+use com::Complete;
impl Default for CompletionState {
fn default() -> Self {
Self::None
diff --git a/src/menu.rs b/src/menu.rs
index 3c5f7a1..5c2d092 100644
--- a/src/menu.rs
+++ b/src/menu.rs
@@ -1,4 +1,3 @@
-pub mod generic;
use std::borrow::Cow;
use std::cmp::Reverse;
use std::sync::LazyLock;
@@ -89,12 +88,3 @@ pub trait Key<'a> {
self.key().into()
}
}
-pub fn charc(c: &str) -> usize {
- c.chars().count()
-}
-
-impl<'a> crate::menu::Key<'a> for &'a str {
- fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> {
- *self
- }
-}
diff --git a/src/menu/generic.rs b/src/menu/generic.rs
deleted file mode 100644
index ba48d76..0000000
--- a/src/menu/generic.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use std::fmt::Debug;
-use std::path::Path;
-
-use Default::default;
-use dsb::Cell;
-
-use crate::menu::{Key, back, filter, next, score};
-use crate::text::TextArea;
-
-pub struct GenericMenu<T: MenuData> {
- pub data: T::Data,
- pub tedit: TextArea,
- pub selection: usize,
- pub vo: usize,
-}
-impl<T: MenuData<Data: Clone>> Clone for GenericMenu<T> {
- fn clone(&self) -> Self {
- Self {
- data: self.data.clone(),
- tedit: self.tedit.clone(),
- selection: self.selection.clone(),
- vo: self.vo.clone(),
- }
- }
-}
-impl<T: MenuData<Data: Default>> Default for GenericMenu<T> {
- fn default() -> Self {
- Self {
- data: default(),
- tedit: default(),
- selection: default(),
- vo: default(),
- }
- }
-}
-impl<T: MenuData<Data: Debug>> Debug for GenericMenu<T> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct(&format!(
- "GenericMenu<{}>",
- std::any::type_name::<T>()
- ))
- .field("data", &self.data)
- .field("tedit", &self.tedit)
- .field("selection", &self.selection)
- .field("vo", &self.vo)
- .finish()
- }
-}
-pub trait MenuData {
- const HEIGHT: usize = 30;
- type Data;
- type Element<'a>: Key<'a>;
-
- fn gn<'a>(
- x: &'a Self::Data,
- ) -> impl Iterator<Item = Self::Element<'a>>;
- fn r(
- data: &'_ Self::Data,
- elem: Self::Element<'_>,
- workspace: &Path,
- columns: usize,
- selected: bool,
- indices: &[u32],
- to: &mut Vec<Cell>,
- );
- fn filter_c<'a>(
- x: &'a Self::Data,
- f: &str,
- ) -> impl Iterator<Item = Self::Element<'a>> {
- filter(Self::gn(x), f)
- }
- fn score_c<'a>(
- x: impl Iterator<Item = Self::Element<'a>>,
- f: &str,
- ) -> Vec<(u32, Self::Element<'a>, Vec<u32>)> {
- score(x, f)
- }
-}
-
-impl<T: MenuData> GenericMenu<T> {
- fn f(&self) -> String {
- self.tedit.rope.to_string()
- }
- pub fn next(&mut self)
- where
- [(); T::HEIGHT]:,
- {
- let n = T::filter_c(&self.data, &self.f()).count();
- // coz its bottom up
- back::<{ T::HEIGHT }>(n, &mut self.selection, &mut self.vo);
- }
-
- pub fn sel(&self) -> T::Element<'_> {
- let f = self.f();
- T::score_c(T::filter_c(&self.data, &f), &f)
- .swap_remove(self.selection)
- .1
- }
-
- pub fn back(&mut self)
- where
- [(); T::HEIGHT]:,
- {
- let n = T::filter_c(&self.data, &self.f()).count();
- next::<{ T::HEIGHT }>(n, &mut self.selection, &mut self.vo);
- }
- pub fn cells(&self, c: usize, ws: &Path) -> Vec<Cell> {
- let f = self.f();
- let mut out = vec![];
- let v = T::score_c(T::filter_c(&self.data, &f), &f);
- let vlen = v.len();
- let i =
- v.into_iter().zip(0..vlen).skip(self.vo).take(T::HEIGHT).rev();
-
- i.for_each(|((_, x, indices), i)| {
- T::r(
- &self.data,
- x,
- ws,
- c,
- i == self.selection,
- &indices,
- &mut out,
- )
- });
-
- out
- }
-}
diff --git a/src/rnd.rs b/src/rnd.rs
index 8e2ba9b..3c27366 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -21,8 +21,8 @@ use crate::edi::{Editor, lsp_m};
use crate::lsp::Rq;
use crate::text::{CoerceOption, RopeExt, col, color_};
use crate::{
- BG, BORDER, CompletionAction, CompletionState, FG, FONT, complete,
- filter, lsp, sig,
+ BG, BORDER, CompletionAction, CompletionState, FG, FONT, com, filter,
+ lsp, sig,
};
#[implicit_fn::implicit_fn]
@@ -134,15 +134,6 @@ pub fn render(
x.style.fg = col!("#FFD173");
});
} }
- if let Some(crate::hov::Hovr{ range:Some(r),..} ) = &ed.requests.hovering.result {
- x.get_range(text.map_to_visual((r.start.character as _, r.start.line as _)),
- text.map_to_visual((r.end.character as usize, r.end.line as _)))
- .for_each(|x| {
- x.style.secondary_color = col!("#73d0ff");
- x.style.flags |= Style::UNDERCURL;
- });
- // x.range;
- }
if let Some((lsp, p)) = lsp_m!(ed + p) && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) {
#[derive(Copy, Clone, Debug)]
enum EType {
@@ -678,13 +669,13 @@ pub fn render(
BORDER,
);
}
- State::Command(x) => 'out: {
+ State::Symbols(Rq { result: Some(x), .. }) => 'out: {
let ws = ed.workspace.as_deref().unwrap();
let c = x.cells(50, ws);
// let (_x, _y) = text.cursor_visual();
let _x = 0;
let _y = r - 1;
- let Ok((_, left, top, w, h)) = place_around(
+ let Ok((_is_above, left, top, w, h)) = place_around(
(_x, _y),
i.copy(),
&c,
@@ -707,14 +698,14 @@ pub fn render(
h as _,
BORDER,
);
- }
- State::Symbols(Rq { result: Some(x), .. }) => 'out: {
+ },
+ State::Command(x) => 'out: {
let ws = ed.workspace.as_deref().unwrap();
let c = x.cells(50, ws);
// let (_x, _y) = text.cursor_visual();
let _x = 0;
let _y = r - 1;
- let Ok((_is_above, left, top, w, h)) = place_around(
+ let Ok((is_above, left, top, w, mut h)) = place_around(
(_x, _y),
i.copy(),
&c,
@@ -744,7 +735,7 @@ pub fn render(
CompletionState::Complete(Rq {
result: Some(ref x), ..
}) => {
- let c = complete::s(x, 40, &filter(&text));
+ let c = com::s(x, 40, &filter(&text));
if c.len() == 0 {
ed.requests
.complete
@@ -942,17 +933,15 @@ pub fn render(
}
pub fn simplify_path(x: &str) -> String {
- static DEP: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new(r"\.cargo\/git\/checkouts\/(?<name>[^/]+)\-[a-f0-9]+\/[a-f0-9]+").unwrap()
- });
- static DEP2: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new(r"\.cargo\/registry\/src/index.crates.io-[0-9a-f]+/(?<name>[^/]+)\-(?<version>[0-9]+\.[0-9]+\.[0-9]+)").unwrap()
- });
- static RUST_SRC: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new(r".rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library").unwrap()
- });
- let x = x.replace(env!("HOME"), " ");
- [(&*RUST_SRC, " "), (&*DEP, " /$name"), (&*DEP2, " /$name")]
- .into_iter()
- .fold(x, |acc, (r, repl)| r.replace(&acc, repl).into_owned())
+ static DEP: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\.cargo\/git\/checkouts\/(?<name>[^/]+)\-[a-f0-9]+\/[a-f0-9]+").unwrap());
+ static DEP2: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\.cargo\/registry\/src/index.crates.io-[0-9a-f]+/(?<name>[^/]+)\-(?<version>[0-9]+\.[0-9]+\.[0-9]+)").unwrap());
+ static RUST_SRC: LazyLock<Regex> = LazyLock::new(|| Regex::new(r".rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library").unwrap());
+ let x = x.replace(env!("HOME"), " ");
+ [
+ (&*RUST_SRC, " "),
+ (&*DEP, " /$name"),
+ (&*DEP2, " /$name"),
+ ].into_iter().fold(x, |acc, (r, repl)| {
+ r.replace(&acc, repl).into_owned()
+ })
}
diff --git a/src/sym.rs b/src/sym.rs
index 1a0db44..327e3e7 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -9,50 +9,18 @@ use itern::Iter3;
use lsp_types::*;
use crate::FG;
-use crate::menu::generic::{GenericMenu, MenuData};
-use crate::menu::{Key, charc};
-use crate::text::{col, color_, set_a};
-pub enum Symb {}
-impl MenuData for Symb {
- type Data = (SymbolsList, Vec<SymbolInformation>, SymbolsType);
+use crate::menu::{Key, back, filter, next, score};
+use crate::text::{TextArea, col, color_, set_a};
- type Element<'a> = UsedSI<'a>;
-
- fn gn(
- (r, tree, _): &Self::Data,
- ) -> impl Iterator<Item = Self::Element<'_>> {
- match r {
- SymbolsList::Document(DocumentSymbolResponse::Flat(x)) =>
- Iter3::A(x.iter().map(UsedSI::from)),
- SymbolsList::Document(DocumentSymbolResponse::Nested(x)) =>
- Iter3::B(x.iter().flat_map(|x| gen move {
- let mut q = VecDeque::with_capacity(12);
- q.push_back(x);
- while let Some(x) = q.pop_front() {
- q.extend(x.children.iter().flatten());
- yield x.into();
- }
- })),
- SymbolsList::Workspace(WorkspaceSymbolResponse::Flat(x)) =>
- Iter3::C(chain(tree, x.iter()).map(UsedSI::from)),
- _ => unreachable!("please no"),
- }
- }
-
- fn r(
- &(.., sty): &Self::Data,
- x: Self::Element<'_>,
- workspace: &Path,
- c: usize,
- selected: bool,
- indices: &[u32],
- to: &mut Vec<Cell>,
- ) {
- r(x, workspace, c, selected, indices, to, sty)
- }
+#[derive(Debug, Default)]
+pub struct Symbols {
+ pub r: SymbolsList,
+ pub tree: Vec<SymbolInformation>,
+ pub tedit: TextArea,
+ pub selection: usize,
+ pub vo: usize,
+ pub ty: SymbolsType,
}
-pub type Symbols = GenericMenu<Symb>;
-
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum GoTo<'a> {
Loc(&'a Location),
@@ -124,8 +92,8 @@ impl Default for SymbolsList {
Self::Workspace(WorkspaceSymbolResponse::Flat(vec![]))
}
}
-
-impl GenericMenu<Symb> {
+const N: usize = 30;
+impl Symbols {
pub fn new(tree: &[PathBuf]) -> Self {
let tree = tree
.iter()
@@ -145,7 +113,80 @@ impl GenericMenu<Symb> {
tags: None,
})
.collect();
- Self { data: (default(), tree, default()), ..default() }
+ Self { tree, ..Default::default() }
+ }
+ fn f(&self) -> String {
+ self.tedit.rope.to_string()
+ }
+ pub fn next(&mut self) {
+ let n = filter_c(self, &self.f()).count();
+ // coz its bottom up
+ back::<N>(n, &mut self.selection, &mut self.vo);
+ }
+
+ pub fn sel(&self) -> UsedSI<'_> {
+ let f = self.f();
+ score_c(filter_c(self, &f), &f)[self.selection].1
+ }
+
+ pub fn back(&mut self) {
+ let n = filter_c(self, &self.f()).count();
+ next::<N>(n, &mut self.selection, &mut self.vo);
+ }
+ pub fn cells(&self, c: usize, ws: &Path) -> Vec<Cell> {
+ let f = self.f();
+ let mut out = vec![];
+ let v = score_c(filter_c(self, &f), &f);
+ let vlen = v.len();
+ let i = v.into_iter().zip(0..vlen).skip(self.vo).take(N).rev();
+
+ // let Some((s, x)) = i.next() else {
+ // return vec![];
+ // };
+
+ // let mut q = Dq::<_, 13>::new((s, x));
+ // for (s, x) in i {
+ // if q.first().0 <= s {
+ // q.push_front((s, x));
+ // }
+ // }
+
+ // fuzzy_aho_corasick::FuzzyAhoCorasickBuilder::new()
+ // .fuzzy(
+ // FuzzyLimits::new()
+ // .insertions(20)
+ // .deletions(2)
+ // .edits(4)
+ // .substitutions(5)
+ // .swaps(3),
+ // .penalties(FuzzyPenalties {
+ // )
+ // insertion: 0.0,
+ // deletion: 1.0,
+ // substitution: 0.5,
+ // swap: 0.5,
+ // })
+ // .build(
+ // y.iter().map(|x| x.filter_text.as_deref().unwrap_or(&x.label)),
+ // )
+ // .search(filter, 0.25)
+ // .into_iter()
+ // .map(|x| &y[x.pattern_index])
+ // // .take(13);
+ // // for x in y
+ // // .iter()
+ // // .filter(|x| {
+ // // x.filter_text
+ // // .as_deref()
+ // // .unwrap_or(&x.label)
+ // // .starts_with(filter)
+ // // })
+ // .take(13)
+ i.for_each(|((_, x, indices), i)| {
+ r(x, ws, c, i == self.selection, &indices, &mut out, self.ty)
+ });
+
+ out
}
}
@@ -154,6 +195,41 @@ impl<'a> Key<'a> for UsedSI<'a> {
self.name
}
}
+fn score_c<'a>(
+ x: impl Iterator<Item = UsedSI<'a>>,
+ filter: &'_ str,
+) -> Vec<(u32, UsedSI<'a>, Vec<u32>)> {
+ score(x, filter)
+}
+
+fn filter_c<'a>(
+ syms: &'a Symbols,
+ f: &'_ str,
+) -> impl Iterator<Item = UsedSI<'a>> {
+ let x = &syms.r;
+ filter(
+ match x {
+ SymbolsList::Document(DocumentSymbolResponse::Flat(x)) =>
+ Iter3::A(x.iter().map(UsedSI::from)),
+ SymbolsList::Document(DocumentSymbolResponse::Nested(x)) =>
+ Iter3::B(x.iter().flat_map(|x| gen move {
+ let mut q = VecDeque::with_capacity(12);
+ q.push_back(x);
+ while let Some(x) = q.pop_front() {
+ q.extend(x.children.iter().flatten());
+ yield x.into();
+ }
+ })),
+ SymbolsList::Workspace(WorkspaceSymbolResponse::Flat(x)) =>
+ Iter3::C(chain(&syms.tree, x.iter()).map(UsedSI::from)),
+ _ => unreachable!("please no"),
+ },
+ f,
+ )
+}
+fn charc(c: &str) -> usize {
+ c.chars().count()
+}
#[implicit_fn::implicit_fn]
fn r<'a>(
x: UsedSI<'a>,
diff --git a/src/text.rs b/src/text.rs
index 2f4d30a..00a2e65 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -478,50 +478,19 @@ impl TextArea {
}
Ok(())
}
- pub fn apply_snippet_tedit_raw(
- SnippetTextEdit { range,new_text, insert_text_format, .. }: &SnippetTextEdit,
- text: &'_ mut Rope,
- ) -> Option<()> {
- match insert_text_format {
- Some(lsp_types::InsertTextFormat::SNIPPET) => {
- let begin = text.l_position(range.start)?;
- let end = text.l_position(range.end)?;
- text.try_remove(begin..end).ok()?;
- let (_, tex) =
- crate::sni::Snippet::parse(&new_text, begin)?;
- text.try_insert(begin, &tex).ok()?;
- }
- _ => {
- let begin = text.l_position(range.start)?;
- let end = text.l_position(range.end)?;
- text.try_remove(begin..end).ok()?;
- text.try_insert(begin, &new_text).ok()?;
- }
- }
- Some(())
- }
pub fn apply_snippet_tedit(
&mut self,
- SnippetTextEdit { range,new_text, insert_text_format, .. }: &SnippetTextEdit,
+ SnippetTextEdit { text_edit, insert_text_format, .. }: &SnippetTextEdit,
) -> anyhow::Result<()> {
match insert_text_format {
- Some(lsp_types::InsertTextFormat::SNIPPET) => self
- .apply_snippet(&TextEdit {
- range: range.clone(),
- new_text: new_text.clone(),
- })
- .unwrap(),
+ Some(lsp_types::InsertTextFormat::SNIPPET) =>
+ self.apply_snippet(&text_edit).unwrap(),
_ => {
- self.apply_adjusting(&TextEdit {
- range: range.clone(),
- new_text: new_text.clone(),
- })
- .unwrap();
+ self.apply_adjusting(text_edit).unwrap();
}
}
Ok(())
}
-
pub fn apply_snippet(&mut self, x: &TextEdit) -> anyhow::Result<()> {
let begin = self
.l_position(x.range.start)
@@ -948,7 +917,7 @@ impl TextArea {
// arc_swap::Guard<Arc<Box<[SemanticToken]>>>,
// &SemanticTokensLegend,
// )>;
- if leg.is_none() || self.tokens.is_empty() {
+ if leg.is_none() {
self.tree_sit(path, &mut cells);
}
if let Some(tabstops) = &self.tabstops {
@@ -1110,28 +1079,6 @@ impl TextArea {
}
best
}
-
- pub(crate) fn apply_tedits_adjusting(
- &mut self,
- teds: &mut [TextEdit],
- ) -> Result<(), ()> {
- teds.sort_tedits();
- for ted in teds {
- self.apply_adjusting(ted)?;
- }
- Ok(())
- }
-
- pub(crate) fn apply_tedits(
- &mut self,
- teds: &mut [TextEdit],
- ) -> Result<(), ()> {
- teds.sort_tedits();
- for ted in teds {
- self.apply(ted)?;
- }
- Ok(())
- }
}
pub fn is_word(r: char) -> bool {
@@ -1730,7 +1677,7 @@ impl SortTedits for [TextEdit] {
}
impl SortTedits for [SnippetTextEdit] {
fn sort_tedits(&mut self) {
- self.as_mut().sort_by_key(|t| Reverse(t.range.start));
+ self.as_mut().sort_by_key(|t| Reverse(t.text_edit.range.start));
}
}
diff --git a/test b/test
new file mode 100644
index 0000000..df7c913
--- /dev/null
+++ b/test
@@ -0,0 +1,525 @@
+gracilaria v0.1.0 (/run/media/Programming/CodingShit/Rust/Act5Scratch/gracilaria)
+├── amap v0.1.4 (proc-macro)
+│ ├── itertools v0.14.0
+│ │ └── either v1.15.0
+│ ├── proc-macro2 v1.0.106
+│ │ └── unicode-ident v1.0.22
+│ ├── quote v1.0.44
+│ │ └── proc-macro2 v1.0.106 (*)
+│ └── syn v2.0.114
+│ ├── proc-macro2 v1.0.106 (*)
+│ ├── quote v1.0.44 (*)
+│ └── unicode-ident v1.0.22
+├── anyhow v1.0.100
+├── arc-swap v1.8.0
+│ └── rustversion v1.0.22 (proc-macro)
+├── array_chunks v1.0.0
+├── atools v0.1.12
+├── bendy v0.6.1
+│ ├── rustversion v1.0.22 (proc-macro)
+│ ├── serde v1.0.228
+│ │ ├── serde_core v1.0.228
+│ │ └── serde_derive v1.0.228 (proc-macro)
+│ ├── serde_bytes v0.11.19
+│ │ └── serde_core v1.0.228
+│ └── thiserror v2.0.18
+│ └── thiserror-impl v2.0.18 (proc-macro)
+├── car v0.1.3 (proc-macro)
+│ ├── proc-macro2 v1.0.106 (*)
+│ ├── quote v1.0.44 (*)
+│ └── syn v2.0.114 (*)
+├── clipp v0.1.1
+├── crossbeam v0.8.4
+│ ├── crossbeam-channel v0.5.15
+│ │ └── crossbeam-utils v0.8.21
+│ ├── crossbeam-deque v0.8.6
+│ │ ├── crossbeam-epoch v0.9.18
+│ │ └── crossbeam-utils v0.8.21
+│ ├── crossbeam-epoch v0.9.18 (*)
+│ ├── crossbeam-queue v0.3.12
+│ │ └── crossbeam-utils v0.8.21
+│ └── crossbeam-utils v0.8.21
+├── diff-match-patch-rs v0.5.1 (https://git.bendn.org/dmp#8d727531)
+│ ├── percent-encoding v2.3.2
+│ ├── serde v1.0.228 (*)
+│ └── serde_derive v1.0.228 (proc-macro) (*)
+├── dsb v0.1.0 (https://git.bendn.org/dsb#02744132)
+│ ├── array_chunks v1.0.0
+│ ├── atools v0.1.12 (https://git.bendn.org/atools#fd0a7b17)
+│ ├── car v0.1.3 (proc-macro) (*)
+│ ├── fimg v0.4.51 (https://git.bendn.org/fimg#cf01f538)
+│ │ ├── array_chunks v1.0.0
+│ │ ├── atools v0.1.12
+│ │ ├── car v0.1.3 (proc-macro) (*)
+│ │ ├── clipline v0.4.0
+│ │ ├── fer v0.1.1
+│ │ ├── hinted v1.0.0
+│ │ ├── libc v0.2.180
+│ │ ├── lower v0.2.1
+│ │ ├── mattr v0.0.2
+│ │ ├── png v0.17.16
+│ │ ├── qwant v1.0.3
+│ │ ├── umath v0.0.7
+│ │ └── vecto v0.1.1
+│ ├── implicit-fn v0.1.0 (proc-macro)
+│ │ ├── proc-macro2 v1.0.106 (*)
+│ │ ├── quote v1.0.44 (*)
+│ │ └── syn v2.0.114 (*)
+│ ├── itertools v0.14.0 (*)
+│ ├── lower v0.2.1 (*)
+│ ├── lru-cache v0.1.2
+│ │ └── linked-hash-map v0.5.6
+│ ├── serde v1.0.228 (*)
+│ ├── swash v0.2.6
+│ │ ├── skrifa v0.37.0
+│ │ ├── yazi v0.2.1
+│ │ └── zeno v0.3.3
+│ ├── swizzle v0.1.0
+│ └── umath v0.0.7
+├── env_logger v0.11.8
+│ ├── anstream v0.6.21
+│ │ ├── anstyle v1.0.13
+│ │ ├── anstyle-parse v0.2.7
+│ │ ├── anstyle-query v1.1.5
+│ │ ├── colorchoice v1.0.4
+│ │ ├── is_terminal_polyfill v1.70.2
+│ │ └── utf8parse v0.2.2
+│ ├── anstyle v1.0.13
+│ ├── env_filter v0.1.4
+│ │ ├── log v0.4.29
+│ │ └── regex v1.12.2
+│ ├── jiff v0.2.18
+│ └── log v0.4.29
+├── fimg v0.4.51 (https://git.bendn.org/fimg#cf01f538) (*)
+├── helix-core v25.7.1 (https://git.bendn.org/helix#bcfbb875)
+│ ├── anyhow v1.0.100
+│ ├── arc-swap v1.8.0 (*)
+│ ├── bitflags v2.10.0
+│ ├── chrono v0.4.43
+│ │ └── num-traits v0.2.19
+│ │ [build-dependencies]
+│ ├── encoding_rs v0.8.35
+│ │ └── cfg-if v1.0.4
+│ ├── foldhash v0.2.0
+│ ├── globset v0.4.16
+│ │ ├── aho-corasick v1.1.4
+│ │ ├── bstr v1.12.1
+│ │ ├── log v0.4.29
+│ │ ├── regex-automata v0.4.13
+│ │ └── regex-syntax v0.8.8
+│ ├── helix-loader v25.7.1 (https://git.bendn.org/helix#bcfbb875)
+│ │ ├── anyhow v1.0.100
+│ │ ├── cc v1.2.55
+│ │ ├── etcetera v0.10.0
+│ │ ├── helix-stdx v25.7.1 (https://git.bendn.org/helix#bcfbb875)
+│ │ ├── log v0.4.29
+│ │ ├── once_cell v1.21.3
+│ │ ├── serde v1.0.228 (*)
+│ │ ├── tempfile v3.24.0
+│ │ ├── threadpool v1.8.1
+│ │ ├── toml v0.9.11+spec-1.1.0
+│ │ └── tree-house v0.3.0
+│ ├── helix-parsec v25.7.1 (https://git.bendn.org/helix#bcfbb875)
+│ ├── helix-stdx v25.7.1 (https://git.bendn.org/helix#bcfbb875) (*)
+│ ├── imara-diff v0.2.0
+│ │ ├── hashbrown v0.15.5
+│ │ └── memchr v2.7.6
+│ ├── log v0.4.29
+│ ├── nucleo v0.5.0
+│ │ ├── nucleo-matcher v0.3.1
+│ │ ├── parking_lot v0.12.5
+│ │ └── rayon v1.11.0
+│ ├── once_cell v1.21.3
+│ ├── parking_lot v0.12.5 (*)
+│ ├── regex v1.12.2 (*)
+│ ├── regex-cursor v0.1.5
+│ │ ├── log v0.4.29
+│ │ ├── memchr v2.7.6
+│ │ ├── regex-automata v0.4.13 (*)
+│ │ ├── regex-syntax v0.8.8
+│ │ └── ropey v1.6.1
+│ ├── ropey v1.6.1 (*)
+│ ├── serde v1.0.228 (*)
+│ ├── serde_json v1.0.149
+│ │ ├── indexmap v2.13.0
+│ │ ├── itoa v1.0.17
+│ │ ├── memchr v2.7.6
+│ │ ├── serde_core v1.0.228
+│ │ └── zmij v1.0.19
+│ ├── slotmap v1.1.1
+│ │ [build-dependencies]
+│ │ └── version_check v0.9.5
+│ ├── smallvec v1.15.1
+│ ├── smartstring v1.0.1
+│ │ └── static_assertions v1.1.0
+│ │ [build-dependencies]
+│ │ ├── autocfg v1.5.0
+│ │ └── version_check v0.9.5
+│ ├── textwrap v0.16.2
+│ │ ├── smawk v0.3.2
+│ │ ├── unicode-linebreak v0.1.5
+│ │ └── unicode-width v0.2.2
+│ ├── toml v0.9.11+spec-1.1.0 (*)
+│ ├── tree-house v0.3.0 (*)
+│ ├── unicode-general-category v1.1.0
+│ ├── unicode-segmentation v1.12.0
+│ ├── unicode-width v0.1.12
+│ └── url v2.5.8
+│ ├── form_urlencoded v1.2.2
+│ ├── idna v1.1.0
+│ ├── percent-encoding v2.3.2
+│ ├── serde v1.0.228 (*)
+│ └── serde_derive v1.0.228 (proc-macro) (*)
+├── helix-loader v25.7.1 (https://git.bendn.org/helix#bcfbb875) (*)
+├── helix-lsp-types v0.95.1 (https://git.bendn.org/helix#bcfbb875)
+│ ├── bitflags v2.10.0
+│ ├── serde v1.0.228 (*)
+│ ├── serde_json v1.0.149 (*)
+│ └── url v2.5.8 (*)
+├── implicit-fn v0.1.0 (proc-macro) (*)
+├── itertools v0.14.0 (*)
+├── libc v0.2.180
+├── log v0.4.29
+├── lower v0.2.1 (*)
+├── lsp-server v0.7.9 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ ├── crossbeam-channel v0.5.15 (*)
+│ ├── helix-lsp-types v0.95.1 (https://git.bendn.org/helix#bcfbb875) (*)
+│ ├── log v0.4.29
+│ ├── serde v1.0.228 (*)
+│ ├── serde_derive v1.0.228 (proc-macro) (*)
+│ └── serde_json v1.0.149 (*)
+├── markdown v1.0.0
+│ └── unicode-id v0.3.6
+├── memchr v2.7.6
+├── niri-ipc v25.11.0
+│ ├── serde v1.0.228 (*)
+│ └── serde_json v1.0.149 (*)
+├── nucleo v0.5.0 (*)
+├── papaya v0.2.3
+│ ├── equivalent v1.0.2
+│ └── seize v0.5.1
+│ └── libc v0.2.180
+├── pattypan v0.1.0 (https://git.bendn.org/pattypan#51c4d77c)
+│ ├── anstream v0.6.21 (*)
+│ ├── anyhow v1.0.100
+│ ├── array_chunks v1.0.0
+│ ├── atools v0.1.12
+│ ├── chumsky v0.13.0 (https://github.com/zesterer/chumsky#b81c34c3)
+│ │ ├── hashbrown v0.15.5 (*)
+│ │ ├── stacker v0.1.22
+│ │ │ [build-dependencies]
+│ │ ├── unicode-ident v1.0.22
+│ │ └── unicode-segmentation v1.12.0
+│ ├── color-hex v0.2.0 (proc-macro)
+│ ├── ctlfun v0.1.0 (https://git.bendn.org/ctlfun#f00e5752)
+│ ├── dsb v0.1.0 (https://git.bendn.org/dsb#02744132) (*)
+│ ├── fimg v0.4.51 (https://git.bendn.org/fimg#cf01f538) (*)
+│ ├── implicit-fn v0.1.0 (proc-macro) (*)
+│ ├── libc v0.2.180
+│ ├── minifb v0.28.0
+│ │ ├── dlib v0.5.2
+│ │ ├── lazy_static v1.5.0
+│ │ ├── libc v0.2.180
+│ │ ├── raw-window-handle v0.6.2
+│ │ ├── tempfile v3.24.0 (*)
+│ │ ├── wayland-client v0.29.5
+│ │ │ [build-dependencies]
+│ │ ├── wayland-cursor v0.29.5
+│ │ ├── wayland-protocols v0.29.5
+│ │ │ [build-dependencies]
+│ │ └── x11-dl v2.21.0
+│ │ [build-dependencies]
+│ │ [build-dependencies]
+│ │ └── cc v1.2.55 (*)
+│ ├── nix v0.30.1
+│ │ ├── bitflags v2.10.0
+│ │ ├── cfg-if v1.0.4
+│ │ └── libc v0.2.180
+│ │ [build-dependencies]
+│ │ └── cfg_aliases v0.2.1
+│ ├── parking_lot v0.12.5 (*)
+│ ├── swash v0.2.6 (*)
+│ └── winit v0.30.12
+│ ├── ahash v0.8.12
+│ │ [build-dependencies]
+│ ├── bitflags v2.10.0
+│ ├── bytemuck v1.25.0
+│ ├── calloop v0.13.0
+│ ├── cursor-icon v1.2.0
+│ ├── dpi v0.1.2
+│ ├── libc v0.2.180
+│ ├── memmap2 v0.9.9
+│ ├── percent-encoding v2.3.2
+│ ├── raw-window-handle v0.6.2
+│ ├── rustix v0.38.44
+│ ├── sctk-adwaita v0.10.1
+│ ├── smithay-client-toolkit v0.19.2
+│ ├── smol_str v0.2.2
+│ ├── tracing v0.1.44
+│ ├── wayland-backend v0.3.12
+│ │ [build-dependencies]
+│ ├── wayland-client v0.31.12
+│ ├── wayland-protocols v0.32.10
+│ ├── wayland-protocols-plasma v0.3.10
+│ ├── x11-dl v2.21.0 (*)
+│ ├── x11rb v0.13.2
+│ └── xkbcommon-dl v0.4.2
+│ [build-dependencies]
+│ └── cfg_aliases v0.2.1
+├── pin-project v1.1.10
+│ └── pin-project-internal v1.1.10 (proc-macro)
+│ ├── proc-macro2 v1.0.106 (*)
+│ ├── quote v1.0.44 (*)
+│ └── syn v2.0.114 (*)
+├── png v0.18.0
+│ ├── bitflags v2.10.0
+│ ├── crc32fast v1.5.0
+│ │ └── cfg-if v1.0.4
+│ ├── fdeflate v0.3.7
+│ │ └── simd-adler32 v0.3.8
+│ ├── flate2 v1.1.8
+│ │ ├── crc32fast v1.5.0 (*)
+│ │ └── miniz_oxide v0.8.9
+│ └── miniz_oxide v0.8.9 (*)
+├── regex v1.12.2 (*)
+├── regex-cursor v0.1.5 (*)
+├── replace_with v0.1.8
+├── ropey v1.6.1 (*)
+├── run_times v0.1.0
+├── rust-analyzer v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ ├── anyhow v1.0.100
+│ ├── base64 v0.22.1
+│ ├── cargo_metadata v0.23.1
+│ │ ├── camino v1.2.2
+│ │ ├── cargo-platform v0.3.2
+│ │ ├── semver v1.0.27
+│ │ ├── serde v1.0.228 (*)
+│ │ ├── serde_json v1.0.149 (*)
+│ │ └── thiserror v2.0.18 (*)
+│ ├── cfg v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── intern v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── rustc-hash v2.1.1
+│ │ ├── span v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── syntax v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── tracing v0.1.44 (*)
+│ │ └── tt v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ ├── crossbeam-channel v0.5.15 (*)
+│ ├── dirs v6.0.0
+│ │ └── dirs-sys v0.5.0
+│ ├── dissimilar v1.0.10
+│ ├── helix-lsp-types v0.95.1 (https://git.bendn.org/helix#bcfbb875) (*)
+│ ├── hir v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── arrayvec v0.7.6
+│ │ ├── base-db v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── cfg v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── either v1.15.0
+│ │ ├── hir-def v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── hir-expand v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── hir-ty v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── intern v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── itertools v0.14.0 (*)
+│ │ ├── ra-ap-rustc_type_ir v0.143.0
+│ │ ├── rustc-hash v2.1.1
+│ │ ├── serde_json v1.0.149 (*)
+│ │ ├── smallvec v1.15.1
+│ │ ├── span v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── stdx v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── syntax v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── tracing v0.1.44 (*)
+│ │ ├── triomphe v0.1.15
+│ │ └── tt v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── hir-def v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── hir-ty v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── ide v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── arrayvec v0.7.6
+│ │ ├── cfg v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── cov-mark v2.2.0
+│ │ ├── dot v0.1.4
+│ │ ├── either v1.15.0
+│ │ ├── hir v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── ide-assists v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── ide-completion v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── ide-db v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── ide-diagnostics v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── ide-ssr v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── itertools v0.14.0 (*)
+│ │ ├── macros v0.0.0 (proc-macro) (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── nohash-hasher v0.2.0
+│ │ ├── oorandom v11.1.5
+│ │ ├── profile v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── pulldown-cmark v0.9.6
+│ │ ├── pulldown-cmark-to-cmark v10.0.4
+│ │ ├── rustc_apfloat v0.2.3+llvm-462a31f5a5ab
+│ │ ├── smallvec v1.15.1
+│ │ ├── span v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── stdx v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── syntax v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── toolchain v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── tracing v0.1.44 (*)
+│ │ ├── triomphe v0.1.15
+│ │ └── url v2.5.8 (*)
+│ ├── ide-completion v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── ide-db v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── ide-ssr v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── indexmap v2.13.0 (*)
+│ ├── itertools v0.14.0 (*)
+│ ├── load-cargo v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── anyhow v1.0.100
+│ │ ├── crossbeam-channel v0.5.15 (*)
+│ │ ├── hir-expand v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── ide-db v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── intern v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── itertools v0.14.0 (*)
+│ │ ├── proc-macro-api v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── project-model v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── span v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── tracing v0.1.44 (*)
+│ │ ├── tt v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ │ ├── vfs v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ └── vfs-notify v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ ├── lsp-server v0.7.9 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── memchr v2.7.6
+│ ├── num_cpus v1.17.0
+│ │ └── libc v0.2.180
+│ ├── oorandom v11.1.5
+│ ├── parking_lot v0.12.5 (*)
+│ ├── parser v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── drop_bomb v0.1.5
+│ │ ├── edition v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ ├── ra-ap-rustc_lexer v0.143.0
+│ │ ├── rustc-literal-escaper v0.0.4
+│ │ ├── tracing v0.1.44 (*)
+│ │ └── winnow v0.7.14
+│ ├── paths v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4)
+│ │ └── camino v1.2.2 (*)
+│ ├── proc-macro-api v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── process-wrap v8.2.1
+│ │ ├── indexmap v2.13.0 (*)
+│ │ ├── nix v0.30.1 (*)
+│ │ └── tracing v0.1.44 (*)
+│ ├── profile v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── project-model v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── ra-ap-rustc_type_ir v0.143.0 (*)
+│ ├── rayon v1.11.0 (*)
+│ ├── rustc-hash v2.1.1
+│ ├── scip v0.5.2
+│ │ └── protobuf v3.7.1
+│ ├── semver v1.0.27 (*)
+│ ├── serde v1.0.228 (*)
+│ ├── serde_derive v1.0.228 (proc-macro) (*)
+│ ├── serde_json v1.0.149 (*)
+│ ├── smallvec v1.15.1
+│ ├── stdx v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── syntax v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── tenthash v1.1.0
+│ ├── toml v0.9.11+spec-1.1.0 (*)
+│ ├── toolchain v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── tracing v0.1.44 (*)
+│ ├── tracing-subscriber v0.3.22
+│ │ ├── sharded-slab v0.1.7
+│ │ ├── thread_local v1.1.9
+│ │ ├── time v0.3.46
+│ │ ├── tracing-core v0.1.36
+│ │ └── tracing-log v0.2.0
+│ ├── tracing-tree v0.4.1
+│ │ ├── nu-ansi-term v0.50.3
+│ │ ├── tracing-core v0.1.36 (*)
+│ │ ├── tracing-log v0.2.0 (*)
+│ │ └── tracing-subscriber v0.3.22 (*)
+│ ├── triomphe v0.1.15
+│ ├── vfs v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── vfs-notify v0.0.0 (https://git.bendn.org/rust-analyzer#286d7dd4) (*)
+│ ├── walkdir v2.5.0
+│ │ └── same-file v1.0.6
+│ └── xflags v0.3.2
+│ └── xflags-macros v0.3.2 (proc-macro)
+├── rust-fsm v0.8.0 (https://git.bendn.org/rust-fsm#16afc8ee)
+│ ├── aquamarine v0.6.0 (proc-macro)
+│ │ ├── include_dir v0.7.4
+│ │ ├── itertools v0.10.5
+│ │ ├── proc-macro-error2 v2.0.1
+│ │ ├── proc-macro2 v1.0.106 (*)
+│ │ ├── quote v1.0.44 (*)
+│ │ └── syn v2.0.114 (*)
+│ ├── replace_with v0.1.8
+│ └── rust-fsm-dsl v0.8.0 (proc-macro) (https://git.bendn.org/rust-fsm#16afc8ee)
+│ ├── proc-macro2 v1.0.106 (*)
+│ ├── quote v1.0.44 (*)
+│ └── syn v2.0.114 (*)
+├── rustc-hash v2.1.1
+├── scopeguard v1.2.0
+├── serde v1.0.228 (*)
+├── serde_derive v1.0.228 (proc-macro) (*)
+├── serde_json v1.0.149 (*)
+├── softbuffer v0.4.8
+│ ├── as-raw-xcb-connection v1.0.1
+│ ├── bytemuck v1.25.0 (*)
+│ ├── drm v0.14.1
+│ │ ├── bitflags v2.10.0
+│ │ ├── bytemuck v1.25.0 (*)
+│ │ ├── drm-ffi v0.9.0
+│ │ ├── drm-fourcc v2.2.0
+│ │ └── rustix v0.38.44 (*)
+│ ├── fastrand v2.3.0
+│ ├── memmap2 v0.9.9 (*)
+│ ├── raw-window-handle v0.6.2
+│ ├── rustix v1.1.3
+│ │ ├── bitflags v2.10.0
+│ │ └── linux-raw-sys v0.11.0
+│ ├── tiny-xlib v0.2.4
+│ │ ├── as-raw-xcb-connection v1.0.1
+│ │ ├── ctor-lite v0.1.1
+│ │ ├── libloading v0.8.9
+│ │ └── tracing v0.1.44 (*)
+│ │ [build-dependencies]
+│ │ └── pkg-config v0.3.32
+│ ├── tracing v0.1.44 (*)
+│ ├── wayland-backend v0.3.12 (*)
+│ ├── wayland-client v0.31.12 (*)
+│ ├── wayland-sys v0.31.8
+│ │ ├── dlib v0.5.2 (*)
+│ │ ├── log v0.4.29
+│ │ └── once_cell v1.21.3
+│ │ [build-dependencies]
+│ │ └── pkg-config v0.3.32
+│ └── x11rb v0.13.2 (*)
+├── swash v0.2.6 (*)
+├── swizzle v0.1.0
+├── test-log v0.2.19
+│ ├── env_logger v0.11.8 (*)
+│ └── test-log-macros v0.2.19 (proc-macro)
+│ ├── proc-macro2 v1.0.106 (*)
+│ ├── quote v1.0.44 (*)
+│ └── syn v2.0.114 (*)
+├── tokio v1.49.0
+│ └── pin-project-lite v0.2.16
+├── tokio-util v0.7.18
+│ ├── bytes v1.11.0
+│ ├── futures-core v0.3.31
+│ ├── futures-sink v0.3.31
+│ ├── futures-util v0.3.31
+│ │ ├── futures-core v0.3.31
+│ │ ├── futures-macro v0.3.31 (proc-macro)
+│ │ ├── futures-task v0.3.31
+│ │ ├── pin-project-lite v0.2.16
+│ │ ├── pin-utils v0.1.0
+│ │ └── slab v0.4.12
+│ ├── pin-project-lite v0.2.16
+│ └── tokio v1.49.0 (*)
+├── tree-house v0.3.0 (*)
+├── tree-sitter v0.25.10
+│ ├── regex v1.12.2 (*)
+│ ├── regex-syntax v0.8.8
+│ ├── streaming-iterator v0.1.9
+│ └── tree-sitter-language v0.1.6
+│ [build-dependencies]
+│ ├── cc v1.2.55 (*)
+│ └── serde_json v1.0.149
+│ ├── indexmap v2.13.0
+│ ├── itoa v1.0.17
+│ ├── memchr v2.7.6
+│ ├── serde_core v1.0.228
+│ └── zmij v1.0.19
+├── url v2.5.8 (*)
+├── walkdir v2.5.0 (*)
+└── winit v0.30.12 (*)