helix/helix-term/src/commands/engine.rs

174 lines
5.5 KiB
Rust
Raw Normal View History

2023-08-25 09:12:15 +08:00
use helix_view::{document::Mode, input::KeyEvent};
2023-05-08 12:53:19 +08:00
2023-08-27 08:18:13 +08:00
use std::{borrow::Cow, collections::HashMap};
2023-05-08 12:53:19 +08:00
use crate::{
2023-08-25 09:12:15 +08:00
compositor,
2023-08-27 08:18:13 +08:00
keymap::{KeyTrie, KeymapResult},
2023-08-25 09:12:15 +08:00
ui::{self, PromptEvent},
2023-05-08 12:53:19 +08:00
};
2023-08-25 11:01:49 +08:00
use super::{shell_impl, Context, MappableCommand, TYPABLE_COMMAND_LIST};
2023-05-08 12:53:19 +08:00
2023-08-25 09:12:15 +08:00
#[cfg(feature = "steel")]
2023-06-06 12:09:27 +08:00
mod components;
2023-08-25 09:12:15 +08:00
#[cfg(feature = "steel")]
pub mod scheme;
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
// For now, we will allow _one_ embedded scripting engine to live inside of helix.
// In theory, we could allow multiple, with some kind of hierarchy where we check
// each one, with some kind of precedence.
struct PluginEngine<T: PluginSystem>(T);
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
// 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);
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
#[cfg(not(feature = "steel"))]
2023-08-27 08:18:13 +08:00
/// The default plugin system used ends up with no ops for all of the behavior.
2023-08-25 09:12:15 +08:00
static PLUGIN_SYSTEM: PluginEngine<NoEngine> = PluginEngine(NoEngine);
2023-08-10 13:24:29 +08:00
2023-08-25 11:01:49 +08:00
// enum PluginSystemTypes {
// None,
// Steel,
// }
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
// The order in which the plugins will be evaluated against - if we wanted to include, lets say `rhai`,
2023-08-25 11:01:49 +08:00
// we would have to order the precedence for searching for exported commands, or somehow merge them?
// static PLUGIN_PRECEDENCE: &[PluginSystemTypes] = &[PluginSystemTypes::Steel];
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
pub struct NoEngine;
2023-08-10 13:24:29 +08:00
2023-08-25 09:12:15 +08:00
// This will be the boundary layer between the editor and the engine.
pub struct ScriptingEngine;
2023-08-10 13:24:29 +08:00
2023-07-12 12:23:03 +08:00
impl ScriptingEngine {
pub fn initialize() {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM.0.initialize();
2023-07-12 12:23:03 +08:00
}
pub fn run_initialization_script(cx: &mut Context) {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM.0.run_initialization_script(cx);
2023-07-12 12:23:03 +08:00
}
2023-08-27 08:18:13 +08:00
pub fn get_keybindings() -> Option<HashMap<Mode, KeyTrie>> {
PLUGIN_SYSTEM.0.get_keybindings()
}
2023-08-25 09:12:15 +08:00
pub fn handle_keymap_event(
editor: &mut ui::EditorView,
mode: Mode,
cxt: &mut Context,
event: KeyEvent,
) -> KeymapResult {
PLUGIN_SYSTEM
.0
.handle_keymap_event(editor, mode, cxt, event)
2023-07-12 12:23:03 +08:00
}
2023-08-10 13:24:29 +08:00
pub fn call_function_if_global_exists(
cx: &mut Context,
name: &str,
args: Vec<Cow<str>>,
) -> bool {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM
.0
.call_function_if_global_exists(cx, name, args)
2023-07-12 12:23:03 +08:00
}
pub fn call_typed_command_if_global_exists<'a>(
cx: &mut compositor::Context,
input: &'a str,
parts: &'a [&'a str],
event: PromptEvent,
) -> bool {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM
.0
.call_typed_command_if_global_exists(cx, input, parts, event)
2023-07-12 12:23:03 +08:00
}
pub fn get_doc_for_identifier(ident: &str) -> Option<String> {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM.0.get_doc_for_identifier(ident)
2023-07-12 12:23:03 +08:00
}
2023-07-12 12:37:40 +08:00
2023-08-25 09:12:15 +08:00
pub fn fuzzy_match<'a>(
2023-07-12 12:37:40 +08:00
fuzzy_matcher: &'a fuzzy_matcher::skim::SkimMatcherV2,
input: &'a str,
) -> Vec<(String, i64)> {
2023-08-25 09:12:15 +08:00
PLUGIN_SYSTEM.0.fuzzy_match(fuzzy_matcher, input)
2023-07-12 12:37:40 +08:00
}
2023-07-12 12:23:03 +08:00
}
2023-08-25 09:12:15 +08:00
impl PluginSystem for NoEngine {}
2023-05-30 12:41:13 +08:00
2023-08-25 09:12:15 +08:00
/// These methods are the main entry point for interaction with the rest of
/// the editor system.
pub trait PluginSystem {
2023-08-27 08:18:13 +08:00
/// If any initialization needs to happen prior to the initialization script being run,
/// this is done here. This is run before the context is available.
2023-08-25 09:12:15 +08:00
fn initialize(&self) {}
2023-05-30 12:41:13 +08:00
2023-08-27 08:18:13 +08:00
/// 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.
2023-08-25 11:01:49 +08:00
fn run_initialization_script(&self, _cx: &mut Context) {}
2023-05-30 12:41:13 +08:00
2023-08-27 08:18:13 +08:00
/// Fetch the keybindings so that these can be loaded in to the keybinding map. These are
/// keybindings that overwrite the default ones.
fn get_keybindings(&self) -> Option<HashMap<Mode, KeyTrie>> {
None
}
/// Allow the engine to directly handle a keymap event. This is some of the tightest integration
/// with the engine, directly intercepting any keymap events. By default, this just delegates to the
/// editors default keybindings.
2023-08-25 09:12:15 +08:00
fn handle_keymap_event(
2023-05-30 12:41:13 +08:00
&self,
2023-08-25 09:12:15 +08:00
editor: &mut ui::EditorView,
mode: Mode,
2023-08-25 11:01:49 +08:00
_cxt: &mut Context,
2023-08-25 09:12:15 +08:00
event: KeyEvent,
) -> KeymapResult {
editor.keymaps.get(mode, event)
2023-05-30 12:41:13 +08:00
}
2023-08-27 08:18:13 +08:00
/// This attempts to call a function in the engine with the name `name` using the args `args`. The context
/// is available here. Returns a bool indicating whether the function exists or not.
2023-08-25 09:12:15 +08:00
fn call_function_if_global_exists(
&self,
2023-08-25 11:01:49 +08:00
_cx: &mut Context,
_name: &str,
_args: Vec<Cow<str>>,
2023-08-25 09:12:15 +08:00
) -> bool {
false
2023-05-30 12:41:13 +08:00
}
2023-08-27 08:18:13 +08:00
/// This is explicitly for calling a function via the typed command interface, e.g. `:vsplit`. The context here
/// that is available is more limited than the context available in `call_function_if_global_exists`. This also
/// gives the ability to handle in progress commands with `PromptEvent`.
2023-08-25 09:12:15 +08:00
fn call_typed_command_if_global_exists<'a>(
&self,
2023-08-25 11:01:49 +08:00
_cx: &mut compositor::Context,
_input: &'a str,
_parts: &'a [&'a str],
_event: PromptEvent,
2023-08-25 09:12:15 +08:00
) -> bool {
false
2023-05-30 12:41:13 +08:00
}
2023-08-27 08:18:13 +08:00
/// Given an identifier, extract the documentation from the engine.
2023-08-25 11:01:49 +08:00
fn get_doc_for_identifier(&self, _ident: &str) -> Option<String> {
2023-05-30 12:41:13 +08:00
None
}
2023-08-27 08:18:13 +08:00
/// Fuzzy match the input against the fuzzy matcher, used for handling completions on typed commands
2023-08-25 09:12:15 +08:00
fn fuzzy_match<'a>(
&self,
2023-08-25 11:01:49 +08:00
_fuzzy_matcher: &'a fuzzy_matcher::skim::SkimMatcherV2,
_input: &'a str,
2023-05-08 12:53:19 +08:00
) -> Vec<(String, i64)> {
2023-08-25 09:12:15 +08:00
Vec::new()
2023-05-08 12:53:19 +08:00
}
2023-08-20 13:13:56 +08:00
}