225 lines · 8.8 KB · Rust
RawDownload
mod popover;mod state; use crate::actions::ShowSignatureHelp;use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp};use gpui::{AppContext, ViewContext};use language::markdown::parse_markdown;use multi_buffer::{Anchor, ToOffset};use settings::Settings;use std::ops::Range; pub use popover::SignatureHelpPopover;pub use state::SignatureHelpState; // Language-specific settings may define quotes as "brackets", so filter them out separately.const QUOTE_PAIRS: [(&str, &str); 3] = [("'", "'"), ("\"", "\""), ("`", "`")]; #[derive(Debug, Clone, Copy, PartialEq)]pub enum SignatureHelpHiddenBy {    AutoClose,    Escape,    Selection,} impl Editor {    pub fn toggle_auto_signature_help_menu(        &mut self,        _: &ToggleAutoSignatureHelp,        cx: &mut ViewContext<Self>,    ) {        self.auto_signature_help = self            .auto_signature_help            .map(|auto_signature_help| !auto_signature_help)            .or_else(|| Some(!EditorSettings::get_global(cx).auto_signature_help));        match self.auto_signature_help {            Some(auto_signature_help) if auto_signature_help => {                self.show_signature_help(&ShowSignatureHelp, cx);            }            Some(_) => {                self.hide_signature_help(cx, SignatureHelpHiddenBy::AutoClose);            }            None => {}        }        cx.notify();    }     pub(super) fn hide_signature_help(        &mut self,        cx: &mut ViewContext<Self>,        signature_help_hidden_by: SignatureHelpHiddenBy,    ) -> bool {        if self.signature_help_state.is_shown() {            self.signature_help_state.kill_task();            self.signature_help_state.hide(signature_help_hidden_by);            cx.notify();            true        } else {            false        }    }     pub fn auto_signature_help_enabled(&self, cx: &AppContext) -> bool {        if let Some(auto_signature_help) = self.auto_signature_help {            auto_signature_help        } else {            EditorSettings::get_global(cx).auto_signature_help        }    }     pub(super) fn should_open_signature_help_automatically(        &mut self,        old_cursor_position: &Anchor,        backspace_pressed: bool,        cx: &mut ViewContext<Self>,    ) -> bool {        if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {            return false;        }        let newest_selection = self.selections.newest::<usize>(cx);        let head = newest_selection.head();         // There are two cases where the head and tail of a selection are different: selecting multiple ranges and using backspace.        // If we don’t exclude the backspace case, signature_help will blink every time backspace is pressed, so we need to prevent this.        if !newest_selection.is_empty() && !backspace_pressed && head != newest_selection.tail() {            self.signature_help_state                .hide(SignatureHelpHiddenBy::Selection);            return false;        }         let buffer_snapshot = self.buffer().read(cx).snapshot(cx);        let bracket_range = |position: usize| match (position, position + 1) {            (0, b) if b <= buffer_snapshot.len() => 0..b,            (0, b) => 0..b - 1,            (a, b) if b <= buffer_snapshot.len() => a - 1..b,            (a, b) => a - 1..b - 1,        };        let not_quote_like_brackets = |start: Range<usize>, end: Range<usize>| {            let text = buffer_snapshot.text();            let (text_start, text_end) = (text.get(start), text.get(end));            QUOTE_PAIRS                .into_iter()                .all(|(start, end)| text_start != Some(start) && text_end != Some(end))        };         let previous_position = old_cursor_position.to_offset(&buffer_snapshot);        let previous_brackets_range = bracket_range(previous_position);        let previous_brackets_surround = buffer_snapshot            .innermost_enclosing_bracket_ranges(                previous_brackets_range,                Some(&not_quote_like_brackets),            )            .filter(|(start_bracket_range, end_bracket_range)| {                start_bracket_range.start != previous_position                    && end_bracket_range.end != previous_position            });        let current_brackets_range = bracket_range(head);        let current_brackets_surround = buffer_snapshot            .innermost_enclosing_bracket_ranges(                current_brackets_range,                Some(&not_quote_like_brackets),            )            .filter(|(start_bracket_range, end_bracket_range)| {                start_bracket_range.start != head && end_bracket_range.end != head            });         match (previous_brackets_surround, current_brackets_surround) {            (None, None) => {                self.signature_help_state                    .hide(SignatureHelpHiddenBy::AutoClose);                false            }            (Some(_), None) => {                self.signature_help_state                    .hide(SignatureHelpHiddenBy::AutoClose);                false            }            (None, Some(_)) => true,            (Some(previous), Some(current)) => {                let condition = self.signature_help_state.hidden_by_selection()                    || previous != current                    || (previous == current && self.signature_help_state.is_shown());                if !condition {                    self.signature_help_state                        .hide(SignatureHelpHiddenBy::AutoClose);                }                condition            }        }    }     pub fn show_signature_help(&mut self, _: &ShowSignatureHelp, cx: &mut ViewContext<Self>) {        if self.pending_rename.is_some() || self.has_active_completions_menu() {            return;        }         let position = self.selections.newest_anchor().head();        let Some((buffer, buffer_position)) =            self.buffer.read(cx).text_anchor_for_position(position, cx)        else {            return;        };         self.signature_help_state            .set_task(cx.spawn(move |editor, mut cx| async move {                let signature_help = editor                    .update(&mut cx, |editor, cx| {                        let language = editor.language_at(position, cx);                        let project = editor.project.clone()?;                        let (markdown, language_registry) = {                            project.update(cx, |project, cx| {                                let language_registry = project.languages().clone();                                (                                    project.signature_help(&buffer, buffer_position, cx),                                    language_registry,                                )                            })                        };                        Some((markdown, language_registry, language))                    })                    .ok()                    .flatten();                let signature_help_popover = if let Some((                    signature_help_task,                    language_registry,                    language,                )) = signature_help                {                    // TODO allow multiple signature helps inside the same popover                    if let Some(mut signature_help) = signature_help_task.await.into_iter().next() {                        let mut parsed_content = parse_markdown(                            signature_help.markdown.as_str(),                            &language_registry,                            language,                        )                        .await;                        parsed_content                            .highlights                            .append(&mut signature_help.highlights);                        Some(SignatureHelpPopover { parsed_content })                    } else {                        None                    }                } else {                    None                };                editor                    .update(&mut cx, |editor, cx| {                        let previous_popover = editor.signature_help_state.popover();                        if previous_popover != signature_help_popover.as_ref() {                            if let Some(signature_help_popover) = signature_help_popover {                                editor                                    .signature_help_state                                    .set_popover(signature_help_popover);                            } else {                                editor                                    .signature_help_state                                    .hide(SignatureHelpHiddenBy::AutoClose);                            }                            cx.notify();                        }                    })                    .ok();            }));    }}