1use std::ops::Range;
15
16use rust_i18n::t;
17
18use crate::model::event::Event;
19
20use super::Editor;
21
22impl Editor {
23 pub fn add_overlay(
27 &mut self,
28 namespace: Option<crate::view::overlay::OverlayNamespace>,
29 range: Range<usize>,
30 face: crate::model::event::OverlayFace,
31 priority: i32,
32 message: Option<String>,
33 ) -> crate::view::overlay::OverlayHandle {
34 let event = Event::AddOverlay {
35 namespace,
36 range,
37 face,
38 priority,
39 message,
40 extend_to_line_end: false,
41 url: None,
42 };
43 self.apply_event_to_active_buffer(&event);
44 let state = self.active_state();
46 state
47 .overlays
48 .all()
49 .last()
50 .map(|o| o.handle.clone())
51 .unwrap_or_default()
52 }
53
54 pub fn remove_overlay(&mut self, handle: crate::view::overlay::OverlayHandle) {
56 let event = Event::RemoveOverlay { handle };
57 self.apply_event_to_active_buffer(&event);
58 }
59
60 pub fn remove_overlays_in_range(&mut self, range: Range<usize>) {
62 let event = Event::RemoveOverlaysInRange { range };
63 self.active_event_log_mut().append(event.clone());
64 self.apply_event_to_active_buffer(&event);
65 }
66
67 pub fn clear_overlays(&mut self) {
69 let event = Event::ClearOverlays;
70 self.active_event_log_mut().append(event.clone());
71 self.apply_event_to_active_buffer(&event);
72 }
73
74 pub fn show_popup(&mut self, popup: crate::model::event::PopupData) {
78 let event = Event::ShowPopup { popup };
79 self.active_event_log_mut().append(event.clone());
80 self.apply_event_to_active_buffer(&event);
81 let hint = self.popup_focus_key_hint();
87 if let Some(top) = self.active_state_mut().popups.top_mut() {
88 top.focus_key_hint = hint;
89 }
90 }
91
92 pub fn show_popup_with_resolver(
97 &mut self,
98 popup: crate::model::event::PopupData,
99 resolver: crate::view::popup::PopupResolver,
100 ) {
101 self.show_popup(popup);
102 if let Some(top) = self.active_state_mut().popups.top_mut() {
103 top.resolver = resolver;
104 }
105 }
106
107 pub fn hide_popup(&mut self) {
109 if self.global_popups.is_visible() {
113 self.global_popups.hide();
114
115 if let Some(handle) = self.hover.take_symbol_overlay() {
119 let remove_overlay_event = crate::model::event::Event::RemoveOverlay { handle };
120 self.apply_event_to_active_buffer(&remove_overlay_event);
121 }
122 self.hover.set_symbol_range(None);
123 return;
124 }
125
126 let event = Event::HidePopup;
127 self.active_event_log_mut().append(event.clone());
128 self.apply_event_to_active_buffer(&event);
129
130 let active = self.active_buffer();
132 if let Some((wait_id, true)) = self.wait_tracking.remove(&active) {
133 self.completed_waits.push(wait_id);
134 }
135
136 if let Some(handle) = self.hover.take_symbol_overlay() {
138 let remove_overlay_event = crate::model::event::Event::RemoveOverlay { handle };
139 self.apply_event_to_active_buffer(&remove_overlay_event);
140 }
141 self.hover.set_symbol_range(None);
142 }
143
144 pub(super) fn dismiss_transient_popups(&mut self) {
147 let is_transient_popup = self
150 .active_state()
151 .popups
152 .top()
153 .is_some_and(|p| p.transient);
154
155 if is_transient_popup {
156 self.hide_popup();
157 tracing::trace!("Dismissed transient popup");
158 }
159 }
160
161 pub(super) fn scroll_popup(&mut self, delta: i32) {
164 if let Some(popup) = self.global_popups.top_mut() {
165 popup.scroll_by(delta);
166 return;
167 }
168 if let Some(popup) = self.active_state_mut().popups.top_mut() {
169 popup.scroll_by(delta);
170 tracing::debug!(
171 "Scrolled popup by {}, new offset: {}",
172 delta,
173 popup.scroll_offset
174 );
175 }
176 }
177
178 pub(super) fn on_editor_focus_lost(&mut self) {
186 self.active_state_mut().on_focus_lost();
188
189 self.mouse_state.lsp_hover_state = None;
191 self.mouse_state.lsp_hover_request_sent = false;
192 self.hover.clear_pending();
193
194 if let Some(handle) = self.hover.take_symbol_overlay() {
196 let remove_overlay_event = crate::model::event::Event::RemoveOverlay { handle };
197 self.apply_event_to_active_buffer(&remove_overlay_event);
198 }
199 self.hover.set_symbol_range(None);
200
201 self.goto_line_preview = None;
205 }
206
207 pub fn clear_popups(&mut self) {
209 let event = Event::ClearPopups;
210 self.active_event_log_mut().append(event.clone());
211 self.apply_event_to_active_buffer(&event);
212 }
213
214 pub fn show_lsp_confirmation_popup(&mut self, language: &str) {
221 use crate::model::event::{
222 PopupContentData, PopupData, PopupKindHint, PopupListItemData, PopupPositionData,
223 };
224
225 let server_info = if let Some(lsp) = &self.lsp {
227 if let Some(config) = lsp.get_config(language) {
228 if !config.command.is_empty() {
229 format!("{} ({})", language, config.command)
230 } else {
231 language.to_string()
232 }
233 } else {
234 language.to_string()
235 }
236 } else {
237 language.to_string()
238 };
239
240 let popup = PopupData {
241 kind: PopupKindHint::List,
242 title: Some(format!("Start LSP Server: {}?", server_info)),
243 description: None,
244 transient: false,
245 content: PopupContentData::List {
246 items: vec![
247 PopupListItemData {
248 text: "Allow this time".to_string(),
249 detail: Some("Start the LSP server for this session".to_string()),
250 icon: None,
251 data: Some("allow_once".to_string()),
252 },
253 PopupListItemData {
254 text: "Always allow".to_string(),
255 detail: Some("Always start this LSP server automatically".to_string()),
256 icon: None,
257 data: Some("allow_always".to_string()),
258 },
259 PopupListItemData {
260 text: "Don't start".to_string(),
261 detail: Some("Cancel LSP server startup".to_string()),
262 icon: None,
263 data: Some("deny".to_string()),
264 },
265 ],
266 selected: 0,
267 },
268 position: PopupPositionData::Centered,
269 width: 50,
270 max_height: 8,
271 bordered: true,
272 };
273
274 self.show_popup_with_resolver(
278 popup,
279 crate::view::popup::PopupResolver::LspConfirm {
280 language: language.to_string(),
281 },
282 );
283 }
284
285 pub fn handle_lsp_confirmation_response(&mut self, language: &str, action: &str) -> bool {
295 let language = language.to_string();
296
297 let file_path = self
299 .buffer_metadata
300 .get(&self.active_buffer())
301 .and_then(|meta| meta.file_path().cloned());
302
303 match action {
304 "allow_once" => {
305 if let Some(lsp) = &mut self.lsp {
307 lsp.allow_language(&language);
309 if lsp.force_spawn(&language, file_path.as_deref()).is_some() {
311 tracing::info!("LSP server for {} started (allowed once)", language);
312 self.set_status_message(
313 t!("lsp.server_started", language = language).to_string(),
314 );
315 } else {
316 self.set_status_message(
317 t!("lsp.failed_to_start", language = language).to_string(),
318 );
319 }
320 }
321 self.notify_lsp_current_file_opened(&language);
323 }
324 "allow_always" => {
325 if let Some(lsp) = &mut self.lsp {
327 lsp.allow_language(&language);
328 if lsp.force_spawn(&language, file_path.as_deref()).is_some() {
330 tracing::info!("LSP server for {} started (always allowed)", language);
331 self.set_status_message(
332 t!("lsp.server_started_auto", language = language).to_string(),
333 );
334 } else {
335 self.set_status_message(
336 t!("lsp.failed_to_start", language = language).to_string(),
337 );
338 }
339 }
340 self.notify_lsp_current_file_opened(&language);
342 }
343 _ => {
344 tracing::info!("LSP server for {} startup declined by user", language);
346 self.set_status_message(
347 t!("lsp.startup_cancelled", language = language).to_string(),
348 );
349 }
350 }
351
352 true
353 }
354
355 fn notify_lsp_current_file_opened(&mut self, language: &str) {
360 let metadata = match self.buffer_metadata.get(&self.active_buffer()) {
362 Some(m) => m,
363 None => {
364 tracing::debug!(
365 "notify_lsp_current_file_opened: no metadata for buffer {:?}",
366 self.active_buffer()
367 );
368 return;
369 }
370 };
371
372 if !metadata.lsp_enabled {
373 tracing::debug!("notify_lsp_current_file_opened: LSP disabled for this buffer");
374 return;
375 }
376
377 let file_path = metadata.file_path().cloned();
379
380 let uri = match metadata.file_uri() {
382 Some(u) => u.clone(),
383 None => {
384 tracing::debug!(
385 "notify_lsp_current_file_opened: no URI for buffer (not a file or URI creation failed)"
386 );
387 return;
388 }
389 };
390
391 let active_buffer = self.active_buffer();
393
394 let file_language = match self.buffers.get(&active_buffer).map(|s| s.language.clone()) {
396 Some(l) => l,
397 None => {
398 tracing::debug!("notify_lsp_current_file_opened: no buffer state");
399 return;
400 }
401 };
402
403 if file_language != language {
405 tracing::debug!(
406 "notify_lsp_current_file_opened: file language {} doesn't match server {}",
407 file_language,
408 language
409 );
410 return;
411 }
412 let (text, line_count, buffer_version) =
413 if let Some(state) = self.buffers.get(&active_buffer) {
414 let text = match state.buffer.to_string() {
415 Some(t) => t,
416 None => {
417 tracing::debug!("notify_lsp_current_file_opened: buffer not fully loaded");
418 return;
419 }
420 };
421 let line_count = state.buffer.line_count().unwrap_or(1000);
422 (text, line_count, state.buffer.version())
423 } else {
424 tracing::debug!("notify_lsp_current_file_opened: no buffer state");
425 return;
426 };
427
428 if let Some(lsp) = &mut self.lsp {
430 if lsp.force_spawn(language, file_path.as_deref()).is_some() {
432 tracing::info!("Sending didOpen to LSP servers for: {}", uri.as_str());
433 let mut any_opened = false;
434 for sh in lsp.get_handles_mut(language) {
435 if let Err(e) = sh.handle.did_open(
436 uri.as_uri().clone(),
437 text.clone(),
438 file_language.clone(),
439 ) {
440 tracing::warn!("Failed to send didOpen to '{}': {}", sh.name, e);
441 } else {
442 any_opened = true;
443 }
444 }
445
446 if any_opened {
447 tracing::info!("Successfully sent didOpen to LSP after confirmation");
448
449 if let Some(handle) = lsp.get_handle_mut(language) {
451 let previous_result_id =
452 self.diagnostic_result_ids.get(uri.as_str()).cloned();
453 let request_id = self.next_lsp_request_id;
454 self.next_lsp_request_id += 1;
455
456 if let Err(e) = handle.document_diagnostic(
457 request_id,
458 uri.as_uri().clone(),
459 previous_result_id,
460 ) {
461 tracing::debug!(
462 "Failed to request pull diagnostics (server may not support): {}",
463 e
464 );
465 }
466
467 if self.config.editor.enable_inlay_hints {
469 let request_id = self.next_lsp_request_id;
470 self.next_lsp_request_id += 1;
471
472 let last_line = line_count.saturating_sub(1) as u32;
473 let last_char = 10000u32;
474
475 if let Err(e) = handle.inlay_hints(
476 request_id,
477 uri.as_uri().clone(),
478 0,
479 0,
480 last_line,
481 last_char,
482 ) {
483 tracing::debug!(
484 "Failed to request inlay hints (server may not support): {}",
485 e
486 );
487 } else {
488 self.pending_inlay_hints_requests.insert(
489 request_id,
490 super::InlayHintsRequest {
491 buffer_id: active_buffer,
492 version: buffer_version,
493 },
494 );
495 }
496 }
497 }
498 }
499 }
500 }
501 }
502
503 pub fn has_pending_lsp_confirmation(&self) -> bool {
508 use crate::view::popup::PopupResolver;
509 let matches_lsp_confirm = |p: &crate::view::popup::Popup| -> bool {
510 matches!(p.resolver, PopupResolver::LspConfirm { .. })
511 };
512 self.global_popups.top().is_some_and(matches_lsp_confirm)
513 || self
514 .active_state()
515 .popups
516 .top()
517 .is_some_and(matches_lsp_confirm)
518 }
519
520 pub fn popup_select_next(&mut self) {
522 if let Some(popup) = self.global_popups.top_mut() {
523 popup.select_next();
524 return;
525 }
526 let event = Event::PopupSelectNext;
527 self.active_event_log_mut().append(event.clone());
528 self.apply_event_to_active_buffer(&event);
529 }
530
531 pub fn popup_select_prev(&mut self) {
533 if let Some(popup) = self.global_popups.top_mut() {
534 popup.select_prev();
535 return;
536 }
537 let event = Event::PopupSelectPrev;
538 self.active_event_log_mut().append(event.clone());
539 self.apply_event_to_active_buffer(&event);
540 }
541
542 pub fn popup_page_down(&mut self) {
544 if let Some(popup) = self.global_popups.top_mut() {
545 popup.page_down();
546 return;
547 }
548 let event = Event::PopupPageDown;
549 self.active_event_log_mut().append(event.clone());
550 self.apply_event_to_active_buffer(&event);
551 }
552
553 pub fn popup_page_up(&mut self) {
555 if let Some(popup) = self.global_popups.top_mut() {
556 popup.page_up();
557 return;
558 }
559 let event = Event::PopupPageUp;
560 self.active_event_log_mut().append(event.clone());
561 self.apply_event_to_active_buffer(&event);
562 }
563}