From d6c3804ed8d81f00a98258fae9234c20453286a5 Mon Sep 17 00:00:00 2001 From: mattwparas Date: Sat, 20 Jan 2024 21:32:12 -0800 Subject: [PATCH] use in memory representation of configuration --- Cargo.lock | 23 +- Cargo.toml | 4 +- helix-term/src/application.rs | 6 +- helix-term/src/commands/engine.rs | 18 +- helix-term/src/commands/engine/scheme.rs | 623 +++++++++++++++++++---- helix-view/src/editor.rs | 1 + helix-view/src/extension.rs | 25 +- 7 files changed, 597 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 427444183..678819c49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1686,9 +1686,12 @@ dependencies = [ [[package]] name = "im-lists" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8ce6c776654abe411e4ccd52524f87e8ca16e2c487dbe127521eb048a46f7e6" +checksum = "0c07eff2c41645923382085ea8627509e5184f7a668f75d0c1e16091f7af4798" +dependencies = [ + "smallvec", +] [[package]] name = "im-rc" @@ -2524,8 +2527,8 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel-core" -version = "0.5.0" -source = "git+https://github.com/mattwparas/steel.git#03fe335199334f44396949fda2508de3545bc895" +version = "0.6.0" +source = "git+https://github.com/mattwparas/steel.git#9cc058dcd868edf81985909e09cafd94dbb9a1e3" dependencies = [ "abi_stable", "anyhow", @@ -2549,6 +2552,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "smallvec", "steel-derive", "steel-gen", "steel-parser", @@ -2558,8 +2562,8 @@ dependencies = [ [[package]] name = "steel-derive" -version = "0.4.0" -source = "git+https://github.com/mattwparas/steel.git#03fe335199334f44396949fda2508de3545bc895" +version = "0.5.0" +source = "git+https://github.com/mattwparas/steel.git#9cc058dcd868edf81985909e09cafd94dbb9a1e3" dependencies = [ "proc-macro2", "quote", @@ -2569,7 +2573,7 @@ dependencies = [ [[package]] name = "steel-gen" version = "0.2.0" -source = "git+https://github.com/mattwparas/steel.git#03fe335199334f44396949fda2508de3545bc895" +source = "git+https://github.com/mattwparas/steel.git#9cc058dcd868edf81985909e09cafd94dbb9a1e3" dependencies = [ "codegen", "serde", @@ -2578,9 +2582,10 @@ dependencies = [ [[package]] name = "steel-parser" -version = "0.4.0" -source = "git+https://github.com/mattwparas/steel.git#03fe335199334f44396949fda2508de3545bc895" +version = "0.6.0" +source = "git+https://github.com/mattwparas/steel.git#9cc058dcd868edf81985909e09cafd94dbb9a1e3" dependencies = [ + "fxhash", "lasso", "num-bigint", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 4a64d3e66..1ae3e37a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ default-members = [ [workspace.dependencies] # If working locally, use the local path dependency -# steel-core = { path = "../../steel/crates/steel-core", version = "0.5.0", features = ["modules", "anyhow", "dylibs"] } -steel-core = { git = "https://github.com/mattwparas/steel.git", version = "0.5.0", features = ["modules", "anyhow", "dylibs"] } +# steel-core = { path = "../../steel/crates/steel-core", version = "0.6.0", features = ["anyhow", "dylibs"] } +steel-core = { git = "https://github.com/mattwparas/steel.git", version = "0.6.0", features = ["anyhow", "dylibs"] } tree-sitter = { version = "0.20", git = "https://github.com/tree-sitter/tree-sitter", rev = "ab09ae20d640711174b8da8a654f6b3dec93da1a" } nucleo = "0.2.0" diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 7c1f81cbb..50c646789 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -260,7 +260,10 @@ impl Application { jobs: &mut app.jobs, }; - crate::commands::ScriptingEngine::run_initialization_script(&mut cx); + crate::commands::ScriptingEngine::run_initialization_script( + &mut cx, + app.config.clone(), + ); } Ok(app) @@ -405,6 +408,7 @@ impl Application { }; self.config.store(Arc::new(app_config)); } + ConfigEvent::Change => {} } // Update all the relevant members in the editor after updating diff --git a/helix-term/src/commands/engine.rs b/helix-term/src/commands/engine.rs index 7df0f8bab..5fb13a831 100644 --- a/helix-term/src/commands/engine.rs +++ b/helix-term/src/commands/engine.rs @@ -1,10 +1,12 @@ +use arc_swap::ArcSwapAny; use helix_core::syntax::Configuration; use helix_view::{document::Mode, input::KeyEvent, Theme}; -use std::borrow::Cow; +use std::{borrow::Cow, sync::Arc}; use crate::{ compositor, + config::Config, keymap::KeymapResult, ui::{self, PromptEvent}, }; @@ -60,9 +62,12 @@ impl ScriptingEngine { } } - pub fn run_initialization_script(cx: &mut Context) { + pub fn run_initialization_script( + cx: &mut Context, + configuration: Arc>>, + ) { for kind in PLUGIN_PRECEDENCE { - manual_dispatch!(kind, run_initialization_script(cx)) + manual_dispatch!(kind, run_initialization_script(cx, configuration.clone())) } } @@ -188,7 +193,12 @@ pub trait PluginSystem { /// 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. - fn run_initialization_script(&self, _cx: &mut Context) {} + fn run_initialization_script( + &self, + _cx: &mut Context, + _configuration: Arc>>, + ) { + } /// 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 diff --git a/helix-term/src/commands/engine/scheme.rs b/helix-term/src/commands/engine/scheme.rs index bb3459d10..86fcedca8 100644 --- a/helix-term/src/commands/engine/scheme.rs +++ b/helix-term/src/commands/engine/scheme.rs @@ -1,18 +1,23 @@ +use arc_swap::ArcSwapAny; use helix_core::{ extensions::steel_implementations::{rope_module, SteelRopeSlice}, graphemes, path::expand_tilde, regex::Regex, shellwords::Shellwords, - syntax::Configuration, + syntax::{AutoPairConfig, Configuration, SoftWrap}, Range, Selection, Tendril, }; use helix_event::register_hook; -use helix_loader::merge_toml_values; +use helix_loader::{config_dir, merge_toml_values}; use helix_lsp::lsp::CompletionItem; use helix_view::{ document::Mode, - editor::{Action, ConfigEvent}, + editor::{ + Action, BufferLine, ConfigEvent, CursorShapeConfig, FilePickerConfig, GutterConfig, + IndentGuidesConfig, LineEndingConfig, LineNumber, LspConfig, SearchConfig, SmartTabConfig, + StatusLineConfig, TerminalConfig, WhitespaceConfig, + }, extension::document_id_to_usize, input::KeyEvent, Document, DocumentId, Editor, Theme, ViewId, @@ -22,14 +27,14 @@ use serde_json::Value; use steel::{ gc::unsafe_erased_pointers::CustomReference, rerrs::ErrorKind, - rvals::{as_underlying_type, AsRefMutSteelValFromRef, FromSteelVal, IntoSteelVal, SteelString}, + rvals::{as_underlying_type, FromSteelVal, IntoSteelVal, SteelString}, steel_vm::{engine::Engine, register_fn::RegisterFn}, steelerr, List, SteelErr, SteelVal, }; use std::{ borrow::Cow, cell::RefCell, collections::HashMap, ops::Deref, path::PathBuf, rc::Rc, - sync::atomic::AtomicUsize, + sync::atomic::AtomicUsize, time::Duration, }; use std::{ collections::HashSet, @@ -384,9 +389,10 @@ fn load_static_commands(engine: &mut Engine) { template_function_no_context("get-helix-scm-path"); template_function_no_context("get-init-scm-path"); - let mut target_directory = PathBuf::from(std::env::var("STEEL_HOME").unwrap()); - target_directory.push("cogs"); - target_directory.push("helix"); + // let mut target_directory = PathBuf::from(std::env::var("HELIX_RUNTIME").unwrap()); + // target_directory.push("cogs"); + // target_directory.push("helix"); + let mut target_directory = helix_runtime_search_path(); if !target_directory.exists() { std::fs::create_dir(&target_directory).unwrap(); @@ -470,10 +476,11 @@ fn load_typed_commands(engine: &mut Engine) { )); } - let mut target_directory = PathBuf::from(std::env::var("STEEL_HOME").unwrap()); - target_directory.push("cogs"); - target_directory.push("helix"); + // let mut target_directory = PathBuf::from(std::env::var("HELIX_RUNTIME").unwrap()); + // target_directory.push("cogs"); + // target_directory.push("helix"); + let mut target_directory = helix_runtime_search_path(); if !target_directory.exists() { std::fs::create_dir(&target_directory).unwrap(); } @@ -485,6 +492,243 @@ fn load_typed_commands(engine: &mut Engine) { engine.register_module(module); } +fn fp_hidden(config: &mut FilePickerConfig, option: bool) { + config.hidden = option; +} + +fn fp_follow_symlinks(config: &mut FilePickerConfig, option: bool) { + config.follow_symlinks = option; +} + +fn fp_deduplicate_links(config: &mut FilePickerConfig, option: bool) { + config.deduplicate_links = option; +} + +fn fp_parents(config: &mut FilePickerConfig, option: bool) { + config.parents = option; +} + +fn fp_ignore(config: &mut FilePickerConfig, option: bool) { + config.ignore = option; +} + +fn fp_git_ignore(config: &mut FilePickerConfig, option: bool) { + config.git_ignore = option; +} + +fn fp_git_global(config: &mut FilePickerConfig, option: bool) { + config.git_global = option; +} + +fn fp_git_exclude(config: &mut FilePickerConfig, option: bool) { + config.git_exclude = option; +} + +fn fp_max_depth(config: &mut FilePickerConfig, option: Option) { + config.max_depth = option; +} + +fn load_configuration_api(engine: &mut Engine) { + let mut module = BuiltInModule::new("helix/core/configuration"); + + module.register_fn("update-configuration!", |ctx: &mut Context| { + ctx.editor + .config_events + .0 + .send(ConfigEvent::Change) + .unwrap(); + }); + + let mut builtin_configuration_module = + "(require-builtin helix/core/configuration as helix.)".to_string(); + + builtin_configuration_module.push_str(&format!( + r#" +(provide update-configuration!) +(define (update-configuration!) + (helix.update-configuration! *helix.cx*)) +"#, + )); + + let mut template_file_picker_function = |name: &str| { + builtin_configuration_module.push_str(&format!( + r#" +(provide {}) +(define ({} arg) + (lambda (picker) + (helix.{} picker arg) + picker)) +"#, + name, name, name + )); + }; + + let file_picker_functions = &[ + "fp-hidden", + "fp-follow-symlinks", + "fp-deduplicate-links", + "fp-parents", + "fp-ignore", + "fp-git-ignore", + "fp-git-global", + "fp-git-exclude", + "fp-max-depth", + ]; + + for name in file_picker_functions { + template_file_picker_function(name); + } + + builtin_configuration_module.push_str(&format!( + r#" +(provide file-picker) +(define (file-picker . args) + (helix.register-file-picker + *helix.config* + (foldl (lambda (func config) (func config)) (helix.raw-file-picker) args))) +"#, + )); + + module + .register_fn("raw-file-picker", || FilePickerConfig::default()) + .register_fn("register-file-picker", HelixConfiguration::file_picker) + .register_fn("fp-hidden", fp_hidden) + .register_fn("fp-follow-symlinks", fp_follow_symlinks) + .register_fn("fp-deduplicate-links", fp_deduplicate_links) + .register_fn("fp-parents", fp_parents) + .register_fn("fp-ignore", fp_ignore) + .register_fn("fp-git-ignore", fp_git_ignore) + .register_fn("fp-git-global", fp_git_global) + .register_fn("fp-git-exclude", fp_git_exclude) + .register_fn("fp-max-depth", fp_max_depth); + + let mut template_function_arity_1 = |name: &str| { + builtin_configuration_module.push_str(&format!( + r#" +(provide {}) +(define ({} arg) + (helix.{} *helix.config* arg)) +"#, + name, name, name + )); + }; + + let functions = &[ + "scrolloff", + "scroll_lines", + "mouse", + "shell", + "line-number", + "cursorline", + "cursorcolumn", + "middle-click-paste", + // "auto-pairs", + "auto-completion", + "auto-format", + "auto-save", + "text-width", + "idle-timeout", + "completion-timeout", + "preview-completion-insert", + "completion-trigger-len", + "completion-replace", + "auto-info", + "cursor-shape", + "true-color", + "insert-final-newline", + "color-modes", + "gutters", + // "file-picker", + "statusline", + "undercurl", + "search", + "lsp", + "terminal", + "rulers", + "whitespace", + "bufferline", + "indent-guides", + // "soft-wrap", + "workspace-lsp-roots", + "default-line-ending", + "smart-tab", + ]; + + for func in functions { + template_function_arity_1(func); + } + + module + .register_fn("scrolloff", HelixConfiguration::scrolloff) + .register_fn("scroll_lines", HelixConfiguration::scroll_lines) + .register_fn("mouse", HelixConfiguration::mouse) + .register_fn("shell", HelixConfiguration::shell) + .register_fn("line-number", HelixConfiguration::line_number) + .register_fn("cursorline", HelixConfiguration::cursorline) + .register_fn("cursorcolumn", HelixConfiguration::cursorcolumn) + .register_fn("middle-click-paste", HelixConfiguration::middle_click_paste) + // .register_fn("auto-pairs", HelixConfiguration::auto_pairs) + .register_fn("auto-completion", HelixConfiguration::auto_completion) + .register_fn("auto-format", HelixConfiguration::auto_format) + .register_fn("auto-save", HelixConfiguration::auto_save) + .register_fn("text-width", HelixConfiguration::text_width) + .register_fn("idle-timeout", HelixConfiguration::idle_timeout) + .register_fn("completion-timeout", HelixConfiguration::completion_timeout) + .register_fn( + "preview-completion-insert", + HelixConfiguration::preview_completion_insert, + ) + .register_fn( + "completion-trigger-len", + HelixConfiguration::completion_trigger_len, + ) + .register_fn("completion-replace", HelixConfiguration::completion_replace) + .register_fn("auto-info", HelixConfiguration::auto_info) + .register_fn("cursor-shape", HelixConfiguration::cursor_shape) + .register_fn("true-color", HelixConfiguration::true_color) + .register_fn( + "insert-final-newline", + HelixConfiguration::insert_final_newline, + ) + .register_fn("color-modes", HelixConfiguration::color_modes) + .register_fn("gutters", HelixConfiguration::gutters) + // .register_fn("file-picker", HelixConfiguration::file_picker) + .register_fn("statusline", HelixConfiguration::statusline) + .register_fn("undercurl", HelixConfiguration::undercurl) + .register_fn("search", HelixConfiguration::search) + .register_fn("lsp", HelixConfiguration::lsp) + .register_fn("terminal", HelixConfiguration::terminal) + .register_fn("rulers", HelixConfiguration::rulers) + .register_fn("whitespace", HelixConfiguration::whitespace) + .register_fn("bufferline", HelixConfiguration::bufferline) + .register_fn("indent-guides", HelixConfiguration::indent_guides) + // .register_fn("soft-wrap", HelixConfiguration::soft_wrap) + .register_fn( + "workspace-lsp-roots", + HelixConfiguration::workspace_lsp_roots, + ) + .register_fn( + "default-line-ending", + HelixConfiguration::default_line_ending, + ) + .register_fn("smart-tab", HelixConfiguration::smart_tab); + + // let mut target_directory = PathBuf::from(std::env::var("HELIX_RUNTIME").unwrap()); + // target_directory.push("cogs"); + // target_directory.push("helix"); + let mut target_directory = helix_runtime_search_path(); + + if !target_directory.exists() { + std::fs::create_dir(&target_directory).unwrap(); + } + + target_directory.push("configuration.scm"); + + std::fs::write(target_directory, builtin_configuration_module).unwrap(); + + engine.register_module(module); +} + fn load_editor_api(engine: &mut Engine) { let mut module = BuiltInModule::new("helix/core/editor"); @@ -578,9 +822,10 @@ fn load_editor_api(engine: &mut Engine) { // module.register_fn("cx->themes", get_themes); // Not the best usage here, duplicating it in a bunch of spots for now - let mut target_directory = PathBuf::from(std::env::var("STEEL_HOME").unwrap()); - target_directory.push("cogs"); - target_directory.push("helix"); + // let mut target_directory = PathBuf::from(std::env::var("HELIX_RUNTIME").unwrap()); + // target_directory.push("cogs"); + // target_directory.push("helix"); + let mut target_directory = helix_runtime_search_path(); if !target_directory.exists() { std::fs::create_dir(&target_directory).unwrap(); @@ -604,8 +849,12 @@ impl super::PluginSystem for SteelScriptingEngine { super::PluginSystemKind::Steel } - fn run_initialization_script(&self, cx: &mut Context) { - run_initialization_script(cx); + fn run_initialization_script( + &self, + cx: &mut Context, + configuration: Arc>>, + ) { + run_initialization_script(cx, configuration); } fn handle_keymap_event( @@ -635,7 +884,7 @@ impl super::PluginSystem for SteelScriptingEngine { args: &[Cow], ) -> bool { if ENGINE.with(|x| x.borrow().global_exists(name)) { - let mut args = args + let args = args .iter() .map(|x| x.clone().into_steelval().unwrap()) .collect::>(); @@ -649,8 +898,6 @@ impl super::PluginSystem for SteelScriptingEngine { let context = arguments[0].clone(); engine.update_value("*helix.cx*", context); - // arguments.append(&mut args); - // TODO: Get rid of this clone engine.call_function_by_name_with_args(name, args.clone()) }, @@ -978,9 +1225,257 @@ pub fn steel_init_file() -> PathBuf { helix_loader::config_dir().join("init.scm") } +#[derive(Clone)] +struct HelixConfiguration { + configuration: Arc>>, +} + +impl Custom for HelixConfiguration {} +// impl Custom for LineNumber {} + +impl HelixConfiguration { + fn load_config(&self) -> Config { + (*self.configuration.load().clone()).clone() + } + + fn store_config(&self, config: Config) { + self.configuration.store(Arc::new(config)); + } + + fn scrolloff(&self, lines: usize) { + let mut app_config = self.load_config(); + app_config.editor.scrolloff = lines; + self.store_config(app_config); + } + + fn scroll_lines(&self, lines: isize) { + let mut app_config = self.load_config(); + app_config.editor.scroll_lines = lines; + self.store_config(app_config); + } + + fn mouse(&self, m: bool) { + let mut app_config = self.load_config(); + app_config.editor.mouse = m; + self.store_config(app_config); + } + + fn shell(&self, shell: Vec) { + let mut app_config = self.load_config(); + app_config.editor.shell = shell; + self.store_config(app_config); + } + + // TODO: Make this a symbol, probably! + fn line_number(&self, mode: LineNumber) { + let mut app_config = self.load_config(); + app_config.editor.line_number = mode; + self.store_config(app_config); + } + + fn cursorline(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.cursorline = option; + self.store_config(app_config); + } + + fn cursorcolumn(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.cursorcolumn = option; + self.store_config(app_config); + } + + fn middle_click_paste(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.middle_click_paste = option; + self.store_config(app_config); + } + + fn auto_pairs(&self, config: AutoPairConfig) { + let mut app_config = self.load_config(); + app_config.editor.auto_pairs = config; + self.store_config(app_config); + } + + fn auto_completion(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.auto_completion = option; + self.store_config(app_config); + } + + fn auto_format(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.auto_format = option; + self.store_config(app_config); + } + + fn auto_save(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.auto_save = option; + self.store_config(app_config); + } + + fn text_width(&self, width: usize) { + let mut app_config = self.load_config(); + app_config.editor.text_width = width; + self.store_config(app_config); + } + + fn idle_timeout(&self, ms: usize) { + let mut app_config = self.load_config(); + app_config.editor.idle_timeout = Duration::from_millis(ms as u64); + self.store_config(app_config); + } + + fn completion_timeout(&self, ms: usize) { + let mut app_config = self.load_config(); + app_config.editor.completion_timeout = Duration::from_millis(ms as u64); + self.store_config(app_config); + } + + fn preview_completion_insert(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.preview_completion_insert = option; + self.store_config(app_config); + } + + // TODO: Make sure this conversion works automatically + fn completion_trigger_len(&self, length: u8) { + let mut app_config = self.load_config(); + app_config.editor.completion_trigger_len = length; + self.store_config(app_config); + } + + fn completion_replace(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.completion_replace = option; + self.store_config(app_config); + } + + fn auto_info(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.auto_info = option; + self.store_config(app_config); + } + + fn cursor_shape(&self, config: CursorShapeConfig) { + let mut app_config = self.load_config(); + app_config.editor.cursor_shape = config; + self.store_config(app_config); + } + + fn true_color(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.true_color = option; + self.store_config(app_config); + } + + fn insert_final_newline(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.insert_final_newline = option; + self.store_config(app_config); + } + + fn color_modes(&self, option: bool) { + let mut app_config = self.load_config(); + app_config.editor.color_modes = option; + self.store_config(app_config); + } + + fn gutters(&self, config: GutterConfig) { + let mut app_config = self.load_config(); + app_config.editor.gutters = config; + self.store_config(app_config); + } + + fn file_picker(&self, picker: FilePickerConfig) { + let mut app_config = self.load_config(); + app_config.editor.file_picker = picker; + self.store_config(app_config); + } + + fn statusline(&self, config: StatusLineConfig) { + let mut app_config = self.load_config(); + app_config.editor.statusline = config; + self.store_config(app_config); + } + + fn undercurl(&self, undercurl: bool) { + let mut app_config = self.load_config(); + app_config.editor.undercurl = undercurl; + self.store_config(app_config); + } + + fn search(&self, config: SearchConfig) { + let mut app_config = self.load_config(); + app_config.editor.search = config; + self.store_config(app_config); + } + + fn lsp(&self, config: LspConfig) { + let mut app_config = self.load_config(); + app_config.editor.lsp = config; + self.store_config(app_config); + } + + fn terminal(&self, config: Option) { + let mut app_config = self.load_config(); + app_config.editor.terminal = config; + self.store_config(app_config); + } + + fn rulers(&self, cols: Vec) { + let mut app_config = self.load_config(); + app_config.editor.rulers = cols; + self.store_config(app_config); + } + + fn whitespace(&self, config: WhitespaceConfig) { + let mut app_config = self.load_config(); + app_config.editor.whitespace = config; + self.store_config(app_config); + } + + fn bufferline(&self, config: BufferLine) { + let mut app_config = self.load_config(); + app_config.editor.bufferline = config; + self.store_config(app_config); + } + + fn indent_guides(&self, config: IndentGuidesConfig) { + let mut app_config = self.load_config(); + app_config.editor.indent_guides = config; + self.store_config(app_config); + } + + fn soft_wrap(&self, config: SoftWrap) { + let mut app_config = self.load_config(); + app_config.editor.soft_wrap = config; + self.store_config(app_config); + } + + fn workspace_lsp_roots(&self, roots: Vec) { + let mut app_config = self.load_config(); + app_config.editor.workspace_lsp_roots = roots; + self.store_config(app_config); + } + + fn default_line_ending(&self, config: LineEndingConfig) { + let mut app_config = self.load_config(); + app_config.editor.default_line_ending = config; + self.store_config(app_config); + } + + fn smart_tab(&self, config: Option) { + let mut app_config = self.load_config(); + app_config.editor.smart_tab = config; + self.store_config(app_config); + } +} + /// 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 -fn run_initialization_script(cx: &mut Context) { +fn run_initialization_script(cx: &mut Context, configuration: Arc>>) { log::info!("Loading init.scm..."); let helix_module_path = helix_module_file(); @@ -989,6 +1484,16 @@ fn run_initialization_script(cx: &mut Context) { ENGINE.with(|engine| { let mut guard = engine.borrow_mut(); + // Embed the configuration so we don't have to communicate over the refresh + // channel. The state is still stored within the `Application` struct, but + // now we can just access it and signal a refresh of the config when we need to. + guard.update_value( + "*helix.config*", + HelixConfiguration { configuration } + .into_steelval() + .unwrap(), + ); + let res = guard.run_with_reference( cx, "*helix.cx*", @@ -1018,7 +1523,7 @@ fn run_initialization_script(cx: &mut Context) { let docs = exported .iter() .filter_map(|x| { - if let Ok(value) = guard.run(&format!( + if let Ok(value) = guard.compile_and_run_raw_program(format!( "(#%function-ptr-table-get #%function-ptr-table {})", x )) { @@ -1314,7 +1819,7 @@ impl SteelEngine { } pub fn require_module(&mut self, module: SteelString) -> steel::rvals::Result<()> { - self.0.run(&format!("(require \"{}\")", module)).map(|_| ()) + self.0.run(format!("(require \"{}\")", module)).map(|_| ()) } } @@ -1428,9 +1933,7 @@ fn load_misc_api(engine: &mut Engine) { template_function_arity_2("enqueue-thread-local-callback-with-delay"); template_function_arity_2("helix-await-callback"); - let mut target_directory = PathBuf::from(std::env::var("STEEL_HOME").unwrap()); - target_directory.push("cogs"); - target_directory.push("helix"); + let mut target_directory = helix_runtime_search_path(); if !target_directory.exists() { std::fs::create_dir(&target_directory).unwrap(); @@ -1443,20 +1946,26 @@ fn load_misc_api(engine: &mut Engine) { engine.register_module(module); } +fn helix_runtime_search_path() -> PathBuf { + helix_loader::config_dir().join("helix") +} + fn configure_engine_impl(mut engine: Engine) -> Engine { log::info!("Loading engine!"); + engine.add_search_directory(helix_loader::config_dir()); + engine.register_value("*helix.cx*", SteelVal::Void); + engine.register_value("*helix.config*", SteelVal::Void); - // TODO: Load (require-builtin helix/core/editor) in more or less every file that needs it load_editor_api(&mut engine); + load_configuration_api(&mut engine); load_typed_commands(&mut engine); load_static_commands(&mut engine); load_keymap_api(&mut engine, KeyMapApi::new()); load_theme_api(&mut engine); load_rope_api(&mut engine); load_language_configuration_api(&mut engine); - load_misc_api(&mut engine); // load_engine_api(&mut engine); @@ -1749,7 +2258,7 @@ fn get_selection(cx: &mut Context) -> String { fn run_in_engine(cx: &mut Context, arg: String) -> anyhow::Result<()> { let callback = async move { let output = ENGINE - .with(|x| x.borrow_mut().run(&arg)) + .with(|x| x.borrow_mut().run(arg)) .map(|x| format!("{:?}", x)); let (output, success) = match output { @@ -1814,34 +2323,14 @@ fn set_scratch_buffer_name(cx: &mut Context, name: String) { } } -// TODO: Use this over handing around the editor reference, probably -// fn cx_current_focus(cx: &mut Context) -> helix_view::ViewId { -// cx.editor.tree.focus -// } - -// TODO: Expose the below in a separate module, make things a bit more clear! -fn current_focus(editor: &mut Editor) -> helix_view::ViewId { - editor.tree.focus -} - fn cx_current_focus(cx: &mut Context) -> helix_view::ViewId { cx.editor.tree.focus } -// Get the document id -fn get_document_id(editor: &mut Editor, view_id: helix_view::ViewId) -> DocumentId { - editor.tree.get(view_id).doc -} - fn cx_get_document_id(cx: &mut Context, view_id: helix_view::ViewId) -> DocumentId { cx.editor.tree.get(view_id).doc } -// Get the document from the document id - TODO: Add result type here -fn get_document(editor: &mut Editor, doc_id: DocumentId) -> &Document { - editor.documents.get(&doc_id).unwrap() -} - fn cx_get_document<'a>(cx: &'a mut Context, doc_id: DocumentId) -> &'a Document { cx.editor.documents.get(&doc_id).unwrap() } @@ -1850,14 +2339,6 @@ fn document_to_text(doc: &Document) -> SteelRopeSlice { SteelRopeSlice::new(doc.text().clone()) } -fn is_document_in_view(editor: &mut Editor, doc_id: DocumentId) -> Option { - editor - .tree - .traverse() - .find(|(_, v)| v.doc == doc_id) - .map(|(id, _)| id) -} - fn cx_is_document_in_view(cx: &mut Context, doc_id: DocumentId) -> Option { cx.editor .tree @@ -1866,10 +2347,6 @@ fn cx_is_document_in_view(cx: &mut Context, doc_id: DocumentId) -> Option bool { - editor.documents.get(&doc_id).is_some() -} - fn cx_document_exists(cx: &mut Context, doc_id: DocumentId) -> bool { cx.editor.documents.get(&doc_id).is_some() } @@ -1878,48 +2355,22 @@ fn document_path(doc: &Document) -> Option { doc.path().and_then(|x| x.to_str()).map(|x| x.to_string()) } -// Get the time the document was focused -fn document_focused_at(doc: &Document) -> std::time::Instant { - doc.focused_at -} - -// Get all the editor documents -fn editor_all_documents(editor: &mut Editor) -> Vec { - editor.documents.keys().copied().collect() -} - fn cx_editor_all_documents(cx: &mut Context) -> Vec { cx.editor.documents.keys().copied().collect() } -fn switch(editor: &mut Editor, doc_id: DocumentId) { - editor.switch(doc_id, Action::VerticalSplit) -} - fn cx_switch(cx: &mut Context, doc_id: DocumentId) { cx.editor.switch(doc_id, Action::VerticalSplit) } -fn editor_get_mode(editor: &mut Editor) -> Mode { - editor.mode -} - fn cx_get_mode(cx: &mut Context) -> Mode { cx.editor.mode } -fn editor_set_mode(editor: &mut Editor, mode: Mode) { - editor.mode = mode -} - fn cx_set_mode(cx: &mut Context, mode: Mode) { cx.editor.mode = mode } -fn is_context(value: SteelVal) -> bool { - Context::as_mut_ref_from_ref(&value).is_ok() -} - // 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? diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 27eba23fb..27d27fcc8 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -971,6 +971,7 @@ pub enum ConfigEvent { Refresh, Update(Box), UpdateLanguageConfiguration, + Change, } enum ThemeAction { diff --git a/helix-view/src/extension.rs b/helix-view/src/extension.rs index 0197ab371..2db31a6e1 100644 --- a/helix-view/src/extension.rs +++ b/helix-view/src/extension.rs @@ -10,7 +10,15 @@ mod steel_implementations { use steel::{gc::unsafe_erased_pointers::CustomReference, rvals::Custom}; use crate::{ - document::Mode, graphics::Rect, input::Event, Document, DocumentId, Editor, ViewId, + document::Mode, + editor::{ + BufferLine, CursorShapeConfig, FilePickerConfig, GutterConfig, IndentGuidesConfig, + LineEndingConfig, LineNumber, LspConfig, SearchConfig, SmartTabConfig, + StatusLineConfig, TerminalConfig, WhitespaceConfig, + }, + graphics::Rect, + input::Event, + Document, DocumentId, Editor, ViewId, }; impl steel::gc::unsafe_erased_pointers::CustomReference for Editor {} @@ -26,4 +34,19 @@ mod steel_implementations { impl Custom for DocumentId {} impl Custom for ViewId {} impl CustomReference for Document {} + + impl Custom for FilePickerConfig {} + impl Custom for StatusLineConfig {} + impl Custom for SearchConfig {} + impl Custom for TerminalConfig {} + impl Custom for WhitespaceConfig {} + impl Custom for CursorShapeConfig {} + impl Custom for BufferLine {} + impl Custom for LineNumber {} + impl Custom for GutterConfig {} + impl Custom for LspConfig {} + impl Custom for IndentGuidesConfig {} + impl Custom for LineEndingConfig {} + impl Custom for SmartTabConfig {} + // impl Custom for SoftW }