mirror of https://github.com/helix-editor/helix
buffer specific keybindings
parent
34144490ec
commit
3ee5829ed7
|
@ -2732,7 +2732,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||
|
||||
[[package]]
|
||||
name = "steel-core"
|
||||
version = "0.2.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"anyhow",
|
||||
|
|
|
@ -48,7 +48,7 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std"
|
|||
etcetera = "0.8"
|
||||
textwrap = "0.16.0"
|
||||
|
||||
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.4.0", features = ["modules", "anyhow", "blocking_requests"] }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -67,7 +67,7 @@ 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.4.0", features = ["modules", "anyhow", "blocking_requests"] }
|
||||
dlopen = "0.1.8"
|
||||
dlopen_derive = "0.1.4"
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ use movement::Movement;
|
|||
|
||||
use crate::{
|
||||
args,
|
||||
commands::engine::CallbackQueue,
|
||||
commands::engine::{CallbackQueue, ScriptingEngine},
|
||||
compositor::{self, Component, Compositor},
|
||||
filter_picker_entry,
|
||||
job::Callback,
|
||||
|
@ -200,29 +200,7 @@ impl MappableCommand {
|
|||
pub fn execute(&self, cx: &mut Context) {
|
||||
log::info!("Running command");
|
||||
|
||||
// TODO: Move this out to a standalone function
|
||||
while let Some(callback) = CallbackQueue::dequeue() {
|
||||
log::info!("Found callback: {}", callback);
|
||||
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
let mut guard = x.borrow_mut();
|
||||
|
||||
{
|
||||
let res = guard.run_with_reference::<Context, Context>(
|
||||
cx,
|
||||
"*context*",
|
||||
&format!("({} *context*)", callback),
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
}) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
}
|
||||
|
||||
match &self {
|
||||
// TODO: @Matt - Add delegating to the engine to run scripts here
|
||||
Self::Typable { name, args, doc: _ } => {
|
||||
let args: Vec<Cow<str>> = args.iter().map(Cow::from).collect();
|
||||
// TODO: Swap the order to allow overriding the existing commands?
|
||||
|
@ -235,33 +213,8 @@ impl MappableCommand {
|
|||
if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
} else if ENGINE.with(|x| x.borrow().global_exists(name)) {
|
||||
let args = steel::List::from(
|
||||
args.iter()
|
||||
.map(|x| x.clone().into_steelval().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
let mut guard = x.borrow_mut();
|
||||
|
||||
{
|
||||
guard
|
||||
.register_value("_helix_args", steel::rvals::SteelVal::ListV(args));
|
||||
|
||||
let res = guard.run_with_reference::<Context, Context>(
|
||||
cx,
|
||||
"*context*",
|
||||
&format!("(apply {} (cons *context* _helix_args))", name),
|
||||
);
|
||||
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::Void);
|
||||
|
||||
res
|
||||
}
|
||||
}) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
} else {
|
||||
ScriptingEngine::call_function_if_global_exists(cx, name, args)
|
||||
}
|
||||
}
|
||||
Self::Static { fun, .. } => (fun)(cx),
|
||||
|
@ -682,8 +635,6 @@ fn move_impl(cx: &mut Context, move_fn: MoveFn, dir: Direction, behaviour: Movem
|
|||
|
||||
use helix_core::movement::{move_horizontally, move_vertically};
|
||||
|
||||
use self::engine::ENGINE;
|
||||
|
||||
fn move_char_left(cx: &mut Context) {
|
||||
move_impl(cx, move_horizontally, Direction::Backward, Movement::Move)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use fuzzy_matcher::FuzzyMatcher;
|
||||
use helix_core::{graphemes, Selection, Tendril};
|
||||
use helix_core::{graphemes, shellwords::Shellwords, Selection, Tendril};
|
||||
use helix_view::{
|
||||
document::Mode, editor::Action, extension::document_id_to_usize, Document, DocumentId, Editor,
|
||||
document::Mode, editor::Action, extension::document_id_to_usize, input::KeyEvent, Document,
|
||||
DocumentId, Editor,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use steel::{
|
||||
|
@ -14,6 +15,8 @@ use steel::{
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{HashMap, VecDeque},
|
||||
marker::PhantomData,
|
||||
path::PathBuf,
|
||||
sync::Mutex,
|
||||
};
|
||||
use std::{
|
||||
|
@ -25,8 +28,9 @@ use steel::{rvals::Custom, steel_vm::builtin::BuiltInModule};
|
|||
|
||||
use crate::{
|
||||
compositor::{self, Component, Compositor},
|
||||
config::Config,
|
||||
job::{self, Callback},
|
||||
keymap::{merge_keys, KeyTrie},
|
||||
keymap::{self, merge_keys, KeyTrie, KeymapResult, Keymaps},
|
||||
ui::{self, menu::Item, overlay::overlaid, Popup, Prompt, PromptEvent},
|
||||
};
|
||||
|
||||
|
@ -49,6 +53,189 @@ pub struct ExternalContainersAndModules {
|
|||
|
||||
mod components;
|
||||
|
||||
// pub struct PluginEngine<T: PluginSystem>(PhantomData<T>);
|
||||
|
||||
pub struct ScriptingEngine;
|
||||
|
||||
pub trait PluginSystem {
|
||||
fn initialize();
|
||||
fn run_initialization_script(cx: &mut Context);
|
||||
|
||||
fn call_function_if_global_exists(cx: &mut Context, name: &str, args: Vec<Cow<str>>);
|
||||
fn call_typed_command_if_global_exists<'a>(
|
||||
cx: &mut compositor::Context,
|
||||
input: &'a str,
|
||||
parts: &'a [&'a str],
|
||||
event: PromptEvent,
|
||||
) -> bool;
|
||||
|
||||
fn get_doc_for_identifier(ident: &str) -> Option<String>;
|
||||
}
|
||||
|
||||
impl ScriptingEngine {
|
||||
pub fn initialize() {
|
||||
initialize_engine();
|
||||
}
|
||||
|
||||
pub fn run_initialization_script(cx: &mut Context) {
|
||||
run_initialization_script(cx)
|
||||
}
|
||||
|
||||
// Attempt to fetch the keymap for the extension
|
||||
pub fn get_keymap_for_extension<'a>(cx: &'a mut Context) -> Option<SteelVal> {
|
||||
// Get the currently activated extension, also need to check the
|
||||
// buffer type.
|
||||
let extension = {
|
||||
let current_focus = cx.editor.tree.focus;
|
||||
let view = cx.editor.tree.get(current_focus);
|
||||
let doc = &view.doc;
|
||||
let current_doc = cx.editor.documents.get(doc);
|
||||
|
||||
current_doc
|
||||
.and_then(|x| x.path())
|
||||
.and_then(|x| x.extension())
|
||||
.and_then(|x| x.to_str())
|
||||
};
|
||||
|
||||
if let Some(extension) = extension {
|
||||
let special_buffer_map = "*buffer-or-extension-keybindings*";
|
||||
|
||||
let value = ENGINE.with(|x| x.borrow().extract_value(special_buffer_map).clone());
|
||||
|
||||
if let Ok(SteelVal::HashMapV(map)) = value {
|
||||
if let Some(value) = map.get(&SteelVal::StringV(extension.into())) {
|
||||
if let SteelVal::Custom(inner) = value {
|
||||
if let Some(_) = steel::rvals::as_underlying_type::<EmbeddedKeyMap>(
|
||||
inner.borrow().as_ref(),
|
||||
) {
|
||||
return Some(value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn call_function_if_global_exists(cx: &mut Context, name: &str, args: Vec<Cow<str>>) {
|
||||
if ENGINE.with(|x| x.borrow().global_exists(name)) {
|
||||
let args = steel::List::from(
|
||||
args.iter()
|
||||
.map(|x| x.clone().into_steelval().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
let mut guard = x.borrow_mut();
|
||||
|
||||
{
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::ListV(args));
|
||||
|
||||
let res = guard.run_with_reference::<Context, Context>(
|
||||
cx,
|
||||
"*context*",
|
||||
&format!("(apply {} (cons *context* _helix_args))", name),
|
||||
);
|
||||
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::Void);
|
||||
|
||||
res
|
||||
}
|
||||
}) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_typed_command_if_global_exists<'a>(
|
||||
cx: &mut compositor::Context,
|
||||
input: &'a str,
|
||||
parts: &'a [&'a str],
|
||||
event: PromptEvent,
|
||||
) -> bool {
|
||||
if ENGINE.with(|x| x.borrow().global_exists(parts[0])) {
|
||||
let shellwords = Shellwords::from(input);
|
||||
let args = shellwords.words();
|
||||
|
||||
// We're finalizing the event - we actually want to call the function
|
||||
if event == PromptEvent::Validate {
|
||||
// TODO: @Matt - extract this whole API call here to just be inside the engine module
|
||||
// For what its worth, also explore a more elegant API for calling apply with some arguments,
|
||||
// this does work, but its a little opaque.
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
let args = steel::List::from(
|
||||
args[1..]
|
||||
.iter()
|
||||
.map(|x| x.clone().into_steelval().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let mut guard = x.borrow_mut();
|
||||
// let mut maybe_callback = None;
|
||||
|
||||
let res = {
|
||||
let mut cx = Context {
|
||||
register: None,
|
||||
count: std::num::NonZeroUsize::new(1),
|
||||
editor: cx.editor,
|
||||
callback: None,
|
||||
on_next_key_callback: None,
|
||||
jobs: cx.jobs,
|
||||
};
|
||||
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::ListV(args));
|
||||
|
||||
let res = guard.run_with_reference::<Context, Context>(
|
||||
&mut cx,
|
||||
"*context*",
|
||||
&format!("(apply {} (cons *context* _helix_args))", parts[0]),
|
||||
);
|
||||
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::Void);
|
||||
|
||||
// if let Some(callback) = cx.callback.take() {
|
||||
// panic!("Found a callback!");
|
||||
// maybe_callback = Some(callback);
|
||||
// }
|
||||
|
||||
res
|
||||
};
|
||||
|
||||
// TODO: Recursively (or otherwise) keep retrying until we're back
|
||||
// into the engine context, executing a function. We might need to set up
|
||||
// some sort of fuel or something
|
||||
// if let Some(callback) = maybe_callback {
|
||||
// (callback)(_, cx);
|
||||
// }
|
||||
|
||||
res
|
||||
}) {
|
||||
compositor_present_error(cx, e);
|
||||
};
|
||||
}
|
||||
|
||||
// Global exists
|
||||
true
|
||||
} else {
|
||||
// Global does not exist
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_doc_for_identifier(ident: &str) -> Option<String> {
|
||||
if ENGINE.with(|x| x.borrow().global_exists(ident)) {
|
||||
if let Some(v) = super::engine::ExportedIdentifiers::engine_get_doc(ident) {
|
||||
return Some(v.into());
|
||||
}
|
||||
|
||||
return Some("Run this plugin command!".into());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// 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>>> =
|
||||
|
@ -134,6 +321,39 @@ pub fn present_error(cx: &mut Context, e: SteelErr) {
|
|||
cx.jobs.callback(callback);
|
||||
}
|
||||
|
||||
// Key maps
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EmbeddedKeyMap(pub HashMap<Mode, KeyTrie>);
|
||||
impl Custom for EmbeddedKeyMap {}
|
||||
|
||||
pub fn get_keymap() -> EmbeddedKeyMap {
|
||||
// Snapsnot current configuration for use in forking the keymap
|
||||
let keymap = Config::load_default().unwrap();
|
||||
|
||||
// These are the actual mappings that we want
|
||||
let map = keymap.keys;
|
||||
|
||||
EmbeddedKeyMap(map)
|
||||
}
|
||||
|
||||
// Base level - no configuration
|
||||
pub fn default_keymap() -> EmbeddedKeyMap {
|
||||
EmbeddedKeyMap(keymap::default())
|
||||
}
|
||||
|
||||
// Completely empty, allow for overriding
|
||||
pub fn empty_keymap() -> EmbeddedKeyMap {
|
||||
EmbeddedKeyMap(HashMap::default())
|
||||
}
|
||||
|
||||
pub fn string_to_embedded_keymap(value: String) -> EmbeddedKeyMap {
|
||||
EmbeddedKeyMap(serde_json::from_str(&value).unwrap())
|
||||
}
|
||||
|
||||
pub fn merge_keybindings(left: &mut EmbeddedKeyMap, right: EmbeddedKeyMap) {
|
||||
merge_keys(&mut left.0, right.0)
|
||||
}
|
||||
|
||||
/// Run the initialization script located at `$helix_config/init.scm`
|
||||
/// This runs the script in the global environment, and does _not_ load it as a module directly
|
||||
pub fn run_initialization_script(cx: &mut Context) {
|
||||
|
@ -238,13 +458,13 @@ impl CallbackQueue {
|
|||
/// queue that the engine and the config push and pull from. Alternatively, we could use a channel
|
||||
/// directly, however this was easy enough to set up.
|
||||
pub struct SharedKeyBindingsEventQueue {
|
||||
raw_bindings: Arc<Mutex<VecDeque<String>>>,
|
||||
raw_bindings: Arc<Mutex<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl SharedKeyBindingsEventQueue {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
raw_bindings: std::sync::Arc::new(std::sync::Mutex::new(VecDeque::new())),
|
||||
raw_bindings: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,17 +473,18 @@ impl SharedKeyBindingsEventQueue {
|
|||
.raw_bindings
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(other_as_json);
|
||||
.push(other_as_json);
|
||||
}
|
||||
|
||||
pub fn get() -> Option<HashMap<Mode, KeyTrie>> {
|
||||
let mut guard = KEYBINDING_QUEUE.raw_bindings.lock().unwrap();
|
||||
let guard = KEYBINDING_QUEUE.raw_bindings.lock().unwrap();
|
||||
|
||||
if let Some(initial) = guard.pop_front() {
|
||||
let mut initial = serde_json::from_str(&initial).unwrap();
|
||||
if let Some(first) = guard.get(0).clone() {
|
||||
let mut initial = serde_json::from_str(first).unwrap();
|
||||
|
||||
while let Some(remaining_event) = guard.pop_front() {
|
||||
let bindings = serde_json::from_str(&remaining_event).unwrap();
|
||||
// while let Some(remaining_event) = guard.pop_front() {
|
||||
for remaining_event in guard.iter() {
|
||||
let bindings = serde_json::from_str(remaining_event).unwrap();
|
||||
|
||||
merge_keys(&mut initial, bindings);
|
||||
}
|
||||
|
@ -419,6 +640,14 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
|
|||
// );
|
||||
|
||||
engine.register_fn("enqueue-callback!", CallbackQueue::enqueue);
|
||||
engine.register_fn("helix-current-keymap", get_keymap);
|
||||
engine.register_fn("helix-empty-keymap", empty_keymap);
|
||||
engine.register_fn("helix-default-keymap", default_keymap);
|
||||
engine.register_fn("helix-merge-keybindings", merge_keybindings);
|
||||
engine.register_fn("helix-string->keymap", string_to_embedded_keymap);
|
||||
|
||||
// Use this to get at buffer specific keybindings
|
||||
engine.register_value("*buffer-or-extension-keybindings*", SteelVal::Void);
|
||||
|
||||
// Find the workspace
|
||||
engine.register_fn("helix-find-workspace", || {
|
||||
|
@ -509,18 +738,18 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
|
|||
|
||||
let cloned_func = callback_fn.clone();
|
||||
|
||||
ENGINE
|
||||
.with(|x| {
|
||||
x.borrow_mut()
|
||||
.with_mut_reference::<Context, Context>(&mut ctx)
|
||||
.consume(move |engine, mut args| {
|
||||
// Add the string as an argument to the callback
|
||||
args.push(input.into_steelval().unwrap());
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
x.borrow_mut()
|
||||
.with_mut_reference::<Context, Context>(&mut ctx)
|
||||
.consume(move |engine, mut args| {
|
||||
// Add the string as an argument to the callback
|
||||
args.push(input.into_steelval().unwrap());
|
||||
|
||||
engine.call_function_with_args(cloned_func.clone(), args)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
engine.call_function_with_args(cloned_func.clone(), args)
|
||||
})
|
||||
}) {
|
||||
present_error(&mut ctx, e);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -792,6 +1021,9 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
|
|||
engine.register_fn("push-component!", push_component);
|
||||
engine.register_fn("enqueue-thread-local-callback", enqueue_command);
|
||||
|
||||
// Create directory since we can't do that in the current state
|
||||
engine.register_fn("hx.create-directory", create_directory);
|
||||
|
||||
let helix_module_path = helix_loader::helix_module_file();
|
||||
|
||||
engine
|
||||
|
@ -1131,15 +1363,15 @@ fn enqueue_command(cx: &mut Context, callback_fn: SteelVal) {
|
|||
|
||||
let cloned_func = callback_fn.clone();
|
||||
|
||||
ENGINE
|
||||
.with(|x| {
|
||||
x.borrow_mut()
|
||||
.with_mut_reference::<Context, Context>(&mut ctx)
|
||||
.consume(move |engine, args| {
|
||||
engine.call_function_with_args(cloned_func.clone(), args)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
x.borrow_mut()
|
||||
.with_mut_reference::<Context, Context>(&mut ctx)
|
||||
.consume(move |engine, args| {
|
||||
engine.call_function_with_args(cloned_func.clone(), args)
|
||||
})
|
||||
}) {
|
||||
present_error(&mut ctx, e);
|
||||
}
|
||||
},
|
||||
);
|
||||
Ok(call)
|
||||
|
@ -1147,6 +1379,17 @@ fn enqueue_command(cx: &mut Context, callback_fn: SteelVal) {
|
|||
cx.jobs.local_callback(callback);
|
||||
}
|
||||
|
||||
// Check that we successfully created a directory?
|
||||
fn create_directory(path: String) {
|
||||
let path = helix_core::path::get_canonicalized_path(&PathBuf::from(path)).unwrap();
|
||||
|
||||
if path.exists() {
|
||||
return;
|
||||
} else {
|
||||
std::fs::create_dir(path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// fn enqueue_callback(cx: &mut Context, thunk: SteelVal) {
|
||||
// log::info!("Enqueueing callback!");
|
||||
|
||||
|
|
|
@ -3004,67 +3004,9 @@ pub(super) fn command_mode(cx: &mut Context) {
|
|||
if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
} else if ENGINE.with(|x| x.borrow().global_exists(parts[0])) {
|
||||
let shellwords = Shellwords::from(input);
|
||||
let args = shellwords.words();
|
||||
|
||||
// We're finalizing the event - we actually want to call the function
|
||||
if event == PromptEvent::Validate {
|
||||
// TODO: @Matt - extract this whole API call here to just be inside the engine module
|
||||
// For what its worth, also explore a more elegant API for calling apply with some arguments,
|
||||
// this does work, but its a little opaque.
|
||||
if let Err(e) = ENGINE.with(|x| {
|
||||
let args = steel::List::from(
|
||||
args[1..]
|
||||
.iter()
|
||||
.map(|x| x.clone().into_steelval().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let mut guard = x.borrow_mut();
|
||||
// let mut maybe_callback = None;
|
||||
|
||||
let res = {
|
||||
let mut cx = Context {
|
||||
register: None,
|
||||
count: std::num::NonZeroUsize::new(1),
|
||||
editor: cx.editor,
|
||||
callback: None,
|
||||
on_next_key_callback: None,
|
||||
jobs: cx.jobs,
|
||||
};
|
||||
|
||||
guard
|
||||
.register_value("_helix_args", steel::rvals::SteelVal::ListV(args));
|
||||
|
||||
let res = guard.run_with_reference::<Context, Context>(
|
||||
&mut cx,
|
||||
"*context*",
|
||||
&format!("(apply {} (cons *context* _helix_args))", parts[0]),
|
||||
);
|
||||
|
||||
guard.register_value("_helix_args", steel::rvals::SteelVal::Void);
|
||||
|
||||
// if let Some(callback) = cx.callback.take() {
|
||||
// panic!("Found a callback!");
|
||||
// maybe_callback = Some(callback);
|
||||
// }
|
||||
|
||||
res
|
||||
};
|
||||
|
||||
// TODO: Recursively (or otherwise) keep retrying until we're back
|
||||
// into the engine context, executing a function. We might need to set up
|
||||
// some sort of fuel or something
|
||||
// if let Some(callback) = maybe_callback {
|
||||
// (callback)(_, cx);
|
||||
// }
|
||||
|
||||
res
|
||||
}) {
|
||||
compositor_present_error(cx, e)
|
||||
};
|
||||
}
|
||||
} else if ScriptingEngine::call_typed_command_if_global_exists(cx, input, &parts, event)
|
||||
{
|
||||
// Engine handles the other cases
|
||||
} else if event == PromptEvent::Validate {
|
||||
cx.editor
|
||||
.set_error(format!("no such command: '{}'", parts[0]));
|
||||
|
@ -3081,18 +3023,8 @@ pub(super) fn command_mode(cx: &mut Context) {
|
|||
return Some((*doc).into());
|
||||
}
|
||||
return Some(format!("{}\nAliases: {}", doc, aliases.join(", ")).into());
|
||||
} else if ENGINE.with(|x| x.borrow().global_exists(part)) {
|
||||
if let Some(v) = super::engine::ExportedIdentifiers::engine_get_doc(part) {
|
||||
return Some(v.into());
|
||||
}
|
||||
|
||||
// if let Ok(v) = ENGINE.with(|x| x.borrow().extract_value(&format!("{part}__doc__"))) {
|
||||
// if let steel::rvals::SteelVal::StringV(s) = v {
|
||||
// return Some(s.to_string().into());
|
||||
// }
|
||||
// }
|
||||
|
||||
return Some("Run this plugin command!".into());
|
||||
} else if let Some(doc) = ScriptingEngine::get_doc_for_identifier(part) {
|
||||
return Some(doc.into());
|
||||
}
|
||||
|
||||
None
|
||||
|
|
|
@ -303,12 +303,14 @@ impl Keymaps {
|
|||
self.sticky.as_ref()
|
||||
}
|
||||
|
||||
/// Lookup `key` in the keymap to try and find a command to execute. Escape
|
||||
/// key cancels pending keystrokes. If there are no pending keystrokes but a
|
||||
/// sticky node is in use, it will be cleared.
|
||||
pub fn get(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult {
|
||||
pub(crate) fn get_with_map(
|
||||
&mut self,
|
||||
keymaps: &HashMap<Mode, KeyTrie>,
|
||||
mode: Mode,
|
||||
key: KeyEvent,
|
||||
) -> KeymapResult {
|
||||
// TODO: remove the sticky part and look up manually
|
||||
let keymaps = &*self.map();
|
||||
// let keymaps = &*self.map();
|
||||
let keymap = &keymaps[&mode];
|
||||
|
||||
if key!(Esc) == key {
|
||||
|
@ -356,6 +358,13 @@ impl Keymaps {
|
|||
None => KeymapResult::Cancelled(self.state.drain(..).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lookup `key` in the keymap to try and find a command to execute. Escape
|
||||
/// key cancels pending keystrokes. If there are no pending keystrokes but a
|
||||
/// sticky node is in use, it will be cleared.
|
||||
pub fn get(&mut self, mode: Mode, key: KeyEvent) -> KeymapResult {
|
||||
self.get_with_map(&*self.map(), mode, key)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Keymaps {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
commands::{self, OnKeyCallback},
|
||||
commands::{self, engine::ScriptingEngine, OnKeyCallback},
|
||||
compositor::{Component, Context, Event, EventResult},
|
||||
job::{self, Callback},
|
||||
key,
|
||||
|
@ -798,21 +798,27 @@ impl EditorView {
|
|||
) -> Option<KeymapResult> {
|
||||
let mut last_mode = mode;
|
||||
self.pseudo_pending.extend(self.keymaps.pending());
|
||||
let key_result = self.keymaps.get(mode, event);
|
||||
|
||||
// let key_result = self.keymaps.get(mode, event);
|
||||
|
||||
// Check the engine for any buffer specific keybindings first
|
||||
let key_result = ScriptingEngine::get_keymap_for_extension(cxt)
|
||||
.and_then(|map| {
|
||||
if let steel::SteelVal::Custom(inner) = map {
|
||||
if let Some(underlying) = steel::rvals::as_underlying_type::<
|
||||
commands::engine::EmbeddedKeyMap,
|
||||
>(inner.borrow().as_ref())
|
||||
{
|
||||
return Some(self.keymaps.get_with_map(&underlying.0, mode, event));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.unwrap_or_else(|| self.keymaps.get(mode, event));
|
||||
|
||||
cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox());
|
||||
|
||||
// Get the currently activated minor modes
|
||||
// let extension = {
|
||||
// let current_focus = cxt.editor.tree.focus;
|
||||
// let view = cxt.editor.tree.get(current_focus);
|
||||
// let doc = &view.doc;
|
||||
// let current_doc = cxt.editor.documents.get(doc);
|
||||
|
||||
// current_doc
|
||||
// .and_then(|x| x.path())
|
||||
// .and_then(|x| x.extension());
|
||||
// };
|
||||
|
||||
let mut execute_command = |command: &commands::MappableCommand| {
|
||||
command.execute(cxt);
|
||||
let current_mode = cxt.editor.mode();
|
||||
|
@ -849,20 +855,6 @@ impl EditorView {
|
|||
|
||||
match &key_result {
|
||||
KeymapResult::Matched(command) => {
|
||||
// TODO: @Matt - check minor modes here.
|
||||
// Check current path:
|
||||
|
||||
// let current_focus = cxt.editor.tree.focus;
|
||||
// let view = cxt.editor.tree.get(current_focus);
|
||||
// let doc = &view.doc;
|
||||
// let current_doc = cxt.editor.documents.get(doc);
|
||||
|
||||
// let extension =
|
||||
|
||||
// current_doc.and_then(|x| x.path().and_then(|x| x.to_str().map(|x| x.to_string())))
|
||||
|
||||
// cxt.editor.
|
||||
|
||||
execute_command(command);
|
||||
}
|
||||
KeymapResult::Pending(node) => cxt.editor.autoinfo = Some(node.infobox()),
|
||||
|
|
|
@ -26,4 +26,4 @@ once_cell = "1.18"
|
|||
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"] }
|
||||
steel-core = { path = "../../../steel/crates/steel-core", version = "0.4.0", features = ["modules", "anyhow", "blocking_requests"] }
|
|
@ -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.4.0", features = ["modules", "anyhow", "blocking_requests"] }
|
||||
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
|
|
@ -1571,6 +1571,8 @@ impl Editor {
|
|||
if prev_id != view_id {
|
||||
log::info!("Changing focus: {:?}", view_id);
|
||||
|
||||
// TODO: Consult map for modes to change given file type?
|
||||
|
||||
self.enter_normal_mode();
|
||||
self.ensure_cursor_in_view(view_id);
|
||||
|
||||
|
|
|
@ -36,20 +36,33 @@ impl Info {
|
|||
.unwrap();
|
||||
let mut text = String::new();
|
||||
|
||||
let mut height = 0;
|
||||
|
||||
for (item, desc) in body {
|
||||
let _ = writeln!(
|
||||
text,
|
||||
"{:width$} {}",
|
||||
item.as_ref(),
|
||||
desc.as_ref(),
|
||||
width = item_width
|
||||
);
|
||||
let mut line_iter = desc.as_ref().lines();
|
||||
|
||||
if let Some(first_line) = line_iter.next() {
|
||||
let _ = writeln!(
|
||||
text,
|
||||
"{:width$} {}",
|
||||
item.as_ref(),
|
||||
first_line,
|
||||
width = item_width
|
||||
);
|
||||
height += 1;
|
||||
}
|
||||
|
||||
for line in line_iter {
|
||||
let _ = writeln!(text, "{:width$} {}", "", line, width = item_width);
|
||||
height += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
title: title.to_string(),
|
||||
width: text.lines().map(|l| l.width()).max().unwrap() as u16,
|
||||
height: body.len() as u16,
|
||||
// height: (body.len() + newlines) as u16,
|
||||
height,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue