1use std::path::Path;
12use std::sync::Arc;
13
14use anyhow::Result as AnyhowResult;
15use rust_i18n::t;
16
17use crate::model::event::BufferId;
18use crate::state::EditorState;
19use crate::view::split::SplitViewState;
20
21use super::Editor;
22
23impl Editor {
24 pub fn open_stdin_buffer(
30 &mut self,
31 temp_path: &Path,
32 thread_handle: Option<std::thread::JoinHandle<anyhow::Result<()>>>,
33 ) -> AnyhowResult<BufferId> {
34 self.position_history.commit_pending_movement();
36
37 let cursors = self.active_cursors();
39 let position = cursors.primary().position;
40 let anchor = cursors.primary().anchor;
41 self.position_history
42 .record_movement(self.active_buffer(), position, anchor);
43 self.position_history.commit_pending_movement();
44
45 let replace_current = {
48 let current_state = self.buffers.get(&self.active_buffer()).unwrap();
49 !current_state.is_composite_buffer
50 && current_state.buffer.is_empty()
51 && !current_state.buffer.is_modified()
52 && current_state.buffer.file_path().is_none()
53 };
54
55 let buffer_id = if replace_current {
56 self.active_buffer()
58 } else {
59 let id = BufferId(self.next_buffer_id);
61 self.next_buffer_id += 1;
62 id
63 };
64
65 let file_size = self.authority.filesystem.metadata(temp_path)?.size as usize;
67
68 let mut state = EditorState::from_file_with_languages(
71 temp_path,
72 self.terminal_width,
73 self.terminal_height,
74 self.config.editor.large_file_threshold_bytes as usize,
75 &self.grammar_registry,
76 &self.config.languages,
77 Arc::clone(&self.authority.filesystem),
78 )?;
79
80 state.buffer.clear_file_path();
83 state.buffer.clear_modified();
85
86 state.buffer_settings.tab_size = self.config.editor.tab_size;
88 state.buffer_settings.auto_close = self.config.editor.auto_close;
89 state.buffer_settings.auto_surround = self.config.editor.auto_surround;
90
91 state
93 .margins
94 .configure_for_line_numbers(self.config.editor.line_numbers);
95
96 self.buffers.insert(buffer_id, state);
97 self.event_logs
98 .insert(buffer_id, crate::model::event::EventLog::new());
99
100 let metadata =
102 super::types::BufferMetadata::new_unnamed(t!("stdin.display_name").to_string());
103 self.buffer_metadata.insert(buffer_id, metadata);
104
105 let active_split = self.split_manager.active_split();
107 let line_wrap = self.resolve_line_wrap_for_buffer(buffer_id);
108 let wrap_column = self.resolve_wrap_column_for_buffer(buffer_id);
109 if let Some(view_state) = self.split_view_states.get_mut(&active_split) {
110 view_state.add_buffer(buffer_id);
111 let buf_state = view_state.ensure_buffer_state(buffer_id);
112 buf_state.apply_config_defaults(
113 self.config.editor.line_numbers,
114 self.config.editor.highlight_current_line,
115 line_wrap,
116 self.config.editor.wrap_indent,
117 wrap_column,
118 self.config.editor.rulers.clone(),
119 );
120 }
121
122 self.set_active_buffer(buffer_id);
123
124 self.stdin_stream
128 .start(temp_path.to_path_buf(), buffer_id, file_size, thread_handle);
129
130 self.status_message = Some(t!("stdin.streaming").to_string());
132
133 Ok(buffer_id)
134 }
135
136 pub fn poll_stdin_streaming(&mut self) -> bool {
139 use super::stdin_stream::ThreadOutcome;
140
141 if !self.stdin_stream.is_active() {
142 return false;
143 }
144
145 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
146 return false;
147 };
148 let temp_path = self.stdin_stream.temp_path().unwrap().to_path_buf();
149 let last_known = self.stdin_stream.last_known_size();
150
151 let mut changed = false;
152
153 let current_size = self
155 .authority
156 .filesystem
157 .metadata(&temp_path)
158 .map(|m| m.size as usize)
159 .unwrap_or(last_known);
160
161 if self.stdin_stream.record_growth(current_size) {
163 if let Some(editor_state) = self.buffers.get_mut(&buffer_id) {
164 editor_state
165 .buffer
166 .extend_streaming(&temp_path, current_size);
167 }
168 self.status_message =
169 Some(t!("stdin.streaming_bytes", bytes = current_size).to_string());
170 changed = true;
171 }
172
173 if let Some(outcome) = self.stdin_stream.take_finished_thread_outcome() {
175 match outcome {
176 ThreadOutcome::Success => {
177 tracing::info!("Stdin streaming completed successfully");
178 }
179 ThreadOutcome::Error(msg) => {
180 tracing::warn!("Stdin streaming error: {}", msg);
181 self.status_message = Some(t!("stdin.read_error", error = msg).to_string());
182 }
183 ThreadOutcome::Panic => {
184 tracing::warn!("Stdin streaming thread panicked");
185 self.status_message = Some(t!("stdin.read_error_panic").to_string());
186 }
187 }
188 self.complete_stdin_streaming();
189 changed = true;
190 }
191
192 changed
193 }
194
195 pub fn complete_stdin_streaming(&mut self) {
198 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
199 return;
200 };
201 let Some(temp_path) = self.stdin_stream.temp_path().map(Path::to_path_buf) else {
202 return;
203 };
204
205 self.stdin_stream.mark_complete();
206
207 let final_size = self
209 .authority
210 .filesystem
211 .metadata(&temp_path)
212 .map(|m| m.size as usize)
213 .unwrap_or(self.stdin_stream.last_known_size());
214
215 if self.stdin_stream.record_growth(final_size) {
216 if let Some(editor_state) = self.buffers.get_mut(&buffer_id) {
217 editor_state.buffer.extend_streaming(&temp_path, final_size);
218 }
219 }
220
221 self.status_message = Some(
222 t!(
223 "stdin.read_complete",
224 bytes = self.stdin_stream.last_known_size()
225 )
226 .to_string(),
227 );
228 }
229
230 pub fn is_stdin_streaming(&self) -> bool {
232 self.stdin_stream.is_active()
233 }
234
235 pub fn create_virtual_buffer_detached(
253 &mut self,
254 name: String,
255 mode: String,
256 read_only: bool,
257 ) -> BufferId {
258 let buffer_id = BufferId(self.next_buffer_id);
259 self.next_buffer_id += 1;
260
261 let mut state = EditorState::new(
262 self.terminal_width,
263 self.terminal_height,
264 self.config.editor.large_file_threshold_bytes as usize,
265 Arc::clone(&self.authority.filesystem),
266 );
267 state.set_language_from_name(&name, &self.grammar_registry);
270 state
271 .margins
272 .configure_for_line_numbers(self.config.editor.line_numbers);
273
274 self.buffers.insert(buffer_id, state);
275 self.event_logs
276 .insert(buffer_id, crate::model::event::EventLog::new());
277
278 let metadata = super::types::BufferMetadata::virtual_buffer(name, mode, read_only);
280 self.buffer_metadata.insert(buffer_id, metadata);
281
282 buffer_id
283 }
284
285 pub fn create_virtual_buffer(
286 &mut self,
287 name: String,
288 mode: String,
289 read_only: bool,
290 ) -> BufferId {
291 let buffer_id = BufferId(self.next_buffer_id);
292 self.next_buffer_id += 1;
293
294 let mut state = EditorState::new(
295 self.terminal_width,
296 self.terminal_height,
297 self.config.editor.large_file_threshold_bytes as usize,
298 Arc::clone(&self.authority.filesystem),
299 );
300 state.set_language_from_name(&name, &self.grammar_registry);
304
305 state
307 .margins
308 .configure_for_line_numbers(self.config.editor.line_numbers);
309
310 self.buffers.insert(buffer_id, state);
311 self.event_logs
312 .insert(buffer_id, crate::model::event::EventLog::new());
313
314 let metadata = super::types::BufferMetadata::virtual_buffer(name, mode, read_only);
316 self.buffer_metadata.insert(buffer_id, metadata);
317
318 let active_split = self.split_manager.active_split();
320 let line_wrap = self.resolve_line_wrap_for_buffer(buffer_id);
321 let wrap_column = self.resolve_wrap_column_for_buffer(buffer_id);
322 if let Some(view_state) = self.split_view_states.get_mut(&active_split) {
323 view_state.add_buffer(buffer_id);
324 let buf_state = view_state.ensure_buffer_state(buffer_id);
325 buf_state.apply_config_defaults(
326 self.config.editor.line_numbers,
327 self.config.editor.highlight_current_line,
328 line_wrap,
329 self.config.editor.wrap_indent,
330 wrap_column,
331 self.config.editor.rulers.clone(),
332 );
333 } else {
334 let mut view_state =
336 SplitViewState::with_buffer(self.terminal_width, self.terminal_height, buffer_id);
337 view_state.apply_config_defaults(
338 self.config.editor.line_numbers,
339 self.config.editor.highlight_current_line,
340 line_wrap,
341 self.config.editor.wrap_indent,
342 wrap_column,
343 self.config.editor.rulers.clone(),
344 );
345 self.split_view_states.insert(active_split, view_state);
346 }
347
348 buffer_id
349 }
350
351 pub fn set_virtual_buffer_content(
357 &mut self,
358 buffer_id: BufferId,
359 entries: Vec<crate::primitives::text_property::TextPropertyEntry>,
360 ) -> Result<(), String> {
361 let state = self
362 .buffers
363 .get_mut(&buffer_id)
364 .ok_or_else(|| "Buffer not found".to_string())?;
365
366 let (text, properties, collected_overlays) =
368 crate::primitives::text_property::TextPropertyManager::from_entries(entries);
369
370 state.overlays.clear(&mut state.marker_list);
375
376 let current_len = state.buffer.len();
377 if current_len > 0 {
378 state.buffer.delete_bytes(0, current_len);
379 }
380 state.buffer.insert(0, &text);
381
382 state.buffer.clear_modified();
384
385 state.text_properties = properties;
387
388 {
393 use crate::view::overlay::{Overlay, OverlayFace};
394 use fresh_core::overlay::OverlayNamespace;
395
396 let inline_ns = OverlayNamespace::from_string("_inline".to_string());
397 let mut new_overlays = Vec::with_capacity(collected_overlays.len());
398
399 for co in collected_overlays {
400 let face = OverlayFace::from_options(&co.options);
401 let mut overlay = Overlay::with_namespace(
402 &mut state.marker_list,
403 co.range,
404 face,
405 inline_ns.clone(),
406 );
407 overlay.extend_to_line_end = co.options.extend_to_line_end;
408 if let Some(url) = co.options.url {
409 overlay.url = Some(url);
410 }
411 new_overlays.push(overlay);
412 }
413 state.overlays.extend(new_overlays);
414 }
415
416 let new_len = state.buffer.len();
420 let buffer = &self
424 .buffers
425 .get(&buffer_id)
426 .expect("buffer still present")
427 .buffer;
428 for view_state in self.split_view_states.values_mut() {
429 let Some(buf_state) = view_state.keyed_states.get_mut(&buffer_id) else {
430 continue;
431 };
432 buf_state.cursors.map(|cursor| {
433 let pos = cursor.position.min(new_len);
434 cursor.position = buffer.snap_to_char_boundary(pos);
435 if let Some(anchor) = cursor.anchor {
436 let clamped = anchor.min(new_len);
437 cursor.anchor = Some(buffer.snap_to_char_boundary(clamped));
438 }
439 });
440 }
441 Ok(())
442 }
443}