1#![cfg_attr(coverage_nightly, allow(unused_features))]
2#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
3use std::sync::Arc;
43
44use {
45 reovim_driver_annotation::{AnnotationSourceKey, AnnotationSourceRegistry, LineNumberMode},
46 reovim_driver_command::{CommandHandler, CommandHandlerStore, CommandProvider},
47 reovim_driver_input::{
48 KeybindingStore, LookupPolicyStore, ModeInfo, ModeInfoStore, ModeProviderKey,
49 ModeProviderRegistry, ResolverRegistry,
50 },
51 reovim_driver_manifest::ModeBridgeStore,
52 reovim_driver_session::{InitialModeProvider, LeaderKeyProvider, bridges::BridgeProvider},
53 reovim_kernel::api::v1::{
54 KeybindingRegistration, ModeId, Module, ModuleContext, ModuleError, ModuleId, ProbeResult,
55 Version, pr_info,
56 },
57};
58
59pub mod annotation;
60pub mod bindings;
61pub mod commands;
62pub mod fallback;
63pub mod ids;
64pub mod macros;
65pub mod modes;
66pub mod operators;
67pub mod providers;
68pub mod resolvers;
69pub mod session_state;
70pub mod vim_lookup_policy;
71pub mod visual;
72
73#[cfg(test)]
74mod ids_tests;
75#[cfg(test)]
76mod lib_tests;
77#[cfg(test)]
78mod macros_tests;
79#[cfg(test)]
80mod modes_tests;
81#[cfg(test)]
82mod providers_tests;
83#[cfg(test)]
84mod registry_integration;
85#[cfg(test)]
86mod session_state_tests;
87#[cfg(test)]
88mod vim_lookup_policy_tests;
89
90pub use modes::{VIM_MODULE, VimMode};
92
93pub use providers::{VimDefaultModeProvider, VimModuleProviderExt};
95
96pub use session_state::{PendingCharOp, VimSessionState};
98
99pub use ids::{CHANGE, DELETE, OperatorId, YANK};
101
102pub use operators::{
104 ChangeCommand, ChangeOperator, DeleteCommand, DeleteOperator, Operator, OperatorContext,
105 OperatorError, Range, YankCommand, YankOperator, operator_commands,
106};
107
108pub use fallback::VimFallbackHandler;
110
111pub use vim_lookup_policy::VimLookupPolicy;
113
114pub use commands::{
116 ChangeLine, ChangeToEndOfLine, EnterCommandLineMode, EnterInsertEndOfLine,
117 EnterInsertFirstNonBlank, EnterInsertMode, EnterInsertModeAppend, EnterReplaceMode,
118 EnterSearchBackward, EnterSearchForward, EnterWindowMode, ExecuteFindChar, ExitCommandLineMode,
119 ExitToNormal, OpenLineAbove, OpenLineBelow, ReplaceBackspace,
120};
121
122pub use resolvers::{
124 VimCaseResolver, VimChangeResolver, VimCommandLineResolver, VimDeleteResolver,
125 VimInsertResolver, VimNormalResolver, VimReplaceResolver, VimVisualResolver, VimYankResolver,
126};
127
128pub use visual::{
130 ChangeSelection,
132 DedentSelection,
133 DeleteSelection,
134 EnterVisualBlockMode,
136 EnterVisualLineMode,
137 EnterVisualMode,
138 ExitVisualMode,
140 IndentSelection,
141 LowercaseSelection,
142 ReselectLast,
144 SwapAnchor,
145 ToggleCaseSelection,
146 ToggleVisualBlock,
147 ToggleVisualChar,
148 ToggleVisualLine,
149 UppercaseSelection,
150 YankSelection,
151 visual_commands,
153 visual_entry_commands,
154 visual_exit_commands,
155 visual_operator_commands,
156 visual_selection_commands,
157};
158
159pub struct VimModule;
164
165impl VimModule {
166 #[must_use]
168 pub const fn new() -> Self {
169 Self
170 }
171}
172
173#[cfg_attr(coverage_nightly, coverage(off))]
174impl Default for VimModule {
175 fn default() -> Self {
176 Self::new()
177 }
178}
179
180#[cfg_attr(coverage_nightly, coverage(off))]
181impl Module for VimModule {
182 fn id(&self) -> ModuleId {
183 ModuleId::new("vim")
184 }
185
186 fn name(&self) -> &'static str {
187 "Vim"
188 }
189
190 fn version(&self) -> Version {
191 Version::new(0, 9, 0)
192 }
193
194 fn dependencies(&self) -> Vec<ModuleId> {
195 vec![ModuleId::new("editor"), ModuleId::new("motions")]
196 }
197
198 #[cfg_attr(coverage_nightly, coverage(off))]
199 fn init(&mut self, ctx: &ModuleContext) -> ProbeResult {
200 let provider = ctx.services.get_or_create::<BridgeProvider>();
202 provider.register(operators::yank_flash::YankFlashBridge);
203
204 let mode_registry = ctx.services.get_or_create::<ModeProviderRegistry>();
206 mode_registry.register(ModeProviderKey::Entry, Arc::new(VimDefaultModeProvider::new()));
207
208 let mode_store = ctx.services.get_or_create::<ModeInfoStore>();
210 for mode in VimMode::ALL {
211 mode_store.add(ModeInfo::from_mode(*mode));
212 }
213
214 let resolver_registry = ctx.services.get_or_create::<ResolverRegistry>();
216 resolver_registry.register(resolvers::VimNormalResolver::new());
217 resolver_registry.register(resolvers::VimInsertResolver::new());
218 resolver_registry.register(resolvers::VimDeleteResolver::new());
219 resolver_registry.register(resolvers::VimYankResolver::new());
220 resolver_registry.register(resolvers::VimChangeResolver::new());
221 resolver_registry.register(resolvers::VimCommandLineResolver::new());
222 resolver_registry.register(resolvers::VimWindowResolver::new());
224 resolver_registry.register(resolvers::VimVisualResolver::character_wise());
226 resolver_registry.register(resolvers::VimVisualResolver::line_wise());
227 resolver_registry.register(resolvers::VimVisualResolver::block_wise());
228
229 resolver_registry.register(resolvers::VimReplaceResolver::new());
231
232 resolver_registry.register(resolvers::VimCaseResolver::new(
234 resolvers::operator_common::OperatorType::Lowercase,
235 ));
236 resolver_registry.register(resolvers::VimCaseResolver::new(
237 resolvers::operator_common::OperatorType::Uppercase,
238 ));
239 resolver_registry.register(resolvers::VimCaseResolver::new(
240 resolvers::operator_common::OperatorType::ToggleCase,
241 ));
242
243 let policy_store = ctx.services.get_or_create::<LookupPolicyStore>();
245 policy_store.set(Arc::new(VimLookupPolicy));
246
247 let initial_mode_provider = ctx.services.get_or_create::<InitialModeProvider>();
249 if let Some(prev) = initial_mode_provider.set(ModeId::new(VIM_MODULE, "normal")) {
250 tracing::warn!(previous = %prev, "VimModule overrode existing initial mode");
251 }
252
253 let leader_provider = ctx.services.get_or_create::<LeaderKeyProvider>();
255 if let Some(prev) = leader_provider.set("<Space>") {
256 tracing::warn!(previous = prev, "VimModule overrode existing leader key");
257 }
258
259 let command_store = ctx.services.get_or_create::<CommandHandlerStore>();
261 for handler in self.command_handlers() {
262 command_store.add(handler);
263 }
264
265 let keybinding_store = ctx.services.get_or_create::<KeybindingStore>();
267 keybinding_store.add_all(self.keybindings());
268
269 if let Err(e) = load_personality_manifest(ctx, &keybinding_store) {
271 return ProbeResult::Failed(e);
272 }
273
274 let source_registry = ctx.services.get_or_create::<AnnotationSourceRegistry>();
276 source_registry.register(
277 AnnotationSourceKey::new("builtin.line_number"),
278 annotation::create_line_number_source(LineNumberMode::Absolute),
279 );
280
281 tracing::info!("VimModule: registered LineNumberSource in AnnotationSourceRegistry");
282
283 pr_info!("Vim module initialized");
284 ProbeResult::Success
285 }
286
287 fn exit(&mut self) -> Result<(), ModuleError> {
288 pr_info!("Vim module exiting");
289 Ok(())
290 }
291
292 fn provides(&self) -> &[&'static str] {
293 &[reovim_capabilities::MODE_MANAGEMENT]
294 }
295
296 fn extension_kinds(&self) -> &[&'static str] {
297 &[operators::yank_flash::KIND]
298 }
299
300 #[cfg_attr(coverage_nightly, coverage(off))]
301 fn keybindings(&self) -> Vec<KeybindingRegistration> {
302 bindings::all()
303 }
304}
305
306impl CommandProvider for VimModule {
307 fn command_handlers(&self) -> Vec<Box<dyn CommandHandler>> {
308 let mut handlers = Vec::new();
309 handlers.extend(commands::mode_commands());
310 handlers.extend(visual::visual_commands());
311 handlers.extend(operators::operator_commands()); handlers
313 }
314}
315
316const VIM_MANIFEST_TOML: &str = include_str!("../data/vim.toml");
322
323#[cfg_attr(coverage_nightly, coverage(off))]
334fn load_personality_manifest(
335 ctx: &ModuleContext,
336 keybinding_store: &KeybindingStore,
337) -> Result<(), ModuleError> {
338 let manifest = match reovim_driver_manifest::PersonalityManifest::parse(VIM_MANIFEST_TOML) {
339 Ok(m) => m,
340 Err(e) => {
341 return Err(ModuleError::InitFailed(format!(
342 "Failed to parse vim.toml personality manifest: {e}"
343 )));
344 }
345 };
346
347 let conflicts = manifest.detect_conflicts();
349 for warning in &conflicts {
350 tracing::warn!("vim.toml: {warning}");
351 }
352
353 let registrations = manifest.to_keybinding_registrations();
357 let count = registrations.len();
358 keybinding_store.add_all(registrations);
359 tracing::info!(count, "VimModule: loaded personality manifest keybindings");
360
361 let option_specs = manifest.to_option_specs(&VIM_MODULE);
363
364 let bridge_store = ModeBridgeStore::new(manifest.mode_bridges);
366 ctx.services.register(Arc::new(bridge_store));
367 tracing::info!("VimModule: registered ModeBridgeStore");
368
369 for spec in option_specs {
371 ctx.kernel.options.register(spec).map_err(|e| {
372 ModuleError::InitFailed(format!("Failed to register manifest option: {e}"))
373 })?;
374 }
375
376 Ok(())
377}
378
379#[cfg(feature = "dynamic")]
381reovim_module_macros::declare_module!(VimModule);