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 );
149 }
150
151 self.set_active_buffer(buffer_id);
152
153 self.stdin_stream
157 .start(temp_path.to_path_buf(), buffer_id, file_size, thread_handle);
158
159 self.active_window_mut().status_message = Some(t!("stdin.streaming").to_string());
161
162 Ok(buffer_id)
163 }
164
165 pub fn poll_stdin_streaming(&mut self) -> bool {
168 use super::stdin_stream::ThreadOutcome;
169
170 if !self.stdin_stream.is_active() {
171 return false;
172 }
173
174 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
175 return false;
176 };
177 let temp_path = self.stdin_stream.temp_path().unwrap().to_path_buf();
178 let last_known = self.stdin_stream.last_known_size();
179
180 let mut changed = false;
181
182 let current_size = self
184 .authority
185 .filesystem
186 .metadata(&temp_path)
187 .map(|m| m.size as usize)
188 .unwrap_or(last_known);
189
190 if self.stdin_stream.record_growth(current_size) {
192 if let Some(editor_state) = self
193 .windows
194 .get_mut(&self.active_window)
195 .map(|w| &mut w.buffers)
196 .expect("active window present")
197 .get_mut(&buffer_id)
198 {
199 editor_state
200 .buffer
201 .extend_streaming(&temp_path, current_size);
202 }
203 self.active_window_mut().status_message =
204 Some(t!("stdin.streaming_bytes", bytes = current_size).to_string());
205 changed = true;
206 }
207
208 if let Some(outcome) = self.stdin_stream.take_finished_thread_outcome() {
210 match outcome {
211 ThreadOutcome::Success => {
212 tracing::info!("Stdin streaming completed successfully");
213 }
214 ThreadOutcome::Error(msg) => {
215 tracing::warn!("Stdin streaming error: {}", msg);
216 self.active_window_mut().status_message =
217 Some(t!("stdin.read_error", error = msg).to_string());
218 }
219 ThreadOutcome::Panic => {
220 tracing::warn!("Stdin streaming thread panicked");
221 self.active_window_mut().status_message =
222 Some(t!("stdin.read_error_panic").to_string());
223 }
224 }
225 self.complete_stdin_streaming();
226 changed = true;
227 }
228
229 changed
230 }
231
232 pub fn complete_stdin_streaming(&mut self) {
235 let Some(buffer_id) = self.stdin_stream.buffer_id() else {
236 return;
237 };
238 let Some(temp_path) = self.stdin_stream.temp_path().map(Path::to_path_buf) else {
239 return;
240 };
241
242 self.stdin_stream.mark_complete();
243
244 let final_size = self
246 .authority
247 .filesystem
248 .metadata(&temp_path)
249 .map(|m| m.size as usize)
250 .unwrap_or(self.stdin_stream.last_known_size());
251
252 if self.stdin_stream.record_growth(final_size) {
253 if let Some(editor_state) = self
254 .windows
255 .get_mut(&self.active_window)
256 .map(|w| &mut w.buffers)
257 .expect("active window present")
258 .get_mut(&buffer_id)
259 {
260 editor_state.buffer.extend_streaming(&temp_path, final_size);
261 }
262 }
263
264 self.active_window_mut().status_message = Some(
265 t!(
266 "stdin.read_complete",
267 bytes = self.stdin_stream.last_known_size()
268 )
269 .to_string(),
270 );
271 }
272
273 pub fn is_stdin_streaming(&self) -> bool {
275 self.stdin_stream.is_active()
276 }
277
278 pub fn set_virtual_buffer_content(
302 &mut self,
303 buffer_id: BufferId,
304 entries: Vec<crate::primitives::text_property::TextPropertyEntry>,
305 ) -> Result<(), String> {
306 self.active_window_mut()
307 .set_virtual_buffer_content(buffer_id, entries)
308 }
309}
310
311impl crate::app::window::Window {
312 pub fn create_virtual_buffer_detached(
321 &mut self,
322 name: String,
323 mode: String,
324 read_only: bool,
325 ) -> BufferId {
326 let buffer_id = self.alloc_buffer_id();
327
328 let mut state = EditorState::new(
329 self.terminal_width,
330 self.terminal_height,
331 self.config().editor.large_file_threshold_bytes as usize,
332 Arc::clone(&self.authority().filesystem),
333 );
334 state.set_language_from_name(&name, &self.resources.grammar_registry);
335 state
336 .margins
337 .configure_for_line_numbers(self.config().editor.line_numbers);
338
339 self.buffers.insert(buffer_id, state);
340 self.event_logs
341 .insert(buffer_id, crate::model::event::EventLog::new());
342
343 let metadata = crate::app::types::BufferMetadata::virtual_buffer(name, mode, read_only);
344 self.buffer_metadata.insert(buffer_id, metadata);
345
346 buffer_id
347 }
348
349 pub fn create_virtual_buffer(
351 &mut self,
352 name: String,
353 mode: String,
354 read_only: bool,
355 ) -> BufferId {
356 let buffer_id = self.alloc_buffer_id();
357
358 let mut state = EditorState::new(
359 self.terminal_width,
360 self.terminal_height,
361 self.config().editor.large_file_threshold_bytes as usize,
362 Arc::clone(&self.authority().filesystem),
363 );
364 state.set_language_from_name(&name, &self.resources.grammar_registry);
365 state
366 .margins
367 .configure_for_line_numbers(self.config().editor.line_numbers);
368
369 self.buffers.insert(buffer_id, state);
370 self.event_logs
371 .insert(buffer_id, crate::model::event::EventLog::new());
372
373 let metadata = crate::app::types::BufferMetadata::virtual_buffer(name, mode, read_only);
374 self.buffer_metadata.insert(buffer_id, metadata);
375
376 let (mgr, _) = self
377 .buffers
378 .splits()
379 .expect("active window must have a populated split layout");
380 let active_split = mgr.active_split();
381 let line_wrap = self.resolve_line_wrap_for_buffer(buffer_id);
382 let wrap_column = self.resolve_wrap_column_for_buffer(buffer_id);
383 let cfg = self.config().editor.clone();
384 let terminal_width = self.terminal_width;
385 let terminal_height = self.terminal_height;
386 if let Some(view_state) = self
387 .split_view_states_mut()
388 .expect("active window must have a populated split layout")
389 .get_mut(&active_split)
390 {
391 view_state.add_buffer(buffer_id);
392 let buf_state = view_state.ensure_buffer_state(buffer_id);
393 buf_state.apply_config_defaults(
394 cfg.line_numbers,
395 cfg.highlight_current_line,
396 line_wrap,
397 cfg.wrap_indent,
398 wrap_column,
399 cfg.rulers.clone(),
400 );
401 } else {
402 let mut view_state =
403 SplitViewState::with_buffer(terminal_width, terminal_height, buffer_id);
404 view_state.apply_config_defaults(
405 cfg.line_numbers,
406 cfg.highlight_current_line,
407 line_wrap,
408 cfg.wrap_indent,
409 wrap_column,
410 cfg.rulers,
411 );
412 self.split_view_states_mut()
413 .expect("active window must have a populated split layout")
414 .insert(active_split, view_state);
415 }
416
417 buffer_id
418 }
419
420 pub fn set_virtual_buffer_content(
426 &mut self,
427 buffer_id: BufferId,
428 entries: Vec<crate::primitives::text_property::TextPropertyEntry>,
429 ) -> Result<(), String> {
430 let state = self
431 .buffers
432 .get_mut(&buffer_id)
433 .ok_or_else(|| "Buffer not found".to_string())?;
434
435 let (text, properties, collected_overlays) =
437 crate::primitives::text_property::TextPropertyManager::from_entries(entries);
438
439 state.overlays.clear(&mut state.marker_list);
444
445 let current_len = state.buffer.len();
446 if current_len > 0 {
447 state.buffer.delete_bytes(0, current_len);
448 }
449 state.buffer.insert(0, &text);
450
451 state.buffer.clear_modified();
453
454 state.text_properties = properties;
456
457 {
462 use crate::view::overlay::{Overlay, OverlayFace};
463 use fresh_core::overlay::OverlayNamespace;
464
465 let inline_ns = OverlayNamespace::from_string("_inline".to_string());
466 let mut new_overlays = Vec::with_capacity(collected_overlays.len());
467
468 for co in collected_overlays {
469 let face = OverlayFace::from_options(&co.options);
470 let mut overlay = Overlay::with_namespace(
471 &mut state.marker_list,
472 co.range,
473 face,
474 inline_ns.clone(),
475 );
476 overlay.extend_to_line_end = co.options.extend_to_line_end;
477 if let Some(url) = co.options.url {
478 overlay.url = Some(url);
479 }
480 new_overlays.push(overlay);
481 }
482 state.overlays.extend(new_overlays);
483 }
484
485 self.buffers
489 .with_buffer_and_view_states(buffer_id, |state, vs_map| {
490 let new_len = state.buffer.len();
491 let buffer = &state.buffer;
492 for view_state in vs_map.values_mut() {
493 let Some(buf_state) = view_state.keyed_states.get_mut(&buffer_id) else {
494 continue;
495 };
496 buf_state.cursors.map(|cursor| {
497 let pos = cursor.position.min(new_len);
498 cursor.position = buffer.snap_to_char_boundary(pos);
499 if let Some(anchor) = cursor.anchor {
500 let clamped = anchor.min(new_len);
501 cursor.anchor = Some(buffer.snap_to_char_boundary(clamped));
502 }
503 });
504 }
505 });
506 Ok(())
507 }
508}