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.active_window_mut()
36 .position_history
37 .commit_pending_movement();
38
39 let cursors = self.active_cursors();
41 let position = cursors.primary().position;
42 let anchor = cursors.primary().anchor;
43 let active_buffer_id = self.active_buffer();
44 let ph = &mut self.active_window_mut().position_history;
45 ph.record_movement(active_buffer_id, position, anchor);
46 ph.commit_pending_movement();
47
48 let replace_current = {
51 let current_state = self
52 .windows
53 .get(&self.active_window)
54 .map(|w| &w.buffers)
55 .expect("active window present")
56 .get(&self.active_buffer())
57 .unwrap();
58 !current_state.is_composite_buffer
59 && current_state.buffer.is_empty()
60 && !current_state.buffer.is_modified()
61 && current_state.buffer.file_path().is_none()
62 };
63
64 let buffer_id = if replace_current {
65 self.active_buffer()
67 } else {
68 let id = self.alloc_buffer_id();
70 id
71 };
72
73 let file_size = self.authority().filesystem.metadata(temp_path)?.size as usize;
75
76 let mut state = EditorState::from_file_with_languages(
79 temp_path,
80 self.terminal_width,
81 self.terminal_height,
82 self.config.editor.large_file_threshold_bytes as usize,
83 &self.grammar_registry,
84 &self.config.languages,
85 Arc::clone(&self.authority().filesystem),
86 )?;
87
88 state.buffer.clear_file_path();
91 state.buffer.clear_modified();
93
94 state.buffer_settings.tab_size = self.config.editor.tab_size;
96 state.buffer_settings.auto_close = self.config.editor.auto_close;
97 state.buffer_settings.auto_surround = self.config.editor.auto_surround;
98
99 state
101 .margins
102 .configure_for_line_numbers(self.config.editor.line_numbers);
103
104 self.windows
105 .get_mut(&self.active_window)
106 .map(|w| &mut w.buffers)
107 .expect("active window present")
108 .insert(buffer_id, state);
109 self.active_window_mut()
110 .event_logs
111 .insert(buffer_id, crate::model::event::EventLog::new());
112
113 let metadata =
115 super::types::BufferMetadata::new_unnamed(t!("stdin.display_name").to_string());
116 self.active_window_mut()
117 .buffer_metadata
118 .insert(buffer_id, metadata);
119
120 let active_split = self
122 .windows
123 .get(&self.active_window)
124 .and_then(|w| w.buffers.splits())
125 .map(|(mgr, _)| mgr)
126 .expect("active window must have a populated split layout")
127 .active_split();
128 let line_wrap = self.active_window().resolve_line_wrap_for_buffer(buffer_id);
129 let wrap_column = self
130 .active_window()
131 .resolve_wrap_column_for_buffer(buffer_id);
132 if let Some(view_state) = self
133 .windows
134 .get_mut(&self.active_window)
135 .and_then(|w| w.split_view_states_mut())
136 .expect("active window must have a populated split layout")
137 .get_mut(&active_split)
138 {
139 view_state.add_buffer(buffer_id);
140 let buf_state = view_state.ensure_buffer_state(buffer_id);
141 buf_state.apply_config_defaults(
142 self.config.editor.line_numbers,
143 self.config.editor.highlight_current_line,
144 line_wrap,
145 self.config.editor.wrap_indent,
146 wrap_column,
147 self.config.editor.rulers.clone(),
148 self.config.editor.scroll_offset,
149 );
150 }
151
152 self.set_active_buffer(buffer_id);
153
154 self.stdin_stream
158 .start(temp_path.to_path_buf(), buffer_id, file_size, thread_handle);
159
160 self.active_window_mut().status_message = Some(t!("stdin.streaming").to_string());
162
163 Ok(buffer_id)
164 }
165
166 pub fn poll_stdin_streaming(&mut self) -> bool {
169 use super::stdin_stream::ThreadOutcome;
170
171 if !self.stdin_stream.is_active() {
172 return false;
173 }
174
175 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
176 return false;
177 };
178 let temp_path = self.stdin_stream.temp_path().unwrap().to_path_buf();
179 let last_known = self.stdin_stream.last_known_size();
180
181 let mut changed = false;
182
183 let current_size = self
185 .authority()
186 .filesystem
187 .metadata(&temp_path)
188 .map(|m| m.size as usize)
189 .unwrap_or(last_known);
190
191 if self.stdin_stream.record_growth(current_size) {
193 if let Some(editor_state) = self
194 .windows
195 .get_mut(&self.active_window)
196 .map(|w| &mut w.buffers)
197 .expect("active window present")
198 .get_mut(&buffer_id)
199 {
200 editor_state
201 .buffer
202 .extend_streaming(&temp_path, current_size);
203 }
204 self.active_window_mut().status_message =
205 Some(t!("stdin.streaming_bytes", bytes = current_size).to_string());
206 changed = true;
207 }
208
209 if let Some(outcome) = self.stdin_stream.take_finished_thread_outcome() {
211 match outcome {
212 ThreadOutcome::Success => {
213 tracing::info!("Stdin streaming completed successfully");
214 }
215 ThreadOutcome::Error(msg) => {
216 tracing::warn!("Stdin streaming error: {}", msg);
217 self.active_window_mut().status_message =
218 Some(t!("stdin.read_error", error = msg).to_string());
219 }
220 ThreadOutcome::Panic => {
221 tracing::warn!("Stdin streaming thread panicked");
222 self.active_window_mut().status_message =
223 Some(t!("stdin.read_error_panic").to_string());
224 }
225 }
226 self.complete_stdin_streaming();
227 changed = true;
228 }
229
230 changed
231 }
232
233 pub fn complete_stdin_streaming(&mut self) {
236 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
237 return;
238 };
239 let Some(temp_path) = self.stdin_stream.temp_path().map(Path::to_path_buf) else {
240 return;
241 };
242
243 self.stdin_stream.mark_complete();
244
245 let final_size = self
247 .authority()
248 .filesystem
249 .metadata(&temp_path)
250 .map(|m| m.size as usize)
251 .unwrap_or(self.stdin_stream.last_known_size());
252
253 if self.stdin_stream.record_growth(final_size) {
254 if let Some(editor_state) = self
255 .windows
256 .get_mut(&self.active_window)
257 .map(|w| &mut w.buffers)
258 .expect("active window present")
259 .get_mut(&buffer_id)
260 {
261 editor_state.buffer.extend_streaming(&temp_path, final_size);
262 }
263 }
264
265 self.active_window_mut().status_message = Some(
266 t!(
267 "stdin.read_complete",
268 bytes = self.stdin_stream.last_known_size()
269 )
270 .to_string(),
271 );
272 }
273
274 pub fn is_stdin_streaming(&self) -> bool {
276 self.stdin_stream.is_active()
277 }
278
279 pub fn set_virtual_buffer_content(
303 &mut self,
304 buffer_id: BufferId,
305 entries: Vec<crate::primitives::text_property::TextPropertyEntry>,
306 ) -> Result<(), String> {
307 self.active_window_mut()
308 .set_virtual_buffer_content(buffer_id, entries)
309 }
310}
311
312impl crate::app::window::Window {
313 pub fn create_virtual_buffer_detached(
322 &mut self,
323 name: String,
324 mode: String,
325 read_only: bool,
326 ) -> BufferId {
327 let buffer_id = self.alloc_buffer_id();
328
329 let mut state = EditorState::new(
330 self.terminal_width,
331 self.terminal_height,
332 self.config().editor.large_file_threshold_bytes as usize,
333 Arc::clone(&self.authority().filesystem),
334 );
335 state.set_language_from_name(&name, &self.resources.grammar_registry);
336 state
337 .margins
338 .configure_for_line_numbers(self.config().editor.line_numbers);
339
340 self.buffers.insert(buffer_id, state);
341 self.event_logs
342 .insert(buffer_id, crate::model::event::EventLog::new());
343
344 let metadata = crate::app::types::BufferMetadata::virtual_buffer(name, mode, read_only);
345 self.buffer_metadata.insert(buffer_id, metadata);
346
347 buffer_id
348 }
349
350 pub fn create_virtual_buffer(
352 &mut self,
353 name: String,
354 mode: String,
355 read_only: bool,
356 ) -> BufferId {
357 let buffer_id = self.alloc_buffer_id();
358
359 let mut state = EditorState::new(
360 self.terminal_width,
361 self.terminal_height,
362 self.config().editor.large_file_threshold_bytes as usize,
363 Arc::clone(&self.authority().filesystem),
364 );
365 state.set_language_from_name(&name, &self.resources.grammar_registry);
366 state
367 .margins
368 .configure_for_line_numbers(self.config().editor.line_numbers);
369
370 self.buffers.insert(buffer_id, state);
371 self.event_logs
372 .insert(buffer_id, crate::model::event::EventLog::new());
373
374 let metadata = crate::app::types::BufferMetadata::virtual_buffer(name, mode, read_only);
375 self.buffer_metadata.insert(buffer_id, metadata);
376
377 let (mgr, _) = self
378 .buffers
379 .splits()
380 .expect("active window must have a populated split layout");
381 let active_split = mgr.active_split();
382 let line_wrap = self.resolve_line_wrap_for_buffer(buffer_id);
383 let wrap_column = self.resolve_wrap_column_for_buffer(buffer_id);
384 let cfg = self.config().editor.clone();
385 let terminal_width = self.terminal_width;
386 let terminal_height = self.terminal_height;
387 if let Some(view_state) = self
388 .split_view_states_mut()
389 .expect("active window must have a populated split layout")
390 .get_mut(&active_split)
391 {
392 view_state.add_buffer(buffer_id);
393 let buf_state = view_state.ensure_buffer_state(buffer_id);
394 buf_state.apply_config_defaults(
395 cfg.line_numbers,
396 cfg.highlight_current_line,
397 line_wrap,
398 cfg.wrap_indent,
399 wrap_column,
400 cfg.rulers.clone(),
401 cfg.scroll_offset,
402 );
403 } else {
404 let mut view_state =
405 SplitViewState::with_buffer(terminal_width, terminal_height, buffer_id);
406 view_state.apply_config_defaults(
407 cfg.line_numbers,
408 cfg.highlight_current_line,
409 line_wrap,
410 cfg.wrap_indent,
411 wrap_column,
412 cfg.rulers,
413 cfg.scroll_offset,
414 );
415 self.split_view_states_mut()
416 .expect("active window must have a populated split layout")
417 .insert(active_split, view_state);
418 }
419
420 buffer_id
421 }
422
423 pub fn set_virtual_buffer_content(
429 &mut self,
430 buffer_id: BufferId,
431 entries: Vec<crate::primitives::text_property::TextPropertyEntry>,
432 ) -> Result<(), String> {
433 let state = self
434 .buffers
435 .get_mut(&buffer_id)
436 .ok_or_else(|| "Buffer not found".to_string())?;
437
438 let (text, properties, collected_overlays) =
440 crate::primitives::text_property::TextPropertyManager::from_entries(entries);
441
442 state.overlays.clear(&mut state.marker_list);
447
448 let current_len = state.buffer.len();
449 if current_len > 0 {
450 state.buffer.delete_bytes(0, current_len);
451 }
452 state.buffer.insert(0, &text);
453
454 state.buffer.clear_modified();
456
457 state.text_properties = properties;
459
460 {
465 use crate::view::overlay::{Overlay, OverlayFace};
466 use fresh_core::overlay::OverlayNamespace;
467
468 let inline_ns = OverlayNamespace::from_string("_inline".to_string());
469 let mut new_overlays = Vec::with_capacity(collected_overlays.len());
470
471 for co in collected_overlays {
472 let face = OverlayFace::from_options(&co.options);
473 let mut overlay = Overlay::with_namespace(
474 &mut state.marker_list,
475 co.range,
476 face,
477 inline_ns.clone(),
478 );
479 overlay.extend_to_line_end = co.options.extend_to_line_end;
480 if let Some(url) = co.options.url {
481 overlay.url = Some(url);
482 }
483 new_overlays.push(overlay);
484 }
485 state.overlays.extend(new_overlays);
486 }
487
488 self.buffers
492 .with_buffer_and_view_states(buffer_id, |state, vs_map| {
493 let new_len = state.buffer.len();
494 let buffer = &state.buffer;
495 for view_state in vs_map.values_mut() {
496 let Some(buf_state) = view_state.keyed_states.get_mut(&buffer_id) else {
497 continue;
498 };
499 buf_state.cursors.map(|cursor| {
500 let pos = cursor.position.min(new_len);
501 cursor.position = buffer.snap_to_char_boundary(pos);
502 if let Some(anchor) = cursor.anchor {
503 let clamped = anchor.min(new_len);
504 cursor.anchor = Some(buffer.snap_to_char_boundary(clamped));
505 }
506 });
507 }
508 });
509 Ok(())
510 }
511}