reovim_plugin_statusline/
lib.rs

1//! Statusline extension plugin for reovim
2//!
3//! This plugin provides a section-based API for other plugins to contribute
4//! content to the status line.
5//!
6//! # Usage
7//!
8//! Other plugins can register sections by emitting `StatuslineSectionRegister`:
9//!
10//! ```ignore
11//! use reovim_plugin_statusline::{
12//!     StatuslineSectionRegister, StatuslineSection, SectionContent
13//! };
14//! use reovim_core::plugin::SectionAlignment;
15//!
16//! bus.emit(StatuslineSectionRegister {
17//!     section: StatuslineSection::new(
18//!         "my_plugin_status",
19//!         100, // priority
20//!         SectionAlignment::Right,
21//!         |ctx| SectionContent::new(" LSP ")
22//!     ),
23//! });
24//! ```
25
26pub mod command;
27pub mod context_section;
28pub mod section;
29pub mod state;
30
31use std::{any::TypeId, sync::Arc};
32
33use {
34    reovim_core::{
35        event_bus::{EventBus, EventResult},
36        plugin::{Plugin, PluginContext, PluginId, PluginStateRegistry},
37    },
38    reovim_plugin_context::CursorContextUpdated,
39};
40
41use {
42    command::{StatuslineSectionRegister, StatuslineSectionUnregister},
43    state::{CachedCursorContext, SharedStatuslineManager},
44};
45
46// Re-export for external use
47pub use {
48    command::StatuslineRefresh,
49    section::{SectionContent, SectionRenderContext, StatuslineSection},
50    state::StatuslineManagerHandle,
51};
52
53/// Statusline extension plugin
54///
55/// Provides a section-based API for plugins to contribute to the status line.
56pub struct StatuslinePlugin {
57    manager: Arc<SharedStatuslineManager>,
58}
59
60impl Default for StatuslinePlugin {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl StatuslinePlugin {
67    /// Create a new statusline plugin
68    #[must_use]
69    pub fn new() -> Self {
70        Self {
71            manager: Arc::new(SharedStatuslineManager::new()),
72        }
73    }
74}
75
76impl Plugin for StatuslinePlugin {
77    fn id(&self) -> PluginId {
78        PluginId::new("reovim:statusline")
79    }
80
81    fn name(&self) -> &'static str {
82        "Statusline"
83    }
84
85    fn description(&self) -> &'static str {
86        "Extensible statusline with section-based API"
87    }
88
89    fn dependencies(&self) -> Vec<TypeId> {
90        vec![]
91    }
92
93    fn build(&self, ctx: &mut PluginContext) {
94        // Register refresh command
95        let _ = ctx.register_command(StatuslineRefresh);
96
97        tracing::debug!("StatuslinePlugin: registered commands");
98    }
99
100    fn init_state(&self, registry: &PluginStateRegistry) {
101        use reovim_core::plugin::StatuslineSectionProvider;
102
103        // Register the manager in plugin state for other plugins to access
104        registry.register(Arc::clone(&self.manager));
105
106        // Register as the statusline provider (cast to trait object)
107        registry.set_statusline_provider(
108            Arc::clone(&self.manager) as Arc<dyn StatuslineSectionProvider>
109        );
110
111        tracing::debug!("StatuslinePlugin: registered as statusline provider");
112    }
113
114    fn subscribe(&self, bus: &EventBus, _state: Arc<PluginStateRegistry>) {
115        use reovim_core::option::{
116            OptionCategory, OptionConstraint, OptionSpec, OptionValue, RegisterOption,
117        };
118
119        // Register settings
120        bus.emit(RegisterOption::new(
121            OptionSpec::new(
122                "context_breadcrumb_enabled",
123                "Show context breadcrumb in statusline",
124                OptionValue::Bool(true),
125            )
126            .with_category(OptionCategory::Display)
127            .with_section("Statusline")
128            .with_display_order(50),
129        ));
130
131        bus.emit(RegisterOption::new(
132            OptionSpec::new(
133                "context_separator",
134                "Context breadcrumb separator",
135                OptionValue::String(" > ".into()),
136            )
137            .with_category(OptionCategory::Display)
138            .with_section("Statusline")
139            .with_display_order(51),
140        ));
141
142        bus.emit(RegisterOption::new(
143            OptionSpec::new(
144                "context_max_items",
145                "Maximum breadcrumb items",
146                OptionValue::Integer(4),
147            )
148            .with_category(OptionCategory::Display)
149            .with_section("Statusline")
150            .with_display_order(52)
151            .with_constraint(OptionConstraint::range(1, 10)),
152        ));
153
154        // Auto-register context breadcrumb section
155        bus.emit(StatuslineSectionRegister {
156            section: context_section::create_context_section(),
157        });
158
159        // Subscribe to section registration events
160        let manager = Arc::clone(&self.manager);
161        bus.subscribe::<StatuslineSectionRegister, _>(100, move |event, ctx| {
162            manager.register_section(event.section.clone());
163            ctx.request_render();
164            EventResult::Handled
165        });
166
167        // Subscribe to section unregistration events
168        let manager = Arc::clone(&self.manager);
169        bus.subscribe::<StatuslineSectionUnregister, _>(100, move |event, ctx| {
170            manager.unregister_section(event.id);
171            ctx.request_render();
172            EventResult::Handled
173        });
174
175        // Subscribe to refresh command
176        let _manager = Arc::clone(&self.manager);
177        bus.subscribe::<StatuslineRefresh, _>(100, move |_event, ctx| {
178            ctx.request_render();
179            EventResult::Handled
180        });
181
182        // Subscribe to cursor context updates from context plugin
183        let manager = Arc::clone(&self.manager);
184        bus.subscribe::<CursorContextUpdated, _>(100, move |event, ctx| {
185            // Cache the context for rendering
186            manager.set_cached_cursor_context(CachedCursorContext {
187                buffer_id: event.buffer_id,
188                line: event.line,
189                col: event.col,
190                context: event.context.clone(),
191            });
192
193            // Request render to update statusline
194            ctx.request_render();
195
196            tracing::trace!(
197                buffer_id = event.buffer_id,
198                line = event.line,
199                col = event.col,
200                has_context = event.context.is_some(),
201                "StatuslinePlugin: received CursorContextUpdated"
202            );
203
204            EventResult::Handled
205        });
206
207        tracing::debug!("StatuslinePlugin: subscribed to events");
208    }
209}