more integration

pull/8675/merge^2
mattwparas 2 years ago
parent 7dd04240b2
commit ba1a38d497

213
Cargo.lock generated

@ -2,6 +2,54 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "abi_stable"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f69d9465d88d24382d43fa68335a92fe9d3c53a918549c693403ed9a85eff50"
dependencies = [
"abi_stable_derive",
"abi_stable_shared",
"const_panic",
"core_extensions",
"crossbeam-channel",
"generational-arena",
"libloading 0.7.4",
"lock_api",
"parking_lot",
"paste",
"repr_offset",
"rustc_version",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "abi_stable_derive"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aecd3efa5a5294f5c67913d45f985ccb382b3c93327581529610eeecdf4821a"
dependencies = [
"abi_stable_shared",
"as_derive_utils",
"core_extensions",
"proc-macro2 1.0.56",
"quote 1.0.26",
"rustc_version",
"syn 1.0.109",
"typed-arena",
]
[[package]]
name = "abi_stable_shared"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63"
dependencies = [
"core_extensions",
]
[[package]]
name = "adler"
version = "1.0.2"
@ -25,7 +73,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"getrandom",
"once_cell",
"version_check",
@ -76,6 +124,18 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "as_derive_utils"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4"
dependencies = [
"core_extensions",
"proc-macro2 1.0.56",
"quote 1.0.26",
"syn 1.0.109",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -192,6 +252,12 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -204,7 +270,7 @@ version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"encoding_rs",
"memchr",
]
@ -271,6 +337,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "const_panic"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
[[package]]
name = "content_inspector"
version = "0.2.4"
@ -295,13 +367,28 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "core_extensions"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee"
dependencies = [
"core_extensions_proc_macros",
]
[[package]]
name = "core_extensions_proc_macros"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -310,7 +397,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
@ -324,7 +411,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@ -334,7 +421,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
@ -346,7 +433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
"memoffset",
"scopeguard",
@ -358,7 +445,7 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@ -368,7 +455,7 @@ version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -413,6 +500,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "custom-commands"
version = "0.1.0"
dependencies = [
"anyhow",
"helix-core",
"helix-term",
"helix-view",
"steel-core",
]
[[package]]
name = "cxx"
version = "1.0.94"
@ -463,7 +561,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"num_cpus",
]
@ -473,7 +571,7 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"dirs-sys-next",
]
@ -529,7 +627,7 @@ version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -578,7 +676,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51822eedc6129d8c4d96cec86d56b785e983f943c9ce9fb892e0c2a99a7f47a0"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"home",
]
@ -606,7 +704,7 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.2.16",
"windows-sys 0.48.0",
@ -744,13 +842,22 @@ dependencies = [
"byteorder",
]
[[package]]
name = "generational-arena"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "getrandom"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
@ -1403,6 +1510,7 @@ dependencies = [
"slotmap",
"smallvec",
"smartstring",
"steel-core",
"textwrap",
"toml",
"tree-sitter",
@ -1433,7 +1541,7 @@ dependencies = [
"anyhow",
"cc",
"etcetera",
"libloading",
"libloading 0.8.0",
"log",
"once_cell",
"serde",
@ -1477,6 +1585,8 @@ dependencies = [
"chrono",
"content_inspector",
"crossterm 0.26.1",
"dlopen",
"dlopen_derive",
"fern",
"futures-util",
"fuzzy-matcher",
@ -1520,6 +1630,7 @@ dependencies = [
"log",
"once_cell",
"serde",
"steel-core",
"termini",
"unicode-segmentation",
]
@ -1713,7 +1824,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -1793,13 +1904,23 @@ version = "0.2.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if 1.0.0",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"windows-sys 0.48.0",
]
@ -1834,7 +1955,7 @@ version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -2003,13 +2124,19 @@ version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"windows-sys 0.45.0",
]
[[package]]
name = "paste"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "percent-encoding"
version = "2.2.0"
@ -2215,6 +2342,15 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "repr_offset"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea"
dependencies = [
"tstr",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -2240,6 +2376,15 @@ dependencies = [
"str_indices",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.37.15"
@ -2303,6 +2448,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.160"
@ -2479,6 +2630,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
name = "steel-core"
version = "0.2.0"
dependencies = [
"abi_stable",
"anyhow",
"bincode",
"chrono",
@ -2582,7 +2734,7 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"fastrand",
"redox_syscall 0.3.5",
"rustix",
@ -2658,7 +2810,7 @@ version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"once_cell",
]
@ -2811,6 +2963,21 @@ dependencies = [
"regex",
]
[[package]]
name = "tstr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e"
dependencies = [
"tstr_proc_macros",
]
[[package]]
name = "tstr_proc_macros"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a"
[[package]]
name = "typed-arena"
version = "2.0.2"
@ -2963,7 +3130,7 @@ version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]

@ -9,6 +9,7 @@ members = [
"helix-loader",
"helix-vcs",
"helix-parsec",
"custom-commands",
"xtask",
]
@ -18,7 +19,7 @@ default-members = [
[profile.release]
lto = "thin"
# debug = true
debug = true
[profile.opt]
inherits = "release"

@ -48,6 +48,9 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std"
etcetera = "0.7"
textwrap = "0.16.0"
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests", "dylibs"] }
[dev-dependencies]
quickcheck = { version = "1", default-features = false }
indoc = "2.0.1"

@ -0,0 +1 @@
impl steel::rvals::Custom for crate::Position {}

@ -761,6 +761,160 @@ static LISP_WORDS: Lazy<std::collections::HashSet<&'static str>> = Lazy::new(||
words.iter().copied().collect()
});
// TODO: Allow for injecting hooks on indent
#[allow(clippy::too_many_arguments)]
fn call_indent_hook(
language_config: Option<&LanguageConfiguration>,
syntax: Option<&Syntax>,
indent_style: &IndentStyle,
tab_width: usize,
text: RopeSlice,
line_before: usize,
line_before_end_pos: usize,
current_line: usize,
) -> Option<String> {
if let Some(config) = language_config {
// TODO: If possible, this would be very cool to be implemented in steel itself. If not,
// a rust native method that is embedded in a dylib that this uses would also be helpful
if config.language_id == "scheme" {
log::info!("Implement better scheme indent mode!");
// TODO: walk backwards to find the previous s-expression?
// log::info!("{}", text);
// log::info!("{}", text.line(line_before));
let byte_pos = text.char_to_byte(line_before_end_pos);
let text_up_to_cursor = text.byte_slice(0..byte_pos);
let mut cursor = line_before;
let mut depth = 0;
// for line in text_up_to_cursor.lines().reversed() {
loop {
let line = text_up_to_cursor.line(cursor);
// We want to ignore comments
if let Some(l) = line.as_str() {
if l.starts_with(";") {
if cursor == 0 {
break;
}
cursor -= 1;
continue;
}
}
// log::info!("Line: {}", line);
for (index, char) in line.chars_at(line.len_chars()).reversed().enumerate() {
match char {
')' | ']' | '}' => {
depth += 1;
}
'(' | '[' | '{' => {
// stack.push('(')
if depth == 0 {
log::info!(
"Found unmatched paren on line, index: {}, {}",
line,
index
);
// TODO: Here, then walk FORWARD, parsing the identifiers until there is a thing to line up with, for example:
// (define (foo-bar) RET) <-
// ^probably indent to here
let offset = line.len_chars() - index;
let mut char_iter_from_paren =
line.chars_at(line.len_chars() - index).enumerate();
let end;
// Walk until we've found whitespace, and then crunch the whitespace until the start of the next symbol
// if there is _no_ symbol after that, we should just default to the default behavior
while let Some((index, char)) = char_iter_from_paren.next() {
if char.is_whitespace() {
let mut last = index;
// This is the end of our range
end = index;
// If we have multiple parens in a row, match to the start:
// for instance, (cond [(equal? x 10) RET])
// ^ We want to line up to this
//
// To do so, just create an indent that is the width of the offset.
match line.get_char(offset) {
Some('(' | '[' | '{') => {
return Some(" ".repeat(offset));
}
_ => {}
}
// TODO: Don't unwrap here, we don't want that
if LISP_WORDS.contains(
line.slice(offset..offset + end).as_str().unwrap(),
) {
return Some(" ".repeat(offset + 1));
}
for _ in char_iter_from_paren
.take_while(|(_, x)| x.is_whitespace())
{
last += 1;
}
// If we have something like (list RET)
// We want the result to look like:
// (list
// )
//
// So we special case the lack of an additional word after
// the first symbol
if line.len_chars() == last + offset + 1 {
if let Some(c) = line.get_char(last + offset) {
if c.is_whitespace() {
return Some(" ".repeat(offset + 1));
}
}
}
return Some(" ".repeat(last + offset + 1));
}
}
log::info!("Found no symbol after the initial opening symbol");
return Some(" ".repeat(offset + 1));
}
depth -= 1;
}
_ => {}
}
}
if cursor == 0 {
break;
}
cursor -= 1;
}
// TODO: Implement heuristic for large files so we don't necessarily traverse the entire file backwards to check the matched parens?
return Some("".to_string());
}
}
None
}
/// TODO: Come up with some elegant enough FFI for this, so that Steel can expose an API for this.
/// Problem is - the issues with the `Any` type and using things with type id.
#[allow(clippy::too_many_arguments)]

@ -30,6 +30,8 @@ pub mod textobject;
mod transaction;
pub mod wrap;
pub mod extensions;
pub mod unicode {
pub use unicode_general_category as category;
pub use unicode_segmentation as segmentation;

@ -67,7 +67,9 @@ grep-regex = "0.1.11"
grep-searcher = "0.1.11"
# plugin support
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests"] }
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests", "dylibs"] }
dlopen = "0.1.8"
dlopen_derive = "0.1.4"
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }

@ -1,6 +1,7 @@
pub(crate) mod dap;
pub(crate) mod engine;
pub(crate) mod lsp;
pub mod plugin;
pub(crate) mod typed;
pub use dap::*;
@ -83,6 +84,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
pub type OnKeyCallback = Box<dyn FnOnce(&mut Context, KeyEvent)>;
#[repr(C)]
pub struct Context<'a> {
pub register: Option<char>,
pub count: Option<NonZeroUsize>,

@ -4,8 +4,9 @@ use helix_view::{document::Mode, Editor};
use once_cell::sync::Lazy;
use steel::{
gc::unsafe_erased_pointers::CustomReference,
rvals::{IntoSteelVal, SteelString},
steel_vm::register_fn::RegisterFn,
rvals::{FromSteelVal, IntoSteelVal, SteelString},
steel_vm::{engine::Engine, register_fn::RegisterFn},
SteelVal,
};
use std::{
@ -21,14 +22,15 @@ use std::{
use steel::{rvals::Custom, steel_vm::builtin::BuiltInModule};
use crate::{
compositor::{self, Compositor},
compositor::{self, Component, Compositor},
job::{self, Callback},
keymap::{merge_keys, Keymap},
ui::{self, Popup, PromptEvent},
ui::{self, overlay::overlaid, Popup, PromptEvent},
};
use super::{
insert::{insert_char, insert_string},
plugin::{DylibContainers, ExternalModule},
shell_impl, Context, MappableCommand, TYPABLE_COMMAND_LIST,
};
@ -36,6 +38,40 @@ thread_local! {
pub static ENGINE: std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine::Engine>> = configure_engine();
}
pub struct ExternalContainersAndModules {
containers: DylibContainers,
modules: Vec<ExternalModule>,
}
// External modules that can load via rust dylib. These can then be consumed from
// steel as needed, via the standard FFI for plugin functions.
pub(crate) static EXTERNAL_DYLIBS: Lazy<Arc<RwLock<ExternalContainersAndModules>>> =
Lazy::new(|| {
let mut containers = DylibContainers::new();
// Load the plugins with respect to the extensions directory.
// containers.load_modules_from_directory(Some(
// helix_loader::config_dir()
// .join("extensions")
// .to_str()
// .unwrap()
// .to_string(),
// ));
println!("Found dylibs: {}", containers.containers.len());
let modules = containers.create_commands();
println!("Modules length: {}", modules.len());
Arc::new(RwLock::new(ExternalContainersAndModules {
containers,
modules,
}))
// Arc::new(RwLock::new(containers))
});
pub fn initialize_engine() {
ENGINE.with(|x| x.borrow().globals().first().copied());
}
@ -60,7 +96,7 @@ pub fn run_initialization_script(cx: &mut Context) {
}
// Start the worker thread - i.e. message passing to the workers
configure_background_thread()
// configure_background_thread()
}
pub static KEYBINDING_QUEUE: Lazy<SharedKeyBindingsEventQueue> =
@ -140,6 +176,10 @@ fn get_editor<'a>(cx: &'a mut Context<'a>) -> &'a mut Editor {
cx.editor
}
fn get_ro_editor<'a>(cx: &'a mut Context<'a>) -> &'a Editor {
&cx.editor
}
fn get_themes(cx: &mut Context) -> Vec<String> {
ui::completers::theme(cx.editor, "")
.into_iter()
@ -147,25 +187,499 @@ fn get_themes(cx: &mut Context) -> Vec<String> {
.collect()
}
fn configure_background_thread() {
std::thread::spawn(move || {
let mut engine = steel::steel_vm::engine::Engine::new();
// TODO: This is not necessary anymore. We can get away with native threads in steel, and otherwise run background tasks
// that may or may not live the duration of the editor time in there.
// fn configure_background_thread() {
// std::thread::spawn(move || {
// let mut engine = steel::steel_vm::engine::Engine::new();
// engine.register_fn("set-status-line!", StatusLineMessage::set);
// let helix_module_path = helix_loader::config_dir().join("background.scm");
// if let Ok(contents) = std::fs::read_to_string(&helix_module_path) {
// engine.run(&contents).ok();
// }
// });
// }
/// A dynamic component, used for rendering thing
#[derive(Clone)]
// TODO: Implement `trace` method for objects that hold steel vals
struct SteelDynamicComponent {
name: String,
// This _should_ be a struct, but in theory can be whatever you want. It will be the first argument
// passed to the functions in the remainder of the struct.
state: SteelVal,
handle_event: Option<SteelVal>,
should_update: Option<SteelVal>,
render: SteelVal,
cursor: Option<SteelVal>,
required_size: Option<SteelVal>,
}
impl SteelDynamicComponent {
fn new(name: String, state: SteelVal, render: SteelVal, h: HashMap<String, SteelVal>) -> Self {
// if let SteelVal::HashMapV(h) = functions {
Self {
name,
state,
render,
handle_event: h.get("handle_event").cloned(),
should_update: h.get("should_update").cloned(),
cursor: h.get("cursor").cloned(),
required_size: h.get("required_size").cloned(),
}
engine.register_fn("set-status-line!", StatusLineMessage::set);
// } else {
// panic!("Implement better error handling")
// }
}
let helix_module_path = helix_loader::config_dir().join("background.scm");
fn new_dyn(
name: String,
state: SteelVal,
render: SteelVal,
h: HashMap<String, SteelVal>,
) -> WrappedDynComponent {
let s = Self::new(name, state, render, h);
if let Ok(contents) = std::fs::read_to_string(&helix_module_path) {
engine.run(&contents).ok();
WrappedDynComponent {
inner: Some(Box::new(s)),
}
});
}
fn get_state(&self) -> SteelVal {
self.state.clone()
}
fn get_render(&self) -> SteelVal {
self.render.clone()
}
fn get_handle_event(&self) -> Option<SteelVal> {
self.handle_event.clone()
}
fn get_should_update(&self) -> Option<SteelVal> {
self.should_update.clone()
}
fn get_cursor(&self) -> Option<SteelVal> {
self.cursor.clone()
}
fn get_required_size(&self) -> Option<SteelVal> {
self.required_size.clone()
}
}
impl Custom for SteelDynamicComponent {}
impl Custom for compositor::EventResult {}
impl FromSteelVal for compositor::EventResult {
fn from_steelval(val: &SteelVal) -> steel::rvals::Result<Self> {
match val {
SteelVal::SymbolV(v) if v.as_str() == "EventResult::Ignored" => {
Ok(compositor::EventResult::Ignored(None))
}
SteelVal::SymbolV(v) if v.as_str() == "EventResult::Consumed" => {
Ok(compositor::EventResult::Consumed(None))
}
_ => Err(steel::SteelErr::new(
steel::rerrs::ErrorKind::TypeMismatch,
"Unable to convert value to event result".to_string(),
)),
}
}
}
// impl CustomReference for tui::buffer::Buffer {}
// TODO: Call the function inside the component, using the global engine. Consider running in its own engine
// but leaving it all in the same one is kinda nice
impl Component for SteelDynamicComponent {
fn render(
&mut self,
area: helix_view::graphics::Rect,
frame: &mut tui::buffer::Buffer,
ctx: &mut compositor::Context,
) {
let mut ctx = Context {
register: None,
count: None,
editor: ctx.editor,
callback: None,
on_next_key_callback: None,
jobs: ctx.jobs,
};
// Pass the `state` object through - this can be used for storing the state of whatever plugin thing we're
// attempting to render
let thunk = |engine: &mut Engine, f, c| {
engine.call_function_with_args(
self.render.clone(),
vec![self.state.clone(), area.into_steelval().unwrap(), f, c],
)
};
ENGINE
.with(|x| {
x.borrow_mut()
.with_mut_reference::<tui::buffer::Buffer, tui::buffer::Buffer>(frame)
.with_mut_reference::<Context, Context>(&mut ctx)
.consume(|engine, args| {
let mut arg_iter = args.into_iter();
(thunk)(engine, arg_iter.next().unwrap(), arg_iter.next().unwrap())
})
// .run_with_references::<tui::buffer::Buffer, tui::buffer::Buffer, Context, Context>(
// frame, &mut ctx, thunk,
// )
})
.unwrap();
log::info!("Calling dynamic render!");
}
// TODO: Pass in event as well? Need to have immutable reference type
// Otherwise, we're gonna be in a bad spot. For now - just clone the object and pass it through.
// Clong is _not_ ideal, but it might be all we can do for now.
fn handle_event(
&mut self,
event: &helix_view::input::Event,
ctx: &mut compositor::Context,
) -> compositor::EventResult {
if let Some(handle_event) = &mut self.handle_event {
let mut ctx = Context {
register: None,
count: None,
editor: ctx.editor,
callback: None,
on_next_key_callback: None,
jobs: ctx.jobs,
};
// Pass the `state` object through - this can be used for storing the state of whatever plugin thing we're
// attempting to render
let thunk = |engine: &mut Engine, c| {
engine.call_function_with_args(
handle_event.clone(),
vec![
self.state.clone(),
// TODO: We do _not_ want to clone here, we would need to create a bunch of methods on the engine for various
// combinations of reference passing to do this safely. Right now its limited to mutable references, but we should
// expose more - investigate macros on how to do that with recursively crunching the list to generate the combinations.
// Experimentation needed.
event.clone().into_steelval().unwrap(),
c,
],
)
};
match ENGINE.with(|x| {
x.borrow_mut()
.run_thunk_with_reference::<Context, Context>(&mut ctx, thunk)
}) {
Ok(v) => compositor::EventResult::from_steelval(&v)
.unwrap_or_else(|_| compositor::EventResult::Ignored(None)),
Err(_) => compositor::EventResult::Ignored(None),
}
} else {
compositor::EventResult::Ignored(None)
}
}
fn should_update(&self) -> bool {
if let Some(should_update) = &self.should_update {
match ENGINE.with(|x| {
x.borrow_mut()
.call_function_with_args(should_update.clone(), vec![self.state.clone()])
}) {
Ok(v) => bool::from_steelval(&v).unwrap_or(true),
Err(_) => true,
}
} else {
true
}
}
// TODO: Implement immutable references. Right now I'm only supporting mutable references.
fn cursor(
&self,
area: helix_view::graphics::Rect,
ctx: &Editor,
) -> (
Option<helix_core::Position>,
helix_view::graphics::CursorKind,
) {
if let Some(cursor) = &self.cursor {
// Pass the `state` object through - this can be used for storing the state of whatever plugin thing we're
// attempting to render
let thunk = |engine: &mut Engine, e| {
engine.call_function_with_args(
cursor.clone(),
vec![self.state.clone(), area.into_steelval().unwrap(), e],
)
};
<(
Option<helix_core::Position>,
helix_view::graphics::CursorKind,
)>::from_steelval(&ENGINE.with(|x| {
x.borrow_mut()
.run_thunk_with_ro_reference::<Editor, Editor>(ctx, thunk)
.unwrap()
}))
.unwrap()
} else {
(None, helix_view::graphics::CursorKind::Hidden)
}
}
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
let name = self.type_name();
if let Some(required_size) = &mut self.required_size {
log::info!("Calling required-size inside: {}", name);
// TODO: Create some token that we can grab to enqueue function calls internally. Referencing
// the external API would cause problems - we just need to include a handle to the interpreter
// instance. Something like:
// ENGINE.call_function_or_enqueue? OR - this is the externally facing render function. Internal
// render calls do _not_ go through this interface. Instead, they are just called directly.
//
// If we go through this interface, we're going to get an already borrowed mut error, since it is
// re-entrant attempting to grab the ENGINE instead mutably, since we have to break the recursion
// somehow. By putting it at the edge, we then say - hey for these functions on this interface,
// call the engine instance. Otherwise, all computation happens inside the engine.
let res = ENGINE
.with(|x| {
x.borrow_mut().call_function_with_args(
required_size.clone(),
vec![self.state.clone(), viewport.into_steelval().unwrap()],
)
})
.and_then(|x| Option::<(u16, u16)>::from_steelval(&x))
.unwrap();
res
} else {
None
}
}
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn id(&self) -> Option<&'static str> {
None
}
}
// Does this work?
impl Custom for Box<dyn Component> {}
struct WrappedDynComponent {
inner: Option<Box<dyn Component>>,
}
impl Custom for WrappedDynComponent {}
struct BoxDynComponent {
inner: Box<dyn Component>,
}
impl BoxDynComponent {
pub fn new(inner: Box<dyn Component>) -> Self {
Self { inner }
}
}
impl Component for BoxDynComponent {
fn handle_event(
&mut self,
_event: &helix_view::input::Event,
_ctx: &mut compositor::Context,
) -> compositor::EventResult {
self.inner.handle_event(_event, _ctx)
}
fn should_update(&self) -> bool {
self.inner.should_update()
}
fn cursor(
&self,
_area: helix_view::graphics::Rect,
_ctx: &Editor,
) -> (
Option<helix_core::Position>,
helix_view::graphics::CursorKind,
) {
self.inner.cursor(_area, _ctx)
}
fn required_size(&mut self, _viewport: (u16, u16)) -> Option<(u16, u16)> {
self.inner.required_size(_viewport)
}
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn id(&self) -> Option<&'static str> {
None
}
fn render(
&mut self,
area: helix_view::graphics::Rect,
frame: &mut tui::buffer::Buffer,
ctx: &mut compositor::Context,
) {
self.inner.render(area, frame, ctx)
}
}
fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine::Engine>> {
let mut engine = steel::steel_vm::engine::Engine::new();
println!("Loading engine!");
// Load native modules from the directory. Another idea - have a separate dlopen loading system
// in place that does not use the type id, and instead we generate the module after the dylib
// is added. That way functions _must_ have a specific signature, and then we add the integration
// later.
// engine.load_modules_from_directory(
// helix_loader::config_dir()
// .join("extensions")
// .to_str()
// .unwrap()
// .to_string(),
// );
// Get the current OS
engine.register_fn("current-os!", || std::env::consts::OS);
engine.register_fn("new-component!", SteelDynamicComponent::new_dyn);
engine.register_fn("SteelDynamicComponent?", |object: SteelVal| {
if let SteelVal::Custom(v) = object {
if let Some(wrapped) = v.borrow().as_any_ref().downcast_ref::<BoxDynComponent>() {
return wrapped.inner.as_any().is::<SteelDynamicComponent>();
} else {
false
}
} else {
false
}
});
engine.register_fn(
"SteelDynamicComponent-state",
SteelDynamicComponent::get_state,
);
engine.register_fn(
"SteelDynamicComponent-render",
SteelDynamicComponent::get_render,
);
engine.register_fn(
"SteelDynamicComponent-handle-event",
SteelDynamicComponent::get_handle_event,
);
engine.register_fn(
"SteelDynamicComponent-should-update",
SteelDynamicComponent::should_update,
);
engine.register_fn(
"SteelDynamicComponent-cursor",
SteelDynamicComponent::cursor,
);
engine.register_fn(
"SteelDynamicComponent-required-size",
SteelDynamicComponent::get_required_size,
);
// engine.register_fn("WrappedComponent", WrappedDynComponent::new)
engine.register_fn(
"Popup::new",
|contents: &mut WrappedDynComponent,
position: helix_core::Position|
-> WrappedDynComponent {
let inner = contents.inner.take().unwrap(); // Panic, for now
WrappedDynComponent {
inner: Some(Box::new(
Popup::<BoxDynComponent>::new("popup", BoxDynComponent::new(inner))
.position(Some(position)),
)),
}
},
);
// engine.register_fn(
// "Picker::new",
// |contents: &mut Wrapped
// )
engine.register_fn("Component::Text", |contents: String| WrappedDynComponent {
inner: Some(Box::new(crate::ui::Text::new(contents))),
});
// Separate this out into its own component module - This just lets us call the underlying
// component, not sure if we can go from trait object -> trait object easily but we'll see!
engine.register_fn(
"Component::render",
|t: &mut WrappedDynComponent,
area: helix_view::graphics::Rect,
frame: &mut tui::buffer::Buffer,
ctx: &mut Context| {
t.inner.as_mut().unwrap().render(
area,
frame,
&mut compositor::Context {
jobs: ctx.jobs,
editor: ctx.editor,
scroll: None,
},
)
},
);
engine.register_fn(
"Component::handle-event",
|s: &mut WrappedDynComponent, event: &helix_view::input::Event, ctx: &mut Context| {
s.inner.as_mut().unwrap().handle_event(
event,
&mut compositor::Context {
jobs: ctx.jobs,
editor: ctx.editor,
scroll: None,
},
)
},
);
engine.register_fn("Component::should-update", |s: &mut WrappedDynComponent| {
s.inner.as_mut().unwrap().should_update()
});
engine.register_fn(
"Component::cursor",
|s: &WrappedDynComponent, area: helix_view::graphics::Rect, ctx: &Editor| {
s.inner.as_ref().unwrap().cursor(area, ctx)
},
);
engine.register_fn(
"Component::required-size",
|s: &mut WrappedDynComponent, viewport: (u16, u16)| {
s.inner.as_mut().unwrap().required_size(viewport)
},
);
let mut module = BuiltInModule::new("helix/core/keybindings".to_string());
module.register_fn("set-keybindings!", SharedKeyBindingsEventQueue::merge);
@ -181,6 +695,29 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
helix_view::Editor,
>::register_fn(&mut engine, "cx-editor!", get_editor);
// RegisterFn::<
// _,
// steel::steel_vm::register_fn::MarkerWrapper7<(
// Context<'_>,
// helix_view::Editor,
// helix_view::Editor,
// Context<'static>,
// )>,
// helix_view::Editor,
// >::register_fn(&mut engine, "cx-editor-ro!", get_ro_editor);
engine.register_fn("editor-cursor", Editor::cursor);
engine.register_fn("cx->cursor", |cx: &mut Context| cx.editor.cursor());
// TODO:
// Position related functions. These probably should be defined alongside the actual impl for Custom in the core crate
engine.register_fn("Position::new", helix_core::Position::new);
engine.register_fn("Position::default", helix_core::Position::default);
engine.register_fn("Position-row", |position: helix_core::Position| {
position.row
});
engine.register_fn("cx->themes", get_themes);
engine.register_fn("set-status-line!", StatusLineMessage::set);
@ -208,6 +745,59 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
module.register_fn(command.name, func);
}
// // Load the plugins with respect to the extensions directory.
// EXTERNAL_DYLIBS
// .write()
// .unwrap()
// .load_modules_from_directory(Some(
// helix_loader::config_dir()
// .join("extensions")
// .to_str()
// .unwrap()
// .to_string(),
// ));
// let commands = EXTERNAL_DYLIBS.read().unwrap().create_commands();
// TODO: @Matt - these commands need to get loaded into their _own_ module, and registered as such.
// for dylib in &EXTERNAL_DYLIBS.read().unwrap().modules {
// let mut module = BuiltInModule::new(dylib.name.to_string());
// println!("{}", dylib.get_name());
// // println!("{}", dylib.name);
// for command in dylib.commands.iter() {
// // TODO: The name needs to be registered for static - but we shouldn't _need_ it to be
// // registered for static. We can probably get away with accepting an owned string and pay the price,
// // if need be.
// let inner = command.fun.clone();
// let func = move |cx: &mut Context,
// args: Box<[Box<str>]>,
// event: PromptEvent|
// -> anyhow::Result<()> {
// // Ensure the lifetime of these variables
// let _config = cx.editor.config.clone();
// let _theme_loader = cx.editor.theme_loader.clone();
// let _syn_loader = cx.editor.syn_loader.clone();
// println!("{}", Arc::strong_count(&_config));
// println!("{}", Arc::strong_count(&_theme_loader));
// println!("{}", Arc::strong_count(&_syn_loader));
// // println!("{:p}", _theme_loader);
// // println!("{:p}", _syn_loader);
// (inner)(cx, &_theme_loader, &_syn_loader, args, &event)
// };
// module.register_owned_fn(command.name.to_string(), func);
// }
// engine.register_module(module);
// }
engine.register_module(module);
let mut module = BuiltInModule::new("helix/core/static".to_string());
@ -231,6 +821,8 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
engine.register_module(module);
engine.register_fn("push-component!", push_component);
let helix_module_path = helix_loader::helix_module_file();
engine
@ -436,3 +1028,46 @@ fn run_shell_command_text(
anyhow::bail!("Command failed!: {}", output.to_string())
}
}
// Pin the value to _this_ thread?
// Overlay the dynamic component, see what happens?
// Probably need to pin the values to this thread - wrap it in a shim which pins the value
// to this thread? - call methods on the thread local value?
fn push_component(cx: &mut Context, component: &mut WrappedDynComponent) {
// let component = crate::ui::Text::new("Hello world!".to_string());
log::info!("Pushing dynamic component!");
// todo!();
// let callback = async move {
// let call: job::Callback = Callback::EditorCompositor(Box::new(
// move |_editor: &mut Editor, compositor: &mut Compositor| {
// compositor.push(Box::new(component));
// },
// ));
// Ok(call)
// };
// cx.jobs.callback(callback);
// Why does this not work? - UPDATE: This does work when called in a static command context, but
// in a typed command context, we do not have access to the real compositor. Thus, we need a callback
// that then requires the values to be moved over threads. We'll need some message passing scheme
// to call values from the typed command context.
cx.push_layer(component.inner.take().unwrap());
// TODO: This _needs_ to go through a callback. Otherwise the new layer is just dropped.
// Set up some sort of callback queue for dynamic components that we can pull from instead, so that
// things stay thread local?
// let root = helix_core::find_workspace().0;
// let picker = ui::file_picker(root, &cx.editor.config());
// cx.push_layer(Box::new(overlaid(picker)));
}
// fn push_component_raw(cx: &mut Context, component: Box<dyn Component>) {
// cx.push_layer(component);
// }

@ -0,0 +1,112 @@
use std::{borrow::Cow, path::PathBuf, sync::Arc};
use dlopen::wrapper::{Container, WrapperApi};
use dlopen_derive::WrapperApi;
use crate::ui::PromptEvent;
use super::{CommandSignature, Context};
// use super::builtin::BuiltInModule;
#[repr(C)]
#[derive(Clone)]
pub struct ExternalModule {
pub name: Box<str>,
pub commands: Box<[CrossBoundaryTypableCommand]>,
}
impl ExternalModule {
pub fn new(name: String, commands: Vec<CrossBoundaryTypableCommand>) -> Self {
println!("Name: {}", name);
Self {
name: name.into_boxed_str(),
commands: commands.into_boxed_slice(),
}
}
pub fn get_name(&self) -> &str {
&self.name
}
}
// pub syn_loader: Arc<syntax::Loader>,
// pub theme_loader: Arc<theme::Loader>,
#[repr(C)]
#[derive(Clone)]
pub struct CrossBoundaryTypableCommand {
pub name: Box<str>,
pub aliases: Box<[String]>,
pub doc: Box<str>,
pub fun: for<'a> extern "C" fn(
&mut Context<'a>,
&helix_view::theme::Loader,
&helix_core::syntax::Loader,
Box<[Box<str>]>,
*const PromptEvent,
) -> anyhow::Result<()>,
pub signature: CommandSignature,
}
#[derive(WrapperApi, Clone)]
pub struct ModuleApi {
generate_module: fn() -> ExternalModule,
}
#[derive(Clone)]
pub(crate) struct DylibContainers {
pub(crate) containers: Vec<Arc<Container<ModuleApi>>>,
}
impl DylibContainers {
pub fn new() -> Self {
Self {
containers: Vec::new(),
}
}
pub fn load_modules_from_directory(&mut self, home: Option<String>) {
if let Some(home) = home {
let mut home = PathBuf::from(home);
home.push("native");
if home.exists() {
let paths = std::fs::read_dir(home).unwrap();
for path in paths {
println!("{:?}", path);
let path = path.unwrap().path();
if path.extension().unwrap() != "so" && path.extension().unwrap() != "dylib" {
continue;
}
let path_name = path.file_name().and_then(|x| x.to_str()).unwrap();
log::info!(target: "dylibs", "Loading dylib: {}", path_name);
// Load in the dylib
let cont: Container<ModuleApi> = unsafe { Container::load(path) }
.expect("Could not open library or load symbols");
// Keep the container alive for the duration of the program
// This should probably just get wrapped up with the engine as well, when registering modules, directly
// register an external dylib
self.containers.push(Arc::new(cont));
}
} else {
log::warn!(target: "dylibs", "$STEEL_HOME/native directory does not exist")
}
} else {
log::warn!(target: "dylibs", "STEEL_HOME variable missing - unable to read shared dylibs")
}
}
pub fn create_commands(&self) -> Vec<ExternalModule> {
self.containers
.iter()
.map(|x| x.generate_module())
.collect()
}
}

@ -33,6 +33,7 @@ impl TypableCommand {
}
#[derive(Clone)]
#[repr(C)]
pub struct CommandSignature {
// Arguments with specific completion methods based on their position.
positional_args: &'static [Completer],
@ -42,21 +43,21 @@ pub struct CommandSignature {
}
impl CommandSignature {
const fn none() -> Self {
pub const fn none() -> Self {
Self {
positional_args: &[],
var_args: completers::none,
}
}
const fn positional(completers: &'static [Completer]) -> Self {
pub const fn positional(completers: &'static [Completer]) -> Self {
Self {
positional_args: completers,
var_args: completers::none,
}
}
const fn all(completer: Completer) -> Self {
pub const fn all(completer: Completer) -> Self {
Self {
positional_args: &[],
var_args: completer,

@ -3,6 +3,7 @@ use crate::{
compositor::{Callback, Component, Context, Event, EventResult},
ctrl, key,
};
use steel::rvals::Custom;
use tui::buffer::Buffer as Surface;
use helix_core::Position;
@ -14,6 +15,8 @@ use helix_view::{
// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
// a width/height hint. maybe Popup(Box<Component>)
impl<T: steel::rvals::IntoSteelVal + Component> Custom for Popup<T> {}
pub struct Popup<T: Component> {
contents: T,
position: Option<Position>,

@ -35,6 +35,7 @@ pub struct Prompt {
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum PromptEvent {
/// The prompt input has been updated.
Update,

@ -3,6 +3,8 @@ use tui::buffer::Buffer as Surface;
use helix_view::graphics::Rect;
impl steel::rvals::Custom for Text {}
pub struct Text {
pub(crate) contents: tui::text::Text<'static>,
size: (u16, u16),

@ -26,3 +26,4 @@ once_cell = "1.17"
log = "~0.4"
helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
helix-core = { version = "0.6", path = "../helix-core" }
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests", "dylibs"] }

@ -0,0 +1,5 @@
use crate::{buffer::Buffer, widgets::Widget};
use steel::{gc::unsafe_erased_pointers::CustomReference, rvals::Custom};
impl CustomReference for Buffer {}

@ -130,6 +130,7 @@
pub mod backend;
pub mod buffer;
pub mod extension;
pub mod layout;
pub mod symbols;
pub mod terminal;

@ -46,7 +46,7 @@ which = "4.4"
parking_lot = "0.12.1"
# plugin support
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests"] }
steel-core = { path = "../../../steel/crates/steel-core", version = "0.2.0", features = ["modules", "anyhow", "blocking_requests", "dylibs"] }
[target.'cfg(windows)'.dependencies]

@ -804,6 +804,7 @@ use futures_util::stream::{Flatten, Once};
impl steel::gc::unsafe_erased_pointers::CustomReference for Editor {}
#[repr(C)]
pub struct Editor {
/// Current editing mode.
pub mode: Mode,

@ -0,0 +1,7 @@
use steel::{gc::unsafe_erased_pointers::CustomReference, rvals::Custom};
use crate::{graphics::Rect, input::Event};
impl CustomReference for Event {}
impl Custom for Rect {}
impl Custom for crate::graphics::CursorKind {}

@ -17,6 +17,8 @@ pub enum Event {
IdleTimeout,
}
impl steel::rvals::Custom for Event {}
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
pub struct MouseEvent {
/// The kind of mouse event that was caused.

@ -19,6 +19,8 @@ pub mod theme;
pub mod tree;
pub mod view;
pub mod extension;
use std::num::NonZeroUsize;
// uses NonZeroUsize so Option<DocumentId> use a byte rather than two

Loading…
Cancel
Save