Close #27 <img width="756" height="674" alt="image" src="https://github.com/user-attachments/assets/07884a7e-366b-47b8-8bd7-a88f3cae52b8" />
122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
use anyhow::Result;
|
|
use gpui::{App, Context, MouseMoveEvent, Task, Window};
|
|
use std::rc::Rc;
|
|
|
|
use crate::input::{popovers::ContextMenu, InputState, RopeExt};
|
|
|
|
mod code_actions;
|
|
mod completions;
|
|
mod definitions;
|
|
mod hover;
|
|
|
|
pub use code_actions::*;
|
|
pub use completions::*;
|
|
pub use definitions::*;
|
|
pub use hover::*;
|
|
|
|
/// LSP ServerCapabilities
|
|
///
|
|
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#serverCapabilities
|
|
pub struct Lsp {
|
|
/// The completion provider.
|
|
pub completion_provider: Option<Rc<dyn CompletionProvider>>,
|
|
/// The code action providers.
|
|
pub code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
|
|
/// The hover provider.
|
|
pub hover_provider: Option<Rc<dyn HoverProvider>>,
|
|
/// The definition provider.
|
|
pub definition_provider: Option<Rc<dyn DefinitionProvider>>,
|
|
_hover_task: Task<Result<()>>,
|
|
}
|
|
|
|
impl Default for Lsp {
|
|
fn default() -> Self {
|
|
Self {
|
|
completion_provider: None,
|
|
code_action_providers: vec![],
|
|
hover_provider: None,
|
|
definition_provider: None,
|
|
_hover_task: Task::ready(Ok(())),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl InputState {
|
|
pub(crate) fn hide_context_menu(&mut self, cx: &mut Context<Self>) {
|
|
self.context_menu = None;
|
|
self._context_menu_task = Task::ready(Ok(()));
|
|
cx.notify();
|
|
}
|
|
|
|
pub(crate) fn is_context_menu_open(&self, cx: &App) -> bool {
|
|
let Some(menu) = self.context_menu.as_ref() else {
|
|
return false;
|
|
};
|
|
|
|
menu.is_open(cx)
|
|
}
|
|
|
|
/// Handles an action for the completion menu, if it exists.
|
|
///
|
|
/// Return true if the action was handled, otherwise false.
|
|
pub fn handle_action_for_context_menu(
|
|
&mut self,
|
|
action: Box<dyn gpui::Action>,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let Some(menu) = self.context_menu.as_ref() else {
|
|
return false;
|
|
};
|
|
|
|
let mut handled = false;
|
|
|
|
match menu {
|
|
ContextMenu::Completion(menu) => {
|
|
_ = menu.update(cx, |menu, cx| {
|
|
handled = menu.handle_action(action, window, cx)
|
|
});
|
|
}
|
|
ContextMenu::CodeAction(menu) => {
|
|
_ = menu.update(cx, |menu, cx| {
|
|
handled = menu.handle_action(action, window, cx)
|
|
});
|
|
}
|
|
ContextMenu::MouseContext(..) => {}
|
|
};
|
|
|
|
handled
|
|
}
|
|
|
|
/// Apply a list of [`lsp_types::TextEdit`] to mutate the text.
|
|
pub fn apply_lsp_edits(
|
|
&mut self,
|
|
text_edits: &Vec<lsp_types::TextEdit>,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) {
|
|
for edit in text_edits {
|
|
let start = self.text.position_to_offset(&edit.range.start);
|
|
let end = self.text.position_to_offset(&edit.range.end);
|
|
|
|
let range_utf16 = self.range_to_utf16(&(start..end));
|
|
self.replace_text_in_range_silent(Some(range_utf16), &edit.new_text, window, cx);
|
|
}
|
|
}
|
|
|
|
pub(super) fn handle_mouse_move(
|
|
&mut self,
|
|
offset: usize,
|
|
event: &MouseMoveEvent,
|
|
window: &mut Window,
|
|
cx: &mut Context<InputState>,
|
|
) {
|
|
if event.modifiers.secondary() {
|
|
self.hover_popover = None;
|
|
self.handle_hover_definition(offset, window, cx);
|
|
} else {
|
|
self.hover_definition.clear();
|
|
self.handle_hover_popover(offset, window, cx);
|
|
}
|
|
}
|
|
}
|