mirror of https://github.com/helix-editor/helix
more integration
parent
7dd04240b2
commit
ba1a38d497
|
@ -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);
|
||||
// engine.register_fn("set-status-line!", StatusLineMessage::set);
|
||||
|
||||
let helix_module_path = helix_loader::config_dir().join("background.scm");
|
||||
// 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();
|
||||
// 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(),
|
||||
}
|
||||
});
|
||||
|
||||
// } else {
|
||||
// panic!("Implement better error handling")
|
||||
// }
|
||||
}
|
||||
|
||||
fn new_dyn(
|
||||
name: String,
|
||||
state: SteelVal,
|
||||
render: SteelVal,
|
||||
h: HashMap<String, SteelVal>,
|
||||
) -> WrappedDynComponent {
|
||||
let s = Self::new(name, state, render, h);
|
||||
|
||||
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…
Reference in New Issue