reovim_plugin_explorer/
provider.rs1use reovim_core::{
4 content::{BufferContext, InputResult, PluginBufferProvider},
5 event::KeyEvent,
6 screen::Position,
7};
8
9use reovim_sys::event::KeyCode;
10
11use crate::state::ExplorerState;
12
13pub struct ExplorerBufferProvider;
15
16impl PluginBufferProvider for ExplorerBufferProvider {
17 fn get_lines(&self, ctx: &BufferContext) -> Vec<String> {
18 ctx.state
20 .with::<ExplorerState, _, _>(|explorer| {
21 if !explorer.visible {
22 return Vec::new();
23 }
24
25 let nodes = explorer.visible_nodes();
27 let mut lines = Vec::with_capacity(ctx.height as usize);
28
29 let available_height = if explorer.message.is_some() {
31 ctx.height.saturating_sub(1) as usize
32 } else {
33 ctx.height as usize
34 };
35
36 let start = explorer.scroll_offset;
38 let end = (start + available_height).min(nodes.len());
39
40 for (i, node) in nodes.iter().enumerate().skip(start).take(end - start) {
42 let is_cursor = i == explorer.cursor_index;
43 let is_marked = explorer.selection.selected.contains(&node.path);
44
45 let indent = " ".repeat(node.depth);
47 let icon = if node.is_dir() {
48 if node.is_expanded() { "▾ " } else { "▸ " }
49 } else {
50 " "
51 };
52
53 let marker = if is_marked {
54 "* "
55 } else if is_cursor {
56 "> "
57 } else {
58 " "
59 };
60
61 let size_display = if explorer.show_sizes && !node.is_dir() {
62 if let Some(size) = node.size() {
63 format!(" {:>5} ", super::node::format_size(size))
64 } else {
65 String::new()
66 }
67 } else {
68 String::new()
69 };
70
71 let line = format!("{marker}{indent}{icon}{}{}", node.name, size_display);
72 lines.push(line);
73 }
74
75 tracing::debug!(
77 "ExplorerBufferProvider: message={:?}, input_buffer={:?}",
78 explorer.message,
79 explorer.input_buffer
80 );
81 if let Some(ref message) = explorer.message {
82 let input_display = if !explorer.input_buffer.is_empty() {
83 explorer.input_buffer.as_str()
84 } else {
85 "_" };
87 let prompt_line = format!("{}{}", message, input_display);
88 tracing::info!("ExplorerBufferProvider: Adding prompt line: {}", prompt_line);
89 lines.push(prompt_line);
90 }
91
92 while lines.len() < ctx.height as usize {
94 lines.push(String::new());
95 }
96
97 lines
98 })
99 .unwrap_or_default()
100 }
101
102 fn on_cursor_move(&mut self, position: Position, ctx: &mut BufferContext) {
103 let _ = ctx.state.with_mut::<ExplorerState, _, _>(|explorer| {
105 let new_index = (explorer.scroll_offset + position.y as usize)
106 .min(explorer.visible_nodes().len().saturating_sub(1));
107 explorer.cursor_index = new_index;
108 });
109 }
110
111 fn on_input(&mut self, key: KeyEvent, ctx: &mut BufferContext) -> InputResult {
112 use crate::state::ExplorerInputMode;
113
114 tracing::info!("ExplorerBufferProvider::on_input called with key: {:?}", key);
115
116 let in_input_mode = ctx
118 .state
119 .with::<ExplorerState, _, _>(|explorer| {
120 !matches!(explorer.input_mode, ExplorerInputMode::None)
121 })
122 .unwrap_or(false);
123
124 tracing::info!("ExplorerBufferProvider::on_input: in_input_mode={}", in_input_mode);
125
126 if !in_input_mode {
127 return InputResult::Unhandled;
128 }
129
130 if let KeyCode::Char(c) = key.code {
132 tracing::info!("ExplorerBufferProvider::on_input: Adding char '{}' to input buffer", c);
133 ctx.state.with_mut::<ExplorerState, _, _>(|explorer| {
134 explorer.input_buffer.push(c);
135 });
136 return InputResult::Handled;
137 }
138
139 InputResult::Unhandled
141 }
142
143 fn is_editable(&self) -> bool {
144 false
146 }
147}