diff --git a/helix-term/src/commands/engine/steel.rs b/helix-term/src/commands/engine/steel.rs index 32f90149a..03d2449b0 100644 --- a/helix-term/src/commands/engine/steel.rs +++ b/helix-term/src/commands/engine/steel.rs @@ -17,7 +17,7 @@ use helix_view::{ GutterConfig, IndentGuidesConfig, LineEndingConfig, LineNumber, LspConfig, SearchConfig, SmartTabConfig, StatusLineConfig, TerminalConfig, WhitespaceConfig, }, - events::{DocumentFocusLost, SelectionDidChange}, + events::{DocumentFocusLost, DocumentOpened, SelectionDidChange}, extension::document_id_to_usize, input::KeyEvent, theme::Color, @@ -590,6 +590,22 @@ fn load_configuration_api(engine: &mut Engine, generate_sources: bool) { module.register_fn("get-config-option-value", get_option_value); + module.register_fn("set-configuration-for-file!", set_configuration_for_file); + + module + .register_fn( + "get-language-config", + HelixConfiguration::get_language_config, + ) + .register_fn( + "get-language-config-by-filename", + HelixConfiguration::get_individual_language_config_for_filename, + ) + .register_fn( + "set-language-config!", + HelixConfiguration::update_individual_language_config, + ); + module .register_fn("raw-file-picker", || FilePickerConfig::default()) .register_fn("register-file-picker", HelixConfiguration::file_picker) @@ -713,6 +729,14 @@ fn load_configuration_api(engine: &mut Engine, generate_sources: bool) { "#, )); + builtin_configuration_module.push_str(&format!( + r#" +(provide set-configuration-for-file!) +(define (set-configuration-for-file! path config) + (helix.set-configuration-for-file! *helix.cx* path config)) +"#, + )); + // Register the get keybindings function builtin_configuration_module.push_str(&format!( r#" @@ -847,6 +871,10 @@ fn load_configuration_api(engine: &mut Engine, generate_sources: bool) { "keybindings", "inline-diagnostics-cursor-line-enable", "inline-diagnostics-end-of-line-enable", + // language configuration functions + "get-language-config", + "get-language-config-by-filename", + "set-language-config!", ]; for func in functions { @@ -1449,26 +1477,60 @@ impl IndividualLanguageConfiguration { // Apply end of line configuration on doc open? // pub fn set_end_of_line(&mut self) { - // self. + // self.config.end // } } impl Custom for HelixConfiguration {} -// impl Custom for LineNumber {} +// Set the configuration for an individual file. +fn update_configuration_for_file(ctx: &mut Context, doc: DocumentId) { + if let Some(document) = ctx.editor.documents.get_mut(&doc) { + let path = document.path().unwrap(); + let config_for_file = ctx + .editor + .syn_loader + .load() + .language_config_for_file_name(path); + + document.language = config_for_file; + } +} + +fn set_configuration_for_file( + ctx: &mut Context, + file_name: SteelString, + configuration: IndividualLanguageConfiguration, +) { + if let Some(document) = ctx.editor.document_by_path_mut(file_name.as_str()) { + document.language = Some(Arc::new(configuration.config)); + } +} impl HelixConfiguration { fn store_language_configuration(&self, language_config: syntax::Loader) { self.language_configuration.store(Arc::new(language_config)) } - fn get_individual_language_config_for_filename( + fn get_language_config( &self, - file_name: &str, + language: SteelString, ) -> Option { self.language_configuration .load() - .language_config_for_file_name(std::path::Path::new(file_name)) + .language_config_for_language_id(language.as_str()) + .map(|config| IndividualLanguageConfiguration { + config: (*config).clone(), + }) + } + + fn get_individual_language_config_for_filename( + &self, + file_name: SteelString, + ) -> Option { + self.language_configuration + .load() + .language_config_for_file_name(std::path::Path::new(file_name.as_str())) .map(|config| IndividualLanguageConfiguration { config: (*config).clone(), }) @@ -1495,6 +1557,11 @@ impl HelixConfiguration { } } + // // Refresh configuration for a specific file + // fn refresh_language_configuration(&mut self) { + // todo!() + // } + fn load_config(&self) -> Config { (*self.configuration.load().clone()).clone() } @@ -2040,6 +2107,7 @@ fn register_hook(event_kind: String, callback_fn: SteelVal) -> steel::UnRecovera // and act accordingly? register_hook!(move |event: &mut DocumentFocusLost<'_>| { let cloned_func = rooted.value().clone(); + let doc_id = event.doc; let callback = move |editor: &mut Editor, _compositor: &mut Compositor, @@ -2058,8 +2126,13 @@ fn register_hook(event_kind: String, callback_fn: SteelVal) -> steel::UnRecovera .consume(move |engine, args| { let context = args[0].clone(); engine.update_value("*helix.cx*", context); + let mut args = [doc_id.into_steelval().unwrap()]; + // TODO: Do something with this error! - engine.call_function_with_args(cloned_func.clone(), Vec::new()) + engine.call_function_with_args_from_mut_slice( + cloned_func.clone(), + &mut args, + ) }) { present_error_inside_engine_context(&mut ctx, guard, e); @@ -2079,34 +2152,83 @@ fn register_hook(event_kind: String, callback_fn: SteelVal) -> steel::UnRecovera // is probably the most helpful so that way we can look the document up // and act accordingly? register_hook!(move |event: &mut SelectionDidChange<'_>| { - // let cloned_func = rooted.value().clone(); + let cloned_func = rooted.value().clone(); + let view_id = event.view; - // let callback = move |editor: &mut Editor, - // _compositor: &mut Compositor, - // jobs: &mut job::Jobs| { - // let mut ctx = Context { - // register: None, - // count: None, - // editor, - // callback: Vec::new(), - // on_next_key_callback: None, - // jobs, - // }; - // enter_engine(|guard| { - // if let Err(e) = guard - // .with_mut_reference::(&mut ctx) - // .consume(move |engine, args| { - // let context = args[0].clone(); - // engine.update_value("*helix.cx*", context); - // // TODO: Do something with this error! - // engine.call_function_with_args(cloned_func.clone(), Vec::new()) - // }) - // { - // present_error_inside_engine_context(&mut ctx, guard, e); - // } - // }) - // }; - // job::dispatch_blocking_jobs(callback); + let callback = move |editor: &mut Editor, + _compositor: &mut Compositor, + jobs: &mut job::Jobs| { + let mut ctx = Context { + register: None, + count: None, + editor, + callback: Vec::new(), + on_next_key_callback: None, + jobs, + }; + enter_engine(|guard| { + if let Err(e) = guard + .with_mut_reference::(&mut ctx) + .consume(move |engine, args| { + let context = args[0].clone(); + engine.update_value("*helix.cx*", context); + // TODO: Reuse this allocation + let mut args = [view_id.into_steelval().unwrap()]; + engine.call_function_with_args_from_mut_slice( + cloned_func.clone(), + &mut args, + ) + }) + { + present_error_inside_engine_context(&mut ctx, guard, e); + } + }) + }; + job::dispatch_blocking_jobs(callback); + + Ok(()) + }); + + Ok(SteelVal::Void).into() + } + + "document-opened" => { + // TODO: Share this code with the above since most of it is + // exactly the same + register_hook!(move |event: &mut DocumentOpened<'_>| { + let cloned_func = rooted.value().clone(); + let doc_id = event.doc; + + let callback = move |editor: &mut Editor, + _compositor: &mut Compositor, + jobs: &mut job::Jobs| { + let mut ctx = Context { + register: None, + count: None, + editor, + callback: Vec::new(), + on_next_key_callback: None, + jobs, + }; + enter_engine(|guard| { + if let Err(e) = guard + .with_mut_reference::(&mut ctx) + .consume(move |engine, args| { + let context = args[0].clone(); + engine.update_value("*helix.cx*", context); + // TODO: Reuse this allocation if possible + let mut args = [doc_id.into_steelval().unwrap()]; + engine.call_function_with_args_from_mut_slice( + cloned_func.clone(), + &mut args, + ) + }) + { + present_error_inside_engine_context(&mut ctx, guard, e); + } + }) + }; + job::dispatch_blocking_jobs(callback); Ok(()) }); @@ -2114,13 +2236,6 @@ fn register_hook(event_kind: String, callback_fn: SteelVal) -> steel::UnRecovera Ok(SteelVal::Void).into() } - // Unimplemented! - // "document-did-change" => { - // todo!() - // } - // "selection-did-change" => { - // todo!() - // } _ => steelerr!(Generic => "Unable to register hook: Unknown event type: {}", event_kind) .into(), } diff --git a/helix-term/src/events.rs b/helix-term/src/events.rs index 15d811529..439f86701 100644 --- a/helix-term/src/events.rs +++ b/helix-term/src/events.rs @@ -1,7 +1,7 @@ use helix_event::{events, register_event}; use helix_view::document::Mode; use helix_view::events::{ - DiagnosticsDidChange, DocumentDidChange, DocumentFocusLost, SelectionDidChange, + DiagnosticsDidChange, DocumentDidChange, DocumentFocusLost, DocumentOpened, SelectionDidChange, }; use crate::commands; @@ -21,4 +21,5 @@ pub fn register() { register_event::(); register_event::(); register_event::(); + register_event::(); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 125223c5e..7930a1e6f 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2,9 +2,10 @@ use crate::{ annotations::diagnostics::{DiagnosticFilter, InlineDiagnosticsConfig}, clipboard::ClipboardProvider, document::{ - DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint, + DocumentOpenError, DocumentSavedEvent, DocumentSavedEventFuture, DocumentSavedEventResult, + Mode, SavePoint, }, - events::DocumentFocusLost, + events::{DocumentClosed, DocumentFocusLost, DocumentOpened, DocumentSaved}, graphics::{CursorKind, Rect}, handlers::Handlers, info::Info, @@ -1784,6 +1785,11 @@ impl Editor { let id = self.new_document(doc); self.launch_language_servers(id); + dispatch(DocumentOpened { + editor: self, + doc: id, + }); + id }; @@ -1874,6 +1880,11 @@ impl Editor { self._refresh(); + dispatch(DocumentClosed { + editor: self, + doc: doc_id, + }); + Ok(()) } @@ -1911,6 +1922,11 @@ impl Editor { self.write_count += 1; + dispatch(DocumentSaved { + editor: self, + doc: doc_id, + }); + Ok(()) } diff --git a/helix-view/src/events.rs b/helix-view/src/events.rs index eb97268ce..b8d20bfca 100644 --- a/helix-view/src/events.rs +++ b/helix-view/src/events.rs @@ -15,4 +15,7 @@ events! { DiagnosticsDidChange<'a> { editor: &'a mut Editor, doc: DocumentId } // called **after** a document loses focus (but not when its closed) DocumentFocusLost<'a> { editor: &'a mut Editor, doc: DocumentId } + DocumentOpened<'a> { editor: &'a mut Editor, doc: DocumentId } + DocumentClosed<'a> { editor: &'a mut Editor, doc: DocumentId } + DocumentSaved<'a> { editor: &'a mut Editor, doc: DocumentId } }