#![cfg_attr(coverage_nightly, allow(unused_features))]
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
use std::sync::Arc;
use {
reovim_driver_annotation::{AnnotationSourceKey, AnnotationSourceRegistry, LineNumberMode},
reovim_driver_command::{CommandHandler, CommandHandlerStore, CommandProvider},
reovim_driver_input::{
KeybindingStore, LookupPolicyStore, ModeInfo, ModeInfoStore, ModeProviderKey,
ModeProviderRegistry, ResolverRegistry,
},
reovim_driver_manifest::ModeBridgeStore,
reovim_driver_session::{InitialModeProvider, LeaderKeyProvider, bridges::BridgeProvider},
reovim_kernel::api::v1::{
KeybindingRegistration, ModeId, Module, ModuleContext, ModuleError, ModuleId, ProbeResult,
Version, pr_info,
},
};
pub mod annotation;
pub mod bindings;
pub mod commands;
pub mod fallback;
pub mod ids;
pub mod macros;
pub mod modes;
pub mod operators;
pub mod providers;
pub mod resolvers;
pub mod session_state;
pub mod vim_lookup_policy;
pub mod visual;
#[cfg(test)]
mod ids_tests;
#[cfg(test)]
mod lib_tests;
#[cfg(test)]
mod macros_tests;
#[cfg(test)]
mod modes_tests;
#[cfg(test)]
mod providers_tests;
#[cfg(test)]
mod registry_integration;
#[cfg(test)]
mod session_state_tests;
#[cfg(test)]
mod vim_lookup_policy_tests;
pub use modes::{VIM_MODULE, VimMode};
pub use providers::{VimDefaultModeProvider, VimModuleProviderExt};
pub use session_state::{PendingCharOp, VimSessionState};
pub use ids::{CHANGE, DELETE, OperatorId, YANK};
pub use operators::{
ChangeCommand, ChangeOperator, DeleteCommand, DeleteOperator, Operator, OperatorContext,
OperatorError, Range, YankCommand, YankOperator, operator_commands,
};
pub use fallback::VimFallbackHandler;
pub use vim_lookup_policy::VimLookupPolicy;
pub use commands::{
ChangeLine, ChangeToEndOfLine, EnterCommandLineMode, EnterInsertEndOfLine,
EnterInsertFirstNonBlank, EnterInsertMode, EnterInsertModeAppend, EnterReplaceMode,
EnterSearchBackward, EnterSearchForward, EnterWindowMode, ExecuteFindChar, ExitCommandLineMode,
ExitToNormal, OpenLineAbove, OpenLineBelow, ReplaceBackspace,
};
pub use resolvers::{
VimCaseResolver, VimChangeResolver, VimCommandLineResolver, VimDeleteResolver,
VimInsertResolver, VimNormalResolver, VimReplaceResolver, VimVisualResolver, VimYankResolver,
};
pub use visual::{
ChangeSelection,
DedentSelection,
DeleteSelection,
EnterVisualBlockMode,
EnterVisualLineMode,
EnterVisualMode,
ExitVisualMode,
IndentSelection,
LowercaseSelection,
ReselectLast,
SwapAnchor,
ToggleCaseSelection,
ToggleVisualBlock,
ToggleVisualChar,
ToggleVisualLine,
UppercaseSelection,
YankSelection,
visual_commands,
visual_entry_commands,
visual_exit_commands,
visual_operator_commands,
visual_selection_commands,
};
pub struct VimModule;
impl VimModule {
#[must_use]
pub const fn new() -> Self {
Self
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Default for VimModule {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Module for VimModule {
fn id(&self) -> ModuleId {
ModuleId::new("vim")
}
fn name(&self) -> &'static str {
"Vim"
}
fn version(&self) -> Version {
Version::new(0, 9, 0)
}
fn dependencies(&self) -> Vec<ModuleId> {
vec![ModuleId::new("editor"), ModuleId::new("motions")]
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn init(&mut self, ctx: &ModuleContext) -> ProbeResult {
let provider = ctx.services.get_or_create::<BridgeProvider>();
provider.register(operators::yank_flash::YankFlashBridge);
let mode_registry = ctx.services.get_or_create::<ModeProviderRegistry>();
mode_registry.register(ModeProviderKey::Entry, Arc::new(VimDefaultModeProvider::new()));
let mode_store = ctx.services.get_or_create::<ModeInfoStore>();
for mode in VimMode::ALL {
mode_store.add(ModeInfo::from_mode(*mode));
}
let resolver_registry = ctx.services.get_or_create::<ResolverRegistry>();
resolver_registry.register(resolvers::VimNormalResolver::new());
resolver_registry.register(resolvers::VimInsertResolver::new());
resolver_registry.register(resolvers::VimDeleteResolver::new());
resolver_registry.register(resolvers::VimYankResolver::new());
resolver_registry.register(resolvers::VimChangeResolver::new());
resolver_registry.register(resolvers::VimCommandLineResolver::new());
resolver_registry.register(resolvers::VimWindowResolver::new());
resolver_registry.register(resolvers::VimVisualResolver::character_wise());
resolver_registry.register(resolvers::VimVisualResolver::line_wise());
resolver_registry.register(resolvers::VimVisualResolver::block_wise());
resolver_registry.register(resolvers::VimReplaceResolver::new());
resolver_registry.register(resolvers::VimCaseResolver::new(
resolvers::operator_common::OperatorType::Lowercase,
));
resolver_registry.register(resolvers::VimCaseResolver::new(
resolvers::operator_common::OperatorType::Uppercase,
));
resolver_registry.register(resolvers::VimCaseResolver::new(
resolvers::operator_common::OperatorType::ToggleCase,
));
let policy_store = ctx.services.get_or_create::<LookupPolicyStore>();
policy_store.set(Arc::new(VimLookupPolicy));
let initial_mode_provider = ctx.services.get_or_create::<InitialModeProvider>();
if let Some(prev) = initial_mode_provider.set(ModeId::new(VIM_MODULE, "normal")) {
tracing::warn!(previous = %prev, "VimModule overrode existing initial mode");
}
let leader_provider = ctx.services.get_or_create::<LeaderKeyProvider>();
if let Some(prev) = leader_provider.set("<Space>") {
tracing::warn!(previous = prev, "VimModule overrode existing leader key");
}
let command_store = ctx.services.get_or_create::<CommandHandlerStore>();
for handler in self.command_handlers() {
command_store.add(handler);
}
let keybinding_store = ctx.services.get_or_create::<KeybindingStore>();
keybinding_store.add_all(self.keybindings());
if let Err(e) = load_personality_manifest(ctx, &keybinding_store) {
return ProbeResult::Failed(e);
}
let source_registry = ctx.services.get_or_create::<AnnotationSourceRegistry>();
source_registry.register(
AnnotationSourceKey::new("builtin.line_number"),
annotation::create_line_number_source(LineNumberMode::Absolute),
);
tracing::info!("VimModule: registered LineNumberSource in AnnotationSourceRegistry");
pr_info!("Vim module initialized");
ProbeResult::Success
}
fn exit(&mut self) -> Result<(), ModuleError> {
pr_info!("Vim module exiting");
Ok(())
}
fn provides(&self) -> &[&'static str] {
&[reovim_capabilities::MODE_MANAGEMENT]
}
fn extension_kinds(&self) -> &[&'static str] {
&[operators::yank_flash::KIND]
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn keybindings(&self) -> Vec<KeybindingRegistration> {
bindings::all()
}
}
impl CommandProvider for VimModule {
fn command_handlers(&self) -> Vec<Box<dyn CommandHandler>> {
let mut handlers = Vec::new();
handlers.extend(commands::mode_commands());
handlers.extend(visual::visual_commands());
handlers.extend(operators::operator_commands()); handlers
}
}
const VIM_MANIFEST_TOML: &str = include_str!("../data/vim.toml");
#[cfg_attr(coverage_nightly, coverage(off))]
fn load_personality_manifest(
ctx: &ModuleContext,
keybinding_store: &KeybindingStore,
) -> Result<(), ModuleError> {
let manifest = match reovim_driver_manifest::PersonalityManifest::parse(VIM_MANIFEST_TOML) {
Ok(m) => m,
Err(e) => {
tracing::error!("Failed to parse vim.toml personality manifest: {e}");
return Ok(()); }
};
let conflicts = manifest.detect_conflicts();
for warning in &conflicts {
tracing::warn!("vim.toml: {warning}");
}
let registrations = manifest.to_keybinding_registrations();
let count = registrations.len();
keybinding_store.add_all(registrations);
tracing::info!(count, "VimModule: loaded personality manifest keybindings");
let option_specs = manifest.to_option_specs(&VIM_MODULE);
let bridge_store = ModeBridgeStore::new(manifest.mode_bridges);
ctx.services.register(Arc::new(bridge_store));
tracing::info!("VimModule: registered ModeBridgeStore");
for spec in option_specs {
ctx.kernel.options.register(spec).map_err(|e| {
ModuleError::InitFailed(format!("Failed to register manifest option: {e}"))
})?;
}
Ok(())
}
#[cfg(feature = "dynamic")]
reovim_module_macros::declare_module!(VimModule);