reorganize top level to use precedence for multiple plugins

pull/8675/merge^2
mattwparas 1 year ago
parent 3060bccc0c
commit 2d4bc31d54

@ -16,45 +16,68 @@ mod components;
#[cfg(feature = "steel")] #[cfg(feature = "steel")]
pub mod scheme; pub mod scheme;
// For now, we will allow _one_ embedded scripting engine to live inside of helix. pub enum PluginSystemKind {
// In theory, we could allow multiple, with some kind of hierarchy where we check None,
// each one, with some kind of precedence. #[cfg(feature = "steel")]
struct PluginEngine<T: PluginSystem>(T); Steel,
}
// This is where we can configure our system to use the correct one
#[cfg(feature = "steel")]
static PLUGIN_SYSTEM: PluginEngine<scheme::SteelScriptingEngine> =
PluginEngine(scheme::SteelScriptingEngine);
#[cfg(not(feature = "steel"))]
/// The default plugin system used ends up with no ops for all of the behavior.
static PLUGIN_SYSTEM: PluginEngine<NoEngine> = PluginEngine(NoEngine);
// enum PluginSystemTypes { pub enum PluginSystemTypes {
// None, None(NoEngine),
// Steel, #[cfg(feature = "steel")]
// } Steel(scheme::SteelScriptingEngine),
}
// The order in which the plugins will be evaluated against - if we wanted to include, lets say `rhai`, // The order in which the plugins will be evaluated against - if we wanted to include, lets say `rhai`,
// we would have to order the precedence for searching for exported commands, or somehow merge them? // we would have to order the precedence for searching for exported commands, or somehow merge them?
// static PLUGIN_PRECEDENCE: &[PluginSystemTypes] = &[PluginSystemTypes::Steel]; const PLUGIN_PRECEDENCE: &[PluginSystemTypes] = &[
#[cfg(feature = "steel")]
PluginSystemTypes::Steel(scheme::SteelScriptingEngine),
PluginSystemTypes::None(NoEngine),
];
pub struct NoEngine; pub struct NoEngine;
// This will be the boundary layer between the editor and the engine. // This will be the boundary layer between the editor and the engine.
pub struct ScriptingEngine; pub struct ScriptingEngine;
macro_rules! manual_dispatch {
($kind:expr, $raw:tt ($($args:expr),* $(,)?) ) => {
match $kind {
PluginSystemTypes::None(n) => n.$raw($($args),*),
#[cfg(feature = "steel")]
PluginSystemTypes::Steel(s) => s.$raw($($args),*),
}
};
}
impl ScriptingEngine { impl ScriptingEngine {
pub fn initialize() { pub fn initialize() {
PLUGIN_SYSTEM.0.initialize(); for kind in PLUGIN_PRECEDENCE {
manual_dispatch!(kind, initialize())
}
} }
pub fn run_initialization_script(cx: &mut Context) { pub fn run_initialization_script(cx: &mut Context) {
PLUGIN_SYSTEM.0.run_initialization_script(cx); for kind in PLUGIN_PRECEDENCE {
manual_dispatch!(kind, run_initialization_script(cx))
}
} }
pub fn get_keybindings() -> Option<HashMap<Mode, KeyTrie>> { pub fn get_keybindings() -> Option<HashMap<Mode, KeyTrie>> {
PLUGIN_SYSTEM.0.get_keybindings() let mut map = HashMap::new();
for kind in PLUGIN_PRECEDENCE {
if let Some(keybindings) = manual_dispatch!(kind, get_keybindings()) {
map.extend(keybindings);
}
}
if map.is_empty() {
None
} else {
Some(map)
}
} }
pub fn handle_keymap_event( pub fn handle_keymap_event(
@ -62,10 +85,16 @@ impl ScriptingEngine {
mode: Mode, mode: Mode,
cxt: &mut Context, cxt: &mut Context,
event: KeyEvent, event: KeyEvent,
) -> KeymapResult { ) -> Option<KeymapResult> {
PLUGIN_SYSTEM for kind in PLUGIN_PRECEDENCE {
.0 let res = manual_dispatch!(kind, handle_keymap_event(editor, mode, cxt, event));
.handle_keymap_event(editor, mode, cxt, event)
if res.is_some() {
return res;
}
}
None
} }
pub fn call_function_if_global_exists( pub fn call_function_if_global_exists(
@ -73,9 +102,13 @@ impl ScriptingEngine {
name: &str, name: &str,
args: Vec<Cow<str>>, args: Vec<Cow<str>>,
) -> bool { ) -> bool {
PLUGIN_SYSTEM for kind in PLUGIN_PRECEDENCE {
.0 if manual_dispatch!(kind, call_function_if_global_exists(cx, name, &args)) {
.call_function_if_global_exists(cx, name, args) return true;
}
}
false
} }
pub fn call_typed_command_if_global_exists<'a>( pub fn call_typed_command_if_global_exists<'a>(
@ -84,24 +117,46 @@ impl ScriptingEngine {
parts: &'a [&'a str], parts: &'a [&'a str],
event: PromptEvent, event: PromptEvent,
) -> bool { ) -> bool {
PLUGIN_SYSTEM for kind in PLUGIN_PRECEDENCE {
.0 if manual_dispatch!(
.call_typed_command_if_global_exists(cx, input, parts, event) kind,
call_typed_command_if_global_exists(cx, input, parts, event)
) {
return true;
}
}
false
} }
pub fn get_doc_for_identifier(ident: &str) -> Option<String> { pub fn get_doc_for_identifier(ident: &str) -> Option<String> {
PLUGIN_SYSTEM.0.get_doc_for_identifier(ident) for kind in PLUGIN_PRECEDENCE {
let doc = manual_dispatch!(kind, get_doc_for_identifier(ident));
if doc.is_some() {
return doc;
}
}
None
} }
pub fn fuzzy_match<'a>( pub fn fuzzy_match<'a>(
fuzzy_matcher: &'a fuzzy_matcher::skim::SkimMatcherV2, fuzzy_matcher: &'a fuzzy_matcher::skim::SkimMatcherV2,
input: &'a str, input: &'a str,
) -> Vec<(String, i64)> { ) -> Vec<(String, i64)> {
PLUGIN_SYSTEM.0.fuzzy_match(fuzzy_matcher, input) PLUGIN_PRECEDENCE
.iter()
.flat_map(|kind| manual_dispatch!(kind, fuzzy_match(fuzzy_matcher, input)))
.collect()
} }
} }
impl PluginSystem for NoEngine {} impl PluginSystem for NoEngine {
fn engine_name(&self) -> PluginSystemKind {
PluginSystemKind::None
}
}
/// These methods are the main entry point for interaction with the rest of /// These methods are the main entry point for interaction with the rest of
/// the editor system. /// the editor system.
@ -110,6 +165,8 @@ pub trait PluginSystem {
/// this is done here. This is run before the context is available. /// this is done here. This is run before the context is available.
fn initialize(&self) {} fn initialize(&self) {}
fn engine_name(&self) -> PluginSystemKind;
/// Post initialization, once the context is available. This means you should be able to /// Post initialization, once the context is available. This means you should be able to
/// run anything here that could modify the context before the main editor is available. /// run anything here that could modify the context before the main editor is available.
fn run_initialization_script(&self, _cx: &mut Context) {} fn run_initialization_script(&self, _cx: &mut Context) {}
@ -129,8 +186,8 @@ pub trait PluginSystem {
mode: Mode, mode: Mode,
_cxt: &mut Context, _cxt: &mut Context,
event: KeyEvent, event: KeyEvent,
) -> KeymapResult { ) -> Option<KeymapResult> {
editor.keymaps.get(mode, event) None
} }
/// This attempts to call a function in the engine with the name `name` using the args `args`. The context /// This attempts to call a function in the engine with the name `name` using the args `args`. The context
@ -139,7 +196,7 @@ pub trait PluginSystem {
&self, &self,
_cx: &mut Context, _cx: &mut Context,
_name: &str, _name: &str,
_args: Vec<Cow<str>>, _args: &[Cow<str>],
) -> bool { ) -> bool {
false false
} }

@ -191,6 +191,10 @@ impl super::PluginSystem for SteelScriptingEngine {
initialize_engine(); initialize_engine();
} }
fn engine_name(&self) -> super::PluginSystemKind {
super::PluginSystemKind::Steel
}
fn run_initialization_script(&self, cx: &mut Context) { fn run_initialization_script(&self, cx: &mut Context) {
run_initialization_script(cx); run_initialization_script(cx);
} }
@ -205,27 +209,25 @@ impl super::PluginSystem for SteelScriptingEngine {
mode: Mode, mode: Mode,
cxt: &mut Context, cxt: &mut Context,
event: KeyEvent, event: KeyEvent,
) -> KeymapResult { ) -> Option<KeymapResult> {
SteelScriptingEngine::get_keymap_for_extension(cxt) SteelScriptingEngine::get_keymap_for_extension(cxt).and_then(|map| {
.and_then(|map| { if let steel::SteelVal::Custom(inner) = map {
if let steel::SteelVal::Custom(inner) = map { if let Some(underlying) =
if let Some(underlying) = steel::rvals::as_underlying_type::<EmbeddedKeyMap>(inner.borrow().as_ref())
steel::rvals::as_underlying_type::<EmbeddedKeyMap>(inner.borrow().as_ref()) {
{ return Some(editor.keymaps.get_with_map(&underlying.0, mode, event));
return Some(editor.keymaps.get_with_map(&underlying.0, mode, event));
}
} }
}
None None
}) })
.unwrap_or_else(|| editor.keymaps.get(mode, event))
} }
fn call_function_if_global_exists( fn call_function_if_global_exists(
&self, &self,
cx: &mut Context, cx: &mut Context,
name: &str, name: &str,
args: Vec<Cow<str>>, args: &[Cow<str>],
) -> bool { ) -> bool {
if ENGINE.with(|x| x.borrow().global_exists(name)) { if ENGINE.with(|x| x.borrow().global_exists(name)) {
let args = steel::List::from( let args = steel::List::from(
@ -882,11 +884,28 @@ impl Component for BoxDynComponent {
} }
} }
// Make this be prompt event validate?
fn call_function_in_external_engine(
ctx: &mut Context,
name: String,
args: Vec<String>,
engine_type: String,
) {
// Wire up entire plugins separately?
match engine_type.as_str() {
"rhai" => todo!(),
_ => {}
}
}
fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine::Engine>> { fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine::Engine>> {
let mut engine = steel::steel_vm::engine::Engine::new(); let mut engine = steel::steel_vm::engine::Engine::new();
log::info!("Loading engine!"); log::info!("Loading engine!");
// Include this?
engine.register_fn("call-function-in-context", call_function_in_external_engine);
engine.register_value("*context*", SteelVal::Void); engine.register_value("*context*", SteelVal::Void);
engine.register_fn("hx.context?", |_: &mut Context| true); engine.register_fn("hx.context?", |_: &mut Context| true);

@ -803,7 +803,8 @@ impl EditorView {
self.pseudo_pending.extend(self.keymaps.pending()); self.pseudo_pending.extend(self.keymaps.pending());
// Check the engine for any buffer specific keybindings first // Check the engine for any buffer specific keybindings first
let key_result = ScriptingEngine::handle_keymap_event(self, mode, cxt, event); let key_result = ScriptingEngine::handle_keymap_event(self, mode, cxt, event)
.unwrap_or_else(|| self.keymaps.get(mode, event));
cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox()); cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox());

Loading…
Cancel
Save