diff --git a/Cargo.lock b/Cargo.lock index e9f0c4a18..4bcf43cb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3220,7 +3220,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel-core" version = "0.6.0" -source = "git+https://github.com/mattwparas/steel.git#4aeefae1f46f6129fbb70e89c98b8299981cde2a" +source = "git+https://github.com/mattwparas/steel.git#0287954cc6a05bbd794f2636352bf64a71ea176d" dependencies = [ "abi_stable", "anyhow", @@ -3273,7 +3273,7 @@ dependencies = [ [[package]] name = "steel-derive" version = "0.5.0" -source = "git+https://github.com/mattwparas/steel.git#4aeefae1f46f6129fbb70e89c98b8299981cde2a" +source = "git+https://github.com/mattwparas/steel.git#0287954cc6a05bbd794f2636352bf64a71ea176d" dependencies = [ "proc-macro2", "quote", @@ -3283,7 +3283,7 @@ dependencies = [ [[package]] name = "steel-doc" version = "0.6.0" -source = "git+https://github.com/mattwparas/steel.git#4aeefae1f46f6129fbb70e89c98b8299981cde2a" +source = "git+https://github.com/mattwparas/steel.git#0287954cc6a05bbd794f2636352bf64a71ea176d" dependencies = [ "steel-core", ] @@ -3291,7 +3291,7 @@ dependencies = [ [[package]] name = "steel-gen" version = "0.2.0" -source = "git+https://github.com/mattwparas/steel.git#4aeefae1f46f6129fbb70e89c98b8299981cde2a" +source = "git+https://github.com/mattwparas/steel.git#0287954cc6a05bbd794f2636352bf64a71ea176d" dependencies = [ "codegen", "serde", @@ -3300,7 +3300,7 @@ dependencies = [ [[package]] name = "steel-parser" version = "0.6.0" -source = "git+https://github.com/mattwparas/steel.git#4aeefae1f46f6129fbb70e89c98b8299981cde2a" +source = "git+https://github.com/mattwparas/steel.git#0287954cc6a05bbd794f2636352bf64a71ea176d" dependencies = [ "compact_str", "fxhash", diff --git a/helix-core/src/extensions.rs b/helix-core/src/extensions.rs index a03846e2e..5643a4d19 100644 --- a/helix-core/src/extensions.rs +++ b/helix-core/src/extensions.rs @@ -5,8 +5,11 @@ pub mod steel_implementations { use steel::{ gc::ShareableMut, - rvals::{as_underlying_type, Custom, SteelString}, - steel_vm::{builtin::BuiltInModule, register_fn::RegisterFn}, + rvals::{as_underlying_type, AsRefSteelVal, Custom, SteelString}, + steel_vm::{ + builtin::{BuiltInModule, MarkdownDoc}, + register_fn::RegisterFn, + }, SteelVal, }; @@ -19,6 +22,7 @@ pub mod steel_implementations { impl steel::rvals::Custom for AutoPairConfig {} impl steel::rvals::Custom for SoftWrap {} + #[allow(unused)] pub struct RopeyError(ropey::Error); impl steel::rvals::Custom for RopeyError {} @@ -61,6 +65,10 @@ pub mod steel_implementations { _ => false, } } + + fn fmt(&self) -> Option> { + Some(Ok(format!("#", self.to_slice()))) + } } impl SteelRopeSlice { @@ -90,6 +98,20 @@ pub mod steel_implementations { } } + pub fn insert_str(&self, char_idx: usize, text: SteelString) -> Result { + let slice = self.to_slice(); + let mut rope = ropey::Rope::from(slice); + rope.try_insert(char_idx, &text)?; + Ok(Self::new(rope)) + } + + pub fn insert_char(&self, char_idx: usize, c: char) -> Result { + let slice = self.to_slice(); + let mut rope = ropey::Rope::from(slice); + rope.try_insert_char(char_idx, c)?; + Ok(Self::new(rope)) + } + pub fn line(mut self, cursor: usize) -> Result { match self.kind { RangeKind::Char => { @@ -197,6 +219,10 @@ pub mod steel_implementations { self.to_slice().len_chars() } + pub fn len_bytes(&self) -> usize { + self.to_slice().len_bytes() + } + pub fn get_char(&self, index: usize) -> Option { self.to_slice().get_char(index) } @@ -226,12 +252,6 @@ pub mod steel_implementations { self } - pub fn trimmed_starts_with(&self, pat: SteelString) -> bool { - let maybe_owned = Cow::from(self.to_slice()); - - maybe_owned.trim_start().starts_with(pat.as_str()) - } - pub fn starts_with(&self, pat: SteelString) -> bool { self.to_slice().starts_with(pat.as_str()) } @@ -244,19 +264,142 @@ pub mod steel_implementations { pub fn rope_module() -> BuiltInModule { let mut module = BuiltInModule::new("helix/core/text"); - module - .register_fn("string->rope", SteelRopeSlice::from_string) - .register_fn("rope->slice", SteelRopeSlice::slice) - .register_fn("rope-char->byte", SteelRopeSlice::char_to_byte) - .register_fn("rope->byte-slice", SteelRopeSlice::byte_slice) - .register_fn("rope->line", SteelRopeSlice::line) - .register_fn("rope->string", SteelRopeSlice::to_string) - .register_fn("rope-len-chars", SteelRopeSlice::len_chars) - .register_fn("rope-char-ref", SteelRopeSlice::get_char) - .register_fn("rope-len-lines", SteelRopeSlice::len_lines) - .register_fn("rope-starts-with?", SteelRopeSlice::starts_with) - .register_fn("rope-ends-with?", SteelRopeSlice::ends_with) - .register_fn("rope-trim-start", SteelRopeSlice::trim_start); + macro_rules! register_value { + ($name:expr, $func:expr, $doc:expr) => { + module.register_fn($name, $func); + module.register_doc($name, MarkdownDoc(Cow::Borrowed($doc))); + }; + } + + register_value!( + "Rope?", + |value: SteelVal| SteelRopeSlice::as_ref(&value).is_ok(), + "Check if the given value is a rope" + ); + + register_value!( + "string->rope", + SteelRopeSlice::from_string, + r#"Converts a string into a rope. + +```scheme +(string->rope value) -> Rope? +``` + +* value : string? + "# + ); + + register_value!( + "rope->slice", + SteelRopeSlice::slice, + r#"Take a slice from using character indices from the rope. +Returns a new rope value. + +```scheme +(rope->slice rope start end) -> Rope? +``` + +* rope : Rope? +* start: (and positive? int?) +* end: (and positive? int?) +"# + ); + + register_value!( + "rope-char->byte", + SteelRopeSlice::char_to_byte, + r#"Convert the character offset into a byte offset for a given rope"# + ); + + register_value!( + "rope->byte-slice", + SteelRopeSlice::byte_slice, + r#"Take a slice of this rope using byte offsets + +```scheme +(rope->byte-slice rope start end) -> Rope? +``` + +* rope: Rope? +* start: (and positive? int?) +* end: (and positive? int?) +"# + ); + + register_value!( + "rope->line", + SteelRopeSlice::line, + r#"Get the line at the given line index. Returns a rope. + +```scheme +(rope->line rope index) -> Rope? + +``` + +* rope : Rope? +* index : (and positive? int?) +"# + ); + + register_value!( + "rope->string", + SteelRopeSlice::to_string, + "Convert the given rope to a string" + ); + + register_value!( + "rope-len-chars", + SteelRopeSlice::len_chars, + "Get the length of the rope in characters" + ); + register_value!( + "rope-len-bytes", + SteelRopeSlice::len_chars, + "Get the length of the rope in bytes" + ); + + register_value!( + "rope-char-ref", + SteelRopeSlice::get_char, + "Get the character at the given index" + ); + + register_value!( + "rope-len-lines", + SteelRopeSlice::len_lines, + "Get the number of lines in the rope" + ); + + register_value!( + "rope-starts-with?", + SteelRopeSlice::starts_with, + "Check if the rope starts with a given pattern" + ); + + register_value!( + "rope-ends-with?", + SteelRopeSlice::ends_with, + "Check if the rope ends with a given pattern" + ); + + register_value!( + "rope-trim-start", + SteelRopeSlice::trim_start, + "Remove the leading whitespace from the given rope" + ); + + register_value!( + "rope-insert-string", + SteelRopeSlice::insert_str, + "Insert a string at the given index into the rope" + ); + + register_value!( + "rope-insert-char", + SteelRopeSlice::insert_char, + "Insert a character at the given index" + ); module } diff --git a/helix-term/src/commands/engine/steel.rs b/helix-term/src/commands/engine/steel.rs index d12cdcfa6..82aa0823f 100644 --- a/helix-term/src/commands/engine/steel.rs +++ b/helix-term/src/commands/engine/steel.rs @@ -3280,6 +3280,15 @@ fn configure_lsp_builtins(name: &str, module: &BuiltInModule) { output.push_str("))"); + for value in module.names() { + if let Some(doc) = module.get_documentation(&value) { + output.push_str(&format!( + "(#%module-add-doc #%helix-{}-module {:?} {:?})\n", + name, value, doc + )); + } + } + std::fs::write(path, output).unwrap(); } @@ -3294,80 +3303,6 @@ fn load_rope_api(engine: &mut Engine, generate_sources: bool) { engine.register_module(rope_slice_module); } -// struct SteelEngine(Engine); - -// impl SteelEngine { -// pub fn call_function_by_name( -// &mut self, -// function_name: SteelString, -// args: Vec, -// ) -> steel::rvals::Result { -// self.0 -// .call_function_by_name_with_args(function_name.as_str(), args.into_iter().collect()) -// } - -// /// Calling a function that was not defined in the runtime it was created in could -// /// result in panics. You have been warned. -// pub fn call_function( -// &mut self, -// function: SteelVal, -// args: Vec, -// ) -> steel::rvals::Result { -// self.0 -// .call_function_with_args(function, args.into_iter().collect()) -// } - -// pub fn require_module(&mut self, module: SteelString) -> steel::rvals::Result<()> { -// self.0.run(format!("(require \"{}\")", module)).map(|_| ()) -// } -// } - -// impl Custom for SteelEngine {} - -// static ENGINE_ID: AtomicUsize = AtomicUsize::new(0); - -// thread_local! { -// pub static ENGINE_MAP: SteelVal = -// SteelVal::boxed(SteelVal::empty_hashmap()); -// } - -// Low level API work, these need to be loaded into the global environment in a predictable -// location, otherwise callbacks from plugin engines will not be handled properly! -// fn load_engine_api(engine: &mut Engine) { -// fn id_to_engine(value: SteelVal) -> Option { -// if let SteelVal::Boxed(b) = ENGINE_MAP.with(|x| x.clone()) { -// if let SteelVal::HashMapV(h) = b.read().clone() { -// return h.get(&value).cloned(); -// } -// } - -// None -// } - -// // module -// engine -// .register_fn("helix.controller.create-engine", || { -// SteelEngine(configure_engine_impl(Engine::new())) -// }) -// .register_fn("helix.controller.fresh-engine-id", || { -// ENGINE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst) -// }) -// .register_fn( -// "helix.controller.call-function-by-name", -// SteelEngine::call_function_by_name, -// ) -// .register_fn("helix.controller.call-function", SteelEngine::call_function) -// .register_fn( -// "helix.controller.require-module", -// SteelEngine::require_module, -// ) -// .register_value( -// "helix.controller.engine-map", -// ENGINE_MAP.with(|x| x.clone()), -// ) -// .register_fn("helix.controller.id->engine", id_to_engine); -// } - fn load_misc_api(engine: &mut Engine, generate_sources: bool) { let mut module = BuiltInModule::new("helix/core/misc"); diff --git a/helix-term/src/events.rs b/helix-term/src/events.rs index 80f045cd7..eeb8a991c 100644 --- a/helix-term/src/events.rs +++ b/helix-term/src/events.rs @@ -2,7 +2,7 @@ use helix_event::{events, register_event}; use helix_view::document::Mode; use helix_view::events::{ DiagnosticsDidChange, DocumentDidChange, DocumentDidClose, DocumentDidOpen, DocumentFocusLost, - LanguageServerExited, LanguageServerInitialized, SelectionDidChange, + DocumentSaved, LanguageServerExited, LanguageServerInitialized, SelectionDidChange, }; use crate::commands; @@ -22,6 +22,7 @@ pub fn register() { register_event::(); register_event::(); register_event::(); + register_event::(); register_event::(); register_event::(); register_event::();